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;
+}