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/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