Eliminate dead local variables from dehydrated code.
This should address the root cause of http://review.skia.org/538045.
We currently don't eliminate unreferenced names from the symbol table,
although that'd be free money if we wanted to do it.
Change-Id: I712e23e6a4f3cb48c2b5ae68f042e13031d5f632
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/540038
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 0a96652..29cce65 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -383,7 +383,7 @@
AutoProgramConfig autoConfig(fContext, &config);
SkASSERT(data.fData && (data.fSize != 0));
Rehydrator rehydrator(*this, data.fData, data.fSize, std::move(base));
- LoadedModule result = { kind, rehydrator.symbolTable(), rehydrator.elements() };
+ LoadedModule module = {kind, rehydrator.symbolTable(), rehydrator.elements()};
#else
SkASSERT(this->errorCount() == 0);
SkASSERT(data.fPath);
@@ -394,20 +394,21 @@
abort();
}
ParsedModule baseModule = {std::move(base), /*fElements=*/nullptr};
- LoadedModule result = DSLParser(this, settings, kind,
- std::move(text)).moduleInheritingFrom(std::move(baseModule));
+ LoadedModule module = DSLParser(this, settings, kind, std::move(text))
+ .moduleInheritingFrom(std::move(baseModule));
if (this->errorCount()) {
printf("Unexpected errors: %s\n", this->fErrorText.c_str());
SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
}
+ this->optimizeModuleForDehydration(module, baseModule);
#endif
- return result;
+ return module;
}
ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
- this->optimize(module, base);
+ this->optimizeRehydratedModule(module, base);
// For modules that just declare (but don't define) intrinsic functions, there will be no new
// program elements. In that case, we can share our parent's element map:
@@ -568,7 +569,7 @@
}
}
-bool Compiler::optimize(LoadedModule& module, const ParsedModule& base) {
+bool Compiler::optimizeRehydratedModule(LoadedModule& module, const ParsedModule& base) {
SkASSERT(!this->errorCount());
// Create a temporary program configuration with default settings.
@@ -590,6 +591,31 @@
return this->errorCount() == 0;
}
+bool Compiler::optimizeModuleForDehydration(LoadedModule& module, const ParsedModule& base) {
+ SkASSERT(!this->errorCount());
+
+ // Create a temporary program configuration with default settings.
+ ProgramConfig config;
+ config.fIsBuiltinCode = true;
+ config.fKind = module.fKind;
+ AutoProgramConfig autoConfig(fContext, &config);
+
+ std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module, base);
+
+ while (Transform::EliminateDeadLocalVariables(*fContext, module, usage.get())) {
+ // Removing dead variables may cause more variables to become unreferenced. Try again.
+ }
+
+ // TODO: run EliminateUnreachableCode pass
+
+ // Note that we intentionally don't attempt to eliminate unreferenced global variables or
+ // functions here, since those can be referenced by the finished program even if they're
+ // unreferenced now. We also don't run the inliner to avoid growing the program; that is done in
+ // `optimizeRehydratedModule` above.
+
+ return this->errorCount() == 0;
+}
+
bool Compiler::optimize(Program& program) {
// The optimizer only needs to run when it is enabled.
if (!program.fConfig->fSettings.fOptimize) {
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index ed33fb1..eb20bdf 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -250,8 +250,11 @@
/** Performs final checks to confirm that a fully-assembled/optimized is valid. */
bool finalize(Program& program);
- /** Optimize the module. */
- bool optimize(LoadedModule& module, const ParsedModule& base);
+ /** Optimize a module in preparation for dehydration. */
+ bool optimizeModuleForDehydration(LoadedModule& module, const ParsedModule& base);
+
+ /** Optimize a module after rehydrating it. */
+ bool optimizeRehydratedModule(LoadedModule& module, const ParsedModule& base);
/** Flattens out function calls when it is safe to do so. */
bool runInliner(const std::vector<std::unique_ptr<ProgramElement>>& elements,
diff --git a/src/sksl/generated/sksl_graphite_frag.dehydrated.sksl b/src/sksl/generated/sksl_graphite_frag.dehydrated.sksl
index 88451b6..a39df99 100644
--- a/src/sksl/generated/sksl_graphite_frag.dehydrated.sksl
+++ b/src/sksl/generated/sksl_graphite_frag.dehydrated.sksl
@@ -447,10 +447,7 @@
51,255,255,110,0,0,
36,
51,255,255,110,0,2,0,0,0,
-56,80,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,3,0,0,0,
+40,
32,0,
1,
57,7,0,0,16,
@@ -1654,122 +1651,35 @@
7,0,
3,0,
11,0,30,
-56,129,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,0,0,0,0,
-56,130,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,1,0,0,0,
-56,131,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,2,0,0,0,
-56,132,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,3,0,0,0,
-56,133,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,4,0,0,0,
-56,134,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,5,0,0,0,
-56,135,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,6,0,0,0,
-56,136,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,7,0,0,0,
-56,137,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,8,0,0,0,
-56,138,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,9,0,0,0,
-56,139,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,10,0,0,0,
-56,140,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,11,0,0,0,
-56,141,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,12,0,0,0,
-56,142,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,13,0,0,0,
-56,143,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,14,0,0,0,
-56,144,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,15,0,0,0,
-56,145,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,16,0,0,0,
-56,146,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,17,0,0,0,
-56,147,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,18,0,0,0,
-56,148,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,19,0,0,0,
-56,149,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,20,0,0,0,
-56,150,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,21,0,0,0,
-56,151,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,22,0,0,0,
-56,152,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,23,0,0,0,
-56,153,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,24,0,0,0,
-56,154,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,25,0,0,0,
-56,155,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,26,0,0,0,
-56,156,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,27,0,0,0,
-56,157,0,
-51,255,255,110,0,0,
-36,
-51,255,255,110,0,28,0,0,0,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
+40,
49,0,
52,1,29,0,
51,129,0,
diff --git a/src/sksl/transform/BUILD.bazel b/src/sksl/transform/BUILD.bazel
index 26f61a7..ef08690 100644
--- a/src/sksl/transform/BUILD.bazel
+++ b/src/sksl/transform/BUILD.bazel
@@ -68,10 +68,12 @@
deps = [
":SkSLProgramWriter_hdr",
":SkSLTransform_hdr",
+ "//include/core:SkSpan_hdr",
"//include/core:SkTypes_hdr",
"//include/private:SkSLProgramElement_hdr",
"//include/private:SkSLStatement_hdr",
"//include/private:SkTHash_hdr",
+ "//src/sksl:SkSLCompiler_hdr",
"//src/sksl:SkSLProgramSettings_hdr",
"//src/sksl/ir:SkSLExpressionStatement_hdr",
"//src/sksl/ir:SkSLExpression_hdr",
@@ -111,4 +113,5 @@
name = "SkSLTransform_hdr",
hdrs = ["SkSLTransform.h"],
visibility = ["//:__subpackages__"],
+ deps = ["//include/core:SkSpan_hdr"],
)
diff --git a/src/sksl/transform/SkSLEliminateDeadLocalVariables.cpp b/src/sksl/transform/SkSLEliminateDeadLocalVariables.cpp
index 73110b8..3394340 100644
--- a/src/sksl/transform/SkSLEliminateDeadLocalVariables.cpp
+++ b/src/sksl/transform/SkSLEliminateDeadLocalVariables.cpp
@@ -5,10 +5,12 @@
* found in the LICENSE file.
*/
+#include "include/core/SkSpan.h"
#include "include/core/SkTypes.h"
#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLStatement.h"
#include "include/private/SkTHash.h"
+#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
@@ -22,12 +24,14 @@
#include <memory>
#include <utility>
-#include <vector>
namespace SkSL {
+
class Context;
-bool Transform::EliminateDeadLocalVariables(Program& program, ProgramUsage* usage) {
+static bool eliminate_dead_local_variables(const Context& context,
+ SkSpan<std::unique_ptr<ProgramElement>> elements,
+ ProgramUsage* usage) {
class DeadLocalVariableEliminator : public ProgramWriter {
public:
DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
@@ -87,24 +91,36 @@
using INHERITED = ProgramWriter;
};
- DeadLocalVariableEliminator visitor{*program.fContext, usage};
+ DeadLocalVariableEliminator visitor{context, usage};
- if (program.fConfig->fSettings.fRemoveDeadVariables) {
- for (auto& [var, counts] : usage->fVariableCounts) {
- if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
- // This program contains at least one dead local variable.
- // Scan the program for any dead local variables and eliminate them all.
- for (std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
- if (pe->is<FunctionDefinition>()) {
- visitor.visitProgramElement(*pe);
- }
+ for (auto& [var, counts] : usage->fVariableCounts) {
+ if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
+ // This program contains at least one dead local variable.
+ // Scan the program for any dead local variables and eliminate them all.
+ for (std::unique_ptr<ProgramElement>& pe : elements) {
+ if (pe->is<FunctionDefinition>()) {
+ visitor.visitProgramElement(*pe);
}
- break;
}
+ break;
}
}
return visitor.fMadeChanges;
}
+bool Transform::EliminateDeadLocalVariables(const Context& context,
+ LoadedModule& module,
+ ProgramUsage* usage) {
+ return eliminate_dead_local_variables(context, SkMakeSpan(module.fElements), usage);
+}
+
+bool Transform::EliminateDeadLocalVariables(Program& program, ProgramUsage* usage) {
+ return program.fConfig->fSettings.fRemoveDeadVariables
+ ? eliminate_dead_local_variables(*program.fContext,
+ SkMakeSpan(program.fOwnedElements),
+ usage)
+ : false;
+}
+
} // namespace SkSL
diff --git a/src/sksl/transform/SkSLTransform.h b/src/sksl/transform/SkSLTransform.h
index b2d1f90..99fad7c 100644
--- a/src/sksl/transform/SkSLTransform.h
+++ b/src/sksl/transform/SkSLTransform.h
@@ -8,12 +8,14 @@
#ifndef SKSL_TRANSFORM
#define SKSL_TRANSFORM
+#include "include/core/SkSpan.h"
#include <memory>
#include <vector>
namespace SkSL {
class Context;
+struct LoadedModule;
struct Program;
class ProgramElement;
class ProgramUsage;
@@ -44,6 +46,7 @@
* Eliminates variables in a program which are never read or written (past their initializer).
* Preserves side effects from initializers, if any. Returns true if any changes were made.
*/
+bool EliminateDeadLocalVariables(const Context& context, LoadedModule& module, ProgramUsage* usage);
bool EliminateDeadLocalVariables(Program& program, ProgramUsage* usage);
bool EliminateDeadGlobalVariables(Program& program, ProgramUsage* usage);