Moved Var/VarDecl conversion out of IRGenerator
Change-Id: Ie2c39ede6ee10445091f2f71fd32a30a2d6a5e7c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457121
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 2871d58..7be396e 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -65,142 +65,6 @@
IRGenerator::IRGenerator(const Context* context)
: fContext(*context) {}
-void IRGenerator::checkVarDeclaration(int line, const Modifiers& modifiers, const Type* baseType,
- Variable::Storage storage) {
- if (this->strictES2Mode() && baseType->isArray()) {
- this->errorReporter().error(line, "array size must appear after variable name");
- }
-
- if (baseType->componentType().isOpaque() && storage != Variable::Storage::kGlobal) {
- this->errorReporter().error(
- line,
- "variables of type '" + baseType->displayName() + "' must be global");
- }
- if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
- this->errorReporter().error(line, "'in' variables may not have matrix type");
- }
- if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
- this->errorReporter().error(line, "'in uniform' variables not permitted");
- }
- if (this->isRuntimeEffect()) {
- if (modifiers.fFlags & Modifiers::kIn_Flag) {
- this->errorReporter().error(line, "'in' variables not permitted in runtime effects");
- }
- }
- if (baseType->isEffectChild() && !(modifiers.fFlags & Modifiers::kUniform_Flag)) {
- this->errorReporter().error(
- line, "variables of type '" + baseType->displayName() + "' must be uniform");
- }
- if (modifiers.fLayout.fFlags & Layout::kSRGBUnpremul_Flag) {
- if (!this->isRuntimeEffect()) {
- this->errorReporter().error(line,
- "'srgb_unpremul' is only permitted in runtime effects");
- }
- if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
- this->errorReporter().error(line,
- "'srgb_unpremul' is only permitted on 'uniform' variables");
- }
- auto validColorXformType = [](const Type& t) {
- return t.isVector() && t.componentType().isFloat() &&
- (t.columns() == 3 || t.columns() == 4);
- };
- if (!validColorXformType(*baseType) && !(baseType->isArray() &&
- validColorXformType(baseType->componentType()))) {
- this->errorReporter().error(line,
- "'srgb_unpremul' is only permitted on half3, half4, "
- "float3, or float4 variables");
- }
- }
- int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
- Modifiers::kLowp_Flag;
- if (storage == Variable::Storage::kGlobal) {
- permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag | Modifiers::kUniform_Flag |
- Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
- }
- // TODO(skbug.com/11301): Migrate above checks into building a mask of permitted layout flags
- CheckModifiers(fContext, line, modifiers, permitted, /*permittedLayoutFlags=*/~0);
-}
-
-std::unique_ptr<Variable> IRGenerator::convertVar(int line, const Modifiers& modifiers,
- const Type* baseType, skstd::string_view name,
- bool isArray,
- std::unique_ptr<Expression> arraySize,
- Variable::Storage storage) {
- if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 &&
- (modifiers.fFlags & Modifiers::kOut_Flag) &&
- this->programKind() == ProgramKind::kFragment && name != Compiler::FRAGCOLOR_NAME) {
- this->errorReporter().error(line,
- "out location=0, index=0 is reserved for sk_FragColor");
- }
- const Type* type = baseType;
- int arraySizeValue = 0;
- if (isArray) {
- SkASSERT(arraySize);
- arraySizeValue = type->convertArraySize(fContext, std::move(arraySize));
- if (!arraySizeValue) {
- return {};
- }
- type = fSymbolTable->addArrayDimension(type, arraySizeValue);
- }
- return std::make_unique<Variable>(line, this->modifiersPool().add(modifiers), name,
- type, fContext.fConfig->fIsBuiltinCode, storage);
-}
-
-std::unique_ptr<Statement> IRGenerator::convertVarDeclaration(std::unique_ptr<Variable> var,
- std::unique_ptr<Expression> value,
- bool addToSymbolTable) {
- std::unique_ptr<Statement> varDecl = VarDeclaration::Convert(fContext, var.get(),
- std::move(value));
- if (!varDecl) {
- return nullptr;
- }
-
- // Detect the declaration of magical variables.
- if ((var->storage() == Variable::Storage::kGlobal) && var->name() == Compiler::FRAGCOLOR_NAME) {
- // Silently ignore duplicate definitions of `sk_FragColor`.
- const Symbol* symbol = (*fSymbolTable)[var->name()];
- if (symbol) {
- return nullptr;
- }
- } else if ((var->storage() == Variable::Storage::kGlobal ||
- var->storage() == Variable::Storage::kInterfaceBlock) &&
- var->name() == Compiler::RTADJUST_NAME) {
- // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
- if (fRTAdjust) {
- this->errorReporter().error(var->fLine, "duplicate definition of 'sk_RTAdjust'");
- return nullptr;
- }
- if (var->type() != *fContext.fTypes.fFloat4) {
- this->errorReporter().error(var->fLine, "sk_RTAdjust must have type 'float4'");
- return nullptr;
- }
- fRTAdjust = var.get();
- }
-
- if (addToSymbolTable) {
- fSymbolTable->add(std::move(var));
- } else {
- fSymbolTable->takeOwnershipOfSymbol(std::move(var));
- }
- return varDecl;
-}
-
-std::unique_ptr<Statement> IRGenerator::convertVarDeclaration(int line,
- const Modifiers& modifiers,
- const Type* baseType,
- skstd::string_view name,
- bool isArray,
- std::unique_ptr<Expression> arraySize,
- std::unique_ptr<Expression> value,
- Variable::Storage storage) {
- std::unique_ptr<Variable> var = this->convertVar(line, modifiers, baseType, name, isArray,
- std::move(arraySize), storage);
- if (!var) {
- return nullptr;
- }
- return this->convertVarDeclaration(std::move(var), std::move(value));
-}
-
void IRGenerator::appendRTAdjustFixupToVertexMain(const FunctionDeclaration& decl, Block* body) {
using namespace SkSL::dsl;
using SkSL::dsl::Swizzle; // disambiguate from SkSL::Swizzle
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 992016e..73e1ec9 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -94,6 +94,7 @@
int getRTAdjustFieldIndex() { return fRTAdjustFieldIndex; }
const Context& fContext;
+ const Variable* fRTAdjust = nullptr;
private:
void start(const ParsedModule& base,
@@ -102,23 +103,6 @@
IRGenerator::IRBundle finish();
- void checkVarDeclaration(int line,
- const Modifiers& modifiers,
- const Type* baseType,
- Variable::Storage storage);
- std::unique_ptr<Variable> convertVar(int line, const Modifiers& modifiers,
- const Type* baseType, skstd::string_view name,
- bool isArray, std::unique_ptr<Expression> arraySize,
- Variable::Storage storage);
- std::unique_ptr<Statement> convertVarDeclaration(std::unique_ptr<Variable> var,
- std::unique_ptr<Expression> value,
- bool addToSymbolTable = true);
- std::unique_ptr<Statement> convertVarDeclaration(int line, const Modifiers& modifiers,
- const Type* baseType, skstd::string_view name,
- bool isArray,
- std::unique_ptr<Expression> arraySize,
- std::unique_ptr<Expression> value,
- Variable::Storage storage);
void scanInterfaceBlock(SkSL::InterfaceBlock& intf);
/** Appends sk_Position fixup to the bottom of main() if this is a vertex program. */
void appendRTAdjustFixupToVertexMain(const FunctionDeclaration& decl, Block* body);
@@ -147,7 +131,6 @@
std::unordered_set<const Type*> fDefinedStructs;
std::vector<std::unique_ptr<ProgramElement>>* fProgramElements = nullptr;
std::vector<const ProgramElement*>* fSharedElements = nullptr;
- const Variable* fRTAdjust = nullptr;
const Variable* fRTAdjustInterfaceBlock = nullptr;
int fRTAdjustFieldIndex;
diff --git a/src/sksl/dsl/DSLCore.cpp b/src/sksl/dsl/DSLCore.cpp
index 1e73307..e8750af 100644
--- a/src/sksl/dsl/DSLCore.cpp
+++ b/src/sksl/dsl/DSLCore.cpp
@@ -225,7 +225,7 @@
if (baseType->isArray()) {
baseType = &baseType->componentType();
}
- ThreadContext::IRGenerator().checkVarDeclaration(pos.line(),
+ SkSL::VarDeclaration::ErrorCheck(ThreadContext::Context(), pos.line(),
field.fModifiers.fModifiers, baseType, Variable::Storage::kInterfaceBlock);
GetErrorReporter().reportPendingErrors(field.fPosition);
skslFields.push_back(SkSL::Type::Field(field.fModifiers.fModifiers, field.fName,
diff --git a/src/sksl/dsl/priv/DSLWriter.cpp b/src/sksl/dsl/priv/DSLWriter.cpp
index faa6f1f..d180154 100644
--- a/src/sksl/dsl/priv/DSLWriter.cpp
+++ b/src/sksl/dsl/priv/DSLWriter.cpp
@@ -47,10 +47,8 @@
if (baseType->isArray()) {
baseType = &baseType->componentType();
}
- ThreadContext::IRGenerator().checkVarDeclaration(var.fPosition.line(),
- var.fModifiers.fModifiers, baseType, var.storage());
}
- std::unique_ptr<SkSL::Variable> skslvar = ThreadContext::IRGenerator().convertVar(
+ std::unique_ptr<SkSL::Variable> skslvar = SkSL::Variable::Convert(ThreadContext::Context(),
var.fPosition.line(), var.fModifiers.fModifiers, &var.fType.skslType(), var.fName,
/*isArray=*/false, /*arraySize=*/nullptr, var.storage());
SkSL::Variable* varPtr = skslvar.get();
@@ -62,9 +60,8 @@
// FunctionDeclaration is created which makes this the wrong spot for them, and outside
// of DSLParser we don't even need DSL variables to show up in the symbol table in the
// first place.
- var.fDeclaration = ThreadContext::IRGenerator().convertVarDeclaration(
- std::move(skslvar), var.fInitialValue.releaseIfPossible(),
- /*addToSymbolTable=*/false);
+ var.fDeclaration = VarDeclaration::Convert(ThreadContext::Context(), std::move(skslvar),
+ var.fInitialValue.releaseIfPossible(), /*addToSymbolTable=*/false);
if (var.fDeclaration) {
var.fVar = varPtr;
var.fInitialized = true;
@@ -79,9 +76,9 @@
// This should only be called on undeclared parameter variables, but we allow the creation to go
// ahead regardless so we don't have to worry about null pointers potentially sneaking in and
// breaking things. DSLFunction is responsible for reporting errors for invalid parameters.
- return ThreadContext::IRGenerator().convertVar(var.fPosition.line(), var.fModifiers.fModifiers,
- &var.fType.skslType(), var.fName, /*isArray=*/false, /*arraySize=*/nullptr,
- var.storage());
+ return SkSL::Variable::Convert(ThreadContext::Context(), var.fPosition.line(),
+ var.fModifiers.fModifiers, &var.fType.skslType(), var.fName, /*isArray=*/false,
+ /*arraySize=*/nullptr, var.storage());
}
std::unique_ptr<SkSL::Statement> DSLWriter::Declaration(DSLVarBase& var) {
diff --git a/src/sksl/ir/SkSLVarDeclarations.cpp b/src/sksl/ir/SkSLVarDeclarations.cpp
index 537001d..d63397a 100644
--- a/src/sksl/ir/SkSLVarDeclarations.cpp
+++ b/src/sksl/ir/SkSLVarDeclarations.cpp
@@ -9,8 +9,11 @@
#include "include/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLAnalysis.h"
+#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
+#include "src/sksl/SkSLIRGenerator.h"
#include "src/sksl/SkSLProgramSettings.h"
+#include "src/sksl/SkSLThreadContext.h"
namespace SkSL {
@@ -34,74 +37,169 @@
return result;
}
-std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
- Variable* var,
- std::unique_ptr<Expression> value) {
- if (value) {
- if (var->type().isOpaque()) {
- context.fErrors->error(value->fLine, "opaque type '" + var->type().name() +
- "' cannot use initializer expressions");
- return nullptr;
- }
- if (var->modifiers().fFlags & Modifiers::kIn_Flag) {
- context.fErrors->error(value->fLine,
- "'in' variables cannot use initializer expressions");
- return nullptr;
- }
- if (var->modifiers().fFlags & Modifiers::kUniform_Flag) {
- context.fErrors->error(value->fLine,
- "'uniform' variables cannot use initializer expressions");
- return nullptr;
- }
- if (var->storage() == Variable::Storage::kInterfaceBlock) {
- context.fErrors->error(value->fLine,
- "initializers are not permitted on interface block fields");
- return nullptr;
- }
- value = var->type().coerceExpression(std::move(value), context);
- if (!value) {
- return nullptr;
+void VarDeclaration::ErrorCheck(const Context& context, int line, const Modifiers& modifiers,
+ const Type* baseType, Variable::Storage storage) {
+ if (context.fConfig->strictES2Mode() && baseType->isArray()) {
+ context.fErrors->error(line, "array size must appear after variable name");
+ }
+
+ if (baseType->componentType().isOpaque() && storage != Variable::Storage::kGlobal) {
+ context.fErrors->error(line,
+ "variables of type '" + baseType->displayName() + "' must be global");
+ }
+ if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
+ context.fErrors->error(line, "'in' variables may not have matrix type");
+ }
+ if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
+ context.fErrors->error(line, "'in uniform' variables not permitted");
+ }
+ if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
+ if (modifiers.fFlags & Modifiers::kIn_Flag) {
+ context.fErrors->error(line, "'in' variables not permitted in runtime effects");
}
}
- if (var->modifiers().fFlags & Modifiers::kConst_Flag) {
+ if (baseType->isEffectChild() && !(modifiers.fFlags & Modifiers::kUniform_Flag)) {
+ context.fErrors->error(line,
+ "variables of type '" + baseType->displayName() + "' must be uniform");
+ }
+ if (modifiers.fLayout.fFlags & Layout::kSRGBUnpremul_Flag) {
+ if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
+ context.fErrors->error(line, "'srgb_unpremul' is only permitted in runtime effects");
+ }
+ if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
+ context.fErrors->error(line,
+ "'srgb_unpremul' is only permitted on 'uniform' variables");
+ }
+ auto validColorXformType = [](const Type& t) {
+ return t.isVector() && t.componentType().isFloat() &&
+ (t.columns() == 3 || t.columns() == 4);
+ };
+ if (!validColorXformType(*baseType) && !(baseType->isArray() &&
+ validColorXformType(baseType->componentType()))) {
+ context.fErrors->error(line, "'srgb_unpremul' is only permitted on half3, half4, "
+ "float3, or float4 variables");
+ }
+ }
+ int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
+ Modifiers::kLowp_Flag;
+ if (storage == Variable::Storage::kGlobal) {
+ permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag | Modifiers::kUniform_Flag |
+ Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
+ }
+ // TODO(skbug.com/11301): Migrate above checks into building a mask of permitted layout flags
+ IRGenerator::CheckModifiers(context, line, modifiers, permitted, /*permittedLayoutFlags=*/~0);
+}
+
+bool VarDeclaration::ErrorCheckAndCoerce(const Context& context, const Variable& var,
+ std::unique_ptr<Expression>& value) {
+ const Type* baseType = &var.type();
+ if (baseType->isArray()) {
+ baseType = &baseType->componentType();
+ }
+ ErrorCheck(context, var.fLine, var.modifiers(), baseType, var.storage());
+ if (value) {
+ if (var.type().isOpaque()) {
+ context.fErrors->error(value->fLine,
+ "opaque type '" + var.type().name() + "' cannot use initializer expressions");
+ return false;
+ }
+ if (var.modifiers().fFlags & Modifiers::kIn_Flag) {
+ context.fErrors->error(value->fLine,
+ "'in' variables cannot use initializer expressions");
+ return false;
+ }
+ if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
+ context.fErrors->error(value->fLine,
+ "'uniform' variables cannot use initializer expressions");
+ return false;
+ }
+ if (var.storage() == Variable::Storage::kInterfaceBlock) {
+ context.fErrors->error(value->fLine,
+ "initializers are not permitted on interface block fields");
+ return false;
+ }
+ value = var.type().coerceExpression(std::move(value), context);
if (!value) {
- context.fErrors->error(var->fLine, "'const' variables must be initialized");
- return nullptr;
+ return false;
+ }
+ }
+ if (var.modifiers().fFlags & Modifiers::kConst_Flag) {
+ if (!value) {
+ context.fErrors->error(var.fLine, "'const' variables must be initialized");
+ return false;
}
if (!Analysis::IsConstantExpression(*value)) {
context.fErrors->error(value->fLine,
- "'const' variable initializer must be a constant expression");
- return nullptr;
+ "'const' variable initializer must be a constant expression");
+ return false;
}
}
- if (var->storage() == Variable::Storage::kInterfaceBlock) {
- if (var->type().isOpaque()) {
- context.fErrors->error(var->fLine, "opaque type '" + var->type().name() +
- "' is not permitted in an interface block");
- return nullptr;
+ if (var.storage() == Variable::Storage::kInterfaceBlock) {
+ if (var.type().isOpaque()) {
+ context.fErrors->error(var.fLine, "opaque type '" + var.type().name() +
+ "' is not permitted in an interface block");
+ return false;
}
}
- if (var->storage() == Variable::Storage::kGlobal) {
+ if (var.storage() == Variable::Storage::kGlobal) {
if (value && !Analysis::IsConstantExpression(*value)) {
context.fErrors->error(value->fLine,
- "global variable initializer must be a constant expression");
- return nullptr;
+ "global variable initializer must be a constant expression");
+ return false;
}
}
+ return true;
+}
+
+std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
+ std::unique_ptr<Variable> var, std::unique_ptr<Expression> value, bool addToSymbolTable) {
+ if (!ErrorCheckAndCoerce(context, *var, value)) {
+ return nullptr;
+ }
const Type* baseType = &var->type();
int arraySize = 0;
if (baseType->isArray()) {
arraySize = baseType->columns();
baseType = &baseType->componentType();
}
- return VarDeclaration::Make(context, var, baseType, arraySize, std::move(value));
+ std::unique_ptr<Statement> varDecl = VarDeclaration::Make(context, var.get(), baseType,
+ arraySize, std::move(value));
+ if (!varDecl) {
+ return nullptr;
+ }
+
+ // Detect the declaration of magical variables.
+ if ((var->storage() == Variable::Storage::kGlobal) && var->name() == Compiler::FRAGCOLOR_NAME) {
+ // Silently ignore duplicate definitions of `sk_FragColor`.
+ const Symbol* symbol = (*ThreadContext::SymbolTable())[var->name()];
+ if (symbol) {
+ return nullptr;
+ }
+ } else if ((var->storage() == Variable::Storage::kGlobal ||
+ var->storage() == Variable::Storage::kInterfaceBlock) &&
+ var->name() == Compiler::RTADJUST_NAME) {
+ // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
+ if (ThreadContext::IRGenerator().fRTAdjust) {
+ context.fErrors->error(var->fLine, "duplicate definition of 'sk_RTAdjust'");
+ return nullptr;
+ }
+ if (var->type() != *context.fTypes.fFloat4) {
+ context.fErrors->error(var->fLine, "sk_RTAdjust must have type 'float4'");
+ return nullptr;
+ }
+ ThreadContext::IRGenerator().fRTAdjust = var.get();
+ }
+
+ if (addToSymbolTable) {
+ ThreadContext::SymbolTable()->add(std::move(var));
+ } else {
+ ThreadContext::SymbolTable()->takeOwnershipOfSymbol(std::move(var));
+ }
+ return varDecl;
}
-std::unique_ptr<Statement> VarDeclaration::Make(const Context& context,
- Variable* var,
- const Type* baseType,
- int arraySize,
- std::unique_ptr<Expression> value) {
+std::unique_ptr<Statement> VarDeclaration::Make(const Context& context, Variable* var,
+ const Type* baseType, int arraySize, std::unique_ptr<Expression> value) {
SkASSERT(!baseType->isArray());
// function parameters cannot have variable declarations
SkASSERT(var->storage() != Variable::Storage::kParameter);
diff --git a/src/sksl/ir/SkSLVarDeclarations.h b/src/sksl/ir/SkSLVarDeclarations.h
index e93bf87..aeb445e 100644
--- a/src/sksl/ir/SkSLVarDeclarations.h
+++ b/src/sksl/ir/SkSLVarDeclarations.h
@@ -45,10 +45,15 @@
}
}
+ // Checks the modifiers, baseType, and storage for compatibility with one another and reports
+ // errors if needed. This method is implicitly called during Convert(), but is also explicitly
+ // called while processing interface block fields.
+ static void ErrorCheck(const Context& context, int line, const Modifiers& modifiers,
+ const Type* baseType, Variable::Storage storage);
+
// Does proper error checking and type coercion; reports errors via ErrorReporter.
- static std::unique_ptr<Statement> Convert(const Context& context,
- Variable* var,
- std::unique_ptr<Expression> value);
+ static std::unique_ptr<Statement> Convert(const Context& context, std::unique_ptr<Variable> var,
+ std::unique_ptr<Expression> value, bool addToSymbolTable = true);
// Reports errors via ASSERT.
static std::unique_ptr<Statement> Make(const Context& context,
@@ -87,6 +92,9 @@
String description() const override;
private:
+ static bool ErrorCheckAndCoerce(const Context& context, const Variable& var,
+ std::unique_ptr<Expression>& value);
+
const Variable* fVar;
const Type& fBaseType;
int fArraySize; // zero means "not an array"
diff --git a/src/sksl/ir/SkSLVariable.cpp b/src/sksl/ir/SkSLVariable.cpp
index 0179af9..55b4e96 100644
--- a/src/sksl/ir/SkSLVariable.cpp
+++ b/src/sksl/ir/SkSLVariable.cpp
@@ -7,8 +7,11 @@
#include "src/sksl/ir/SkSLVariable.h"
+#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLMangler.h"
+#include "src/sksl/SkSLProgramSettings.h"
+#include "src/sksl/SkSLThreadContext.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
@@ -25,6 +28,34 @@
return fDeclaration ? fDeclaration->value().get() : nullptr;
}
+std::unique_ptr<Variable> Variable::Convert(const Context& context, int line,
+ const Modifiers& modifiers, const Type* baseType, skstd::string_view name, bool isArray,
+ std::unique_ptr<Expression> arraySize, Variable::Storage storage) {
+ if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 &&
+ (modifiers.fFlags & Modifiers::kOut_Flag) &&
+ context.fConfig->fKind == ProgramKind::kFragment && name != Compiler::FRAGCOLOR_NAME) {
+ context.fErrors->error(line, "out location=0, index=0 is reserved for sk_FragColor");
+ }
+ return Make(context, line, modifiers, baseType, name, isArray, std::move(arraySize), storage);
+}
+
+std::unique_ptr<Variable> Variable::Make(const Context& context, int line,
+ const Modifiers& modifiers, const Type* baseType, skstd::string_view name, bool isArray,
+ std::unique_ptr<Expression> arraySize, Variable::Storage storage) {
+ const Type* type = baseType;
+ int arraySizeValue = 0;
+ if (isArray) {
+ SkASSERT(arraySize);
+ arraySizeValue = type->convertArraySize(context, std::move(arraySize));
+ if (!arraySizeValue) {
+ return nullptr;
+ }
+ type = ThreadContext::SymbolTable()->addArrayDimension(type, arraySizeValue);
+ }
+ return std::make_unique<Variable>(line, context.fModifiersPool->add(modifiers), name, type,
+ context.fConfig->fIsBuiltinCode, storage);
+}
+
Variable::ScratchVariable Variable::MakeScratchVariable(const Context& context,
skstd::string_view baseName,
const Type* type,
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
index 1a01b6e..c2b4957 100644
--- a/src/sksl/ir/SkSLVariable.h
+++ b/src/sksl/ir/SkSLVariable.h
@@ -51,6 +51,13 @@
~Variable() override;
+ static std::unique_ptr<Variable> Convert(const Context& context, int line,
+ const Modifiers& modifiers, const Type* baseType, skstd::string_view name, bool isArray,
+ std::unique_ptr<Expression> arraySize, Variable::Storage storage);
+
+ static std::unique_ptr<Variable> Make(const Context& context, int line,
+ const Modifiers& modifiers, const Type* baseType, skstd::string_view name, bool isArray,
+ std::unique_ptr<Expression> arraySize, Variable::Storage storage);
/**
* Creates a local scratch variable and the associated VarDeclaration statement.
* Useful when doing IR rewrites, e.g. inlining a function call.