Add an "allow-unreachable" flag to CFG basic blocks.

In some cases, it's okay if we can't find a path to a CFG block. Two
such cases are:
- The starting block. This is reached implicitly.
- For-loop increment expressions. These are sometimes required due to
  GLSL ES2 restrictions, even if they can't actually be reached.

Change-Id: I626c424361a7339d4fb1ab0809f60df3f2f72173
Bug: skia:11097
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345162
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/SkSLCFGGenerator.cpp b/src/sksl/SkSLCFGGenerator.cpp
index 49ca3cd..aabd3a8 100644
--- a/src/sksl/SkSLCFGGenerator.cpp
+++ b/src/sksl/SkSLCFGGenerator.cpp
@@ -593,6 +593,11 @@
             if (f.next()) {
                 this->addExpression(cfg, &f.next(), /*constantPropagate=*/true);
             }
+            // The increment expression of a for loop is allowed to be unreachable, because GLSL
+            // ES2 requires us to provide an increment expression for our for-loops whether or not
+            // it can be reached. Reporting it as "unreachable" isn't helpful if the alternative
+            // is an invalid program.
+            cfg.currentBlock().fAllowUnreachable = true;
             cfg.addExit(cfg.fCurrent, loopStart);
             cfg.addExit(cfg.fCurrent, loopExit);
             fLoopContinues.pop();
@@ -644,6 +649,8 @@
     CFG result;
     result.fStart = result.newBlock();
     result.fCurrent = result.fStart;
+    // The starting block is "reached" implicitly, even if nothing points to it.
+    result.currentBlock().fAllowUnreachable = true;
     this->addStatement(result, &f.body());
     result.newBlock();
     result.fExit = result.fCurrent;
diff --git a/src/sksl/SkSLCFGGenerator.h b/src/sksl/SkSLCFGGenerator.h
index 9b824a9..f1dc52c 100644
--- a/src/sksl/SkSLCFGGenerator.h
+++ b/src/sksl/SkSLCFGGenerator.h
@@ -130,6 +130,7 @@
 
     std::vector<Node> fNodes;
     bool fIsReachable = false;
+    bool fAllowUnreachable = false;
     using ExitArray = SkSTArray<4, BlockId>;
     ExitArray fExits;
     // variable definitions upon entering this basic block (null expression = undefined)
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index f18c05a..6b1d5e2 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -1629,7 +1629,7 @@
     // check for unreachable code
     for (size_t i = 0; i < cfg.fBlocks.size(); i++) {
         const BasicBlock& block = cfg.fBlocks[i];
-        if (i != cfg.fStart && !block.fIsReachable && block.fNodes.size()) {
+        if (!block.fIsReachable && !block.fAllowUnreachable && block.fNodes.size()) {
             int offset;
             const BasicBlock::Node& node = block.fNodes[0];
             if (node.isStatement()) {