Merge pull request #1103 from KhronosGroup/fix-1100

MSL: Cleanup temporary use with emit_uninitialized_temporary.
diff --git a/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag b/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag
index b596a84..93bb69e 100644
--- a/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag
+++ b/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag
@@ -14,14 +14,12 @@
 #line 8 "test.frag"
 void frag_main()
 {
-    float _80;
 #line 8 "test.frag"
     FragColor = 1.0f;
 #line 9 "test.frag"
     FragColor = 2.0f;
 #line 10 "test.frag"
-    _80 = vColor;
-    if (_80 < 0.0f)
+    if (vColor < 0.0f)
     {
 #line 12 "test.frag"
         FragColor = 3.0f;
@@ -31,16 +29,16 @@
 #line 16 "test.frag"
         FragColor = 4.0f;
     }
-    for (int _126 = 0; float(_126) < (40.0f + _80); )
+    for (int _126 = 0; float(_126) < (40.0f + vColor); )
     {
 #line 21 "test.frag"
         FragColor += 0.20000000298023223876953125f;
 #line 22 "test.frag"
         FragColor += 0.300000011920928955078125f;
-        _126 += (int(_80) + 5);
+        _126 += (int(vColor) + 5);
         continue;
     }
-    switch (int(_80))
+    switch (int(vColor))
     {
         case 0:
         {
@@ -66,7 +64,7 @@
     }
     for (;;)
     {
-        FragColor += (10.0f + _80);
+        FragColor += (10.0f + vColor);
 #line 43 "test.frag"
         if (FragColor < 100.0f)
         {
diff --git a/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag b/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag
index 30018aa..0d9666d 100644
--- a/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag
+++ b/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag
@@ -17,14 +17,12 @@
 fragment main0_out main0(main0_in in [[stage_in]])
 {
     main0_out out = {};
-    float _80;
 #line 8 "test.frag"
     out.FragColor = 1.0;
 #line 9 "test.frag"
     out.FragColor = 2.0;
 #line 10 "test.frag"
-    _80 = in.vColor;
-    if (_80 < 0.0)
+    if (in.vColor < 0.0)
     {
 #line 12 "test.frag"
         out.FragColor = 3.0;
@@ -34,16 +32,16 @@
 #line 16 "test.frag"
         out.FragColor = 4.0;
     }
-    for (int _126 = 0; float(_126) < (40.0 + _80); )
+    for (int _126 = 0; float(_126) < (40.0 + in.vColor); )
     {
 #line 21 "test.frag"
         out.FragColor += 0.20000000298023223876953125;
 #line 22 "test.frag"
         out.FragColor += 0.300000011920928955078125;
-        _126 += (int(_80) + 5);
+        _126 += (int(in.vColor) + 5);
         continue;
     }
-    switch (int(_80))
+    switch (int(in.vColor))
     {
         case 0:
         {
@@ -69,7 +67,7 @@
     }
     for (;;)
     {
-        out.FragColor += (10.0 + _80);
+        out.FragColor += (10.0 + in.vColor);
 #line 43 "test.frag"
         if (out.FragColor < 100.0)
         {
diff --git a/reference/opt/shaders/asm/frag/line-directive.line.asm.frag b/reference/opt/shaders/asm/frag/line-directive.line.asm.frag
index 30be934..74eb62b 100644
--- a/reference/opt/shaders/asm/frag/line-directive.line.asm.frag
+++ b/reference/opt/shaders/asm/frag/line-directive.line.asm.frag
@@ -7,14 +7,12 @@
 #line 8 "test.frag"
 void main()
 {
-    float _80;
 #line 8 "test.frag"
     FragColor = 1.0;
 #line 9 "test.frag"
     FragColor = 2.0;
 #line 10 "test.frag"
-    _80 = vColor;
-    if (_80 < 0.0)
+    if (vColor < 0.0)
     {
 #line 12 "test.frag"
         FragColor = 3.0;
@@ -24,16 +22,16 @@
 #line 16 "test.frag"
         FragColor = 4.0;
     }
-    for (int _126 = 0; float(_126) < (40.0 + _80); )
+    for (int _126 = 0; float(_126) < (40.0 + vColor); )
     {
 #line 21 "test.frag"
         FragColor += 0.20000000298023223876953125;
 #line 22 "test.frag"
         FragColor += 0.300000011920928955078125;
-        _126 += (int(_80) + 5);
+        _126 += (int(vColor) + 5);
         continue;
     }
-    switch (int(_80))
+    switch (int(vColor))
     {
         case 0:
         {
@@ -59,7 +57,7 @@
     }
     for (;;)
     {
-        FragColor += (10.0 + _80);
+        FragColor += (10.0 + vColor);
 #line 43 "test.frag"
         if (FragColor < 100.0)
         {
diff --git a/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag b/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag
new file mode 100644
index 0000000..910c8fa
--- /dev/null
+++ b/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag
@@ -0,0 +1,17 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+fragment void main0()
+{
+    float3 col;
+    int2 _18;
+    float _23;
+    float _21 = modf(0.1500000059604644775390625, _23);
+    col.x = _23;
+    int _24;
+    float _22 = frexp(0.1500000059604644775390625, _24);
+    _18.y = _24;
+}
+
diff --git a/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag b/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag
new file mode 100644
index 0000000..707fa55
--- /dev/null
+++ b/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag
@@ -0,0 +1,36 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 17
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource ESSL 310
+               OpName %main "main"
+               OpName %col "col"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0_150000006 = OpConstant %float 0.150000006
+    %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+       %int = OpTypeInt 32 1
+     %int_0 = OpConstant %int 0
+     %int_1 = OpConstant %int 1
+	 %v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+%_ptr_Function_int = OpTypePointer Function %int
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %col = OpVariable %_ptr_Function_v3float Function
+        %icol = OpVariable %_ptr_Function_v2int Function
+         %ptr_x = OpAccessChain %_ptr_Function_float %col %int_0
+         %ptr_y = OpAccessChain %_ptr_Function_int %icol %int_1
+         %16 = OpExtInst %float %1 Modf %float_0_150000006 %ptr_x
+         %17 = OpExtInst %float %1 Frexp %float_0_150000006 %ptr_y
+               OpReturn
+               OpFunctionEnd
diff --git a/spirv_cross.cpp b/spirv_cross.cpp
index c29f168..3a9670e 100644
--- a/spirv_cross.cpp
+++ b/spirv_cross.cpp
@@ -3294,10 +3294,11 @@
 		{
 			builder.add_block(block);
 
-			// If a temporary is used in more than one block, we might have to lift continue block
-			// access up to loop header like we did for variables.
 			if (blocks.size() != 1 && is_continue(block))
 			{
+				// The risk here is that inner loop can dominate the continue block.
+				// Any temporary we access in the continue block must be declared before the loop.
+				// This is moot for complex loops however.
 				auto &loop_header_block = get<SPIRBlock>(ir.continue_block_to_loop_header[block]);
 				assert(loop_header_block.merge == SPIRBlock::MergeLoop);
 
@@ -3305,14 +3306,17 @@
 				if (!loop_header_block.complex_continue)
 					builder.add_block(loop_header_block.self);
 			}
-			else if (blocks.size() != 1 && is_single_block_loop(block))
-			{
-				// Awkward case, because the loop header is also the continue block.
-				force_temporary = true;
-			}
 		}
 
 		uint32_t dominating_block = builder.get_dominator();
+
+		if (blocks.size() != 1 && is_single_block_loop(dominating_block))
+		{
+			// Awkward case, because the loop header is also the continue block,
+			// so hoisting to loop header does not help.
+			force_temporary = true;
+		}
+
 		if (dominating_block)
 		{
 			// If we touch a variable in the dominating block, this is the expected setup.
diff --git a/spirv_msl.cpp b/spirv_msl.cpp
index ccb6b40..7b6b070 100644
--- a/spirv_msl.cpp
+++ b/spirv_msl.cpp
@@ -5253,6 +5253,32 @@
 			CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
 		break;
 
+	case GLSLstd450Modf:
+	case GLSLstd450Frexp:
+	{
+		// Special case. If the variable is a scalar access chain, we cannot use it directly. We have to emit a temporary.
+		auto *ptr = maybe_get<SPIRExpression>(args[1]);
+		if (ptr && ptr->access_chain && is_scalar(expression_type(args[1])))
+		{
+			register_call_out_argument(args[1]);
+			forced_temporaries.insert(id);
+
+			// Need to create temporaries and copy over to access chain after.
+			// We cannot directly take the reference of a vector swizzle in MSL, even if it's scalar ...
+			uint32_t &tmp_id = extra_sub_expressions[id];
+			if (!tmp_id)
+				tmp_id = ir.increase_bound_by(1);
+
+			uint32_t tmp_type_id = get_pointee_type_id(ptr->expression_type);
+			emit_uninitialized_temporary_expression(tmp_type_id, tmp_id);
+			emit_binary_func_op(result_type, id, args[0], tmp_id, eop == GLSLstd450Modf ? "modf" : "frexp");
+			statement(to_expression(args[1]), " = ", to_expression(tmp_id), ";");
+		}
+		else
+			CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
+		break;
+	}
+
 	default:
 		CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
 		break;