Remove ProgramSettings from IRGenerator and the Inliner.

These classes now read the program configuration from the Context.

Change-Id: I15c95cacebb9836ee8f2162c4f4b7f99d453639c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/371396
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 59afc0d..c25d393 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -280,16 +280,21 @@
     }
     const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
     AutoSource as(this, source);
-    Program::Settings settings;
+
     SkASSERT(fIRGenerator->fCanInline);
     fIRGenerator->fCanInline = false;
-    settings.fReplaceSettings = false;
+
+    ProgramConfig config;
+    config.fKind = kind;
+    config.fSettings.fReplaceSettings = false;
+
+    fContext->fConfig = &config;
+    SK_AT_SCOPE_EXIT(fContext->fConfig = nullptr);
 
     ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
-    IRGenerator::IRBundle ir =
-            fIRGenerator->convertProgram(kind, &settings, baseModule,
-                                         /*isBuiltinCode=*/true, source->c_str(), source->length(),
-                                         /*externalFunctions=*/nullptr);
+    IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/true,
+                                                            source->c_str(), source->length(),
+                                                            /*externalFunctions=*/nullptr);
     SkASSERT(ir.fSharedElements.empty());
     LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
     fIRGenerator->fCanInline = true;
@@ -1415,7 +1420,7 @@
                             break;
                         } else {
                             if (s.isStatic() &&
-                                !fIRGenerator->fSettings->fPermitInvalidStaticTests) {
+                                !fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
                                 auto [iter, didInsert] = optimizationContext->fSilences.insert(&s);
                                 if (didInsert) {
                                     this->error(s.fOffset, "static switch contains non-static "
@@ -1434,7 +1439,7 @@
                             (*iter)->setStatement(std::move(newBlock), usage);
                         } else {
                             if (s.isStatic() &&
-                                !fIRGenerator->fSettings->fPermitInvalidStaticTests) {
+                                !fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
                                 auto [iter, didInsert] = optimizationContext->fSilences.insert(&s);
                                 if (didInsert) {
                                     this->error(s.fOffset, "static switch contains non-static "
@@ -1589,7 +1594,7 @@
 
     fErrorText = "";
     fErrorCount = 0;
-    fInliner.reset(fIRGenerator->fModifiers.get(), &config->fSettings);
+    fInliner.reset(fIRGenerator->fModifiers.get());
 
     // Not using AutoSource, because caller is likely to call errorText() if we fail to compile
     std::unique_ptr<String> textPtr(new String(std::move(text)));
@@ -1602,9 +1607,9 @@
         pool = Pool::Create();
         pool->attachToThread();
     }
-    IRGenerator::IRBundle ir =
-            fIRGenerator->convertProgram(kind, &settings, baseModule, /*isBuiltinCode=*/false,
-                                         textPtr->c_str(), textPtr->size(), externalFunctions);
+    IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/false,
+                                                            textPtr->c_str(), textPtr->size(),
+                                                            externalFunctions);
     auto program = std::make_unique<Program>(std::move(textPtr),
                                              std::move(config),
                                              fCaps,
@@ -1669,7 +1674,7 @@
     };
 
     // If invalid static tests are permitted, we don't need to check anything.
-    if (fIRGenerator->fSettings->fPermitInvalidStaticTests) {
+    if (fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
         return;
     }
 
@@ -1694,10 +1699,8 @@
     fContext->fConfig = &config;
     SK_AT_SCOPE_EXIT(fContext->fConfig = nullptr);
 
-    // Set this configuration in the IR Generator and Inliner.
-    fIRGenerator->fKind = config.fKind;
-    fIRGenerator->fSettings = &config.fSettings;
-    fInliner.reset(fModifiers.back().get(), &config.fSettings);
+    // Reset the Inliner.
+    fInliner.reset(fModifiers.back().get());
 
     std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
 
@@ -1723,8 +1726,6 @@
 
 bool Compiler::optimize(Program& program) {
     SkASSERT(!fErrorCount);
-    fIRGenerator->fKind = program.fConfig->fKind;
-    fIRGenerator->fSettings = &program.fConfig->fSettings;
     ProgramUsage* usage = program.fUsage.get();
 
     while (fErrorCount == 0) {
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 7b5cd2f..febdccc 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -174,9 +174,9 @@
 }
 
 std::unique_ptr<Extension> IRGenerator::convertExtension(int offset, StringFragment name) {
-    if (fKind != ProgramKind::kFragment &&
-        fKind != ProgramKind::kVertex &&
-        fKind != ProgramKind::kGeometry) {
+    if (this->programKind() != ProgramKind::kFragment &&
+        this->programKind() != ProgramKind::kVertex &&
+        this->programKind() != ProgramKind::kGeometry) {
         this->errorReporter().error(offset, "extensions are not allowed here");
         return nullptr;
     }
@@ -220,7 +220,7 @@
         default:
             // it's an expression
             std::unique_ptr<Statement> result = this->convertExpressionStatement(statement);
-            if (fRTAdjust && fKind == ProgramKind::kGeometry) {
+            if (fRTAdjust && this->programKind() == ProgramKind::kGeometry) {
                 SkASSERT(result->kind() == Statement::Kind::kExpression);
                 Expression& expr = *result->as<ExpressionStatement>().expression();
                 if (expr.kind() == Expression::Kind::kFunctionCall) {
@@ -338,7 +338,7 @@
                 offset,
                 "variables of type '" + baseType->displayName() + "' must be global");
     }
-    if (fKind != ProgramKind::kFragmentProcessor) {
+    if (this->programKind() != ProgramKind::kFragmentProcessor) {
         if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
             this->errorReporter().error(offset, "'in' variables may not have matrix type");
         }
@@ -365,7 +365,7 @@
                                         "'key' is only permitted within fragment processors");
         }
     }
-    if (fKind == ProgramKind::kRuntimeEffect) {
+    if (this->programKind() == ProgramKind::kRuntimeEffect) {
         if ((modifiers.fFlags & Modifiers::kIn_Flag) &&
             *baseType != *fContext.fTypes.fFragmentProcessor) {
             this->errorReporter().error(offset,
@@ -376,7 +376,7 @@
         this->errorReporter().error(offset, "'key' is not permitted on 'uniform' variables");
     }
     if (modifiers.fLayout.fMarker.fLength) {
-        if (fKind != ProgramKind::kRuntimeEffect) {
+        if (this->programKind() != ProgramKind::kRuntimeEffect) {
             this->errorReporter().error(offset,
                                         "'marker' is only permitted in runtime effects");
         }
@@ -390,7 +390,7 @@
         }
     }
     if (modifiers.fLayout.fFlags & Layout::kSRGBUnpremul_Flag) {
-        if (fKind != ProgramKind::kRuntimeEffect) {
+        if (this->programKind() != ProgramKind::kRuntimeEffect) {
             this->errorReporter().error(offset,
                                         "'srgb_unpremul' is only permitted in runtime effects");
         }
@@ -410,7 +410,7 @@
         }
     }
     if (modifiers.fFlags & Modifiers::kVarying_Flag) {
-        if (fKind != ProgramKind::kRuntimeEffect) {
+        if (this->programKind() != ProgramKind::kRuntimeEffect) {
             this->errorReporter().error(offset, "'varying' is only permitted in runtime effects");
         }
         if (!baseType->isFloat() &&
@@ -440,8 +440,8 @@
                                                               std::unique_ptr<Expression> value,
                                                               Variable::Storage storage) {
     if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 &&
-        (modifiers.fFlags & Modifiers::kOut_Flag) && fKind == ProgramKind::kFragment &&
-        name != "sk_FragColor") {
+        (modifiers.fFlags & Modifiers::kOut_Flag) &&
+        this->programKind() == ProgramKind::kFragment && name != "sk_FragColor") {
         this->errorReporter().error(offset,
                                     "out location=0, index=0 is reserved for sk_FragColor");
     }
@@ -540,9 +540,9 @@
 }
 
 std::unique_ptr<ModifiersDeclaration> IRGenerator::convertModifiersDeclaration(const ASTNode& m) {
-    if (fKind != ProgramKind::kFragment &&
-        fKind != ProgramKind::kVertex &&
-        fKind != ProgramKind::kGeometry) {
+    if (this->programKind() != ProgramKind::kFragment &&
+        this->programKind() != ProgramKind::kVertex &&
+        this->programKind() != ProgramKind::kGeometry) {
         this->errorReporter().error(m.fOffset, "layout qualifiers are not allowed here");
         return nullptr;
     }
@@ -550,7 +550,7 @@
     SkASSERT(m.fKind == ASTNode::Kind::kModifiers);
     Modifiers modifiers = m.getModifiers();
     if (modifiers.fLayout.fInvocations != -1) {
-        if (fKind != ProgramKind::kGeometry) {
+        if (this->programKind() != ProgramKind::kGeometry) {
             this->errorReporter().error(m.fOffset,
                                         "'invocations' is only legal in geometry shaders");
             return nullptr;
@@ -876,7 +876,8 @@
 
 std::unique_ptr<Statement> IRGenerator::convertDiscard(const ASTNode& d) {
     SkASSERT(d.fKind == ASTNode::Kind::kDiscard);
-    if (fKind != ProgramKind::kFragment && fKind != ProgramKind::kFragmentProcessor) {
+    if (this->programKind() != ProgramKind::kFragment &&
+        this->programKind() != ProgramKind::kFragmentProcessor) {
         this->errorReporter().error(d.fOffset,
                                     "discard statement is only permitted in fragment shaders");
         return nullptr;
@@ -1070,7 +1071,7 @@
                     // normalization, so SkASSERT that we aren't doing that. It is of course
                     // possible to fix this by adding a normalization before each return, but it
                     // will probably never actually be necessary.
-                    SkASSERT(fIRGenerator->fKind != ProgramKind::kVertex ||
+                    SkASSERT(fIRGenerator->programKind() != ProgramKind::kVertex ||
                              !fIRGenerator->fRTAdjust ||
                              fFunction->name() != "main");
                     ReturnStatement& r = stmt.as<ReturnStatement>();
@@ -1206,8 +1207,8 @@
         }
 
         Modifiers m = pd.fModifiers;
-        if (funcData.fName == "main" && (fKind == ProgramKind::kRuntimeEffect ||
-                                         fKind == ProgramKind::kFragmentProcessor)) {
+        if (funcData.fName == "main" && (this->programKind() == ProgramKind::kRuntimeEffect ||
+                                         this->programKind() == ProgramKind::kFragmentProcessor)) {
             if (i == 0) {
                 // We verify that the type is correct later, for now, if there is a parameter to
                 // a .fp or runtime-effect main(), it's supposed to be the coords:
@@ -1228,7 +1229,7 @@
     };
 
     if (funcData.fName == "main") {
-        switch (fKind) {
+        switch (this->programKind()) {
             case ProgramKind::kRuntimeEffect: {
                 // (half4|float4) main()  -or-  (half4|float4) main(float2)
                 if (*returnType != *fContext.fTypes.fHalf4 &&
@@ -1366,7 +1367,7 @@
         if (needInvocationIDWorkaround) {
             body = this->applyInvocationIDWorkaround(std::move(body));
         }
-        if (ProgramKind::kVertex == fKind && funcData.fName == "main" && fRTAdjust) {
+        if (ProgramKind::kVertex == this->programKind() && funcData.fName == "main" && fRTAdjust) {
             body->children().push_back(this->getNormalizeSkPositionCode());
         }
         auto result = std::make_unique<FunctionDefinition>(
@@ -1396,9 +1397,9 @@
 }
 
 std::unique_ptr<InterfaceBlock> IRGenerator::convertInterfaceBlock(const ASTNode& intf) {
-    if (fKind != ProgramKind::kFragment &&
-        fKind != ProgramKind::kVertex &&
-        fKind != ProgramKind::kGeometry) {
+    if (this->programKind() != ProgramKind::kFragment &&
+        this->programKind() != ProgramKind::kVertex &&
+        this->programKind() != ProgramKind::kGeometry) {
         this->errorReporter().error(intf.fOffset, "interface block is not allowed here");
         return nullptr;
     }
@@ -1661,13 +1662,13 @@
 #ifndef SKSL_STANDALONE
                 case SK_FRAGCOORD_BUILTIN:
                     fInputs.fFlipY = true;
-                    if (fSettings->fFlipY &&
+                    if (this->settings().fFlipY &&
                         (!fCaps || !fCaps->fragCoordConventionsExtensionString())) {
                         fInputs.fRTHeight = true;
                     }
 #endif
             }
-            if (fKind == ProgramKind::kFragmentProcessor &&
+            if (this->programKind() == ProgramKind::kFragmentProcessor &&
                 (modifiers.fFlags & Modifiers::kIn_Flag) &&
                 !(modifiers.fFlags & Modifiers::kUniform_Flag) &&
                 !modifiers.fLayout.fKey &&
@@ -1722,7 +1723,7 @@
 }
 
 std::unique_ptr<Section> IRGenerator::convertSection(const ASTNode& s) {
-    if (fKind != ProgramKind::kFragmentProcessor) {
+    if (this->programKind() != ProgramKind::kFragmentProcessor) {
         this->errorReporter().error(s.fOffset, "syntax error");
         return nullptr;
     }
@@ -1745,7 +1746,7 @@
         return nullptr;
     }
     int offset = expr->fOffset;
-    if (!expr->coercionCost(type).isPossible(fSettings->fAllowNarrowingConversions)) {
+    if (!expr->coercionCost(type).isPossible(this->settings().fAllowNarrowingConversions)) {
         this->errorReporter().error(offset, "expected '" + type.displayName() + "', but found '" +
                                                     expr->type().displayName() + "'");
         return nullptr;
@@ -1976,7 +1977,7 @@
                                                  : VariableReference::RefKind::kWrite)) {
         return nullptr;
     }
-    if (!determine_binary_type(fContext, fSettings->fAllowNarrowingConversions, op,
+    if (!determine_binary_type(fContext, this->settings().fAllowNarrowingConversions, op,
                                *rawLeftType, *rawRightType, &leftType, &rightType, &resultType)) {
         this->errorReporter().error(
                 offset, String("type mismatch: '") + op.operatorName() +
@@ -2026,7 +2027,7 @@
     const Type* trueType;
     const Type* falseType;
     const Type* resultType;
-    if (!determine_binary_type(fContext, fSettings->fAllowNarrowingConversions,
+    if (!determine_binary_type(fContext, this->settings().fAllowNarrowingConversions,
                                Token::Kind::TK_EQEQ, ifTrue->type(), ifFalse->type(),
                                &trueType, &falseType, &resultType) ||
         trueType != falseType) {
@@ -2856,7 +2857,7 @@
     StringFragment field = fieldNode.getString();
     const Type& baseType = base->type();
     if (baseType == *fContext.fTypes.fSkCaps) {
-        if (fSettings->fReplaceSettings && !fIsBuiltinCode) {
+        if (this->settings().fReplaceSettings && !fIsBuiltinCode) {
             return this->valueForSetting(fieldNode.fOffset, field);
         }
         const Type* type = this->typeForSetting(fieldNode.fOffset, field);
@@ -2998,7 +2999,7 @@
         scanner.addDeclaringElement("sk_FragColor");
     }
 
-    switch (fKind) {
+    switch (this->programKind()) {
         case ProgramKind::kFragment:
             // Vulkan requires certain builtin variables be present, even if they're unused. At one
             // time, validation errors would result if sk_Clockwise was missing. Now, it's just
@@ -3014,15 +3015,11 @@
 }
 
 IRGenerator::IRBundle IRGenerator::convertProgram(
-        ProgramKind kind,
-        const Program::Settings* settings,
         const ParsedModule& base,
         bool isBuiltinCode,
         const char* text,
         size_t length,
         const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
-    fKind = kind;
-    fSettings = settings;
     fSymbolTable = base.fSymbols;
     fIntrinsics = base.fIntrinsics.get();
     if (fIntrinsics) {
@@ -3044,7 +3041,7 @@
 
     AutoSymbolTable table(this);
 
-    if (kind == ProgramKind::kGeometry && !fIsBuiltinCode) {
+    if (this->programKind() == ProgramKind::kGeometry && !fIsBuiltinCode) {
         // Declare sk_InvocationID programmatically. With invocations support, it's an 'in' builtin.
         // If we're applying the workaround, then it's a plain global.
         bool workaround = fCaps && !fCaps->gsInvocationsSupport();
@@ -3164,8 +3161,6 @@
         }
     }
 
-    fSettings = nullptr;
-
     return IRBundle{std::move(elements), std::move(sharedElements), this->releaseModifiers(),
                     fSymbolTable, fInputs};
 }
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 8a86b2a..0a9abe7 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -121,8 +121,6 @@
      * Program, but ownership is *not* transferred. It is up to the caller to keep them alive.
      */
     IRBundle convertProgram(
-            ProgramKind kind,
-            const Program::Settings* settings,
             const ParsedModule& base,
             bool isBuiltinCode,
             const char* text,
@@ -133,7 +131,8 @@
     const Type* typeForSetting(int offset, String name) const;
     std::unique_ptr<Expression> valueForSetting(int offset, String name) const;
 
-    const Program::Settings* settings() const { return fSettings; }
+    const Program::Settings& settings() const { return fContext.fConfig->fSettings; }
+    ProgramKind programKind() const { return fContext.fConfig->fKind; }
 
     ErrorReporter& errorReporter() const { return fContext.fErrors; }
 
@@ -281,13 +280,12 @@
     // Runtime effects (and the interpreter, which uses the same CPU runtime) require adherence to
     // the strict rules from The OpenGL ES Shading Language Version 1.00. (Including Appendix A).
     bool strictES2Mode() const {
-        return fKind == ProgramKind::kRuntimeEffect || fKind == ProgramKind::kGeneric;
+        return this->programKind() == ProgramKind::kRuntimeEffect ||
+               this->programKind() == ProgramKind::kGeneric;
     }
 
     Program::Inputs fInputs;
-    const Program::Settings* fSettings = nullptr;
     const ShaderCapsClass* fCaps = nullptr;
-    ProgramKind fKind;
 
     std::unique_ptr<ASTFile> fFile;
 
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index 0243d47..c4d3a6f 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -291,9 +291,8 @@
     }
 }
 
-void Inliner::reset(ModifiersPool* modifiers, const Program::Settings* settings) {
+void Inliner::reset(ModifiersPool* modifiers) {
     fModifiers = modifiers;
-    fSettings = settings;
     fMangler.reset();
     fInlinedStatementCounter = 0;
 }
@@ -622,7 +621,6 @@
     // order guarantees. Since we can't use gotos (which are normally used to replace return
     // statements), we wrap the whole function in a loop and use break statements to jump to the
     // end.
-    SkASSERT(fSettings);
     SkASSERT(fContext);
     SkASSERT(call);
     SkASSERT(this->isSafeToInline(call->function().definition()));
@@ -772,10 +770,8 @@
 }
 
 bool Inliner::isSafeToInline(const FunctionDefinition* functionDef) {
-    SkASSERT(fSettings);
-
     // A threshold of zero indicates that the inliner is completely disabled, so we can just return.
-    if (fSettings->fInlineThreshold <= 0) {
+    if (this->settings().fInlineThreshold <= 0) {
         return false;
     }
 
@@ -1098,7 +1094,7 @@
     auto [iter, wasInserted] = cache->insert({&funcDecl, 0});
     if (wasInserted) {
         iter->second = Analysis::NodeCountUpToLimit(*funcDecl.definition(),
-                                                    fSettings->fInlineThreshold);
+                                                    this->settings().fInlineThreshold);
     }
     return iter->second;
 }
@@ -1130,7 +1126,7 @@
 
     // If the inline threshold is unlimited, or if we have no candidates left, our candidate list is
     // complete.
-    if (fSettings->fInlineThreshold == INT_MAX || candidates.empty()) {
+    if (this->settings().fInlineThreshold == INT_MAX || candidates.empty()) {
         return;
     }
 
@@ -1144,34 +1140,32 @@
         candidateTotalCost[&fnDecl] += this->getFunctionSize(fnDecl, &functionSizeCache);
     }
 
-    candidates.erase(
-            std::remove_if(candidates.begin(),
-                           candidates.end(),
-                           [&](const InlineCandidate& candidate) {
-                               const FunctionDeclaration& fnDecl = candidate_func(candidate);
-                               if (fnDecl.modifiers().fFlags & Modifiers::kInline_Flag) {
-                                   // Functions marked `inline` ignore size limitations.
-                                   return false;
-                               }
-                               if (usage->get(fnDecl) == 1) {
-                                   // If a function is only used once, it's cost-free to inline.
-                                   return false;
-                               }
-                               if (candidateTotalCost[&fnDecl] <= fSettings->fInlineThreshold) {
-                                   // We won't exceed the inline threshold by inlining this.
-                                   return false;
-                               }
-                               // Inlining this function will add too many IRNodes.
-                               return true;
-                           }),
-            candidates.end());
+    candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
+                        [&](const InlineCandidate& candidate) {
+                            const FunctionDeclaration& fnDecl = candidate_func(candidate);
+                            if (fnDecl.modifiers().fFlags & Modifiers::kInline_Flag) {
+                                // Functions marked `inline` ignore size limitations.
+                                return false;
+                            }
+                            if (usage->get(fnDecl) == 1) {
+                                // If a function is only used once, it's cost-free to inline.
+                                return false;
+                            }
+                            if (candidateTotalCost[&fnDecl] <= this->settings().fInlineThreshold) {
+                                // We won't exceed the inline threshold by inlining this.
+                                return false;
+                            }
+                            // Inlining this function will add too many IRNodes.
+                            return true;
+                        }),
+         candidates.end());
 }
 
 bool Inliner::analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements,
                       std::shared_ptr<SymbolTable> symbols,
                       ProgramUsage* usage) {
     // A threshold of zero indicates that the inliner is completely disabled, so we can just return.
-    if (fSettings->fInlineThreshold <= 0) {
+    if (this->settings().fInlineThreshold <= 0) {
         return false;
     }
 
diff --git a/src/sksl/SkSLInliner.h b/src/sksl/SkSLInliner.h
index d323a1e..4103f2a 100644
--- a/src/sksl/SkSLInliner.h
+++ b/src/sksl/SkSLInliner.h
@@ -39,7 +39,7 @@
 public:
     Inliner(const Context* context) : fContext(context) {}
 
-    void reset(ModifiersPool* modifiers, const Program::Settings*);
+    void reset(ModifiersPool* modifiers);
 
     /** Inlines any eligible functions that are found. Returns true if any changes are made. */
     bool analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements,
@@ -55,6 +55,8 @@
         kEarlyReturns,
     };
 
+    const Program::Settings& settings() const { return fContext->fConfig->fSettings; }
+
     void buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements,
                             std::shared_ptr<SymbolTable> symbols, ProgramUsage* usage,
                             InlineCandidateList* candidateList);
@@ -113,7 +115,6 @@
 
     const Context* fContext = nullptr;
     ModifiersPool* fModifiers = nullptr;
-    const Program::Settings* fSettings = nullptr;
     Mangler fMangler;
     int fInlinedStatementCounter = 0;
 };
diff --git a/src/sksl/dsl/priv/DSLWriter.cpp b/src/sksl/dsl/priv/DSLWriter.cpp
index ba0fb71..59a91e8 100644
--- a/src/sksl/dsl/priv/DSLWriter.cpp
+++ b/src/sksl/dsl/priv/DSLWriter.cpp
@@ -26,18 +26,20 @@
 DSLWriter::DSLWriter(SkSL::Compiler* compiler)
     : fCompiler(compiler) {
     SkSL::ParsedModule module = fCompiler->moduleForProgramKind(SkSL::ProgramKind::kFragment);
+    fConfig.fKind = SkSL::ProgramKind::kFragment;
+
     SkSL::IRGenerator& ir = *fCompiler->fIRGenerator;
     fOldSymbolTable = ir.fSymbolTable;
-    fOldSettings = ir.fSettings;
+    fOldConfig = fCompiler->fContext->fConfig;
     ir.fSymbolTable = module.fSymbols;
-    ir.fSettings = &fSettings;
+    fCompiler->fContext->fConfig = &fConfig;
     ir.pushSymbolTable();
 }
 
 DSLWriter::~DSLWriter() {
     SkSL::IRGenerator& ir = *fCompiler->fIRGenerator;
     ir.fSymbolTable = fOldSymbolTable;
-    ir.fSettings = fOldSettings;
+    fCompiler->fContext->fConfig = fOldConfig;
 }
 
 SkSL::IRGenerator& DSLWriter::IRGenerator() {
diff --git a/src/sksl/dsl/priv/DSLWriter.h b/src/sksl/dsl/priv/DSLWriter.h
index 0e1f739..ee0597c 100644
--- a/src/sksl/dsl/priv/DSLWriter.h
+++ b/src/sksl/dsl/priv/DSLWriter.h
@@ -181,10 +181,10 @@
     static void SetInstance(std::unique_ptr<DSLWriter> instance);
 
 private:
-    SkSL::Program::Settings fSettings;
+    SkSL::ProgramConfig fConfig;
     SkSL::Compiler* fCompiler;
     std::shared_ptr<SkSL::SymbolTable> fOldSymbolTable;
-    const SkSL::Program::Settings* fOldSettings;
+    SkSL::ProgramConfig* fOldConfig;
     std::vector<std::unique_ptr<SkSL::ProgramElement>> fProgramElements;
     ErrorHandler* fErrorHandler = nullptr;
     bool fMangle = true;
diff --git a/src/sksl/ir/SkSLSetting.cpp b/src/sksl/ir/SkSLSetting.cpp
index d88c7ec..f3ac3f6 100644
--- a/src/sksl/ir/SkSLSetting.cpp
+++ b/src/sksl/ir/SkSLSetting.cpp
@@ -13,7 +13,7 @@
 
 std::unique_ptr<Expression> Setting::constantPropagate(const IRGenerator& irGenerator,
                                                        const DefinitionMap& definitions) {
-    if (irGenerator.settings()->fReplaceSettings) {
+    if (irGenerator.settings().fReplaceSettings) {
         return irGenerator.valueForSetting(this->fOffset, this->name());
     }
     return nullptr;