Migrate program finalization logic out of IRGenerator.

Most of the logic in IRGenerator::finish has moved to
Compiler::finalize. The @if/@switch pass has been combined with the pass
that verifies no dangling FunctionReference/TypeReference expressions,
saving one walk through the IR tree. Most program-finalization logic now
exists in Compiler and Analysis.

This change reorders our error generation logic slightly, and manages to
squeeze a few extra (valid) errors out of one of our fuzzer-generated
tests, but is not really intended to affect results in any significant
way.

Change-Id: I461de7c31f3980dedf74424e7826c032b1f40fd2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/444757
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index dbb7d5c..f514d11 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -473,7 +473,7 @@
                                              ir.fInputs);
     this->errorReporter().reportPendingErrors(PositionInfo());
     bool success = false;
-    if (this->errorCount()) {
+    if (!this->finalize(*program)) {
         // Do not return programs that failed to compile.
     } else if (!this->optimize(*program)) {
         // Do not return programs that failed to optimize.
@@ -804,8 +804,25 @@
         this->removeDeadGlobalVariables(program, usage);
     }
 
-    if (this->errorCount() == 0) {
-        Analysis::VerifyStaticTests(program);
+    return this->errorCount() == 0;
+}
+
+bool Compiler::finalize(Program& program) {
+    // Do a pass looking for @if/@switch statements that didn't optimize away, or dangling
+    // FunctionReference or TypeReference expressions. Report these as errors.
+    Analysis::VerifyStaticTestsAndExpressions(program);
+
+    // If we're in ES2 mode (runtime effects), do a pass to enforce Appendix A, Section 5 of the
+    // GLSL ES 1.00 spec -- Indexing. Don't bother if we've already found errors - this logic
+    // assumes that all loops meet the criteria of Section 4, and if they don't, could crash.
+    if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) {
+        for (const auto& pe : program.ownedElements()) {
+            Analysis::ValidateIndexingForES2(*pe, this->errorReporter());
+        }
+    }
+
+    if (fContext->fConfig->strictES2Mode()) {
+        Analysis::DetectStaticRecursion(SkMakeSpan(program.ownedElements()), this->errorReporter());
     }
 
     return this->errorCount() == 0;