Avoid mix-up between MAIN_COORDS and FRAGCOORD.

When compiling test shaders, we were setting SK_FRAGCOORD_BUILTIN on the
`coords` parameter to main() instead of SK_MAIN_COORDS_BUILTIN. These
two built-ins don't have the same type (float2 vs. float4) and don't
mean quite the same thing.

The SPIR-V code generator saw a variable with the SK_FRAGCOORD_BUILTIN
builtin value and assumed the presence of a global variable named
`sk_FragCoord`, which didn't exist (because it was never referenced in
the code, so it was never cloned in from the sksl_frag module).

This is only a concern when compiling test shaders with skslc; real
shaders don't hit these code paths. The generated code here is still
imperfect; if you look closely, you'll see the GLSL and Metal code is
referencing the `coords` variable but it's never declared anywhere.

Change-Id: I3ad249469927ff35eb1e75d6536f95317502708f
Bug: skia:12340
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/440520
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>
diff --git a/resources/sksl/intrinsics/DFdx.sksl b/resources/sksl/intrinsics/DFdx.sksl
index 630a0cf..93d23e9 100644
--- a/resources/sksl/intrinsics/DFdx.sksl
+++ b/resources/sksl/intrinsics/DFdx.sksl
@@ -6,11 +6,8 @@
     return (dFdx(testInputs.x)    == expected.x     &&
             dFdx(testInputs.xy)   == expected.xy    &&
             dFdx(testInputs.xyz)  == expected.xyz   &&
-            dFdx(testInputs.xyzw) == expected.xyzw) ? colorGreen : colorRed;
-/*
-            // TODO(skia:12340): This test triggers an RTFlip bug in the SPIR-V code generator
+            dFdx(testInputs.xyzw) == expected.xyzw  &&
             sign(dFdx(coords.xx)) == half2(1, 1)    &&
             sign(dFdx(coords.yy)) == half2(0, 0)    &&
-            sign(dFdx(coords.xy)) == half2(1, 0)
-*/
+            sign(dFdx(coords.xy)) == half2(1, 0)) ? colorGreen : colorRed;
 }
diff --git a/resources/sksl/intrinsics/DFdy.sksl b/resources/sksl/intrinsics/DFdy.sksl
index 3ac4433..29abe06 100644
--- a/resources/sksl/intrinsics/DFdy.sksl
+++ b/resources/sksl/intrinsics/DFdy.sksl
@@ -6,11 +6,8 @@
     return (dFdx(testInputs.x)    == expected.x     &&
             dFdx(testInputs.xy)   == expected.xy    &&
             dFdx(testInputs.xyz)  == expected.xyz   &&
-            dFdx(testInputs.xyzw) == expected.xyzw) ? colorGreen : colorRed;
-/*
-            // TODO(skia:12340): This test triggers an RTFlip bug in the SPIR-V code generator
+            dFdx(testInputs.xyzw) == expected.xyzw  &&
             sign(dFdy(coords.xx)) == half2(0, 0)    &&
             sign(dFdy(coords.yy)) == half2(1, 1)    &&
-            sign(dFdy(coords.xy)) == half2(0, 1)
-*/
+            sign(dFdy(coords.xy)) == half2(0, 1)) ? colorGreen : colorRed;
 }
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
index 1e54c63..e004297 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
@@ -2007,7 +2007,7 @@
                 typeId = this->getType(type, this->memoryLayoutForVariable(var));
             }
             auto entry = fVariableMap.find(&var);
-            SkASSERT(entry != fVariableMap.end());
+            SkASSERTF(entry != fVariableMap.end(), "%s", expr.description().c_str());
             return std::make_unique<PointerLValue>(*this, entry->second,
                                                    /*isMemoryObjectPointer=*/true,
                                                    typeId, precision);
@@ -2069,14 +2069,15 @@
 }
 
 SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) {
-    if (ref.variable()->modifiers().fLayout.fBuiltin == DEVICE_FRAGCOORDS_BUILTIN) {
+    const Variable* variable = ref.variable();
+    if (variable->modifiers().fLayout.fBuiltin == DEVICE_FRAGCOORDS_BUILTIN) {
         // Down below, we rewrite raw references to sk_FragCoord with expressions that reference
         // DEVICE_FRAGCOORDS_BUILTIN. This is a fake variable that means we need to directly access
         // the fragcoord; do so now.
         dsl::DSLGlobalVar fragCoord("sk_FragCoord");
         return this->getLValue(*dsl::DSLExpression(fragCoord).release(), out)->load(out);
     }
-    if (ref.variable()->modifiers().fLayout.fBuiltin == DEVICE_CLOCKWISE_BUILTIN) {
+    if (variable->modifiers().fLayout.fBuiltin == DEVICE_CLOCKWISE_BUILTIN) {
         // Down below, we rewrite raw references to sk_Clockwise with expressions that reference
         // DEVICE_CLOCKWISE_BUILTIN. This is a fake variable that means we need to directly
         // access front facing; do so now.
@@ -2085,7 +2086,6 @@
     }
 
     // Handle inserting use of uniform to flip y when referencing sk_FragCoord.
-    const Variable* variable = ref.variable();
     if (variable->modifiers().fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
         this->addRTFlipUniform(ref.fOffset);
         // Use sk_RTAdjust to compute the flipped coordinate
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.cpp b/src/sksl/ir/SkSLFunctionDeclaration.cpp
index 7892947..d5a447c 100644
--- a/src/sksl/ir/SkSLFunctionDeclaration.cpp
+++ b/src/sksl/ir/SkSLFunctionDeclaration.cpp
@@ -108,9 +108,9 @@
             } else if (context.fConfig->fKind == ProgramKind::kFragment) {
                 // For testing purposes, we have .sksl inputs that are treated as both runtime
                 // effects and fragment shaders. To make that work, fragment shaders are allowed to
-                // have a coords parameter. We turn it into sk_FragCoord.
+                // have a coords parameter.
                 if (type == *context.fTypes.fFloat2) {
-                    m.fLayout.fBuiltin = SK_FRAGCOORD_BUILTIN;
+                    m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
                     param->setModifiers(context.fModifiersPool->add(m));
                 }
             }
@@ -133,9 +133,7 @@
         const Variable& p = *parameters[idx];
         return p.type() == *context.fTypes.fFloat2 &&
                p.modifiers().fFlags == 0 &&
-               p.modifiers().fLayout.fBuiltin == (kind == ProgramKind::kFragment
-                                                           ? SK_FRAGCOORD_BUILTIN
-                                                           : SK_MAIN_COORDS_BUILTIN);
+               p.modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
     };
 
     auto paramIsBuiltinColor = [&](int idx, int builtinID) {
diff --git a/tests/sksl/intrinsics/DFdx.asm.frag b/tests/sksl/intrinsics/DFdx.asm.frag
index d2c93db..ff5be54 100644
--- a/tests/sksl/intrinsics/DFdx.asm.frag
+++ b/tests/sksl/intrinsics/DFdx.asm.frag
@@ -45,9 +45,9 @@
 OpDecorate %67 RelaxedPrecision
 OpDecorate %69 RelaxedPrecision
 OpDecorate %70 RelaxedPrecision
-OpDecorate %81 RelaxedPrecision
-OpDecorate %84 RelaxedPrecision
-OpDecorate %85 RelaxedPrecision
+OpDecorate %110 RelaxedPrecision
+OpDecorate %113 RelaxedPrecision
+OpDecorate %114 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -75,6 +75,9 @@
 %v3float = OpTypeVector %float 3
 %v3bool = OpTypeVector %bool 3
 %v4bool = OpTypeVector %bool 4
+%float_1 = OpConstant %float 1
+%82 = OpConstantComposite %v2float %float_1 %float_1
+%100 = OpConstantComposite %v2float %float_1 %float_0
 %int_1 = OpConstant %int 1
 %int_2 = OpConstant %int 2
 %_entrypoint_v = OpFunction %void None %15
@@ -89,7 +92,7 @@
 %24 = OpFunctionParameter %_ptr_Function_v2float
 %25 = OpLabel
 %expected = OpVariable %_ptr_Function_v4float Function
-%75 = OpVariable %_ptr_Function_v4float Function
+%104 = OpVariable %_ptr_Function_v4float Function
 OpStore %expected %28
 %31 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
 %35 = OpLoad %v4float %31
@@ -138,19 +141,54 @@
 OpBranch %66
 %66 = OpLabel
 %74 = OpPhi %bool %false %53 %73 %65
-OpSelectionMerge %78 None
-OpBranchConditional %74 %76 %77
+OpSelectionMerge %76 None
+OpBranchConditional %74 %75 %76
+%75 = OpLabel
+%79 = OpLoad %v2float %24
+%80 = OpVectorShuffle %v2float %79 %79 0 0
+%78 = OpDPdx %v2float %80
+%77 = OpExtInst %v2float %1 FSign %78
+%83 = OpFOrdEqual %v2bool %77 %82
+%84 = OpAll %bool %83
+OpBranch %76
 %76 = OpLabel
-%79 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
-%81 = OpLoad %v4float %79
-OpStore %75 %81
-OpBranch %78
-%77 = OpLabel
-%82 = OpAccessChain %_ptr_Uniform_v4float %10 %int_2
-%84 = OpLoad %v4float %82
-OpStore %75 %84
-OpBranch %78
-%78 = OpLabel
-%85 = OpLoad %v4float %75
-OpReturnValue %85
+%85 = OpPhi %bool %false %66 %84 %75
+OpSelectionMerge %87 None
+OpBranchConditional %85 %86 %87
+%86 = OpLabel
+%90 = OpLoad %v2float %24
+%91 = OpVectorShuffle %v2float %90 %90 1 1
+%89 = OpDPdx %v2float %91
+%88 = OpExtInst %v2float %1 FSign %89
+%92 = OpFOrdEqual %v2bool %88 %19
+%93 = OpAll %bool %92
+OpBranch %87
+%87 = OpLabel
+%94 = OpPhi %bool %false %76 %93 %86
+OpSelectionMerge %96 None
+OpBranchConditional %94 %95 %96
+%95 = OpLabel
+%99 = OpLoad %v2float %24
+%98 = OpDPdx %v2float %99
+%97 = OpExtInst %v2float %1 FSign %98
+%101 = OpFOrdEqual %v2bool %97 %100
+%102 = OpAll %bool %101
+OpBranch %96
+%96 = OpLabel
+%103 = OpPhi %bool %false %87 %102 %95
+OpSelectionMerge %107 None
+OpBranchConditional %103 %105 %106
+%105 = OpLabel
+%108 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%110 = OpLoad %v4float %108
+OpStore %104 %110
+OpBranch %107
+%106 = OpLabel
+%111 = OpAccessChain %_ptr_Uniform_v4float %10 %int_2
+%113 = OpLoad %v4float %111
+OpStore %104 %113
+OpBranch %107
+%107 = OpLabel
+%114 = OpLoad %v4float %104
+OpReturnValue %114
 OpFunctionEnd
diff --git a/tests/sksl/intrinsics/DFdx.glsl b/tests/sksl/intrinsics/DFdx.glsl
index 5a9aa93..98636cb 100644
--- a/tests/sksl/intrinsics/DFdx.glsl
+++ b/tests/sksl/intrinsics/DFdx.glsl
@@ -5,5 +5,5 @@
 uniform vec4 colorRed;
 vec4 main() {
     vec4 expected = vec4(0.0);
-    return ((dFdx(testInputs.x) == expected.x && dFdx(testInputs.xy) == expected.xy) && dFdx(testInputs.xyz) == expected.xyz) && dFdx(testInputs) == expected ? colorGreen : colorRed;
+    return (((((dFdx(testInputs.x) == expected.x && dFdx(testInputs.xy) == expected.xy) && dFdx(testInputs.xyz) == expected.xyz) && dFdx(testInputs) == expected) && sign(dFdx(coords.xx)) == vec2(1.0, 1.0)) && sign(dFdx(coords.yy)) == vec2(0.0, 0.0)) && sign(dFdx(coords)) == vec2(1.0, 0.0) ? colorGreen : colorRed;
 }
diff --git a/tests/sksl/intrinsics/DFdx.metal b/tests/sksl/intrinsics/DFdx.metal
index 451fc47..0782a9a 100644
--- a/tests/sksl/intrinsics/DFdx.metal
+++ b/tests/sksl/intrinsics/DFdx.metal
@@ -15,6 +15,6 @@
     Outputs _out;
     (void)_out;
     float4 expected = float4(0.0);
-    _out.sk_FragColor = ((dfdx(_uniforms.testInputs.x) == expected.x && all(dfdx(_uniforms.testInputs.xy) == expected.xy)) && all(dfdx(_uniforms.testInputs.xyz) == expected.xyz)) && all(dfdx(_uniforms.testInputs) == expected) ? _uniforms.colorGreen : _uniforms.colorRed;
+    _out.sk_FragColor = (((((dfdx(_uniforms.testInputs.x) == expected.x && all(dfdx(_uniforms.testInputs.xy) == expected.xy)) && all(dfdx(_uniforms.testInputs.xyz) == expected.xyz)) && all(dfdx(_uniforms.testInputs) == expected)) && all(sign(dfdx(coords.xx)) == float2(1.0, 1.0))) && all(sign(dfdx(coords.yy)) == float2(0.0, 0.0))) && all(sign(dfdx(coords)) == float2(1.0, 0.0)) ? _uniforms.colorGreen : _uniforms.colorRed;
     return _out;
 }
diff --git a/tests/sksl/intrinsics/DFdy.asm.frag b/tests/sksl/intrinsics/DFdy.asm.frag
index d2c93db..6ec97e5 100644
--- a/tests/sksl/intrinsics/DFdy.asm.frag
+++ b/tests/sksl/intrinsics/DFdy.asm.frag
@@ -9,6 +9,7 @@
 OpMemberName %_UniformBuffer 0 "testInputs"
 OpMemberName %_UniformBuffer 1 "colorGreen"
 OpMemberName %_UniformBuffer 2 "colorRed"
+OpMemberName %_UniformBuffer 3 "u_skRTFlip"
 OpName %_entrypoint_v "_entrypoint_v"
 OpName %main "main"
 OpName %expected "expected"
@@ -22,32 +23,33 @@
 OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
 OpMemberDecorate %_UniformBuffer 2 Offset 32
 OpMemberDecorate %_UniformBuffer 2 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 3 Offset 16384
 OpDecorate %_UniformBuffer Block
-OpDecorate %10 Binding 0
-OpDecorate %10 DescriptorSet 0
+OpDecorate %11 Binding 0
+OpDecorate %11 DescriptorSet 0
 OpDecorate %expected RelaxedPrecision
-OpDecorate %28 RelaxedPrecision
-OpDecorate %30 RelaxedPrecision
-OpDecorate %35 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
 OpDecorate %36 RelaxedPrecision
 OpDecorate %37 RelaxedPrecision
 OpDecorate %38 RelaxedPrecision
-OpDecorate %42 RelaxedPrecision
-OpDecorate %44 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
 OpDecorate %45 RelaxedPrecision
 OpDecorate %46 RelaxedPrecision
 OpDecorate %47 RelaxedPrecision
-OpDecorate %54 RelaxedPrecision
-OpDecorate %56 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
 OpDecorate %57 RelaxedPrecision
-OpDecorate %59 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
 OpDecorate %60 RelaxedPrecision
-OpDecorate %67 RelaxedPrecision
-OpDecorate %69 RelaxedPrecision
+OpDecorate %61 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
 OpDecorate %70 RelaxedPrecision
-OpDecorate %81 RelaxedPrecision
-OpDecorate %84 RelaxedPrecision
-OpDecorate %85 RelaxedPrecision
+OpDecorate %71 RelaxedPrecision
+OpDecorate %128 RelaxedPrecision
+OpDecorate %131 RelaxedPrecision
+OpDecorate %132 RelaxedPrecision
 %float = OpTypeFloat 32
 %v4float = OpTypeVector %float 4
 %_ptr_Output_v4float = OpTypePointer Output %v4float
@@ -55,18 +57,18 @@
 %bool = OpTypeBool
 %_ptr_Input_bool = OpTypePointer Input %bool
 %sk_Clockwise = OpVariable %_ptr_Input_bool Input
-%_UniformBuffer = OpTypeStruct %v4float %v4float %v4float
-%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
-%10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
-%void = OpTypeVoid
-%15 = OpTypeFunction %void
 %v2float = OpTypeVector %float 2
+%_UniformBuffer = OpTypeStruct %v4float %v4float %v4float %v2float
+%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
+%11 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
+%void = OpTypeVoid
+%17 = OpTypeFunction %void
 %float_0 = OpConstant %float 0
-%19 = OpConstantComposite %v2float %float_0 %float_0
+%20 = OpConstantComposite %v2float %float_0 %float_0
 %_ptr_Function_v2float = OpTypePointer Function %v2float
-%23 = OpTypeFunction %v4float %_ptr_Function_v2float
+%24 = OpTypeFunction %v4float %_ptr_Function_v2float
 %_ptr_Function_v4float = OpTypePointer Function %v4float
-%28 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%29 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
 %false = OpConstantFalse %bool
 %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
 %int = OpTypeInt 32 1
@@ -75,82 +77,137 @@
 %v3float = OpTypeVector %float 3
 %v3bool = OpTypeVector %bool 3
 %v4bool = OpTypeVector %bool 4
+%int_3 = OpConstant %int 3
+%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
+%float_1 = OpConstant %float 1
+%104 = OpConstantComposite %v2float %float_1 %float_1
+%118 = OpConstantComposite %v2float %float_0 %float_1
 %int_1 = OpConstant %int 1
 %int_2 = OpConstant %int 2
-%_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
+%_entrypoint_v = OpFunction %void None %17
+%18 = OpLabel
+%21 = OpVariable %_ptr_Function_v2float Function
+OpStore %21 %20
+%23 = OpFunctionCall %v4float %main %21
+OpStore %sk_FragColor %23
 OpReturn
 OpFunctionEnd
-%main = OpFunction %v4float None %23
-%24 = OpFunctionParameter %_ptr_Function_v2float
-%25 = OpLabel
+%main = OpFunction %v4float None %24
+%25 = OpFunctionParameter %_ptr_Function_v2float
+%26 = OpLabel
 %expected = OpVariable %_ptr_Function_v4float Function
-%75 = OpVariable %_ptr_Function_v4float Function
-OpStore %expected %28
-%31 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%35 = OpLoad %v4float %31
-%36 = OpCompositeExtract %float %35 0
-%30 = OpDPdx %float %36
-%37 = OpLoad %v4float %expected
-%38 = OpCompositeExtract %float %37 0
-%39 = OpFOrdEqual %bool %30 %38
-OpSelectionMerge %41 None
-OpBranchConditional %39 %40 %41
-%40 = OpLabel
-%43 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%44 = OpLoad %v4float %43
-%45 = OpVectorShuffle %v2float %44 %44 0 1
-%42 = OpDPdx %v2float %45
-%46 = OpLoad %v4float %expected
-%47 = OpVectorShuffle %v2float %46 %46 0 1
-%48 = OpFOrdEqual %v2bool %42 %47
-%50 = OpAll %bool %48
-OpBranch %41
+%122 = OpVariable %_ptr_Function_v4float Function
+OpStore %expected %29
+%32 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0
+%36 = OpLoad %v4float %32
+%37 = OpCompositeExtract %float %36 0
+%31 = OpDPdx %float %37
+%38 = OpLoad %v4float %expected
+%39 = OpCompositeExtract %float %38 0
+%40 = OpFOrdEqual %bool %31 %39
+OpSelectionMerge %42 None
+OpBranchConditional %40 %41 %42
 %41 = OpLabel
-%51 = OpPhi %bool %false %25 %50 %40
-OpSelectionMerge %53 None
-OpBranchConditional %51 %52 %53
-%52 = OpLabel
-%55 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%56 = OpLoad %v4float %55
-%57 = OpVectorShuffle %v3float %56 %56 0 1 2
-%54 = OpDPdx %v3float %57
-%59 = OpLoad %v4float %expected
-%60 = OpVectorShuffle %v3float %59 %59 0 1 2
-%61 = OpFOrdEqual %v3bool %54 %60
-%63 = OpAll %bool %61
-OpBranch %53
+%44 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0
+%45 = OpLoad %v4float %44
+%46 = OpVectorShuffle %v2float %45 %45 0 1
+%43 = OpDPdx %v2float %46
+%47 = OpLoad %v4float %expected
+%48 = OpVectorShuffle %v2float %47 %47 0 1
+%49 = OpFOrdEqual %v2bool %43 %48
+%51 = OpAll %bool %49
+OpBranch %42
+%42 = OpLabel
+%52 = OpPhi %bool %false %26 %51 %41
+OpSelectionMerge %54 None
+OpBranchConditional %52 %53 %54
 %53 = OpLabel
-%64 = OpPhi %bool %false %41 %63 %52
-OpSelectionMerge %66 None
-OpBranchConditional %64 %65 %66
-%65 = OpLabel
-%68 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
-%69 = OpLoad %v4float %68
-%67 = OpDPdx %v4float %69
-%70 = OpLoad %v4float %expected
-%71 = OpFOrdEqual %v4bool %67 %70
-%73 = OpAll %bool %71
-OpBranch %66
+%56 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0
+%57 = OpLoad %v4float %56
+%58 = OpVectorShuffle %v3float %57 %57 0 1 2
+%55 = OpDPdx %v3float %58
+%60 = OpLoad %v4float %expected
+%61 = OpVectorShuffle %v3float %60 %60 0 1 2
+%62 = OpFOrdEqual %v3bool %55 %61
+%64 = OpAll %bool %62
+OpBranch %54
+%54 = OpLabel
+%65 = OpPhi %bool %false %42 %64 %53
+OpSelectionMerge %67 None
+OpBranchConditional %65 %66 %67
 %66 = OpLabel
-%74 = OpPhi %bool %false %53 %73 %65
-OpSelectionMerge %78 None
-OpBranchConditional %74 %76 %77
+%69 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0
+%70 = OpLoad %v4float %69
+%68 = OpDPdx %v4float %70
+%71 = OpLoad %v4float %expected
+%72 = OpFOrdEqual %v4bool %68 %71
+%74 = OpAll %bool %72
+OpBranch %67
+%67 = OpLabel
+%75 = OpPhi %bool %false %54 %74 %66
+OpSelectionMerge %77 None
+OpBranchConditional %75 %76 %77
 %76 = OpLabel
-%79 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
-%81 = OpLoad %v4float %79
-OpStore %75 %81
-OpBranch %78
+%80 = OpLoad %v2float %25
+%81 = OpVectorShuffle %v2float %80 %80 0 0
+%79 = OpDPdy %v2float %81
+%83 = OpAccessChain %_ptr_Uniform_v2float %11 %int_3
+%85 = OpLoad %v2float %83
+%86 = OpCompositeExtract %float %85 1
+%87 = OpCompositeConstruct %v2float %86 %86
+%88 = OpFMul %v2float %79 %87
+%78 = OpExtInst %v2float %1 FSign %88
+%89 = OpFOrdEqual %v2bool %78 %20
+%90 = OpAll %bool %89
+OpBranch %77
 %77 = OpLabel
-%82 = OpAccessChain %_ptr_Uniform_v4float %10 %int_2
-%84 = OpLoad %v4float %82
-OpStore %75 %84
-OpBranch %78
-%78 = OpLabel
-%85 = OpLoad %v4float %75
-OpReturnValue %85
+%91 = OpPhi %bool %false %67 %90 %76
+OpSelectionMerge %93 None
+OpBranchConditional %91 %92 %93
+%92 = OpLabel
+%96 = OpLoad %v2float %25
+%97 = OpVectorShuffle %v2float %96 %96 1 1
+%95 = OpDPdy %v2float %97
+%98 = OpAccessChain %_ptr_Uniform_v2float %11 %int_3
+%99 = OpLoad %v2float %98
+%100 = OpCompositeExtract %float %99 1
+%101 = OpCompositeConstruct %v2float %100 %100
+%102 = OpFMul %v2float %95 %101
+%94 = OpExtInst %v2float %1 FSign %102
+%105 = OpFOrdEqual %v2bool %94 %104
+%106 = OpAll %bool %105
+OpBranch %93
+%93 = OpLabel
+%107 = OpPhi %bool %false %77 %106 %92
+OpSelectionMerge %109 None
+OpBranchConditional %107 %108 %109
+%108 = OpLabel
+%112 = OpLoad %v2float %25
+%111 = OpDPdy %v2float %112
+%113 = OpAccessChain %_ptr_Uniform_v2float %11 %int_3
+%114 = OpLoad %v2float %113
+%115 = OpCompositeExtract %float %114 1
+%116 = OpCompositeConstruct %v2float %115 %115
+%117 = OpFMul %v2float %111 %116
+%110 = OpExtInst %v2float %1 FSign %117
+%119 = OpFOrdEqual %v2bool %110 %118
+%120 = OpAll %bool %119
+OpBranch %109
+%109 = OpLabel
+%121 = OpPhi %bool %false %93 %120 %108
+OpSelectionMerge %125 None
+OpBranchConditional %121 %123 %124
+%123 = OpLabel
+%126 = OpAccessChain %_ptr_Uniform_v4float %11 %int_1
+%128 = OpLoad %v4float %126
+OpStore %122 %128
+OpBranch %125
+%124 = OpLabel
+%129 = OpAccessChain %_ptr_Uniform_v4float %11 %int_2
+%131 = OpLoad %v4float %129
+OpStore %122 %131
+OpBranch %125
+%125 = OpLabel
+%132 = OpLoad %v4float %122
+OpReturnValue %132
 OpFunctionEnd
diff --git a/tests/sksl/intrinsics/DFdy.glsl b/tests/sksl/intrinsics/DFdy.glsl
index 5a9aa93..bd61056 100644
--- a/tests/sksl/intrinsics/DFdy.glsl
+++ b/tests/sksl/intrinsics/DFdy.glsl
@@ -1,9 +1,10 @@
 
+uniform vec2 u_skRTFlip;
 out vec4 sk_FragColor;
 uniform vec4 testInputs;
 uniform vec4 colorGreen;
 uniform vec4 colorRed;
 vec4 main() {
     vec4 expected = vec4(0.0);
-    return ((dFdx(testInputs.x) == expected.x && dFdx(testInputs.xy) == expected.xy) && dFdx(testInputs.xyz) == expected.xyz) && dFdx(testInputs) == expected ? colorGreen : colorRed;
+    return (((((dFdx(testInputs.x) == expected.x && dFdx(testInputs.xy) == expected.xy) && dFdx(testInputs.xyz) == expected.xyz) && dFdx(testInputs) == expected) && sign(u_skRTFlip.y * dFdy(coords.xx)) == vec2(0.0, 0.0)) && sign(u_skRTFlip.y * dFdy(coords.yy)) == vec2(1.0, 1.0)) && sign(u_skRTFlip.y * dFdy(coords)) == vec2(0.0, 1.0) ? colorGreen : colorRed;
 }
diff --git a/tests/sksl/intrinsics/DFdy.metal b/tests/sksl/intrinsics/DFdy.metal
index 451fc47..1911db7 100644
--- a/tests/sksl/intrinsics/DFdy.metal
+++ b/tests/sksl/intrinsics/DFdy.metal
@@ -11,10 +11,13 @@
 struct Outputs {
     float4 sk_FragColor [[color(0)]];
 };
-fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+struct sksl_synthetic_uniforms {
+    float2 u_skRTFlip;
+};
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
     Outputs _out;
     (void)_out;
     float4 expected = float4(0.0);
-    _out.sk_FragColor = ((dfdx(_uniforms.testInputs.x) == expected.x && all(dfdx(_uniforms.testInputs.xy) == expected.xy)) && all(dfdx(_uniforms.testInputs.xyz) == expected.xyz)) && all(dfdx(_uniforms.testInputs) == expected) ? _uniforms.colorGreen : _uniforms.colorRed;
+    _out.sk_FragColor = (((((dfdx(_uniforms.testInputs.x) == expected.x && all(dfdx(_uniforms.testInputs.xy) == expected.xy)) && all(dfdx(_uniforms.testInputs.xyz) == expected.xyz)) && all(dfdx(_uniforms.testInputs) == expected)) && all(sign(_anonInterface0.u_skRTFlip.y*dfdy(coords.xx)) == float2(0.0, 0.0))) && all(sign(_anonInterface0.u_skRTFlip.y*dfdy(coords.yy)) == float2(1.0, 1.0))) && all(sign(_anonInterface0.u_skRTFlip.y*dfdy(coords)) == float2(0.0, 1.0)) ? _uniforms.colorGreen : _uniforms.colorRed;
     return _out;
 }