Migrate EliminateUnreachableCode into Transform.
Change-Id: Ib32895a78a8de4c49ce1208f0c1b9b111d092b42
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/456643
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 6033bde..474d3a2 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -191,6 +191,7 @@
"$_src/sksl/ir/SkSLVariableReference.h",
"$_src/sksl/spirv.h",
"$_src/sksl/transform/SkSLBuiltinVariableScanner.cpp",
+ "$_src/sksl/transform/SkSLEliminateUnreachableCode.cpp",
"$_src/sksl/transform/SkSLTransform.h",
]
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index 7bf8c10..c0af003 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -61,8 +61,6 @@
#include "src/sksl/ir/SkSLTypeReference.h"
#include "src/sksl/ir/SkSLVariableReference.h"
-#include <stack>
-
namespace SkSL {
namespace {
@@ -1053,125 +1051,6 @@
}
}
-void Analysis::EliminateUnreachableCode(std::unique_ptr<Statement>& stmt, ProgramUsage* usage) {
- class UnreachableCodeEliminator : public ProgramWriter {
- public:
- UnreachableCodeEliminator(ProgramUsage* usage)
- : fUsage(usage) {
- fFoundFunctionExit.push(false);
- fFoundLoopExit.push(false);
- }
-
- bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
- // We don't need to look inside expressions at all.
- return false;
- }
-
- bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
- if (fFoundFunctionExit.top() || fFoundLoopExit.top()) {
- // If we already found an exit in this section, anything beyond it is dead code.
- if (!stmt->is<Nop>()) {
- // Eliminate the dead statement by substituting a Nop.
- if (fUsage) {
- fUsage->remove(stmt.get());
- }
- stmt = std::make_unique<Nop>();
- }
- return false;
- }
-
- switch (stmt->kind()) {
- case Statement::Kind::kReturn:
- case Statement::Kind::kDiscard:
- // We found a function exit on this path.
- fFoundFunctionExit.top() = true;
- break;
-
- case Statement::Kind::kBreak:
- case Statement::Kind::kContinue:
- // We found a loop exit on this path. Note that we skip over switch statements
- // completely when eliminating code, so any `break` statement would be breaking
- // out of a loop, not out of a switch.
- fFoundLoopExit.top() = true;
- break;
-
- case Statement::Kind::kExpression:
- case Statement::Kind::kInlineMarker:
- case Statement::Kind::kNop:
- case Statement::Kind::kVarDeclaration:
- // These statements don't affect control flow.
- break;
-
- case Statement::Kind::kBlock:
- // Blocks are on the straight-line path and don't affect control flow.
- return INHERITED::visitStatementPtr(stmt);
-
- case Statement::Kind::kDo: {
- // Function-exits are allowed to propagate outside of a do-loop, because it
- // always executes its body at least once.
- fFoundLoopExit.push(false);
- bool result = INHERITED::visitStatementPtr(stmt);
- fFoundLoopExit.pop();
- return result;
- }
- case Statement::Kind::kFor: {
- // Function-exits are not allowed to propagate out, because a for-loop or while-
- // loop could potentially run zero times.
- fFoundFunctionExit.push(false);
- fFoundLoopExit.push(false);
- bool result = INHERITED::visitStatementPtr(stmt);
- fFoundLoopExit.pop();
- fFoundFunctionExit.pop();
- return result;
- }
- case Statement::Kind::kIf: {
- // This statement is conditional and encloses two inner sections of code.
- // If both sides contain a function-exit or loop-exit, that exit is allowed to
- // propagate out.
- IfStatement& ifStmt = stmt->as<IfStatement>();
-
- fFoundFunctionExit.push(false);
- fFoundLoopExit.push(false);
- bool result = (ifStmt.ifTrue() && this->visitStatementPtr(ifStmt.ifTrue()));
- bool foundFunctionExitOnTrue = fFoundFunctionExit.top();
- bool foundLoopExitOnTrue = fFoundLoopExit.top();
- fFoundFunctionExit.pop();
- fFoundLoopExit.pop();
-
- fFoundFunctionExit.push(false);
- fFoundLoopExit.push(false);
- result |= (ifStmt.ifFalse() && this->visitStatementPtr(ifStmt.ifFalse()));
- bool foundFunctionExitOnFalse = fFoundFunctionExit.top();
- bool foundLoopExitOnFalse = fFoundLoopExit.top();
- fFoundFunctionExit.pop();
- fFoundLoopExit.pop();
-
- fFoundFunctionExit.top() |= foundFunctionExitOnTrue && foundFunctionExitOnFalse;
- fFoundLoopExit.top() |= foundLoopExitOnTrue && foundLoopExitOnFalse;
- return result;
- }
- case Statement::Kind::kSwitch:
- case Statement::Kind::kSwitchCase:
- // We skip past switch statements entirely when scanning for dead code. Their
- // control flow is quite complex and we already do a good job of flattening out
- // switches on constant values.
- break;
- }
-
- return false;
- }
-
- ProgramUsage* fUsage;
- std::stack<bool> fFoundFunctionExit;
- std::stack<bool> fFoundLoopExit;
-
- using INHERITED = ProgramWriter;
- };
-
- UnreachableCodeEliminator visitor{usage};
- visitor.visitStatementPtr(stmt);
-}
-
////////////////////////////////////////////////////////////////////////////////
// ProgramVisitor
diff --git a/src/sksl/SkSLAnalysis.h b/src/sksl/SkSLAnalysis.h
index 34cbded..3d53dc8 100644
--- a/src/sksl/SkSLAnalysis.h
+++ b/src/sksl/SkSLAnalysis.h
@@ -167,12 +167,6 @@
*/
void VerifyStaticTestsAndExpressions(const Program& program);
-/**
- * Eliminates statements in a block which cannot be reached; for example, a statement
- * immediately after a `return` or `continue` can safely be eliminated.
- */
-void EliminateUnreachableCode(std::unique_ptr<Statement>& stmt, ProgramUsage* usage = nullptr);
-
} // namespace Analysis
} // namespace SkSL
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index bf028e9..ed96215 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -36,6 +36,7 @@
#include "src/sksl/ir/SkSLUnresolvedFunction.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/transform/SkSLProgramWriter.h"
+#include "src/sksl/transform/SkSLTransform.h"
#include "src/utils/SkBitSet.h"
#include <fstream>
@@ -604,7 +605,7 @@
void Compiler::removeUnreachableCode(Program& program, ProgramUsage* usage) {
for (std::unique_ptr<ProgramElement>& pe : program.ownedElements()) {
if (pe->is<FunctionDefinition>()) {
- Analysis::EliminateUnreachableCode(pe->as<FunctionDefinition>().body(), usage);
+ Transform::EliminateUnreachableCode(pe->as<FunctionDefinition>().body(), usage);
}
}
}
diff --git a/src/sksl/transform/SkSLBuiltinVariableScanner.cpp b/src/sksl/transform/SkSLBuiltinVariableScanner.cpp
index 463848f..7b57d91 100644
--- a/src/sksl/transform/SkSLBuiltinVariableScanner.cpp
+++ b/src/sksl/transform/SkSLBuiltinVariableScanner.cpp
@@ -7,6 +7,7 @@
#include "src/sksl/transform/SkSLTransform.h"
+#include "include/private/SkSLProgramKind.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLIntrinsicMap.h"
diff --git a/src/sksl/transform/SkSLEliminateUnreachableCode.cpp b/src/sksl/transform/SkSLEliminateUnreachableCode.cpp
new file mode 100644
index 0000000..267f5b9
--- /dev/null
+++ b/src/sksl/transform/SkSLEliminateUnreachableCode.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/private/SkSLStatement.h"
+#include "src/sksl/ir/SkSLIfStatement.h"
+#include "src/sksl/ir/SkSLNop.h"
+#include "src/sksl/ir/SkSLProgram.h"
+#include "src/sksl/transform/SkSLProgramWriter.h"
+#include "src/sksl/transform/SkSLTransform.h"
+
+#include <stack>
+
+namespace SkSL {
+
+void Transform::EliminateUnreachableCode(std::unique_ptr<Statement>& stmt, ProgramUsage* usage) {
+ class UnreachableCodeEliminator : public ProgramWriter {
+ public:
+ UnreachableCodeEliminator(ProgramUsage* usage)
+ : fUsage(usage) {
+ fFoundFunctionExit.push(false);
+ fFoundLoopExit.push(false);
+ }
+
+ bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
+ // We don't need to look inside expressions at all.
+ return false;
+ }
+
+ bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
+ if (fFoundFunctionExit.top() || fFoundLoopExit.top()) {
+ // If we already found an exit in this section, anything beyond it is dead code.
+ if (!stmt->is<Nop>()) {
+ // Eliminate the dead statement by substituting a Nop.
+ if (fUsage) {
+ fUsage->remove(stmt.get());
+ }
+ stmt = Nop::Make();
+ }
+ return false;
+ }
+
+ switch (stmt->kind()) {
+ case Statement::Kind::kReturn:
+ case Statement::Kind::kDiscard:
+ // We found a function exit on this path.
+ fFoundFunctionExit.top() = true;
+ break;
+
+ case Statement::Kind::kBreak:
+ case Statement::Kind::kContinue:
+ // We found a loop exit on this path. Note that we skip over switch statements
+ // completely when eliminating code, so any `break` statement would be breaking
+ // out of a loop, not out of a switch.
+ fFoundLoopExit.top() = true;
+ break;
+
+ case Statement::Kind::kExpression:
+ case Statement::Kind::kInlineMarker:
+ case Statement::Kind::kNop:
+ case Statement::Kind::kVarDeclaration:
+ // These statements don't affect control flow.
+ break;
+
+ case Statement::Kind::kBlock:
+ // Blocks are on the straight-line path and don't affect control flow.
+ return INHERITED::visitStatementPtr(stmt);
+
+ case Statement::Kind::kDo: {
+ // Function-exits are allowed to propagate outside of a do-loop, because it
+ // always executes its body at least once.
+ fFoundLoopExit.push(false);
+ bool result = INHERITED::visitStatementPtr(stmt);
+ fFoundLoopExit.pop();
+ return result;
+ }
+ case Statement::Kind::kFor: {
+ // Function-exits are not allowed to propagate out, because a for-loop or while-
+ // loop could potentially run zero times.
+ fFoundFunctionExit.push(false);
+ fFoundLoopExit.push(false);
+ bool result = INHERITED::visitStatementPtr(stmt);
+ fFoundLoopExit.pop();
+ fFoundFunctionExit.pop();
+ return result;
+ }
+ case Statement::Kind::kIf: {
+ // This statement is conditional and encloses two inner sections of code.
+ // If both sides contain a function-exit or loop-exit, that exit is allowed to
+ // propagate out.
+ IfStatement& ifStmt = stmt->as<IfStatement>();
+
+ fFoundFunctionExit.push(false);
+ fFoundLoopExit.push(false);
+ bool result = (ifStmt.ifTrue() && this->visitStatementPtr(ifStmt.ifTrue()));
+ bool foundFunctionExitOnTrue = fFoundFunctionExit.top();
+ bool foundLoopExitOnTrue = fFoundLoopExit.top();
+ fFoundFunctionExit.pop();
+ fFoundLoopExit.pop();
+
+ fFoundFunctionExit.push(false);
+ fFoundLoopExit.push(false);
+ result |= (ifStmt.ifFalse() && this->visitStatementPtr(ifStmt.ifFalse()));
+ bool foundFunctionExitOnFalse = fFoundFunctionExit.top();
+ bool foundLoopExitOnFalse = fFoundLoopExit.top();
+ fFoundFunctionExit.pop();
+ fFoundLoopExit.pop();
+
+ fFoundFunctionExit.top() |= foundFunctionExitOnTrue && foundFunctionExitOnFalse;
+ fFoundLoopExit.top() |= foundLoopExitOnTrue && foundLoopExitOnFalse;
+ return result;
+ }
+ case Statement::Kind::kSwitch:
+ case Statement::Kind::kSwitchCase:
+ // We skip past switch statements entirely when scanning for dead code. Their
+ // control flow is quite complex and we already do a good job of flattening out
+ // switches on constant values.
+ break;
+ }
+
+ return false;
+ }
+
+ ProgramUsage* fUsage;
+ std::stack<bool> fFoundFunctionExit;
+ std::stack<bool> fFoundLoopExit;
+
+ using INHERITED = ProgramWriter;
+ };
+
+ UnreachableCodeEliminator visitor{usage};
+ visitor.visitStatementPtr(stmt);
+}
+
+} // namespace SkSL
diff --git a/src/sksl/transform/SkSLTransform.h b/src/sksl/transform/SkSLTransform.h
index 0f652c0..f8bc59b 100644
--- a/src/sksl/transform/SkSLTransform.h
+++ b/src/sksl/transform/SkSLTransform.h
@@ -8,22 +8,29 @@
#ifndef SKSL_TRANSFORM
#define SKSL_TRANSFORM
-#include "include/private/SkSLProgramKind.h"
-
+#include <memory>
#include <vector>
namespace SkSL {
class Context;
class ProgramElement;
+class ProgramUsage;
+class Statement;
+enum class ProgramKind : int8_t;
namespace Transform {
void FindAndDeclareBuiltinVariables(const Context& context, ProgramKind programKind,
- std::vector<const ProgramElement*>& sharedElements);
+ std::vector<const ProgramElement*>& sharedElements);
+
+/**
+ * Eliminates statements in a block which cannot be reached; for example, a statement
+ * immediately after a `return` or `continue` can safely be eliminated.
+ */
+void EliminateUnreachableCode(std::unique_ptr<Statement>& stmt, ProgramUsage* usage = nullptr);
} // namespace Transform
-
} // namespace SkSL
#endif