Replace Setting IRNodes with actual caps-values during IR generation.
Previously, this happened during optimization, so we couldn't disable
control-flow analysis on any code which used sk_Caps. Now this happens
as soon as the IRNode is rehydrated or instantiated. The logic has
migrated to a static helper function, Setting::Make.
Change-Id: I102557845ccd1b68cc2b12381563f06bc64c621f
Bug: skia:11365, skia:11343, skia:11319
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/375298
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 68f8986..d10eb18 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -87,6 +87,21 @@
const String* fOldSource;
};
+class AutoProgramConfig {
+public:
+ AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
+ : fContext(context.get()) {
+ SkASSERT(!fContext->fConfig);
+ fContext->fConfig = config;
+ }
+
+ ~AutoProgramConfig() {
+ fContext->fConfig = nullptr;
+ }
+
+ Context* fContext;
+};
+
Compiler::Compiler(const ShaderCapsClass* caps)
: fContext(std::make_shared<Context>(/*errors=*/*this, *caps))
, fInliner(fContext.get())
@@ -256,16 +271,24 @@
LoadedModule Compiler::loadModule(ProgramKind kind,
ModuleData data,
- std::shared_ptr<SymbolTable> base) {
- if (!base) {
- // NOTE: This is a workaround. The only time 'base' is null is when dehydrating includes.
- // In that case, skslc doesn't know which module it's preparing, nor what the correct base
- // module is. We can't use 'Root', because many GPU intrinsics reference private types,
- // like samplers or textures. Today, 'Private' does contain the union of all known types,
- // so this is safe. If we ever have types that only exist in 'Public' (for example), this
- // logic needs to be smarter (by choosing the correct base for the module we're compiling).
+ std::shared_ptr<SymbolTable> base,
+ bool dehydrate) {
+ if (dehydrate) {
+ // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module
+ // it's preparing, nor what the correct base module is. We can't use 'Root', because many
+ // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does
+ // contain the union of all known types, so this is safe. If we ever have types that only
+ // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct
+ // base for the module we're compiling).
base = fPrivateSymbolTable;
}
+ SkASSERT(base);
+
+ // Built-in modules always use default program settings.
+ ProgramConfig config;
+ config.fKind = kind;
+ config.fSettings.fReplaceSettings = !dehydrate;
+ AutoProgramConfig autoConfig(fContext, &config);
#if defined(SKSL_STANDALONE)
SkASSERT(data.fPath);
@@ -282,13 +305,6 @@
SkASSERT(fIRGenerator->fCanInline);
fIRGenerator->fCanInline = 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(baseModule, /*isBuiltinCode=*/true,
source->c_str(), source->length(),
@@ -313,7 +329,7 @@
}
ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
- LoadedModule module = this->loadModule(kind, data, base.fSymbols);
+ LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
this->optimize(module);
// For modules that just declare (but don't define) intrinsic functions, there will be no new
@@ -1436,10 +1452,7 @@
// Update our context to point to the program configuration for the duration of compilation.
auto config = std::make_unique<ProgramConfig>(ProgramConfig{kind, settings});
-
- SkASSERT(!fContext->fConfig);
- fContext->fConfig = config.get();
- SK_AT_SCOPE_EXIT(fContext->fConfig = nullptr);
+ AutoProgramConfig autoConfig(fContext, config.get());
fErrorText = "";
fErrorCount = 0;
@@ -1541,11 +1554,7 @@
// Create a temporary program configuration with default settings.
ProgramConfig config;
config.fKind = module.fKind;
-
- // Update our context to point to this configuration for the duration of compilation.
- SkASSERT(!fContext->fConfig);
- fContext->fConfig = &config;
- SK_AT_SCOPE_EXIT(fContext->fConfig = nullptr);
+ AutoProgramConfig autoConfig(fContext, &config);
// Reset the Inliner.
fInliner.reset(fModifiers.back().get());
@@ -1556,11 +1565,11 @@
bool madeChanges = false;
// Scan and optimize based on the control-flow graph for each function.
- // TODO(skia:11365): we always perform CFG-based optimization here to reduce Settings into
- // their final form. We should do this optimization in our Make functions instead.
- for (const auto& element : module.fElements) {
- if (element->is<FunctionDefinition>()) {
- madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage.get());
+ if (config.fSettings.fControlFlowAnalysis) {
+ for (const auto& element : module.fElements) {
+ if (element->is<FunctionDefinition>()) {
+ madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage.get());
+ }
}
}
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index e88ce58..819ac32 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -160,7 +160,8 @@
return ModuleData{/*fPath=*/nullptr, data, size};
}
- LoadedModule loadModule(ProgramKind kind, ModuleData data, std::shared_ptr<SymbolTable> base);
+ LoadedModule loadModule(ProgramKind kind, ModuleData data, std::shared_ptr<SymbolTable> base,
+ bool dehydrate);
ParsedModule parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base);
IRGenerator& irGenerator() {
diff --git a/src/sksl/SkSLDehydrator.cpp b/src/sksl/SkSLDehydrator.cpp
index d348d53..d082814 100644
--- a/src/sksl/SkSLDehydrator.cpp
+++ b/src/sksl/SkSLDehydrator.cpp
@@ -348,7 +348,6 @@
const Setting& s = e->as<Setting>();
this->writeCommand(Rehydrator::kSetting_Command);
this->write(s.name());
- this->write(s.type());
break;
}
case Expression::Kind::kSwizzle: {
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 985c0ba..4c8102e 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -2492,14 +2492,7 @@
StringFragment field = fieldNode.getString();
const Type& baseType = base->type();
if (baseType == *fContext.fTypes.fSkCaps) {
- if (this->settings().fReplaceSettings && !fIsBuiltinCode) {
- return Setting::GetValue(fContext, fieldNode.fOffset, field);
- }
- const Type* type = Setting::GetType(fContext, fieldNode.fOffset, field);
- if (!type) {
- return nullptr;
- }
- return std::make_unique<Setting>(fieldNode.fOffset, field, type);
+ return Setting::Make(fContext, fieldNode.fOffset, field);
}
switch (baseType.typeKind()) {
case Type::TypeKind::kStruct:
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index b178145..9e10d44 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -454,8 +454,9 @@
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
}
- SkSL::LoadedModule module = compiler.loadModule(
- kind, SkSL::Compiler::MakeModulePath(inputPath.c_str()), nullptr);
+ SkSL::LoadedModule module =
+ compiler.loadModule(kind, SkSL::Compiler::MakeModulePath(inputPath.c_str()),
+ /*base=*/nullptr, /*dehydrate=*/true);
SkSL::Dehydrator dehydrator;
dehydrator.write(*module.fSymbols);
dehydrator.write(module.fElements);
@@ -475,8 +476,8 @@
return ResultCode::kOutputError;
}
} else {
- printf("expected output path to end with one of: .glsl, .metal, .spirv, .asm.frag, "
- ".asm.vert, .asm.geom, .cpp, .h (got '%s')\n", outputPath.c_str());
+ printf("expected output path to end with one of: .glsl, .metal, .spirv, .asm.frag, .skvm, "
+ ".stage, .asm.vert, .asm.geom, .cpp, .h (got '%s')\n", outputPath.c_str());
return ResultCode::kConfigurationError;
}
return ResultCode::kSuccess;
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index 55949d4..e935d48 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -514,8 +514,7 @@
}
case Rehydrator::kSetting_Command: {
StringFragment name = this->readString();
- const Type* type = this->type();
- return std::make_unique<Setting>(-1, name, type);
+ return Setting::Make(fContext, /*offset=*/-1, name);
}
case Rehydrator::kSwizzle_Command: {
std::unique_ptr<Expression> base = this->expression();
diff --git a/src/sksl/generated/sksl_gpu.dehydrated.sksl b/src/sksl/generated/sksl_gpu.dehydrated.sksl
index 1b3c896..899dcbc 100644
--- a/src/sksl/generated/sksl_gpu.dehydrated.sksl
+++ b/src/sksl/generated/sksl_gpu.dehydrated.sksl
@@ -3950,7 +3950,6 @@
34,
44,
35,114,8,
-40,14,3,
44,
1,
49,167,3,0,65,
@@ -4311,7 +4310,6 @@
34,
44,
35,159,8,
-40,14,3,
1,
49,209,3,0,49,
1,
@@ -4330,7 +4328,6 @@
34,
44,
35,159,8,
-40,14,3,
1,
49,212,3,0,49,
1,
@@ -6138,7 +6135,6 @@
34,
44,
35,255,9,
-40,14,3,
21,
40,168,0,105,2,1,
6,
@@ -6165,7 +6161,6 @@
34,
44,
35,255,9,
-40,14,3,
21,
40,176,0,109,2,1,
6,
diff --git a/src/sksl/ir/SkSLSetting.cpp b/src/sksl/ir/SkSLSetting.cpp
index 65841b4..9f7a85c 100644
--- a/src/sksl/ir/SkSLSetting.cpp
+++ b/src/sksl/ir/SkSLSetting.cpp
@@ -101,7 +101,7 @@
} // namespace
-const Type* Setting::GetType(const Context& context, int offset, const String& name) {
+static const Type* get_type(const Context& context, int offset, const String& name) {
if (const CapsLookupMethod* caps = caps_lookup_table().lookup(name)) {
return caps->type(context);
}
@@ -110,8 +110,8 @@
return nullptr;
}
-std::unique_ptr<Expression> Setting::GetValue(const Context& context, int offset,
- const String& name) {
+static std::unique_ptr<Expression> get_value(const Context& context, int offset,
+ const String& name) {
if (const CapsLookupMethod* caps = caps_lookup_table().lookup(name)) {
return caps->value(context);
}
@@ -120,12 +120,17 @@
return nullptr;
}
-std::unique_ptr<Expression> Setting::constantPropagate(const IRGenerator& irGenerator,
- const DefinitionMap& definitions) {
- if (irGenerator.fContext.fConfig->fSettings.fReplaceSettings) {
- return GetValue(irGenerator.fContext, fOffset, this->name());
+std::unique_ptr<Expression> Setting::Make(const Context& context, int offset, const String& name) {
+ SkASSERT(context.fConfig);
+
+ if (context.fConfig->fSettings.fReplaceSettings) {
+ // Insert the settings value directly into the IR.
+ return get_value(context, offset, name);
}
- return nullptr;
+
+ // Generate a Setting IRNode.
+ const Type* type = get_type(context, offset, name);
+ return type ? std::make_unique<Setting>(offset, name, type) : nullptr;
}
} // namespace SkSL
diff --git a/src/sksl/ir/SkSLSetting.h b/src/sksl/ir/SkSLSetting.h
index 8ffe827..6a0cb50 100644
--- a/src/sksl/ir/SkSLSetting.h
+++ b/src/sksl/ir/SkSLSetting.h
@@ -14,8 +14,9 @@
namespace SkSL {
/**
- * Represents a compile-time constant setting, such as sk_Caps.fbFetchSupport. These are generally
- * collapsed down to their constant representations during the compilation process.
+ * Represents a compile-time constant setting, such as sk_Caps.fbFetchSupport. These IRNodes should
+ * only exist in a dehydrated module. These nodes are replaced with the value of the setting during
+ * rehydration or compilation (i.e., whenever fReplaceSettings is true).
*/
class Setting final : public Expression {
public:
@@ -25,16 +26,10 @@
: INHERITED(offset, kExpressionKind, type)
, fName(std::move(name)) {}
- static const Type* GetType(const Context& context, int offset, const String& name);
-
- static std::unique_ptr<Expression> GetValue(const Context& context, int offset,
- const String& name);
-
- std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
- const DefinitionMap& definitions) override;
+ static std::unique_ptr<Expression> Make(const Context& context, int offset, const String& name);
std::unique_ptr<Expression> clone() const override {
- return std::unique_ptr<Expression>(new Setting(fOffset, this->name(), &this->type()));
+ return std::make_unique<Setting>(fOffset, this->name(), &this->type());
}
const String& name() const {
@@ -49,10 +44,6 @@
return false;
}
- bool isCompileTimeConstant() const override {
- return true;
- }
-
private:
String fName;