Move static-switch and static-if tests out of scanCFG.

We don't need to do these tests every time we run CFG optimization; we
can do them once at the end of optimization, as a separate step.

Change-Id: If0e72fbacb938b62387fd2ffdbf34d1153bf3bd4
Bug: skia:11319
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/369481
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: 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 40800b6..c778598 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -1171,14 +1171,14 @@
         bool visitStatement(const Statement& stmt) override {
             switch (stmt.kind()) {
                 case Statement::Kind::kBlock:
-                    return this->INHERITED::visitStatement(stmt);
+                    return INHERITED::visitStatement(stmt);
 
                 case Statement::Kind::kBreak:
                     return fInConditional > 0;
 
                 case Statement::Kind::kIf: {
                     ++fInConditional;
-                    bool result = this->INHERITED::visitStatement(stmt);
+                    bool result = INHERITED::visitStatement(stmt);
                     --fInConditional;
                     return result;
                 }
@@ -1203,7 +1203,7 @@
         bool visitStatement(const Statement& stmt) override {
             switch (stmt.kind()) {
                 case Statement::Kind::kBlock:
-                    return this->INHERITED::visitStatement(stmt);
+                    return INHERITED::visitStatement(stmt);
 
                 case Statement::Kind::kBreak:
                     return true;
@@ -1552,39 +1552,6 @@
     } while (optimizationContext.fUpdated);
     SkASSERT(!optimizationContext.fNeedsRescan);
 
-    // verify static ifs & switches, clean up dead variable decls
-    for (BasicBlock& b : cfg.fBlocks) {
-        for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() &&
-             !optimizationContext.fNeedsRescan;) {
-            if (iter->isStatement()) {
-                const Statement& s = **iter->statement();
-                switch (s.kind()) {
-                    case Statement::Kind::kIf:
-                        if (s.as<IfStatement>().isStatic() &&
-                            !fIRGenerator->fSettings->fPermitInvalidStaticTests) {
-                            this->error(s.fOffset, "static if has non-static test");
-                        }
-                        ++iter;
-                        break;
-                    case Statement::Kind::kSwitch:
-                        if (s.as<SwitchStatement>().isStatic() &&
-                            !fIRGenerator->fSettings->fPermitInvalidStaticTests &&
-                            optimizationContext.fSilences.find(&s) ==
-                                    optimizationContext.fSilences.end()) {
-                            this->error(s.fOffset, "static switch has non-static test");
-                        }
-                        ++iter;
-                        break;
-                    default:
-                        ++iter;
-                        break;
-                }
-            } else {
-                ++iter;
-            }
-        }
-    }
-
     // check for missing return
     if (f.declaration().returnType() != *fContext->fTypes.fVoid) {
         if (cfg.fBlocks[cfg.fExit].fIsReachable) {
@@ -1652,6 +1619,52 @@
     return success ? std::move(program) : nullptr;
 }
 
+void Compiler::verifyStaticTests(const Program& program) {
+    class StaticTestVerifier : public ProgramVisitor {
+    public:
+        StaticTestVerifier(ErrorReporter* r) : fReporter(r) {}
+
+        using ProgramVisitor::visitProgramElement;
+
+        bool visitStatement(const Statement& stmt) override {
+            switch (stmt.kind()) {
+                case Statement::Kind::kIf:
+                    if (stmt.as<IfStatement>().isStatic()) {
+                        fReporter->error(stmt.fOffset, "static if has non-static test");
+                    }
+                    break;
+
+                case Statement::Kind::kSwitch:
+                    if (stmt.as<SwitchStatement>().isStatic()) {
+                        fReporter->error(stmt.fOffset, "static switch has non-static test");
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+            return INHERITED::visitStatement(stmt);
+        }
+
+    private:
+        using INHERITED = ProgramVisitor;
+        ErrorReporter* fReporter;
+    };
+
+    // If invalid static tests are permitted, we don't need to check anything.
+    if (fIRGenerator->fSettings->fPermitInvalidStaticTests) {
+        return;
+    }
+
+    // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
+    StaticTestVerifier visitor{this};
+    for (const std::unique_ptr<ProgramElement>& element : program.ownedElements()) {
+        if (element->is<FunctionDefinition>()) {
+            visitor.visitProgramElement(*element);
+        }
+    }
+}
+
 bool Compiler::optimize(LoadedModule& module) {
     SkASSERT(!fErrorCount);
     Program::Settings settings;
@@ -1757,6 +1770,11 @@
             break;
         }
     }
+
+    if (fErrorCount == 0) {
+        this->verifyStaticTests(program);
+    }
+
     return fErrorCount == 0;
 }