Reland "Add switch statement support to PipelineStage."

This is a reland of be056f4f62de76c3f3d6ffb2321bf6592841dc46

The Switch test has been restructured to dodge an iOS bug.

Original change's description:
> Add switch statement support to PipelineStage.
>
> This allows us to write SKSL_TEST_ES3 tests in SkSLTest and have them
> run properly. Previously, such a test would assert inside the pipeline-
> stage generator. In ES2 mode, we will rewrite switches as chained ifs,
> but in ES3 mode we will want to continue emitting them as-is (they will
> be faster than chained ifs on a modern GPU).
>
> `writeSwitchStatement` is adapted from GLSLCodeGenerator.
>
> Change-Id: I532ea5ed49869e7cdffced0cdcd0e353af8d4d79
> Bug: skia:12450
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/450478
> 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>

Bug: skia:12450
Change-Id: I5102081c636ef09cd23f5bc894e6c96e92a4c121
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/450757
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 098a7c6..5a18df9 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -556,6 +556,7 @@
   "/sksl/runtime/LoopFloat.rts",
   "/sksl/runtime/PrecisionQualifiers.rts",
   "/sksl/runtime/SampleWithExplicitCoord.rts",
+  "/sksl/runtime/Switch.rts",
   "/sksl/runtime/VectorIndexing.rts",
 ]
 
diff --git a/resources/sksl/runtime/Switch.rts b/resources/sksl/runtime/Switch.rts
new file mode 100644
index 0000000..bb54a5c
--- /dev/null
+++ b/resources/sksl/runtime/Switch.rts
@@ -0,0 +1,18 @@
+uniform half4 colorGreen, colorRed;
+
+half4 main(float2 coords) {
+    // Basic switch test.
+    half4 color;
+    switch (int(colorGreen.g)) {
+        case 0:
+            color = colorRed;
+            break;
+        case 1:
+            color = colorGreen;
+            break;
+        default:
+            color = colorRed;
+            break;
+    }
+    return color;
+}
diff --git a/resources/sksl/shared/Switch.sksl b/resources/sksl/shared/Switch.sksl
index deabebc..bb54a5c 100644
--- a/resources/sksl/shared/Switch.sksl
+++ b/resources/sksl/shared/Switch.sksl
@@ -1,17 +1,18 @@
-uniform float unknownInput;
+uniform half4 colorGreen, colorRed;
 
-void main() {
+half4 main(float2 coords) {
     // Basic switch test.
-    half value;
-    switch (int(unknownInput)) {
+    half4 color;
+    switch (int(colorGreen.g)) {
         case 0:
-            value = 0.0;
+            color = colorRed;
             break;
         case 1:
-            value = 1.0;
+            color = colorGreen;
             break;
         default:
-            value = 2.0;
+            color = colorRed;
+            break;
     }
-    sk_FragColor = value.xxxx;
+    return color;
 }
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
index fed3718..9e7bcdd 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
@@ -30,6 +30,7 @@
 #include "src/sksl/ir/SkSLPrefixExpression.h"
 #include "src/sksl/ir/SkSLReturnStatement.h"
 #include "src/sksl/ir/SkSLStructDefinition.h"
+#include "src/sksl/ir/SkSLSwitchStatement.h"
 #include "src/sksl/ir/SkSLSwizzle.h"
 #include "src/sksl/ir/SkSLTernaryExpression.h"
 #include "src/sksl/ir/SkSLVarDeclarations.h"
@@ -98,6 +99,7 @@
     void writeDoStatement(const DoStatement& d);
     void writeForStatement(const ForStatement& f);
     void writeReturnStatement(const ReturnStatement& r);
+    void writeSwitchStatement(const SwitchStatement& s);
 
     void writeProgramElement(const ProgramElement& e);
 
@@ -277,6 +279,28 @@
     this->write(";");
 }
 
+void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
+    this->write("switch (");
+    this->writeExpression(*s.value(), Precedence::kTopLevel);
+    this->writeLine(") {");
+    for (const std::unique_ptr<Statement>& stmt : s.cases()) {
+        const SwitchCase& c = stmt->as<SwitchCase>();
+        if (c.value()) {
+            this->write("case ");
+            this->writeExpression(*c.value(), Precedence::kTopLevel);
+            this->writeLine(":");
+        } else {
+            this->writeLine("default:");
+        }
+        if (!c.statement()->isEmpty()) {
+            this->writeStatement(*c.statement());
+            this->writeLine();
+        }
+    }
+    this->writeLine();
+    this->write("}");
+}
+
 String PipelineStageCodeGenerator::functionName(const FunctionDeclaration& decl) {
     if (decl.isMain()) {
         return String(decl.name());
@@ -637,11 +661,13 @@
         case Statement::Kind::kReturn:
             this->writeReturnStatement(s.as<ReturnStatement>());
             break;
+        case Statement::Kind::kSwitch:
+            this->writeSwitchStatement(s.as<SwitchStatement>());
+            break;
         case Statement::Kind::kVarDeclaration:
             this->writeVarDeclaration(s.as<VarDeclaration>());
             break;
         case Statement::Kind::kDiscard:
-        case Statement::Kind::kSwitch:
             SkDEBUGFAIL("Unsupported control flow");
             break;
         case Statement::Kind::kInlineMarker:
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index 53843c5..c167ebe 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -323,6 +323,7 @@
 SKSL_TEST_ES3(SkSLStaticSwitch,                "shared/StaticSwitch.sksl")
 SKSL_TEST(SkSLStructArrayFollowedByScalar,     "shared/StructArrayFollowedByScalar.sksl")
 SKSL_TEST(SkSLStructsInFunctions,              "shared/StructsInFunctions.sksl")
+SKSL_TEST_ES3(SkSLSwitch,                      "shared/Switch.sksl")
 SKSL_TEST(SkSLSwizzleBoolConstants,            "shared/SwizzleBoolConstants.sksl")
 SKSL_TEST(SkSLSwizzleByConstantIndex,          "shared/SwizzleByConstantIndex.sksl")
 SKSL_TEST(SkSLSwizzleConstants,                "shared/SwizzleConstants.sksl")
diff --git a/tests/sksl/runtime/Switch.skvm b/tests/sksl/runtime/Switch.skvm
new file mode 100644
index 0000000..9df38e1
--- /dev/null
+++ b/tests/sksl/runtime/Switch.skvm
@@ -0,0 +1,4 @@
+### Compilation failed:
+
+error: 6: switch statements are not supported
+1 error
diff --git a/tests/sksl/runtime/Switch.stage b/tests/sksl/runtime/Switch.stage
new file mode 100644
index 0000000..9df38e1
--- /dev/null
+++ b/tests/sksl/runtime/Switch.stage
@@ -0,0 +1,4 @@
+### Compilation failed:
+
+error: 6: switch statements are not supported
+1 error
diff --git a/tests/sksl/shared/Switch.asm.frag b/tests/sksl/shared/Switch.asm.frag
index ed5beb9..60559ea 100644
--- a/tests/sksl/shared/Switch.asm.frag
+++ b/tests/sksl/shared/Switch.asm.frag
@@ -1,25 +1,34 @@
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %sk_FragColor %sk_Clockwise
-OpExecutionMode %main OriginUpperLeft
+OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise
+OpExecutionMode %_entrypoint_v OriginUpperLeft
 OpName %sk_FragColor "sk_FragColor"
 OpName %sk_Clockwise "sk_Clockwise"
 OpName %_UniformBuffer "_UniformBuffer"
-OpMemberName %_UniformBuffer 0 "unknownInput"
+OpMemberName %_UniformBuffer 0 "colorGreen"
+OpMemberName %_UniformBuffer 1 "colorRed"
+OpName %_entrypoint_v "_entrypoint_v"
 OpName %main "main"
-OpName %value "value"
+OpName %color "color"
 OpDecorate %sk_FragColor RelaxedPrecision
 OpDecorate %sk_FragColor Location 0
 OpDecorate %sk_FragColor Index 0
 OpDecorate %sk_Clockwise BuiltIn FrontFacing
 OpMemberDecorate %_UniformBuffer 0 Offset 0
+OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 1 Offset 16
+OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
 OpDecorate %_UniformBuffer Block
 OpDecorate %10 Binding 0
 OpDecorate %10 DescriptorSet 0
-OpDecorate %value RelaxedPrecision
-OpDecorate %31 RelaxedPrecision
+OpDecorate %color RelaxedPrecision
 OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %45 RelaxedPrecision
+OpDecorate %46 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -27,38 +36,55 @@
 %bool = OpTypeBool
 %_ptr_Input_bool = OpTypePointer Input %bool
 %sk_Clockwise = OpVariable %_ptr_Input_bool Input
-%_UniformBuffer = OpTypeStruct %float
+%_UniformBuffer = OpTypeStruct %v4float %v4float
 %_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
 %10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
 %void = OpTypeVoid
-%14 = OpTypeFunction %void
-%_ptr_Function_float = OpTypePointer Function %float
-%_ptr_Uniform_float = OpTypePointer Uniform %float
+%15 = OpTypeFunction %void
+%v2float = OpTypeVector %float 2
+%float_0 = OpConstant %float 0
+%19 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%23 = OpTypeFunction %v4float %_ptr_Function_v2float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
 %int = OpTypeInt 32 1
 %int_0 = OpConstant %int 0
-%float_0 = OpConstant %float 0
-%float_1 = OpConstant %float 1
-%float_2 = OpConstant %float 2
-%main = OpFunction %void None %14
-%15 = OpLabel
-%value = OpVariable %_ptr_Function_float Function
-%18 = OpAccessChain %_ptr_Uniform_float %10 %int_0
-%22 = OpLoad %float %18
-%23 = OpConvertFToS %int %22
-OpSelectionMerge %24 None
-OpSwitch %23 %27 0 %25 1 %26
-%25 = OpLabel
-OpStore %value %float_0
-OpBranch %24
-%26 = OpLabel
-OpStore %value %float_1
-OpBranch %24
-%27 = OpLabel
-OpStore %value %float_2
-OpBranch %24
-%24 = OpLabel
-%31 = OpLoad %float %value
-%32 = OpCompositeConstruct %v4float %31 %31 %31 %31
-OpStore %sk_FragColor %32
+%int_1 = OpConstant %int 1
+%_entrypoint_v = OpFunction %void None %15
+%16 = OpLabel
+%20 = OpVariable %_ptr_Function_v2float Function
+OpStore %20 %19
+%22 = OpFunctionCall %v4float %main %20
+OpStore %sk_FragColor %22
 OpReturn
 OpFunctionEnd
+%main = OpFunction %v4float None %23
+%24 = OpFunctionParameter %_ptr_Function_v2float
+%25 = OpLabel
+%color = OpVariable %_ptr_Function_v4float Function
+%28 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%32 = OpLoad %v4float %28
+%33 = OpCompositeExtract %float %32 1
+%34 = OpConvertFToS %int %33
+OpSelectionMerge %35 None
+OpSwitch %34 %38 0 %36 1 %37
+%36 = OpLabel
+%39 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%41 = OpLoad %v4float %39
+OpStore %color %41
+OpBranch %35
+%37 = OpLabel
+%42 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%43 = OpLoad %v4float %42
+OpStore %color %43
+OpBranch %35
+%38 = OpLabel
+%44 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%45 = OpLoad %v4float %44
+OpStore %color %45
+OpBranch %35
+%35 = OpLabel
+%46 = OpLoad %v4float %color
+OpReturnValue %46
+OpFunctionEnd
diff --git a/tests/sksl/shared/Switch.glsl b/tests/sksl/shared/Switch.glsl
index 39b48e2..93d9818 100644
--- a/tests/sksl/shared/Switch.glsl
+++ b/tests/sksl/shared/Switch.glsl
@@ -1,17 +1,19 @@
 
 out vec4 sk_FragColor;
-uniform float unknownInput;
-void main() {
-    float value;
-    switch (int(unknownInput)) {
+uniform vec4 colorGreen;
+uniform vec4 colorRed;
+vec4 main() {
+    vec4 color;
+    switch (int(colorGreen.y)) {
         case 0:
-            value = 0.0;
+            color = colorRed;
             break;
         case 1:
-            value = 1.0;
+            color = colorGreen;
             break;
         default:
-            value = 2.0;
+            color = colorRed;
+            break;
     }
-    sk_FragColor = vec4(value);
+    return color;
 }
diff --git a/tests/sksl/shared/Switch.metal b/tests/sksl/shared/Switch.metal
index fcc6674..16aad7f 100644
--- a/tests/sksl/shared/Switch.metal
+++ b/tests/sksl/shared/Switch.metal
@@ -2,7 +2,8 @@
 #include <simd/simd.h>
 using namespace metal;
 struct Uniforms {
-    float unknownInput;
+    float4 colorGreen;
+    float4 colorRed;
 };
 struct Inputs {
 };
@@ -12,17 +13,18 @@
 fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
     Outputs _out;
     (void)_out;
-    float value;
-    switch (int(_uniforms.unknownInput)) {
+    float4 color;
+    switch (int(_uniforms.colorGreen.y)) {
         case 0:
-            value = 0.0;
+            color = _uniforms.colorRed;
             break;
         case 1:
-            value = 1.0;
+            color = _uniforms.colorGreen;
             break;
         default:
-            value = 2.0;
+            color = _uniforms.colorRed;
+            break;
     }
-    _out.sk_FragColor = float4(value);
+    _out.sk_FragColor = color;
     return _out;
 }