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.