Support swizzled out-params in SPIR-V intrinsic calls.

The code in writeFunctionCall which supported swizzled out-params has
been factored out into a pair of helper functions. The code for emitting
intrinsics now relies on these helpers when emitting out-params.

Change-Id: I4436185ae107d70b529e7e1ea0dd89f844c6a673
Bug: skia:11052
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/446719
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
index 43f8661..0175625 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
@@ -195,12 +195,12 @@
     return (type.isScalar() || type.isVector()) && type.componentType().isBoolean();
 }
 
-static bool is_out(const Variable& var) {
-    return (var.modifiers().fFlags & Modifiers::kOut_Flag) != 0;
+static bool is_out(const Modifiers& m) {
+    return (m.fFlags & Modifiers::kOut_Flag) != 0;
 }
 
-static bool is_in(const Variable& var) {
-    switch (var.modifiers().fFlags & (Modifiers::kOut_Flag | Modifiers::kIn_Flag)) {
+static bool is_in(const Modifiers& m) {
+    switch (m.fFlags & (Modifiers::kOut_Flag | Modifiers::kIn_Flag)) {
         case Modifiers::kOut_Flag:                       // out
             return false;
 
@@ -703,12 +703,8 @@
             // crashes. Making it take a float* and storing the argument in a temporary variable,
             // as glslang does, fixes it. It's entirely possible I simply missed whichever part of
             // the spec makes this make sense.
-//            if (is_out(function->fParameters[i])) {
-                parameterTypes.push_back(this->getPointerType(parameters[i]->type(),
-                                                              SpvStorageClassFunction));
-//            } else {
-//                parameterTypes.push_back(this->getType(function.fParameters[i]->fType));
-//            }
+            parameterTypes.push_back(this->getPointerType(parameters[i]->type(),
+                                                          SpvStorageClassFunction));
         }
         this->writeOpCode(SpvOpTypeFunction, length, fConstantBuffer);
         this->writeWord(result, fConstantBuffer);
@@ -822,10 +818,15 @@
         case kGLSL_STD_450_IntrinsicOpcodeKind: {
             SpvId result = this->nextId(&c.type());
             std::vector<SpvId> argumentIds;
+            std::vector<TempVar> tempVars;
+            argumentIds.reserve(arguments.size());
             for (size_t i = 0; i < arguments.size(); i++) {
-                if (function.parameters()[i]->modifiers().fFlags & Modifiers::kOut_Flag) {
-                    // TODO(skia:11052): swizzled lvalues won't work with getPointer()
-                    argumentIds.push_back(this->getLValue(*arguments[i], out)->getPointer());
+                if (is_out(function.parameters()[i]->modifiers())) {
+                    argumentIds.push_back(
+                            this->writeFunctionCallArgument(*arguments[i],
+                                                            function.parameters()[i]->modifiers(),
+                                                            &tempVars,
+                                                            out));
                 } else {
                     argumentIds.push_back(this->writeExpression(*arguments[i], out));
                 }
@@ -838,6 +839,7 @@
             for (SpvId id : argumentIds) {
                 this->writeWord(id, out);
             }
+            this->copyBackTempVars(tempVars, out);
             return result;
         }
         case kSPIRV_IntrinsicOpcodeKind: {
@@ -847,10 +849,15 @@
             }
             SpvId result = this->nextId(&c.type());
             std::vector<SpvId> argumentIds;
+            std::vector<TempVar> tempVars;
+            argumentIds.reserve(arguments.size());
             for (size_t i = 0; i < arguments.size(); i++) {
-                if (function.parameters()[i]->modifiers().fFlags & Modifiers::kOut_Flag) {
-                    // TODO(skia:11052): swizzled lvalues won't work with getPointer()
-                    argumentIds.push_back(this->getLValue(*arguments[i], out)->getPointer());
+                if (is_out(function.parameters()[i]->modifiers())) {
+                    argumentIds.push_back(
+                            this->writeFunctionCallArgument(*arguments[i],
+                                                            function.parameters()[i]->modifiers(),
+                                                            &tempVars,
+                                                            out));
                 } else {
                     argumentIds.push_back(this->writeExpression(*arguments[i], out));
                 }
@@ -865,6 +872,7 @@
             for (SpvId id : argumentIds) {
                 this->writeWord(id, out);
             }
+            this->copyBackTempVars(tempVars, out);
             return result;
         }
         case kSpecial_IntrinsicOpcodeKind:
@@ -948,6 +956,7 @@
     switch (kind) {
         case kAtan_SpecialIntrinsic: {
             std::vector<SpvId> argumentIds;
+            argumentIds.reserve(arguments.size());
             for (const std::unique_ptr<Expression>& arg : arguments) {
                 argumentIds.push_back(this->writeExpression(*arg, out));
             }
@@ -1170,12 +1179,52 @@
     return result;
 }
 
-namespace {
-struct TempVar {
-    SpvId spvId;
-    const Type* type;
-    std::unique_ptr<SPIRVCodeGenerator::LValue> lvalue;
-};
+SpvId SPIRVCodeGenerator::writeFunctionCallArgument(const Expression& arg,
+                                                    const Modifiers& paramModifiers,
+                                                    std::vector<TempVar>* tempVars,
+                                                    OutputStream& out) {
+    // ID of temporary variable that we will use to hold this argument, or 0 if it is being
+    // passed directly
+    SpvId tmpVar;
+    // if we need a temporary var to store this argument, this is the value to store in the var
+    SpvId tmpValueId = -1;
+
+    if (is_out(paramModifiers)) {
+        std::unique_ptr<LValue> lv = this->getLValue(arg, out);
+        SpvId ptr = lv->getPointer();
+        if (ptr != (SpvId) -1 && lv->isMemoryObjectPointer()) {
+            return ptr;
+        }
+
+        // lvalue cannot simply be read and written via a pointer (e.g. it's a swizzle). We need to
+        // to use a temp variable.
+        if (is_in(paramModifiers)) {
+            tmpValueId = lv->load(out);
+        }
+        tmpVar = this->nextId(&arg.type());
+        tempVars->push_back(TempVar{tmpVar, &arg.type(), std::move(lv)});
+    } else {
+        // See getFunctionType for an explanation of why we're always using pointer parameters.
+        tmpValueId = this->writeExpression(arg, out);
+        tmpVar = this->nextId(nullptr);
+    }
+    this->writeInstruction(SpvOpVariable,
+                           this->getPointerType(arg.type(), SpvStorageClassFunction),
+                           tmpVar,
+                           SpvStorageClassFunction,
+                           fVariableBuffer);
+    if (tmpValueId != (SpvId)-1) {
+        this->writeInstruction(SpvOpStore, tmpVar, tmpValueId, out);
+    }
+    return tmpVar;
+}
+
+void SPIRVCodeGenerator::copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out) {
+    for (const TempVar& tempVar : tempVars) {
+        SpvId load = this->nextId(tempVar.type);
+        this->writeInstruction(SpvOpLoad, this->getType(*tempVar.type), load, tempVar.spvId, out);
+        tempVar.lvalue->store(load, out);
+    }
 }
 
 SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, OutputStream& out) {
@@ -1193,42 +1242,12 @@
     // Temp variables are used to write back out-parameters after the function call is complete.
     std::vector<TempVar> tempVars;
     std::vector<SpvId> argumentIds;
+    argumentIds.reserve(arguments.size());
     for (size_t i = 0; i < arguments.size(); i++) {
-        // id of temporary variable that we will use to hold this argument, or 0 if it is being
-        // passed directly
-        SpvId tmpVar;
-        // if we need a temporary var to store this argument, this is the value to store in the var
-        SpvId tmpValueId = -1;
-        if (is_out(*function.parameters()[i])) {
-            std::unique_ptr<LValue> lv = this->getLValue(*arguments[i], out);
-            SpvId ptr = lv->getPointer();
-            if (ptr != (SpvId) -1 && lv->isMemoryObjectPointer()) {
-                argumentIds.push_back(ptr);
-                continue;
-            } else {
-                // lvalue cannot simply be read and written via a pointer (e.g. a swizzle). Need to
-                // copy it into a temp, call the function, read the value out of the temp, and then
-                // update the lvalue.
-                if (is_in(*function.parameters()[i])) {
-                    tmpValueId = lv->load(out);
-                }
-                tmpVar = this->nextId(&arguments[i]->type());
-                tempVars.push_back(TempVar{tmpVar, &arguments[i]->type(), std::move(lv)});
-            }
-        } else {
-            // See getFunctionType for an explanation of why we're always using pointer parameters.
-            tmpValueId = this->writeExpression(*arguments[i], out);
-            tmpVar = this->nextId(nullptr);
-        }
-        this->writeInstruction(SpvOpVariable,
-                               this->getPointerType(arguments[i]->type(), SpvStorageClassFunction),
-                               tmpVar,
-                               SpvStorageClassFunction,
-                               fVariableBuffer);
-        if (tmpValueId != (SpvId)-1) {
-            this->writeInstruction(SpvOpStore, tmpVar, tmpValueId, out);
-        }
-        argumentIds.push_back(tmpVar);
+        argumentIds.push_back(this->writeFunctionCallArgument(*arguments[i],
+                                                              function.parameters()[i]->modifiers(),
+                                                              &tempVars,
+                                                              out));
     }
     SpvId result = this->nextId(nullptr);
     this->writeOpCode(SpvOpFunctionCall, 4 + (int32_t) arguments.size(), out);
@@ -1239,11 +1258,7 @@
         this->writeWord(id, out);
     }
     // Now that the call is complete, we copy temp out-variables back to their real lvalues.
-    for (const TempVar& tempVar : tempVars) {
-        SpvId load = this->nextId(tempVar.type);
-        this->writeInstruction(SpvOpLoad, getType(*tempVar.type), load, tempVar.spvId, out);
-        tempVar.lvalue->store(load, out);
-    }
+    this->copyBackTempVars(tempVars, out);
     return result;
 }
 
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.h b/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
index 55bdc74..5c08c82 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
@@ -172,6 +172,12 @@
         kRelaxed,
     };
 
+    struct TempVar {
+        SpvId spvId;
+        const Type* type;
+        std::unique_ptr<SPIRVCodeGenerator::LValue> lvalue;
+    };
+
     void setupIntrinsics();
 
     /**
@@ -229,6 +235,13 @@
 
     SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out);
 
+    SpvId writeFunctionCallArgument(const Expression& arg,
+                                    const Modifiers& paramModifiers,
+                                    std::vector<TempVar>* tempVars,
+                                    OutputStream& out);
+
+    void copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out);
+
     SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out);
 
 
diff --git a/tests/sksl/intrinsics/Frexp.asm.frag b/tests/sksl/intrinsics/Frexp.asm.frag
index 0609bc8..dd5e084 100644
--- a/tests/sksl/intrinsics/Frexp.asm.frag
+++ b/tests/sksl/intrinsics/Frexp.asm.frag
@@ -1,8 +1,3 @@
-### Compilation failed:
-
-error: SPIR-V validation error: ID 4294967295[%4294967295] has not been defined
-  %64 = OpExtInst %v2float %1 Frexp %66 %4294967295
-
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -33,10 +28,10 @@
 OpDecorate %32 RelaxedPrecision
 OpDecorate %33 RelaxedPrecision
 OpDecorate %35 RelaxedPrecision
-OpDecorate %110 RelaxedPrecision
-OpDecorate %116 RelaxedPrecision
-OpDecorate %118 RelaxedPrecision
-OpDecorate %119 RelaxedPrecision
+OpDecorate %124 RelaxedPrecision
+OpDecorate %130 RelaxedPrecision
+OpDecorate %132 RelaxedPrecision
+OpDecorate %133 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -69,8 +64,12 @@
 %float_0_75 = OpConstant %float 0.75
 %int_3 = OpConstant %int 3
 %_ptr_Function_bool = OpTypePointer Function %bool
+%v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
 %int_1 = OpConstant %int 1
 %v3float = OpTypeVector %float 3
+%v3int = OpTypeVector %int 3
+%_ptr_Function_v3int = OpTypePointer Function %v3int
 %int_2 = OpConstant %int 2
 %_entrypoint_v = OpFunction %void None %15
 %16 = OpLabel
@@ -87,7 +86,10 @@
 %exp = OpVariable %_ptr_Function_v4int Function
 %result = OpVariable %_ptr_Function_v4float Function
 %ok = OpVariable %_ptr_Function_v4bool Function
-%111 = OpVariable %_ptr_Function_v4float Function
+%48 = OpVariable %_ptr_Function_int Function
+%69 = OpVariable %_ptr_Function_v2int Function
+%92 = OpVariable %_ptr_Function_v3int Function
+%125 = OpVariable %_ptr_Function_v4float Function
 %28 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
 %32 = OpLoad %v4float %28
 %33 = OpVectorShuffle %v4float %32 %32 1 1 1 1
@@ -96,97 +98,105 @@
 %44 = OpLoad %v4float %value
 %45 = OpCompositeExtract %float %44 0
 %46 = OpAccessChain %_ptr_Function_int %exp %int_0
-%43 = OpExtInst %float %1 Frexp %45 %46
-%48 = OpAccessChain %_ptr_Function_float %result %int_0
-OpStore %48 %43
-%51 = OpLoad %v4float %result
-%52 = OpCompositeExtract %float %51 0
-%54 = OpFOrdEqual %bool %52 %float_0_75
-OpSelectionMerge %56 None
-OpBranchConditional %54 %55 %56
-%55 = OpLabel
-%57 = OpLoad %v4int %exp
-%58 = OpCompositeExtract %int %57 0
-%60 = OpIEqual %bool %58 %int_3
-OpBranch %56
-%56 = OpLabel
-%61 = OpPhi %bool %false %25 %60 %55
-%62 = OpAccessChain %_ptr_Function_bool %ok %int_0
-OpStore %62 %61
-%65 = OpLoad %v4float %value
-%66 = OpVectorShuffle %v2float %65 %65 0 1
-%64 = OpExtInst %v2float %1 Frexp %66 %4294967295
-%67 = OpLoad %v4float %result
-%68 = OpVectorShuffle %v4float %67 %64 4 5 2 3
-OpStore %result %68
-%69 = OpLoad %v4float %result
-%70 = OpCompositeExtract %float %69 1
-%71 = OpFOrdEqual %bool %70 %float_0_75
-OpSelectionMerge %73 None
-OpBranchConditional %71 %72 %73
-%72 = OpLabel
-%74 = OpLoad %v4int %exp
-%75 = OpCompositeExtract %int %74 1
-%76 = OpIEqual %bool %75 %int_3
-OpBranch %73
-%73 = OpLabel
-%77 = OpPhi %bool %false %56 %76 %72
-%78 = OpAccessChain %_ptr_Function_bool %ok %int_1
-OpStore %78 %77
-%81 = OpLoad %v4float %value
-%82 = OpVectorShuffle %v3float %81 %81 0 1 2
-%80 = OpExtInst %v3float %1 Frexp %82 %4294967295
-%84 = OpLoad %v4float %result
-%85 = OpVectorShuffle %v4float %84 %80 4 5 6 3
-OpStore %result %85
-%86 = OpLoad %v4float %result
-%87 = OpCompositeExtract %float %86 2
-%88 = OpFOrdEqual %bool %87 %float_0_75
-OpSelectionMerge %90 None
-OpBranchConditional %88 %89 %90
-%89 = OpLabel
-%91 = OpLoad %v4int %exp
-%92 = OpCompositeExtract %int %91 2
-%93 = OpIEqual %bool %92 %int_3
-OpBranch %90
-%90 = OpLabel
-%94 = OpPhi %bool %false %73 %93 %89
-%95 = OpAccessChain %_ptr_Function_bool %ok %int_2
-OpStore %95 %94
-%98 = OpLoad %v4float %value
-%97 = OpExtInst %v4float %1 Frexp %98 %exp
-OpStore %result %97
-%99 = OpLoad %v4float %result
-%100 = OpCompositeExtract %float %99 3
-%101 = OpFOrdEqual %bool %100 %float_0_75
-OpSelectionMerge %103 None
-OpBranchConditional %101 %102 %103
-%102 = OpLabel
-%104 = OpLoad %v4int %exp
-%105 = OpCompositeExtract %int %104 3
-%106 = OpIEqual %bool %105 %int_3
-OpBranch %103
+%43 = OpExtInst %float %1 Frexp %45 %48
+%49 = OpLoad %int %48
+OpStore %46 %49
+%50 = OpAccessChain %_ptr_Function_float %result %int_0
+OpStore %50 %43
+%53 = OpLoad %v4float %result
+%54 = OpCompositeExtract %float %53 0
+%56 = OpFOrdEqual %bool %54 %float_0_75
+OpSelectionMerge %58 None
+OpBranchConditional %56 %57 %58
+%57 = OpLabel
+%59 = OpLoad %v4int %exp
+%60 = OpCompositeExtract %int %59 0
+%62 = OpIEqual %bool %60 %int_3
+OpBranch %58
+%58 = OpLabel
+%63 = OpPhi %bool %false %25 %62 %57
+%64 = OpAccessChain %_ptr_Function_bool %ok %int_0
+OpStore %64 %63
+%67 = OpLoad %v4float %value
+%68 = OpVectorShuffle %v2float %67 %67 0 1
+%66 = OpExtInst %v2float %1 Frexp %68 %69
+%72 = OpLoad %v2int %69
+%73 = OpLoad %v4int %exp
+%74 = OpVectorShuffle %v4int %73 %72 4 5 2 3
+OpStore %exp %74
+%75 = OpLoad %v4float %result
+%76 = OpVectorShuffle %v4float %75 %66 4 5 2 3
+OpStore %result %76
+%77 = OpLoad %v4float %result
+%78 = OpCompositeExtract %float %77 1
+%79 = OpFOrdEqual %bool %78 %float_0_75
+OpSelectionMerge %81 None
+OpBranchConditional %79 %80 %81
+%80 = OpLabel
+%82 = OpLoad %v4int %exp
+%83 = OpCompositeExtract %int %82 1
+%84 = OpIEqual %bool %83 %int_3
+OpBranch %81
+%81 = OpLabel
+%85 = OpPhi %bool %false %58 %84 %80
+%86 = OpAccessChain %_ptr_Function_bool %ok %int_1
+OpStore %86 %85
+%89 = OpLoad %v4float %value
+%90 = OpVectorShuffle %v3float %89 %89 0 1 2
+%88 = OpExtInst %v3float %1 Frexp %90 %92
+%95 = OpLoad %v3int %92
+%96 = OpLoad %v4int %exp
+%97 = OpVectorShuffle %v4int %96 %95 4 5 6 3
+OpStore %exp %97
+%98 = OpLoad %v4float %result
+%99 = OpVectorShuffle %v4float %98 %88 4 5 6 3
+OpStore %result %99
+%100 = OpLoad %v4float %result
+%101 = OpCompositeExtract %float %100 2
+%102 = OpFOrdEqual %bool %101 %float_0_75
+OpSelectionMerge %104 None
+OpBranchConditional %102 %103 %104
 %103 = OpLabel
-%107 = OpPhi %bool %false %90 %106 %102
-%108 = OpAccessChain %_ptr_Function_bool %ok %int_3
-OpStore %108 %107
-%110 = OpLoad %v4bool %ok
-%109 = OpAll %bool %110
-OpSelectionMerge %114 None
-OpBranchConditional %109 %112 %113
-%112 = OpLabel
-%115 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%116 = OpLoad %v4float %115
-OpStore %111 %116
-OpBranch %114
-%113 = OpLabel
-%117 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
-%118 = OpLoad %v4float %117
-OpStore %111 %118
-OpBranch %114
-%114 = OpLabel
-%119 = OpLoad %v4float %111
-OpReturnValue %119
+%105 = OpLoad %v4int %exp
+%106 = OpCompositeExtract %int %105 2
+%107 = OpIEqual %bool %106 %int_3
+OpBranch %104
+%104 = OpLabel
+%108 = OpPhi %bool %false %81 %107 %103
+%109 = OpAccessChain %_ptr_Function_bool %ok %int_2
+OpStore %109 %108
+%112 = OpLoad %v4float %value
+%111 = OpExtInst %v4float %1 Frexp %112 %exp
+OpStore %result %111
+%113 = OpLoad %v4float %result
+%114 = OpCompositeExtract %float %113 3
+%115 = OpFOrdEqual %bool %114 %float_0_75
+OpSelectionMerge %117 None
+OpBranchConditional %115 %116 %117
+%116 = OpLabel
+%118 = OpLoad %v4int %exp
+%119 = OpCompositeExtract %int %118 3
+%120 = OpIEqual %bool %119 %int_3
+OpBranch %117
+%117 = OpLabel
+%121 = OpPhi %bool %false %104 %120 %116
+%122 = OpAccessChain %_ptr_Function_bool %ok %int_3
+OpStore %122 %121
+%124 = OpLoad %v4bool %ok
+%123 = OpAll %bool %124
+OpSelectionMerge %128 None
+OpBranchConditional %123 %126 %127
+%126 = OpLabel
+%129 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%130 = OpLoad %v4float %129
+OpStore %125 %130
+OpBranch %128
+%127 = OpLabel
+%131 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%132 = OpLoad %v4float %131
+OpStore %125 %132
+OpBranch %128
+%128 = OpLabel
+%133 = OpLoad %v4float %125
+OpReturnValue %133
 OpFunctionEnd
-
-1 error
diff --git a/tests/sksl/intrinsics/Modf.asm.frag b/tests/sksl/intrinsics/Modf.asm.frag
index 937b33d..1884aed 100644
--- a/tests/sksl/intrinsics/Modf.asm.frag
+++ b/tests/sksl/intrinsics/Modf.asm.frag
@@ -1,8 +1,3 @@
-### Compilation failed:
-
-error: SPIR-V validation error: ID 4294967295[%4294967295] has not been defined
-  %61 = OpExtInst %v2float %1 Modf %63 %4294967295
-
 OpCapability Shader
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical GLSL450
@@ -33,10 +28,10 @@
 OpDecorate %32 RelaxedPrecision
 OpDecorate %33 RelaxedPrecision
 OpDecorate %35 RelaxedPrecision
-OpDecorate %108 RelaxedPrecision
-OpDecorate %114 RelaxedPrecision
-OpDecorate %116 RelaxedPrecision
-OpDecorate %117 RelaxedPrecision
+OpDecorate %119 RelaxedPrecision
+OpDecorate %125 RelaxedPrecision
+OpDecorate %127 RelaxedPrecision
+OpDecorate %128 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -68,6 +63,7 @@
 %_ptr_Function_bool = OpTypePointer Function %bool
 %int_1 = OpConstant %int 1
 %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
 %int_2 = OpConstant %int 2
 %int_3 = OpConstant %int 3
 %_entrypoint_v = OpFunction %void None %15
@@ -85,7 +81,10 @@
 %whole = OpVariable %_ptr_Function_v4float Function
 %fraction = OpVariable %_ptr_Function_v4float Function
 %ok = OpVariable %_ptr_Function_v4bool Function
-%109 = OpVariable %_ptr_Function_v4float Function
+%46 = OpVariable %_ptr_Function_float Function
+%66 = OpVariable %_ptr_Function_v2float Function
+%87 = OpVariable %_ptr_Function_v3float Function
+%120 = OpVariable %_ptr_Function_v4float Function
 %28 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
 %32 = OpLoad %v4float %28
 %33 = OpVectorShuffle %v4float %32 %32 1 1 1 1
@@ -94,97 +93,105 @@
 %42 = OpLoad %v4float %value
 %43 = OpCompositeExtract %float %42 0
 %44 = OpAccessChain %_ptr_Function_float %whole %int_0
-%41 = OpExtInst %float %1 Modf %43 %44
-%46 = OpAccessChain %_ptr_Function_float %fraction %int_0
-OpStore %46 %41
-%48 = OpLoad %v4float %whole
-%49 = OpCompositeExtract %float %48 0
-%51 = OpFOrdEqual %bool %49 %float_2
-OpSelectionMerge %53 None
-OpBranchConditional %51 %52 %53
-%52 = OpLabel
-%54 = OpLoad %v4float %fraction
-%55 = OpCompositeExtract %float %54 0
-%57 = OpFOrdEqual %bool %55 %float_0_5
-OpBranch %53
-%53 = OpLabel
-%58 = OpPhi %bool %false %25 %57 %52
-%59 = OpAccessChain %_ptr_Function_bool %ok %int_0
-OpStore %59 %58
-%62 = OpLoad %v4float %value
-%63 = OpVectorShuffle %v2float %62 %62 0 1
-%61 = OpExtInst %v2float %1 Modf %63 %4294967295
-%64 = OpLoad %v4float %fraction
-%65 = OpVectorShuffle %v4float %64 %61 4 5 2 3
-OpStore %fraction %65
-%66 = OpLoad %v4float %whole
-%67 = OpCompositeExtract %float %66 1
-%68 = OpFOrdEqual %bool %67 %float_2
-OpSelectionMerge %70 None
-OpBranchConditional %68 %69 %70
-%69 = OpLabel
-%71 = OpLoad %v4float %fraction
-%72 = OpCompositeExtract %float %71 1
-%73 = OpFOrdEqual %bool %72 %float_0_5
-OpBranch %70
-%70 = OpLabel
-%74 = OpPhi %bool %false %53 %73 %69
-%75 = OpAccessChain %_ptr_Function_bool %ok %int_1
-OpStore %75 %74
-%78 = OpLoad %v4float %value
-%79 = OpVectorShuffle %v3float %78 %78 0 1 2
-%77 = OpExtInst %v3float %1 Modf %79 %4294967295
-%81 = OpLoad %v4float %fraction
-%82 = OpVectorShuffle %v4float %81 %77 4 5 6 3
-OpStore %fraction %82
-%83 = OpLoad %v4float %whole
-%84 = OpCompositeExtract %float %83 2
-%85 = OpFOrdEqual %bool %84 %float_2
-OpSelectionMerge %87 None
-OpBranchConditional %85 %86 %87
-%86 = OpLabel
-%88 = OpLoad %v4float %fraction
-%89 = OpCompositeExtract %float %88 2
-%90 = OpFOrdEqual %bool %89 %float_0_5
-OpBranch %87
-%87 = OpLabel
-%91 = OpPhi %bool %false %70 %90 %86
-%92 = OpAccessChain %_ptr_Function_bool %ok %int_2
-OpStore %92 %91
-%95 = OpLoad %v4float %value
-%94 = OpExtInst %v4float %1 Modf %95 %whole
-OpStore %fraction %94
-%96 = OpLoad %v4float %whole
-%97 = OpCompositeExtract %float %96 3
-%98 = OpFOrdEqual %bool %97 %float_2
-OpSelectionMerge %100 None
-OpBranchConditional %98 %99 %100
-%99 = OpLabel
-%101 = OpLoad %v4float %fraction
-%102 = OpCompositeExtract %float %101 3
-%103 = OpFOrdEqual %bool %102 %float_0_5
-OpBranch %100
-%100 = OpLabel
-%104 = OpPhi %bool %false %87 %103 %99
-%105 = OpAccessChain %_ptr_Function_bool %ok %int_3
-OpStore %105 %104
-%108 = OpLoad %v4bool %ok
-%107 = OpAll %bool %108
-OpSelectionMerge %112 None
-OpBranchConditional %107 %110 %111
+%41 = OpExtInst %float %1 Modf %43 %46
+%47 = OpLoad %float %46
+OpStore %44 %47
+%48 = OpAccessChain %_ptr_Function_float %fraction %int_0
+OpStore %48 %41
+%50 = OpLoad %v4float %whole
+%51 = OpCompositeExtract %float %50 0
+%53 = OpFOrdEqual %bool %51 %float_2
+OpSelectionMerge %55 None
+OpBranchConditional %53 %54 %55
+%54 = OpLabel
+%56 = OpLoad %v4float %fraction
+%57 = OpCompositeExtract %float %56 0
+%59 = OpFOrdEqual %bool %57 %float_0_5
+OpBranch %55
+%55 = OpLabel
+%60 = OpPhi %bool %false %25 %59 %54
+%61 = OpAccessChain %_ptr_Function_bool %ok %int_0
+OpStore %61 %60
+%64 = OpLoad %v4float %value
+%65 = OpVectorShuffle %v2float %64 %64 0 1
+%63 = OpExtInst %v2float %1 Modf %65 %66
+%67 = OpLoad %v2float %66
+%68 = OpLoad %v4float %whole
+%69 = OpVectorShuffle %v4float %68 %67 4 5 2 3
+OpStore %whole %69
+%70 = OpLoad %v4float %fraction
+%71 = OpVectorShuffle %v4float %70 %63 4 5 2 3
+OpStore %fraction %71
+%72 = OpLoad %v4float %whole
+%73 = OpCompositeExtract %float %72 1
+%74 = OpFOrdEqual %bool %73 %float_2
+OpSelectionMerge %76 None
+OpBranchConditional %74 %75 %76
+%75 = OpLabel
+%77 = OpLoad %v4float %fraction
+%78 = OpCompositeExtract %float %77 1
+%79 = OpFOrdEqual %bool %78 %float_0_5
+OpBranch %76
+%76 = OpLabel
+%80 = OpPhi %bool %false %55 %79 %75
+%81 = OpAccessChain %_ptr_Function_bool %ok %int_1
+OpStore %81 %80
+%84 = OpLoad %v4float %value
+%85 = OpVectorShuffle %v3float %84 %84 0 1 2
+%83 = OpExtInst %v3float %1 Modf %85 %87
+%89 = OpLoad %v3float %87
+%90 = OpLoad %v4float %whole
+%91 = OpVectorShuffle %v4float %90 %89 4 5 6 3
+OpStore %whole %91
+%92 = OpLoad %v4float %fraction
+%93 = OpVectorShuffle %v4float %92 %83 4 5 6 3
+OpStore %fraction %93
+%94 = OpLoad %v4float %whole
+%95 = OpCompositeExtract %float %94 2
+%96 = OpFOrdEqual %bool %95 %float_2
+OpSelectionMerge %98 None
+OpBranchConditional %96 %97 %98
+%97 = OpLabel
+%99 = OpLoad %v4float %fraction
+%100 = OpCompositeExtract %float %99 2
+%101 = OpFOrdEqual %bool %100 %float_0_5
+OpBranch %98
+%98 = OpLabel
+%102 = OpPhi %bool %false %76 %101 %97
+%103 = OpAccessChain %_ptr_Function_bool %ok %int_2
+OpStore %103 %102
+%106 = OpLoad %v4float %value
+%105 = OpExtInst %v4float %1 Modf %106 %whole
+OpStore %fraction %105
+%107 = OpLoad %v4float %whole
+%108 = OpCompositeExtract %float %107 3
+%109 = OpFOrdEqual %bool %108 %float_2
+OpSelectionMerge %111 None
+OpBranchConditional %109 %110 %111
 %110 = OpLabel
-%113 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%114 = OpLoad %v4float %113
-OpStore %109 %114
-OpBranch %112
+%112 = OpLoad %v4float %fraction
+%113 = OpCompositeExtract %float %112 3
+%114 = OpFOrdEqual %bool %113 %float_0_5
+OpBranch %111
 %111 = OpLabel
-%115 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
-%116 = OpLoad %v4float %115
-OpStore %109 %116
-OpBranch %112
-%112 = OpLabel
-%117 = OpLoad %v4float %109
-OpReturnValue %117
+%115 = OpPhi %bool %false %98 %114 %110
+%116 = OpAccessChain %_ptr_Function_bool %ok %int_3
+OpStore %116 %115
+%119 = OpLoad %v4bool %ok
+%118 = OpAll %bool %119
+OpSelectionMerge %123 None
+OpBranchConditional %118 %121 %122
+%121 = OpLabel
+%124 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%125 = OpLoad %v4float %124
+OpStore %120 %125
+OpBranch %123
+%122 = OpLabel
+%126 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%127 = OpLoad %v4float %126
+OpStore %120 %127
+OpBranch %123
+%123 = OpLabel
+%128 = OpLoad %v4float %120
+OpReturnValue %128
 OpFunctionEnd
-
-1 error