Add parentheses around ambiguous WGSL binary-op intrinsics.

Intrinsic ops like `equal(x, y)` and `lessThan(x, y)` are
implemented in WGSL as binary-ops like `x == y` or `x < y`.
This makes them subject to WGSL's unique associativity rules:
https://www.w3.org/TR/WGSL/#operator-precedence-associativity

In particular, relational and equality operators are lumped
together into one group which do _not_ associate with each
other, and generate a compile error if you try. For instance,
our `runtime_intrinsics_relational` slide has a test like
this:
`notEqual(lessThanEqual(p, v1), greaterThanEqual(p, v1))``

Which would expand in WGSL to:
`p <= v1 != p >= v1`

Note the lack of parentheses; SkSL/GLSL/C++ put (in)equality
and relational-comparison into separate precedence groups.
WGSL, however, just rejects this expression as invalid.

We are now more cautious when emitting binary-op intrinsics,
and emit parentheses whenever a relative op is used.

Change-Id: Ib8ee7ed84915c00040f02b5f5c1b45b8480377a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/744664
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index b92d2c5..c6102b7 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -596,6 +596,7 @@
   "shared/MatrixSwizzleStore.sksl",
   "shared/MatrixToVectorCast.sksl",
   "shared/MultipleAssignments.sksl",
+  "shared/NestedComparisonIntrinsics.sksl",
   "shared/NoFragCoordsPos.vert",
   "shared/NoFragCoordsPosRT.vert",
   "shared/NormalizationVert.vert",
diff --git a/resources/sksl/BUILD.bazel b/resources/sksl/BUILD.bazel
index 487d2ff..7679d79 100644
--- a/resources/sksl/BUILD.bazel
+++ b/resources/sksl/BUILD.bazel
@@ -924,6 +924,7 @@
         "shared/MatrixSwizzleStore.sksl",
         "shared/MatrixToVectorCast.sksl",
         "shared/MultipleAssignments.sksl",
+        "shared/NestedComparisonIntrinsics.sksl",
         "shared/NoFragCoordsPos.vert",
         "shared/NoFragCoordsPosRT.vert",
         "shared/NormalizationVert.vert",
diff --git a/resources/sksl/shared/NestedComparisonIntrinsics.sksl b/resources/sksl/shared/NestedComparisonIntrinsics.sksl
new file mode 100644
index 0000000..a8dd6d8
--- /dev/null
+++ b/resources/sksl/shared/NestedComparisonIntrinsics.sksl
@@ -0,0 +1,10 @@
+uniform half4 colorRed, colorGreen;
+
+half4 main(float2 coords) {
+    // The inner comparisons against colors should evaluate to true in every component.
+    // The outer comparison should evaluate to `true == true`, so is true in each channel.
+    bool4 result = equal(lessThan(colorRed, half4(2.0)),
+                         greaterThan(half4(3.0), colorGreen));
+
+    return all(result) ? colorGreen : colorRed;
+}
diff --git a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
index d5ecf0b..0bc8c10 100644
--- a/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp
@@ -2031,6 +2031,28 @@
     return true;
 }
 
+static bool binary_op_is_ambiguous_in_wgsl(Operator op) {
+    // WGSL always requires parentheses for some operators which are deemed to be ambiguous.
+    // (8.19. Operator Precedence and Associativity)
+    switch (op.kind()) {
+        case OperatorKind::LOGICALOR:
+        case OperatorKind::LOGICALAND:
+        case OperatorKind::BITWISEOR:
+        case OperatorKind::BITWISEAND:
+        case OperatorKind::BITWISEXOR:
+        case OperatorKind::SHL:
+        case OperatorKind::SHR:
+        case OperatorKind::LT:
+        case OperatorKind::GT:
+        case OperatorKind::LTEQ:
+        case OperatorKind::GTEQ:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
 bool WGSLCodeGenerator::binaryOpNeedsComponentwiseMatrixPolyfill(const Type& left,
                                                                  const Type& right,
                                                                  Operator op) {
@@ -2201,28 +2223,9 @@
 
     Precedence precedence = op.getBinaryPrecedence();
     bool needParens = precedence >= parentPrecedence;
-
-    // WGSL always requires parentheses for some operators which are deemed to be ambiguous.
-    // (8.19. Operator Precedence and Associativity)
-    switch (op.kind()) {
-        case OperatorKind::LOGICALOR:
-        case OperatorKind::LOGICALAND:
-        case OperatorKind::BITWISEOR:
-        case OperatorKind::BITWISEAND:
-        case OperatorKind::BITWISEXOR:
-        case OperatorKind::SHL:
-        case OperatorKind::SHR:
-        case OperatorKind::LT:
-        case OperatorKind::GT:
-        case OperatorKind::LTEQ:
-        case OperatorKind::GTEQ:
-            precedence = Precedence::kParentheses;
-            break;
-
-        default:
-            break;
+    if (binary_op_is_ambiguous_in_wgsl(op)) {
+        precedence = Precedence::kParentheses;
     }
-
     if (needParens) {
         expr = "(";
     }
@@ -2412,8 +2415,8 @@
     SkASSERT(!call.type().isVoid());
 
     Precedence precedence = op.getBinaryPrecedence();
-    bool needParens = precedence >= parentPrecedence;
-
+    bool needParens = precedence >= parentPrecedence ||
+                      binary_op_is_ambiguous_in_wgsl(op);
     std::string expr;
     if (needParens) {
         expr.push_back('(');
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index b1f2c51..cb7a728 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -712,6 +712,7 @@
 SKSL_TEST(CPU | GPU,     kApiLevel_T, MatrixToVectorCast,              "shared/MatrixToVectorCast.sksl")
 SKSL_TEST(CPU | GPU,     kApiLevel_T, MultipleAssignments,             "shared/MultipleAssignments.sksl")
 SKSL_TEST(CPU | GPU,     kApiLevel_T, NumberCasts,                     "shared/NumberCasts.sksl")
+SKSL_TEST(CPU | GPU,     kNextRelease,NestedComparisonIntrinsics,      "shared/NestedComparisonIntrinsics.sksl")
 SKSL_TEST(CPU | GPU,     kApiLevel_T, OperatorsES2,                    "shared/OperatorsES2.sksl")
 SKSL_TEST(GPU_ES3,       kNever,      OperatorsES3,                    "shared/OperatorsES3.sksl")
 SKSL_TEST(CPU | GPU,     kApiLevel_T, Ossfuzz36852,                    "shared/Ossfuzz36852.sksl")
diff --git a/tests/sksl/intrinsics/Degrees.wgsl b/tests/sksl/intrinsics/Degrees.wgsl
index aca35882..757adc6 100644
--- a/tests/sksl/intrinsics/Degrees.wgsl
+++ b/tests/sksl/intrinsics/Degrees.wgsl
@@ -21,13 +21,13 @@
     let _skTemp1 = abs(_skTemp0 - -71.61973);
     let _skTemp2 = degrees(_globalUniforms.testInputs.xy);
     let _skTemp3 = abs(_skTemp2 - vec2<f32>(-71.61973, 0.0));
-    let _skTemp4 = all(_skTemp3 < vec2<f32>(0.05));
+    let _skTemp4 = all((_skTemp3 < vec2<f32>(0.05)));
     let _skTemp5 = degrees(_globalUniforms.testInputs.xyz);
     let _skTemp6 = abs(_skTemp5 - vec3<f32>(-71.61973, 0.0, 42.9718361));
-    let _skTemp7 = all(_skTemp6 < vec3<f32>(0.05));
+    let _skTemp7 = all((_skTemp6 < vec3<f32>(0.05)));
     let _skTemp8 = degrees(_globalUniforms.testInputs);
     let _skTemp9 = abs(_skTemp8 - expected);
-    let _skTemp10 = all(_skTemp9 < allowedDelta);
+    let _skTemp10 = all((_skTemp9 < allowedDelta));
     return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>((((_skTemp1 < 0.05) && _skTemp4) && _skTemp7) && _skTemp10));
   }
 }
diff --git a/tests/sksl/intrinsics/PackSnorm2x16.wgsl b/tests/sksl/intrinsics/PackSnorm2x16.wgsl
index fce9cef..bb269c7 100644
--- a/tests/sksl/intrinsics/PackSnorm2x16.wgsl
+++ b/tests/sksl/intrinsics/PackSnorm2x16.wgsl
@@ -29,10 +29,10 @@
     const tolerance: vec2<f32> = vec2<f32>(0.015625);
     let _skTemp2 = unpackSnorm2x16(xy);
     let _skTemp3 = abs(_skTemp2 - vec2<f32>(-1.0, 0.0));
-    let _skTemp4 = all(_skTemp3 < tolerance);
+    let _skTemp4 = all((_skTemp3 < tolerance));
     let _skTemp5 = unpackSnorm2x16(zw);
     let _skTemp6 = abs(_skTemp5 - vec2<f32>(0.75, 1.0));
-    let _skTemp7 = all(_skTemp6 < tolerance);
+    let _skTemp7 = all((_skTemp6 < tolerance));
     return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>(_skTemp4 && _skTemp7));
   }
 }
diff --git a/tests/sksl/intrinsics/PackUnorm2x16.wgsl b/tests/sksl/intrinsics/PackUnorm2x16.wgsl
index 6b8306b..e384704 100644
--- a/tests/sksl/intrinsics/PackUnorm2x16.wgsl
+++ b/tests/sksl/intrinsics/PackUnorm2x16.wgsl
@@ -29,10 +29,10 @@
     const tolerance: vec2<f32> = vec2<f32>(0.015625);
     let _skTemp2 = unpackUnorm2x16(xy);
     let _skTemp3 = abs(_skTemp2);
-    let _skTemp4 = all(_skTemp3 < tolerance);
+    let _skTemp4 = all((_skTemp3 < tolerance));
     let _skTemp5 = unpackUnorm2x16(zw);
     let _skTemp6 = abs(_skTemp5 - vec2<f32>(0.75, 1.0));
-    let _skTemp7 = all(_skTemp6 < tolerance);
+    let _skTemp7 = all((_skTemp6 < tolerance));
     return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>(_skTemp4 && _skTemp7));
   }
 }
diff --git a/tests/sksl/intrinsics/Radians.wgsl b/tests/sksl/intrinsics/Radians.wgsl
index dda213b..f505c83 100644
--- a/tests/sksl/intrinsics/Radians.wgsl
+++ b/tests/sksl/intrinsics/Radians.wgsl
@@ -21,13 +21,13 @@
     let _skTemp1 = abs(_skTemp0 - -0.021816615);
     let _skTemp2 = radians(_globalUniforms.testInputs.xy);
     let _skTemp3 = abs(_skTemp2 - vec2<f32>(-0.021816615, 0.0));
-    let _skTemp4 = all(_skTemp3 < vec2<f32>(0.0005));
+    let _skTemp4 = all((_skTemp3 < vec2<f32>(0.0005)));
     let _skTemp5 = radians(_globalUniforms.testInputs.xyz);
     let _skTemp6 = abs(_skTemp5 - vec3<f32>(-0.021816615, 0.0, 0.01308997));
-    let _skTemp7 = all(_skTemp6 < vec3<f32>(0.0005));
+    let _skTemp7 = all((_skTemp6 < vec3<f32>(0.0005)));
     let _skTemp8 = radians(_globalUniforms.testInputs);
     let _skTemp9 = abs(_skTemp8 - expected);
-    let _skTemp10 = all(_skTemp9 < allowedDelta);
+    let _skTemp10 = all((_skTemp9 < allowedDelta));
     return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>((((_skTemp1 < 0.0005) && _skTemp4) && _skTemp7) && _skTemp10));
   }
 }
diff --git a/tests/sksl/intrinsics/Sqrt.wgsl b/tests/sksl/intrinsics/Sqrt.wgsl
index f0420b4..0c09381 100644
--- a/tests/sksl/intrinsics/Sqrt.wgsl
+++ b/tests/sksl/intrinsics/Sqrt.wgsl
@@ -26,13 +26,13 @@
     let _skTemp3 = abs(_skTemp2 - 1.0);
     let _skTemp4 = sqrt(inputVal.xy);
     let _skTemp5 = abs(_skTemp4 - vec2<f32>(1.0, 2.0));
-    let _skTemp6 = all(_skTemp5 < vec2<f32>(0.05));
+    let _skTemp6 = all((_skTemp5 < vec2<f32>(0.05)));
     let _skTemp7 = sqrt(inputVal.xyz);
     let _skTemp8 = abs(_skTemp7 - vec3<f32>(1.0, 2.0, 3.0));
-    let _skTemp9 = all(_skTemp8 < vec3<f32>(0.05));
+    let _skTemp9 = all((_skTemp8 < vec3<f32>(0.05)));
     let _skTemp10 = sqrt(inputVal);
     let _skTemp11 = abs(_skTemp10 - expected);
-    let _skTemp12 = all(_skTemp11 < allowedDelta);
+    let _skTemp12 = all((_skTemp11 < allowedDelta));
     return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>((((_skTemp3 < 0.05) && _skTemp6) && _skTemp9) && _skTemp12));
   }
 }
diff --git a/tests/sksl/shared/MatrixScalarMath.wgsl b/tests/sksl/shared/MatrixScalarMath.wgsl
index 5aa0712..76f4baf 100644
--- a/tests/sksl/shared/MatrixScalarMath.wgsl
+++ b/tests/sksl/shared/MatrixScalarMath.wgsl
@@ -56,9 +56,9 @@
     var div: mat2x2<f32> = mat * (1.0 / _globalUniforms.testInputs.x);
     mat = mat * (1.0 / _globalUniforms.testInputs.x);
     let _skTemp2 = abs(vec4<f32>(div[0], div[1]) + vec4<f32>(8.0));
-    let _skTemp3 = all(_skTemp2 < vec4<f32>(0.01));
+    let _skTemp3 = all((_skTemp2 < vec4<f32>(0.01)));
     let _skTemp4 = abs(vec4<f32>(mat[0], mat[1]) + vec4<f32>(8.0));
-    let _skTemp5 = all(_skTemp4 < vec4<f32>(0.01));
+    let _skTemp5 = all((_skTemp4 < vec4<f32>(0.01)));
     return _skTemp3 && _skTemp5;
   }
 }
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.asm.frag b/tests/sksl/shared/NestedComparisonIntrinsics.asm.frag
new file mode 100644
index 0000000..3c9050c
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.asm.frag
@@ -0,0 +1,95 @@
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_Clockwise %sk_FragColor
+               OpExecutionMode %_entrypoint_v OriginUpperLeft
+               OpName %sk_Clockwise "sk_Clockwise"
+               OpName %sk_FragColor "sk_FragColor"
+               OpName %_UniformBuffer "_UniformBuffer"
+               OpMemberName %_UniformBuffer 0 "colorRed"
+               OpMemberName %_UniformBuffer 1 "colorGreen"
+               OpName %_entrypoint_v "_entrypoint_v"
+               OpName %main "main"
+               OpName %result "result"
+               OpDecorate %sk_Clockwise BuiltIn FrontFacing
+               OpDecorate %sk_FragColor RelaxedPrecision
+               OpDecorate %sk_FragColor Location 0
+               OpDecorate %sk_FragColor Index 0
+               OpMemberDecorate %_UniformBuffer 0 Offset 0
+               OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
+               OpMemberDecorate %_UniformBuffer 1 Offset 16
+               OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
+               OpDecorate %_UniformBuffer Block
+               OpDecorate %10 Binding 0
+               OpDecorate %10 DescriptorSet 0
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %43 RelaxedPrecision
+               OpDecorate %51 RelaxedPrecision
+               OpDecorate %53 RelaxedPrecision
+               OpDecorate %54 RelaxedPrecision
+       %bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise = OpVariable %_ptr_Input_bool Input
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%sk_FragColor = OpVariable %_ptr_Output_v4float Output
+%_UniformBuffer = OpTypeStruct %v4float %v4float
+%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
+         %10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void
+    %float_0 = OpConstant %float 0
+    %v2float = OpTypeVector %float 2
+         %19 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+         %23 = OpTypeFunction %v4float %_ptr_Function_v2float
+     %v4bool = OpTypeVector %bool 4
+%_ptr_Function_v4bool = OpTypePointer Function %v4bool
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_2 = OpConstant %float 2
+         %37 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+    %float_3 = OpConstant %float 3
+         %40 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
+      %int_1 = OpConstant %int 1
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_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
+               OpReturn
+               OpFunctionEnd
+       %main = OpFunction %v4float None %23
+         %24 = OpFunctionParameter %_ptr_Function_v2float
+         %25 = OpLabel
+     %result = OpVariable %_ptr_Function_v4bool Function
+         %45 = OpVariable %_ptr_Function_v4float Function
+         %31 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+         %35 = OpLoad %v4float %31
+         %30 = OpFOrdLessThan %v4bool %35 %37
+         %41 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+         %43 = OpLoad %v4float %41
+         %38 = OpFOrdGreaterThan %v4bool %40 %43
+         %29 = OpLogicalEqual %v4bool %30 %38
+               OpStore %result %29
+         %44 = OpAll %bool %29
+               OpSelectionMerge %49 None
+               OpBranchConditional %44 %47 %48
+         %47 = OpLabel
+         %50 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+         %51 = OpLoad %v4float %50
+               OpStore %45 %51
+               OpBranch %49
+         %48 = OpLabel
+         %52 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+         %53 = OpLoad %v4float %52
+               OpStore %45 %53
+               OpBranch %49
+         %49 = OpLabel
+         %54 = OpLoad %v4float %45
+               OpReturnValue %54
+               OpFunctionEnd
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.glsl b/tests/sksl/shared/NestedComparisonIntrinsics.glsl
new file mode 100644
index 0000000..7adc997
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.glsl
@@ -0,0 +1,8 @@
+
+out vec4 sk_FragColor;
+uniform vec4 colorRed;
+uniform vec4 colorGreen;
+vec4 main() {
+    bvec4 result = equal(lessThan(colorRed, vec4(2.0)), greaterThan(vec4(3.0), colorGreen));
+    return all(result) ? colorGreen : colorRed;
+}
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.hlsl b/tests/sksl/shared/NestedComparisonIntrinsics.hlsl
new file mode 100644
index 0000000..fa9ad32
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.hlsl
@@ -0,0 +1,45 @@
+cbuffer _UniformBuffer : register(b0, space0)
+{
+    float4 _10_colorRed : packoffset(c0);
+    float4 _10_colorGreen : packoffset(c1);
+};
+
+
+static float4 sk_FragColor;
+
+struct SPIRV_Cross_Output
+{
+    float4 sk_FragColor : SV_Target0;
+};
+
+float4 main(float2 _24)
+{
+    bool4 _30 = bool4(_10_colorRed.x < 2.0f.xxxx.x, _10_colorRed.y < 2.0f.xxxx.y, _10_colorRed.z < 2.0f.xxxx.z, _10_colorRed.w < 2.0f.xxxx.w);
+    bool4 _38 = bool4(3.0f.xxxx.x > _10_colorGreen.x, 3.0f.xxxx.y > _10_colorGreen.y, 3.0f.xxxx.z > _10_colorGreen.z, 3.0f.xxxx.w > _10_colorGreen.w);
+    bool4 _29 = bool4(_30.x == _38.x, _30.y == _38.y, _30.z == _38.z, _30.w == _38.w);
+    bool4 result = _29;
+    float4 _45 = 0.0f.xxxx;
+    if (all(_29))
+    {
+        _45 = _10_colorGreen;
+    }
+    else
+    {
+        _45 = _10_colorRed;
+    }
+    return _45;
+}
+
+void frag_main()
+{
+    float2 _20 = 0.0f.xx;
+    sk_FragColor = main(_20);
+}
+
+SPIRV_Cross_Output main()
+{
+    frag_main();
+    SPIRV_Cross_Output stage_output;
+    stage_output.sk_FragColor = sk_FragColor;
+    return stage_output;
+}
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.metal b/tests/sksl/shared/NestedComparisonIntrinsics.metal
new file mode 100644
index 0000000..c6cae3e
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.metal
@@ -0,0 +1,19 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Uniforms {
+    half4 colorRed;
+    half4 colorGreen;
+};
+struct Inputs {
+};
+struct Outputs {
+    half4 sk_FragColor [[color(0)]];
+};
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _out;
+    (void)_out;
+    bool4 result = ((_uniforms.colorRed < half4(2.0h)) == (half4(3.0h) > _uniforms.colorGreen));
+    _out.sk_FragColor = all(result) ? _uniforms.colorGreen : _uniforms.colorRed;
+    return _out;
+}
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.skrp b/tests/sksl/shared/NestedComparisonIntrinsics.skrp
new file mode 100644
index 0000000..2921625
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.skrp
@@ -0,0 +1,19 @@
+17 instructions
+
+store_src_rg                   coords = src.rg
+init_lane_masks                CondMask = LoopMask = RetMask = true
+copy_4_uniforms                $0..3 = colorRed
+splat_4_constants              $4..7 = 0x40000000 (2.0)
+cmplt_4_floats                 $0..3 = lessThan($0..3, $4..7)
+copy_4_uniforms                $4..7 = colorGreen
+splat_4_constants              $8..11 = 0x40400000 (3.0)
+cmplt_4_floats                 $4..7 = lessThan($4..7, $8..11)
+cmpeq_4_ints                   $0..3 = equal($0..3, $4..7)
+copy_4_slots_unmasked          result = $0..3
+bitwise_and_2_ints             $0..1 &= $2..3
+bitwise_and_int                $0 &= $1
+swizzle_4                      $0..3 = ($0..3).xxxx
+copy_4_uniforms                $4..7 = colorRed
+copy_4_uniforms                $8..11 = colorGreen
+mix_4_ints                     $0..3 = mix($4..7, $8..11, $0..3)
+load_src                       src.rgba = $0..3
diff --git a/tests/sksl/shared/NestedComparisonIntrinsics.wgsl b/tests/sksl/shared/NestedComparisonIntrinsics.wgsl
new file mode 100644
index 0000000..31bb47e
--- /dev/null
+++ b/tests/sksl/shared/NestedComparisonIntrinsics.wgsl
@@ -0,0 +1,26 @@
+diagnostic(off, derivative_uniformity);
+struct FSIn {
+  @builtin(front_facing) sk_Clockwise: bool,
+  @builtin(position) sk_FragCoord: vec4<f32>,
+};
+struct FSOut {
+  @location(0) sk_FragColor: vec4<f32>,
+};
+struct _GlobalUniforms {
+  colorRed: vec4<f32>,
+  colorGreen: vec4<f32>,
+};
+@binding(0) @group(0) var<uniform> _globalUniforms: _GlobalUniforms;
+fn _skslMain(_skParam0: vec2<f32>) -> vec4<f32> {
+  let coords = _skParam0;
+  {
+    var result: vec4<bool> = (_globalUniforms.colorRed < vec4<f32>(2.0)) == (vec4<f32>(3.0) > _globalUniforms.colorGreen);
+    let _skTemp0 = all(result);
+    return select(_globalUniforms.colorRed, _globalUniforms.colorGreen, vec4<bool>(_skTemp0));
+  }
+}
+@fragment fn main(_stageIn: FSIn) -> FSOut {
+  var _stageOut: FSOut;
+  _stageOut.sk_FragColor = _skslMain(_stageIn.sk_FragCoord.xy);
+  return _stageOut;
+}