Add Variable::fBuiltin, to track Variables owned by pre-includes
Similar to the same field on Enum and FunctionDeclaration, will be used
to facilitate cloning builtin variables into Programs that use them.
Bug: skia:10589
Change-Id: Ic63701c61ee4658a5ec72adb506cc96aa0b2836f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/321196
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 2b9bed1..b757c8d 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -240,11 +240,13 @@
StringFragment fpAliasName("shader");
fRootSymbolTable->addWithoutOwnership(fpAliasName, fContext->fFragmentProcessor_Type.get());
+ // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
+ // treat it as builtin (ie, no need to clone it into the Program).
StringFragment skCapsName("sk_Caps");
- fRootSymbolTable->add(
- skCapsName,
- std::make_unique<Variable>(/*offset=*/-1, Modifiers(), skCapsName,
- fContext->fSkCaps_Type.get(), Variable::kGlobal_Storage));
+ fRootSymbolTable->add(skCapsName,
+ std::make_unique<Variable>(/*offset=*/-1, Modifiers(), skCapsName,
+ fContext->fSkCaps_Type.get(),
+ /*builtin=*/false, Variable::kGlobal_Storage));
fIRGenerator->fIntrinsics = fGPUIntrinsics.get();
std::vector<std::unique_ptr<ProgramElement>> gpuIntrinsics;
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 6a418d9..fa798ac 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -441,7 +441,7 @@
}
}
auto var = std::make_unique<Variable>(varDecl.fOffset, modifiers, varData.fName, type,
- storage);
+ fIsBuiltinCode, storage);
if (var->fName == Compiler::RTADJUST_NAME) {
SkASSERT(!fRTAdjust);
SkASSERT(var->type() == *fContext.fFloat4_Type);
@@ -952,8 +952,9 @@
return;
}
StringFragment name = pd.fName;
- const Variable* var = fSymbolTable->takeOwnershipOfSymbol(std::make_unique<Variable>(
- param.fOffset, pd.fModifiers, name, type, Variable::kParameter_Storage));
+ const Variable* var = fSymbolTable->takeOwnershipOfSymbol(
+ std::make_unique<Variable>(param.fOffset, pd.fModifiers, name, type,
+ fIsBuiltinCode, Variable::kParameter_Storage));
parameters.push_back(var);
}
@@ -1185,6 +1186,7 @@
id.fModifiers,
id.fInstanceName.fLength ? id.fInstanceName : id.fTypeName,
type,
+ fIsBuiltinCode,
Variable::kGlobal_Storage));
if (foundRTAdjust) {
fRTAdjustInterfaceBlock = var;
@@ -1253,9 +1255,10 @@
}
value = std::unique_ptr<Expression>(new IntLiteral(fContext, e.fOffset, currentValue));
++currentValue;
- fSymbolTable->add(child.getString(),
- std::make_unique<Variable>(e.fOffset, modifiers, child.getString(), type,
- Variable::kGlobal_Storage, value.get()));
+ fSymbolTable->add(
+ child.getString(),
+ std::make_unique<Variable>(e.fOffset, modifiers, child.getString(), type,
+ fIsBuiltinCode, Variable::kGlobal_Storage, value.get()));
fSymbolTable->takeOwnershipOfIRNode(std::move(value));
}
// Now we orphanize the Enum's symbol table, so that future lookups in it are strict
@@ -2112,7 +2115,8 @@
auto funcCall = std::make_unique<FunctionCall>(offset, returnType, function,
std::move(arguments));
if (fCanInline && fInliner->isSafeToInline(*funcCall, fSettings->fInlineThreshold)) {
- Inliner::InlinedCall inlinedCall = fInliner->inlineCall(funcCall.get(), fSymbolTable.get());
+ Inliner::InlinedCall inlinedCall =
+ fInliner->inlineCall(funcCall.get(), fSymbolTable.get(), fCurrentFunction);
if (inlinedCall.fInlinedBody) {
fExtraStatements.push_back(std::move(inlinedCall.fInlinedBody));
}
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index c178f46..85c9439 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -427,11 +427,12 @@
SymbolTable* symbolTableForStatement,
const Expression* resultExpr,
bool haveEarlyReturns,
- const Statement& statement) {
+ const Statement& statement,
+ bool isBuiltinCode) {
auto stmt = [&](const std::unique_ptr<Statement>& s) -> std::unique_ptr<Statement> {
if (s) {
return this->inlineStatement(offset, varMap, symbolTableForStatement, resultExpr,
- haveEarlyReturns, *s);
+ haveEarlyReturns, *s, isBuiltinCode);
}
return nullptr;
};
@@ -548,6 +549,7 @@
old->fModifiers,
namePtr->c_str(),
typePtr,
+ isBuiltinCode,
old->fStorage,
initialValue.get()));
(*varMap)[old] = std::make_unique<VariableReference>(offset, clone);
@@ -575,7 +577,8 @@
}
Inliner::InlinedCall Inliner::inlineCall(FunctionCall* call,
- SymbolTable* symbolTableForCall) {
+ SymbolTable* symbolTableForCall,
+ const FunctionDeclaration* caller) {
// Inlining is more complicated here than in a typical compiler, because we have to have a
// high-level IR and can't just drop statements into the middle of an expression or even use
// gotos.
@@ -632,7 +635,8 @@
// Add our new variable to the symbol table.
auto newVar = std::make_unique<Variable>(/*offset=*/-1, Modifiers(), nameFrag, type,
- Variable::kLocal_Storage, initialValue->get());
+ caller->fBuiltin, Variable::kLocal_Storage,
+ initialValue->get());
const Variable* variableSymbol = symbolTableForCall->add(nameFrag, std::move(newVar));
// Prepare the variable declaration (taking extra care with `out` params to not clobber any
@@ -694,8 +698,9 @@
auto inlineBlock = std::make_unique<Block>(offset, std::vector<std::unique_ptr<Statement>>{});
inlineBlock->children().reserve(body.children().size());
for (const std::unique_ptr<Statement>& stmt : body.children()) {
- inlineBlock->children().push_back(this->inlineStatement(
- offset, &varMap, symbolTableForCall, resultExpr.get(), hasEarlyReturn, *stmt));
+ inlineBlock->children().push_back(this->inlineStatement(offset, &varMap, symbolTableForCall,
+ resultExpr.get(), hasEarlyReturn,
+ *stmt, caller->fBuiltin));
}
if (hasEarlyReturn) {
// Since we output to backends that don't have a goto statement (which would normally be
@@ -780,6 +785,7 @@
Statement* fParentStmt; // the parent Statement of the enclosing stmt
std::unique_ptr<Statement>* fEnclosingStmt; // the Statement containing the candidate
std::unique_ptr<Expression>* fCandidateExpr; // the candidate FunctionCall to be inlined
+ FunctionDefinition* fEnclosingFunction; // the Function containing the candidate
};
// This is structured much like a ProgramVisitor, but does not actually use ProgramVisitor.
@@ -797,6 +803,8 @@
// adding new instructions. Not all statements are suitable (e.g. a for-loop's initializer).
// The inliner might replace a statement with a block containing the statement.
std::vector<std::unique_ptr<Statement>*> fEnclosingStmtStack;
+ // The function that we're currently processing (i.e. inlining into).
+ FunctionDefinition* fEnclosingFunction = nullptr;
void visit(Program& program) {
fSymbolTableStack.push_back(program.fSymbols.get());
@@ -812,6 +820,7 @@
switch (pe->kind()) {
case ProgramElement::Kind::kFunction: {
FunctionDefinition& funcDef = pe->as<FunctionDefinition>();
+ fEnclosingFunction = &funcDef;
this->visitStatement(&funcDef.fBody);
break;
}
@@ -1065,7 +1074,8 @@
fInlineCandidates.push_back(InlineCandidate{fSymbolTableStack.back(),
find_parent_statement(fEnclosingStmtStack),
fEnclosingStmtStack.back(),
- candidate});
+ candidate,
+ fEnclosingFunction});
}
};
@@ -1108,7 +1118,8 @@
}
// Convert the function call to its inlined equivalent.
- InlinedCall inlinedCall = this->inlineCall(&funcCall, candidate.fSymbols);
+ InlinedCall inlinedCall = this->inlineCall(&funcCall, candidate.fSymbols,
+ &candidate.fEnclosingFunction->fDeclaration);
if (inlinedCall.fInlinedBody) {
// Ensure that the inlined body has a scope if it needs one.
this->ensureScopedBlocks(inlinedCall.fInlinedBody.get(), candidate.fParentStmt);
diff --git a/src/sksl/SkSLInliner.h b/src/sksl/SkSLInliner.h
index e1fc65d..6d95ecf 100644
--- a/src/sksl/SkSLInliner.h
+++ b/src/sksl/SkSLInliner.h
@@ -45,7 +45,7 @@
std::unique_ptr<Block> fInlinedBody;
std::unique_ptr<Expression> fReplacementExpr;
};
- InlinedCall inlineCall(FunctionCall*, SymbolTable*);
+ InlinedCall inlineCall(FunctionCall*, SymbolTable*, const FunctionDeclaration* caller);
/** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */
void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt);
@@ -69,7 +69,8 @@
SymbolTable* symbolTableForStatement,
const Expression* resultExpr,
bool haveEarlyReturns,
- const Statement& statement);
+ const Statement& statement,
+ bool isBuiltinCode);
const Context* fContext = nullptr;
const Program::Settings* fSettings = nullptr;
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index d972957..66a8ffc 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -231,8 +231,8 @@
StringFragment name = this->readString();
const Type* type = this->type();
Variable::Storage storage = (Variable::Storage) this->readU8();
- const Variable* result = fSymbolTable->takeOwnershipOfSymbol(
- std::make_unique<Variable>(/*offset=*/-1, m, name, type, storage));
+ const Variable* result = fSymbolTable->takeOwnershipOfSymbol(std::make_unique<Variable>(
+ /*offset=*/-1, m, name, type, /*builtin=*/true, storage));
this->addSymbol(id, result);
return result;
}
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 38a58d8..81bcb1b 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -1875,6 +1875,7 @@
Modifiers(layout, Modifiers::kUniform_Flag),
name,
&intfStruct,
+ /*builtin=*/false,
Variable::kGlobal_Storage));
InterfaceBlock intf(-1, intfVar, name, String(""),
std::vector<std::unique_ptr<Expression>>(), st);
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
index 033c9eb..62e3b3d 100644
--- a/src/sksl/ir/SkSLVariable.h
+++ b/src/sksl/ir/SkSLVariable.h
@@ -33,11 +33,12 @@
};
Variable(int offset, Modifiers modifiers, StringFragment name, const Type* type,
- Storage storage, Expression* initialValue = nullptr)
+ bool builtin, Storage storage, Expression* initialValue = nullptr)
: INHERITED(offset, kSymbolKind, name, type)
, fModifiers(modifiers)
, fStorage(storage)
, fInitialValue(initialValue)
+ , fBuiltin(builtin)
, fReadCount(0)
, fWriteCount(initialValue ? 1 : 0) {}
@@ -68,6 +69,7 @@
const Storage fStorage;
const Expression* fInitialValue = nullptr;
+ bool fBuiltin;
// Tracks how many sites read from the variable. If this is zero for a non-out variable (or
// becomes zero during optimization), the variable is dead and may be eliminated.