Merge pull request #1410 from KhronosGroup/fix-1406

GLSL: Support switch more properly in legacy ESSL
diff --git a/reference/opt/shaders/legacy/fragment/switch.legacy.frag b/reference/opt/shaders/legacy/fragment/switch.legacy.frag
new file mode 100644
index 0000000..21a4d67
--- /dev/null
+++ b/reference/opt/shaders/legacy/fragment/switch.legacy.frag
@@ -0,0 +1,77 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+varying highp float vIndexF;
+
+void main()
+{
+    int _13 = int(vIndexF);
+    highp vec4 _65;
+    highp vec4 _66;
+    highp vec4 _68;
+    for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)
+    {
+        if (_13 == 2)
+        {
+            _68 = vec4(0.0, 2.0, 3.0, 4.0);
+            break;
+        }
+        else if ((_13 == 4) || (_13 == 5))
+        {
+            _68 = vec4(1.0, 2.0, 3.0, 4.0);
+            break;
+        }
+        else if ((_13 == 8) || (_13 == 9))
+        {
+            _68 = vec4(40.0, 20.0, 30.0, 40.0);
+            break;
+        }
+        else if (_13 == 10)
+        {
+            _65 = vec4(10.0);
+            highp vec4 _45 = _65 + vec4(1.0);
+            _66 = _45;
+            highp vec4 _48 = _66 + vec4(2.0);
+            _68 = _48;
+            break;
+        }
+        else if (_13 == 11)
+        {
+            _65 = vec4(0.0);
+            highp vec4 _45 = _65 + vec4(1.0);
+            _66 = _45;
+            highp vec4 _48 = _66 + vec4(2.0);
+            _68 = _48;
+            break;
+        }
+        else if (_13 == 12)
+        {
+            _66 = vec4(0.0);
+            highp vec4 _48 = _66 + vec4(2.0);
+            _68 = _48;
+            break;
+        }
+        else
+        {
+            _68 = vec4(10.0, 20.0, 30.0, 40.0);
+            break;
+        }
+    }
+    highp vec4 _70;
+    for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)
+    {
+        if ((_13 == 10) || (_13 == 20))
+        {
+            _70 = vec4(40.0);
+            break;
+        }
+        else
+        {
+            _70 = vec4(20.0);
+            break;
+        }
+    }
+    gl_FragData[0] = _68 + _70;
+}
+
diff --git a/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag
index ba66ccf..19af59d 100644
--- a/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag
+++ b/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag
@@ -17,14 +17,6 @@
     int j;
     int _30;
     int _31;
-    if (vIndex != 0 && vIndex != 1 && vIndex != 11 && vIndex != 2 && vIndex != 3 && vIndex != 4 && vIndex != 5)
-    {
-        _30 = 2;
-    }
-    if (vIndex == 1 || vIndex == 11)
-    {
-        _31 = 1;
-    }
     switch (vIndex)
     {
         case 0:
@@ -37,6 +29,7 @@
         }
         default:
         {
+            _30 = 2;
             j = _30;
             _31 = 0;
             j = _31;
@@ -45,6 +38,7 @@
         case 1:
         case 11:
         {
+            _31 = 1;
             j = _31;
             break;
         }
diff --git a/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag
new file mode 100644
index 0000000..d36e782
--- /dev/null
+++ b/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag
@@ -0,0 +1,24 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+vec2 _19;
+
+void main()
+{
+    highp vec2 _30;
+    for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)
+    {
+        if (gl_FragCoord.x != gl_FragCoord.x)
+        {
+            _30 = _19;
+            break;
+        }
+        highp vec2 _29 = _19;
+        _29.y = _19.y;
+        _30 = _29;
+        break;
+    }
+    gl_FragData[0] = vec4(_30, 1.0, 1.0);
+}
+
diff --git a/reference/shaders/legacy/fragment/switch.legacy.frag b/reference/shaders/legacy/fragment/switch.legacy.frag
new file mode 100644
index 0000000..9960ce7
--- /dev/null
+++ b/reference/shaders/legacy/fragment/switch.legacy.frag
@@ -0,0 +1,78 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+varying highp float vIndexF;
+
+void main()
+{
+    int vIndex = int(vIndexF);
+    highp vec4 v = vec4(0.0);
+    for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)
+    {
+        if (vIndex == 2)
+        {
+            v = vec4(0.0, 2.0, 3.0, 4.0);
+            break;
+        }
+        else if ((vIndex == 4) || (vIndex == 5))
+        {
+            v = vec4(1.0, 2.0, 3.0, 4.0);
+            break;
+        }
+        else if ((vIndex == 8) || (vIndex == 9))
+        {
+            v = vec4(40.0, 20.0, 30.0, 40.0);
+            break;
+        }
+        else if (vIndex == 10)
+        {
+            v = vec4(10.0);
+            highp vec4 _43 = v;
+            highp vec4 _44 = vec4(1.0);
+            highp vec4 _45 = _43 + _44;
+            v = _45;
+            highp vec4 _46 = v;
+            highp vec4 _47 = vec4(2.0);
+            highp vec4 _48 = _46 + _47;
+            v = _48;
+            break;
+        }
+        else if (vIndex == 11)
+        {
+            highp vec4 _43 = v;
+            highp vec4 _44 = vec4(1.0);
+            highp vec4 _45 = _43 + _44;
+            v = _45;
+            highp vec4 _46 = v;
+            highp vec4 _47 = vec4(2.0);
+            highp vec4 _48 = _46 + _47;
+            v = _48;
+            break;
+        }
+        else if (vIndex == 12)
+        {
+            highp vec4 _46 = v;
+            highp vec4 _47 = vec4(2.0);
+            highp vec4 _48 = _46 + _47;
+            v = _48;
+            break;
+        }
+        else
+        {
+            v = vec4(10.0, 20.0, 30.0, 40.0);
+            break;
+        }
+    }
+    highp vec4 w = vec4(20.0);
+    for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)
+    {
+        if ((vIndex == 10) || (vIndex == 20))
+        {
+            w = vec4(40.0);
+            break;
+        }
+    }
+    gl_FragData[0] = v + w;
+}
+
diff --git a/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag
new file mode 100644
index 0000000..d2bd15a
--- /dev/null
+++ b/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 54
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color
+               OpExecutionMode %main OriginUpperLeft
+               OpSource ESSL 310
+               OpName %main "main"
+               OpName %gl_FragCoord "gl_FragCoord"
+               OpName %_GLF_color "_GLF_color"
+               OpDecorate %gl_FragCoord BuiltIn FragCoord
+               OpDecorate %_GLF_color Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Input_float = OpTypePointer Input %float
+       %bool = OpTypeBool
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %_GLF_color = OpVariable %_ptr_Output_v4float Output
+    %float_1 = OpConstant %float 1
+         %52 = OpUndef %v2float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpSelectionMerge %9 None
+               OpSwitch %int_0 %8
+          %8 = OpLabel
+         %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0
+         %18 = OpLoad %float %17
+         %22 = OpFOrdNotEqual %bool %18 %18
+               OpSelectionMerge %24 None
+               OpBranchConditional %22 %23 %24
+         %23 = OpLabel
+               OpBranch %9
+         %24 = OpLabel
+         %33 = OpCompositeExtract %float %52 1
+         %51 = OpCompositeInsert %v2float %33 %52 1
+               OpBranch %9
+          %9 = OpLabel
+         %53 = OpPhi %v2float %52 %23 %51 %24
+         %42 = OpCompositeExtract %float %53 0
+         %43 = OpCompositeExtract %float %53 1
+         %48 = OpCompositeConstruct %v4float %42 %43 %float_1 %float_1
+               OpStore %_GLF_color %48
+               OpReturn
+               OpFunctionEnd
diff --git a/shaders/legacy/fragment/switch.legacy.frag b/shaders/legacy/fragment/switch.legacy.frag
new file mode 100644
index 0000000..d511798
--- /dev/null
+++ b/shaders/legacy/fragment/switch.legacy.frag
@@ -0,0 +1,43 @@
+#version 450
+
+layout(location = 0) out vec4 FragColor;
+layout(location = 0) in float vIndexF;
+
+void main()
+{
+	int vIndex = int(vIndexF);
+	vec4 v = vec4(0.0);
+	switch (vIndex)
+	{
+	case 2:
+		v = vec4(0, 2, 3, 4);
+		break;
+	case 4:
+	case 5:
+		v = vec4(1, 2, 3, 4);
+		break;
+	case 8:
+	case 9:
+		v = vec4(40, 20, 30, 40);
+		break;
+	case 10:
+		v = vec4(10.0);
+	case 11:
+		v += 1.0;
+	case 12:
+		v += 2.0;
+		break;
+	default:
+		v = vec4(10, 20, 30, 40);
+		break;
+	}
+
+	vec4 w = vec4(20.0);
+	switch (vIndex)
+	{
+	case 10:
+	case 20:
+		w = vec4(40.0);
+	}
+	FragColor = v + w;
+}
diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp
index 8d8241e..5f2a48f 100644
--- a/spirv_glsl.cpp
+++ b/spirv_glsl.cpp
@@ -540,6 +540,9 @@
 	backend.supports_extensions = true;
 	backend.use_array_constructor = true;
 
+	if (is_legacy_es())
+		backend.support_case_fallthrough = false;
+
 	// Scan the SPIR-V to find trivial uses of extensions.
 	fixup_type_alias();
 	reorder_type_alias();
@@ -13295,6 +13298,8 @@
 			// Order does not matter.
 			if (!injected_block)
 				block_declaration_order.push_back(block.default_block);
+			else if (is_legacy_es())
+				SPIRV_CROSS_THROW("Default case label fallthrough to other case label is not supported in ESSL 1.0.");
 
 			case_constructs[block.default_block] = {};
 		}
@@ -13305,12 +13310,25 @@
 			return is_unsigned_case ? convert_to_string(literal) : convert_to_string(int32_t(literal));
 		};
 
+		const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector<uint32_t> &labels, const char *suffix) -> string {
+			string ret;
+			size_t count = labels.size();
+			for (size_t i = 0; i < count; i++)
+			{
+				if (i)
+					ret += " || ";
+				ret += join(count > 1 ? "(" : "", to_enclosed_expression(condition), " == ", labels[i], suffix,
+				            count > 1 ? ")" : "");
+			}
+			return ret;
+		};
+
 		// We need to deal with a complex scenario for OpPhi. If we have case-fallthrough and Phi in the picture,
 		// we need to flush phi nodes outside the switch block in a branch,
 		// and skip any Phi handling inside the case label to make fall-through work as expected.
 		// This kind of code-gen is super awkward and it's a last resort. Normally we would want to handle this
 		// inside the case label if at all possible.
-		for (size_t i = 1; i < num_blocks; i++)
+		for (size_t i = 1; backend.support_case_fallthrough && i < num_blocks; i++)
 		{
 			if (flush_phi_required(block.self, block_declaration_order[i]) &&
 			    flush_phi_required(block_declaration_order[i - 1], block_declaration_order[i]))
@@ -13364,8 +13382,14 @@
 		// This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic.
 		bool degenerate_switch = block.default_block != block.merge_block && block.cases.empty();
 
-		if (degenerate_switch)
-			statement("do");
+		if (degenerate_switch || is_legacy_es())
+		{
+			// ESSL 1.0 is not guaranteed to support do/while.
+			if (is_legacy_es())
+				statement("for (int SPIRV_Cross_Dummy = 0; SPIRV_Cross_Dummy < 1; SPIRV_Cross_Dummy++)");
+			else
+				statement("do");
+		}
 		else
 		{
 			emit_block_hints(block);
@@ -13382,14 +13406,28 @@
 			{
 				// Default case.
 				if (!degenerate_switch)
-					statement("default:");
+				{
+					if (is_legacy_es())
+						statement("else");
+					else
+						statement("default:");
+				}
 			}
 			else
 			{
-				for (auto &case_literal : literals)
+				if (is_legacy_es())
 				{
-					// The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here.
-					statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
+					statement((i ? "else " : ""), "if (",
+					          to_legacy_case_label(block.condition, literals, label_suffix),
+					          ")");
+				}
+				else
+				{
+					for (auto &case_literal : literals)
+					{
+						// The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here.
+						statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
+					}
 				}
 			}
 
@@ -13424,7 +13462,12 @@
 					statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
 
 				if (block.default_block == block.next_block)
-					statement("default:");
+				{
+					if (is_legacy_es())
+						statement("else");
+					else
+						statement("default:");
+				}
 
 				begin_scope();
 				flush_phi(block.self, block.next_block);
@@ -13433,7 +13476,7 @@
 			}
 		}
 
-		if (degenerate_switch)
+		if (degenerate_switch && !is_legacy_es())
 			end_scope_decl("while(false)");
 		else
 			end_scope();
diff --git a/test_shaders.py b/test_shaders.py
index c0130bf..b455873 100755
--- a/test_shaders.py
+++ b/test_shaders.py
@@ -492,7 +492,7 @@
         remove_file(glsl_path)
         glsl_path = None
 
-    if vulkan or spirv:
+    if (vulkan or spirv) and (not is_legacy):
         subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args)
         validate_shader(vulkan_glsl_path, True, paths)
         # SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output.