do/while loops in SPIR-V no longer cause an assertion failure

BUG=skia:

Change-Id: Ic5f590879d8ada5d4580b5c6e9091ccc9532be4b
Reviewed-on: https://skia-review.googlesource.com/6605
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 1e85f11..8afd136 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2626,6 +2626,12 @@
         case Statement::kFor_Kind:
             this->writeForStatement((ForStatement&) s, out);
             break;
+        case Statement::kWhile_Kind:
+            this->writeWhileStatement((WhileStatement&) s, out);
+            break;
+        case Statement::kDo_Kind:
+            this->writeDoStatement((DoStatement&) s, out);
+            break;
         case Statement::kBreak_Kind:
             this->writeInstruction(SpvOpBranch, fBreakTarget.top(), out);
             break;
@@ -2712,6 +2718,70 @@
     fContinueTarget.pop();
 }
 
+void SPIRVCodeGenerator::writeWhileStatement(const WhileStatement& w, SkWStream& out) {
+    // We believe the while loop code below will work, but Skia doesn't actually use them and
+    // adequately testing this code in the absence of Skia exercising it isn't straightforward. For
+    // the time being, we just fail with an error due to the lack of testing. If you encounter this
+    // message, simply remove the error call below to see whether our while loop support actually
+    // works.
+    fErrors.error(w.fPosition, "internal error: while loop support has been disabled in SPIR-V, "
+                  "see SkSLSPIRVCodeGenerator.cpp for details");
+
+    SpvId header = this->nextId();
+    SpvId start = this->nextId();
+    SpvId body = this->nextId();
+    fContinueTarget.push(start);
+    SpvId end = this->nextId();
+    fBreakTarget.push(end);
+    this->writeInstruction(SpvOpBranch, header, out);
+    this->writeLabel(header, out);
+    this->writeInstruction(SpvOpLoopMerge, end, start, SpvLoopControlMaskNone, out);
+    this->writeInstruction(SpvOpBranch, start, out);
+    this->writeLabel(start, out);
+    SpvId test = this->writeExpression(*w.fTest, out);
+    this->writeInstruction(SpvOpBranchConditional, test, body, end, out);
+    this->writeLabel(body, out);
+    this->writeStatement(*w.fStatement, out);
+    if (fCurrentBlock) {
+        this->writeInstruction(SpvOpBranch, start, out);
+    }
+    this->writeLabel(end, out);
+    fBreakTarget.pop();
+    fContinueTarget.pop();
+}
+
+void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, SkWStream& out) {
+    // We believe the do loop code below will work, but Skia doesn't actually use them and
+    // adequately testing this code in the absence of Skia exercising it isn't straightforward. For
+    // the time being, we just fail with an error due to the lack of testing. If you encounter this
+    // message, simply remove the error call below to see whether our do loop support actually
+    // works.
+    fErrors.error(d.fPosition, "internal error: do loop support has been disabled in SPIR-V, see "
+                  "SkSLSPIRVCodeGenerator.cpp for details");
+
+    SpvId header = this->nextId();
+    SpvId start = this->nextId();
+    SpvId next = this->nextId();
+    fContinueTarget.push(next);
+    SpvId end = this->nextId();
+    fBreakTarget.push(end);
+    this->writeInstruction(SpvOpBranch, header, out);
+    this->writeLabel(header, out);
+    this->writeInstruction(SpvOpLoopMerge, end, start, SpvLoopControlMaskNone, out);
+    this->writeInstruction(SpvOpBranch, start, out);
+    this->writeLabel(start, out);
+    this->writeStatement(*d.fStatement, out);
+    if (fCurrentBlock) {
+        this->writeInstruction(SpvOpBranch, next, out);
+    }
+    this->writeLabel(next, out);
+    SpvId test = this->writeExpression(*d.fTest, out);
+    this->writeInstruction(SpvOpBranchConditional, test, start, end, out);
+    this->writeLabel(end, out);
+    fBreakTarget.pop();
+    fContinueTarget.pop();
+}
+
 void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, SkWStream& out) {
     if (r.fExpression) {
         this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.fExpression, out),
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 96ff187..562bf27 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -18,6 +18,7 @@
 #include "ir/SkSLBinaryExpression.h"
 #include "ir/SkSLBoolLiteral.h"
 #include "ir/SkSLConstructor.h"
+#include "ir/SkSLDoStatement.h"
 #include "ir/SkSLFloatLiteral.h"
 #include "ir/SkSLIfStatement.h"
 #include "ir/SkSLIndexExpression.h"
@@ -38,6 +39,7 @@
 #include "ir/SkSLVarDeclarations.h"
 #include "ir/SkSLVarDeclarationsStatement.h"
 #include "ir/SkSLVariableReference.h"
+#include "ir/SkSLWhileStatement.h"
 #include "spirv.h"
 
 namespace SkSL {
@@ -191,6 +193,10 @@
 
     void writeForStatement(const ForStatement& f, SkWStream& out);
 
+    void writeWhileStatement(const WhileStatement& w, SkWStream& out);
+
+    void writeDoStatement(const DoStatement& d, SkWStream& out);
+
     void writeReturnStatement(const ReturnStatement& r, SkWStream& out);
 
     void writeCapabilities(SkWStream& out);