Fix SPIR-V code generation of dead return statements.

If we encounter code like `return 1; return 2;` we need to synthesize a
label, even though the second return statement isn't actually reachable.
This is harmless and satisfies the SPIR-V validator.

Ideally we'd eliminate the dead code entirely, but this case is rare and
isn't likely to cause any problems as-is.

Change-Id: I2d6219dff6868011353e19a662301bec44a015d6
Bug: skia:12009
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/409402
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 83589d3..193bb6e 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -348,6 +348,7 @@
   "/sksl/shared/DeadDoWhileLoop.sksl",
   "/sksl/shared/DeadIfStatement.sksl",
   "/sksl/shared/DeadLoopVariable.sksl",
+  "/sksl/shared/DeadReturn.sksl",
   "/sksl/shared/DeadStripFunctions.sksl",
   "/sksl/shared/DependentInitializers.sksl",
   "/sksl/shared/DerivativesUnused.sksl",
diff --git a/resources/sksl/shared/DeadReturn.sksl b/resources/sksl/shared/DeadReturn.sksl
new file mode 100644
index 0000000..786e4e3
--- /dev/null
+++ b/resources/sksl/shared/DeadReturn.sksl
@@ -0,0 +1,10 @@
+uniform half4 colorGreen, colorRed;
+
+bool test() {
+    return true;
+    return false;
+}
+
+half4 main(float2 xy) {
+    return test() ? colorGreen : colorRed;
+}
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
index 6c26374..9018f9c 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
@@ -205,7 +205,11 @@
         case SpvOpSwitch:      // fall through
         case SpvOpBranch:      // fall through
         case SpvOpBranchConditional:
-            SkASSERT(fCurrentBlock);
+            if (fCurrentBlock == 0) {
+                // We just encountered dead code--instructions that don't have an associated block.
+                // Synthesize a label if this happens; this is necessary to satisfy the validator.
+                this->writeLabel(this->nextId(nullptr), out);
+            }
             fCurrentBlock = 0;
             break;
         case SpvOpConstant:          // fall through
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index 61bf94f..43b379a 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -227,6 +227,7 @@
 SKSL_TEST_ES3(SkSLConstArray,                  "shared/ConstArray.sksl")
 SKSL_TEST(SkSLConstVariableComparison,         "shared/ConstVariableComparison.sksl")
 SKSL_TEST(SkSLDeadIfStatement,                 "shared/DeadIfStatement.sksl")
+SKSL_TEST(SkSLDeadReturn,                      "shared/DeadReturn.sksl")
 SKSL_TEST(SkSLDeadStripFunctions,              "shared/DeadStripFunctions.sksl")
 SKSL_TEST(SkSLDependentInitializers,           "shared/DependentInitializers.sksl")
 SKSL_TEST(SkSLEmptyBlocksES2,                  "shared/EmptyBlocksES2.sksl")
diff --git a/tests/sksl/shared/DeadReturn.asm.frag b/tests/sksl/shared/DeadReturn.asm.frag
new file mode 100644
index 0000000..e5d0996
--- /dev/null
+++ b/tests/sksl/shared/DeadReturn.asm.frag
@@ -0,0 +1,87 @@
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+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 "colorGreen"
+OpMemberName %_UniformBuffer 1 "colorRed"
+OpName %_entrypoint_v "_entrypoint_v"
+OpName %test_b "test_b"
+OpName %main "main"
+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 %11 Binding 0
+OpDecorate %11 DescriptorSet 0
+OpDecorate %42 RelaxedPrecision
+OpDecorate %45 RelaxedPrecision
+OpDecorate %46 RelaxedPrecision
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%sk_FragColor = OpVariable %_ptr_Output_v4float Output
+%bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise = OpVariable %_ptr_Input_bool Input
+%_UniformBuffer = OpTypeStruct %v4float %v4float
+%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
+%11 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
+%void = OpTypeVoid
+%16 = OpTypeFunction %void
+%v2float = OpTypeVector %float 2
+%float_0 = OpConstant %float 0
+%20 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%24 = OpTypeFunction %bool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%29 = 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
+%int_1 = OpConstant %int 1
+%_entrypoint_v = OpFunction %void None %16
+%17 = OpLabel
+%21 = OpVariable %_ptr_Function_v2float Function
+OpStore %21 %20
+%23 = OpFunctionCall %v4float %main %21
+OpStore %sk_FragColor %23
+OpReturn
+OpFunctionEnd
+%test_b = OpFunction %bool None %24
+%25 = OpLabel
+OpReturnValue %true
+%28 = OpLabel
+OpReturnValue %false
+OpFunctionEnd
+%main = OpFunction %v4float None %29
+%30 = OpFunctionParameter %_ptr_Function_v2float
+%31 = OpLabel
+%33 = OpVariable %_ptr_Function_v4float Function
+%32 = OpFunctionCall %bool %test_b
+OpSelectionMerge %37 None
+OpBranchConditional %32 %35 %36
+%35 = OpLabel
+%38 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0
+%42 = OpLoad %v4float %38
+OpStore %33 %42
+OpBranch %37
+%36 = OpLabel
+%43 = OpAccessChain %_ptr_Uniform_v4float %11 %int_1
+%45 = OpLoad %v4float %43
+OpStore %33 %45
+OpBranch %37
+%37 = OpLabel
+%46 = OpLoad %v4float %33
+OpReturnValue %46
+OpFunctionEnd
diff --git a/tests/sksl/shared/DeadReturn.glsl b/tests/sksl/shared/DeadReturn.glsl
new file mode 100644
index 0000000..397c711
--- /dev/null
+++ b/tests/sksl/shared/DeadReturn.glsl
@@ -0,0 +1,11 @@
+
+out vec4 sk_FragColor;
+uniform vec4 colorGreen;
+uniform vec4 colorRed;
+bool test_b() {
+    return true;
+    return false;
+}
+vec4 main() {
+    return test_b() ? colorGreen : colorRed;
+}
diff --git a/tests/sksl/shared/DeadReturn.metal b/tests/sksl/shared/DeadReturn.metal
new file mode 100644
index 0000000..be14809
--- /dev/null
+++ b/tests/sksl/shared/DeadReturn.metal
@@ -0,0 +1,22 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Uniforms {
+    float4 colorGreen;
+    float4 colorRed;
+};
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+bool test_b() {
+    return true;
+    return false;
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _out;
+    (void)_out;
+    _out.sk_FragColor = test_b() ? _uniforms.colorGreen : _uniforms.colorRed;
+    return _out;
+}