summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts167
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts65
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts92
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts92
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts76
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts71
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts49
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts33
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts34
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts33
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts72
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts172
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts250
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts249
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts250
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts66
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts32
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts172
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts156
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts22
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts21
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts22
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts22
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts21
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts337
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts201
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts347
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts250
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts71
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts73
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts80
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts21
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts21
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts21
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts386
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts63
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts95
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts107
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts71
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts71
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts123
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts122
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts93
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts356
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts89
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts88
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts55
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts55
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts60
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts60
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts66
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts70
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts54
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts137
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts196
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts250
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts229
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts53
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts67
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts70
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts85
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts38
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts53
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts160
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts270
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts134
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts185
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts100
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts65
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts37
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts273
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts163
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts145
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts149
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts136
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts274
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts122
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts46
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts54
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts40
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts40
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts40
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts40
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts40
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts45
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts38
111 files changed, 10757 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
new file mode 100644
index 0000000000..272d0190a5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
@@ -0,0 +1,167 @@
+export const description = `
+Execution tests for the 'abs' builtin function
+
+S is AbstractInt, i32, or u32
+T is S or vecN<S>
+@const fn abs(e: T ) -> T
+The absolute value of e. Component-wise when T is a vector. If e is a signed
+integral scalar type and evaluates to the largest negative value, then the
+result is e. If e is an unsigned integral type, then the result is e.
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn abs(e: T ) -> T
+Returns the absolute value of e (e.g. e with a positive sign bit).
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kBit } from '../../../../../util/constants.js';
+import { i32Bits, TypeF32, TypeI32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { absInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('abs', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', absInterval);
+ },
+});
+
+g.test('abstract_int')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`abstract int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`unsigned int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ await run(t, builtin('abs'), [TypeU32], TypeU32, t.params, [
+ // Min and Max u32
+ { input: u32Bits(kBit.u32.min), expected: u32Bits(kBit.u32.min) },
+ { input: u32Bits(kBit.u32.max), expected: u32Bits(kBit.u32.max) },
+ // Powers of 2: -2^i: 0 =< i =< 31
+ { input: u32Bits(kBit.powTwo.to0), expected: u32Bits(kBit.powTwo.to0) },
+ { input: u32Bits(kBit.powTwo.to1), expected: u32Bits(kBit.powTwo.to1) },
+ { input: u32Bits(kBit.powTwo.to2), expected: u32Bits(kBit.powTwo.to2) },
+ { input: u32Bits(kBit.powTwo.to3), expected: u32Bits(kBit.powTwo.to3) },
+ { input: u32Bits(kBit.powTwo.to4), expected: u32Bits(kBit.powTwo.to4) },
+ { input: u32Bits(kBit.powTwo.to5), expected: u32Bits(kBit.powTwo.to5) },
+ { input: u32Bits(kBit.powTwo.to6), expected: u32Bits(kBit.powTwo.to6) },
+ { input: u32Bits(kBit.powTwo.to7), expected: u32Bits(kBit.powTwo.to7) },
+ { input: u32Bits(kBit.powTwo.to8), expected: u32Bits(kBit.powTwo.to8) },
+ { input: u32Bits(kBit.powTwo.to9), expected: u32Bits(kBit.powTwo.to9) },
+ { input: u32Bits(kBit.powTwo.to10), expected: u32Bits(kBit.powTwo.to10) },
+ { input: u32Bits(kBit.powTwo.to11), expected: u32Bits(kBit.powTwo.to11) },
+ { input: u32Bits(kBit.powTwo.to12), expected: u32Bits(kBit.powTwo.to12) },
+ { input: u32Bits(kBit.powTwo.to13), expected: u32Bits(kBit.powTwo.to13) },
+ { input: u32Bits(kBit.powTwo.to14), expected: u32Bits(kBit.powTwo.to14) },
+ { input: u32Bits(kBit.powTwo.to15), expected: u32Bits(kBit.powTwo.to15) },
+ { input: u32Bits(kBit.powTwo.to16), expected: u32Bits(kBit.powTwo.to16) },
+ { input: u32Bits(kBit.powTwo.to17), expected: u32Bits(kBit.powTwo.to17) },
+ { input: u32Bits(kBit.powTwo.to18), expected: u32Bits(kBit.powTwo.to18) },
+ { input: u32Bits(kBit.powTwo.to19), expected: u32Bits(kBit.powTwo.to19) },
+ { input: u32Bits(kBit.powTwo.to20), expected: u32Bits(kBit.powTwo.to20) },
+ { input: u32Bits(kBit.powTwo.to21), expected: u32Bits(kBit.powTwo.to21) },
+ { input: u32Bits(kBit.powTwo.to22), expected: u32Bits(kBit.powTwo.to22) },
+ { input: u32Bits(kBit.powTwo.to23), expected: u32Bits(kBit.powTwo.to23) },
+ { input: u32Bits(kBit.powTwo.to24), expected: u32Bits(kBit.powTwo.to24) },
+ { input: u32Bits(kBit.powTwo.to25), expected: u32Bits(kBit.powTwo.to25) },
+ { input: u32Bits(kBit.powTwo.to26), expected: u32Bits(kBit.powTwo.to26) },
+ { input: u32Bits(kBit.powTwo.to27), expected: u32Bits(kBit.powTwo.to27) },
+ { input: u32Bits(kBit.powTwo.to28), expected: u32Bits(kBit.powTwo.to28) },
+ { input: u32Bits(kBit.powTwo.to29), expected: u32Bits(kBit.powTwo.to29) },
+ { input: u32Bits(kBit.powTwo.to30), expected: u32Bits(kBit.powTwo.to30) },
+ { input: u32Bits(kBit.powTwo.to31), expected: u32Bits(kBit.powTwo.to31) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`signed int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ await run(t, builtin('abs'), [TypeI32], TypeI32, t.params, [
+ // Min and max i32
+ // If e evaluates to the largest negative value, then the result is e.
+ { input: i32Bits(kBit.i32.negative.min), expected: i32Bits(kBit.i32.negative.min) },
+ { input: i32Bits(kBit.i32.negative.max), expected: i32Bits(kBit.i32.positive.min) },
+ { input: i32Bits(kBit.i32.positive.max), expected: i32Bits(kBit.i32.positive.max) },
+ { input: i32Bits(kBit.i32.positive.min), expected: i32Bits(kBit.i32.positive.min) },
+ // input: -1 * pow(2, n), n = {-31, ..., 0 }, expected: pow(2, n), n = {-31, ..., 0}]
+ { input: i32Bits(kBit.negPowTwo.to0), expected: i32Bits(kBit.powTwo.to0) },
+ { input: i32Bits(kBit.negPowTwo.to1), expected: i32Bits(kBit.powTwo.to1) },
+ { input: i32Bits(kBit.negPowTwo.to2), expected: i32Bits(kBit.powTwo.to2) },
+ { input: i32Bits(kBit.negPowTwo.to3), expected: i32Bits(kBit.powTwo.to3) },
+ { input: i32Bits(kBit.negPowTwo.to4), expected: i32Bits(kBit.powTwo.to4) },
+ { input: i32Bits(kBit.negPowTwo.to5), expected: i32Bits(kBit.powTwo.to5) },
+ { input: i32Bits(kBit.negPowTwo.to6), expected: i32Bits(kBit.powTwo.to6) },
+ { input: i32Bits(kBit.negPowTwo.to7), expected: i32Bits(kBit.powTwo.to7) },
+ { input: i32Bits(kBit.negPowTwo.to8), expected: i32Bits(kBit.powTwo.to8) },
+ { input: i32Bits(kBit.negPowTwo.to9), expected: i32Bits(kBit.powTwo.to9) },
+ { input: i32Bits(kBit.negPowTwo.to10), expected: i32Bits(kBit.powTwo.to10) },
+ { input: i32Bits(kBit.negPowTwo.to11), expected: i32Bits(kBit.powTwo.to11) },
+ { input: i32Bits(kBit.negPowTwo.to12), expected: i32Bits(kBit.powTwo.to12) },
+ { input: i32Bits(kBit.negPowTwo.to13), expected: i32Bits(kBit.powTwo.to13) },
+ { input: i32Bits(kBit.negPowTwo.to14), expected: i32Bits(kBit.powTwo.to14) },
+ { input: i32Bits(kBit.negPowTwo.to15), expected: i32Bits(kBit.powTwo.to15) },
+ { input: i32Bits(kBit.negPowTwo.to16), expected: i32Bits(kBit.powTwo.to16) },
+ { input: i32Bits(kBit.negPowTwo.to17), expected: i32Bits(kBit.powTwo.to17) },
+ { input: i32Bits(kBit.negPowTwo.to18), expected: i32Bits(kBit.powTwo.to18) },
+ { input: i32Bits(kBit.negPowTwo.to19), expected: i32Bits(kBit.powTwo.to19) },
+ { input: i32Bits(kBit.negPowTwo.to20), expected: i32Bits(kBit.powTwo.to20) },
+ { input: i32Bits(kBit.negPowTwo.to21), expected: i32Bits(kBit.powTwo.to21) },
+ { input: i32Bits(kBit.negPowTwo.to22), expected: i32Bits(kBit.powTwo.to22) },
+ { input: i32Bits(kBit.negPowTwo.to23), expected: i32Bits(kBit.powTwo.to23) },
+ { input: i32Bits(kBit.negPowTwo.to24), expected: i32Bits(kBit.powTwo.to24) },
+ { input: i32Bits(kBit.negPowTwo.to25), expected: i32Bits(kBit.powTwo.to25) },
+ { input: i32Bits(kBit.negPowTwo.to26), expected: i32Bits(kBit.powTwo.to26) },
+ { input: i32Bits(kBit.negPowTwo.to27), expected: i32Bits(kBit.powTwo.to27) },
+ { input: i32Bits(kBit.negPowTwo.to28), expected: i32Bits(kBit.powTwo.to28) },
+ { input: i32Bits(kBit.negPowTwo.to29), expected: i32Bits(kBit.powTwo.to29) },
+ { input: i32Bits(kBit.negPowTwo.to30), expected: i32Bits(kBit.powTwo.to30) },
+ { input: i32Bits(kBit.negPowTwo.to31), expected: i32Bits(kBit.powTwo.to31) },
+ ]);
+ });
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`float 32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('abs'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
new file mode 100644
index 0000000000..312b3160f2
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
@@ -0,0 +1,61 @@
+export const description = `
+Execution tests for the 'acos' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn acos(e: T ) -> T
+Returns the arc cosine of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { acosInterval } from '../../../../../util/f32_interval.js';
+import { linearRange, fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const inputs = [
+ ...linearRange(-1, 1, 100), // acos is defined on [-1, 1]
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('acos', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', acosInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', acosInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('acos'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts
new file mode 100644
index 0000000000..89887d05ef
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts
@@ -0,0 +1,65 @@
+export const description = `
+Execution tests for the 'acosh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn acosh(e: T ) -> T
+Returns the hyperbolic arc cosine of e. The result is 0 when e < 1.
+Computes the non-negative functional inverse of cosh.
+Component-wise when T is a vector.
+Note: The result is not mathematically meaningful when e < 1.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { acoshIntervals } from '../../../../../util/f32_interval.js';
+import { biasedRange, fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const inputs = [
+ ...biasedRange(1, 2, 100), // x near 1 can be problematic to implement
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('acosh', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', ...acoshIntervals);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', ...acoshIntervals);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('acosh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
new file mode 100644
index 0000000000..9a2938c1d5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
@@ -0,0 +1,92 @@
+export const description = `
+Execution tests for the 'all' builtin function
+
+S is a bool
+T is S or vecN<S>
+@const fn all(e: T) -> bool
+Returns e if e is scalar.
+Returns true if each component of e is true if e is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import {
+ False,
+ True,
+ TypeBool,
+ TypeVec,
+ vec2,
+ vec3,
+ vec4,
+} from '../../../../../util/conversion.js';
+import { allInputSources, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('bool')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions')
+ .desc(`bool tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const)
+ )
+ .fn(async t => {
+ const overloads = {
+ scalar: {
+ type: TypeBool,
+ cases: [
+ { input: False, expected: False },
+ { input: True, expected: True },
+ ],
+ },
+ vec2: {
+ type: TypeVec(2, TypeBool),
+ cases: [
+ { input: vec2(False, False), expected: False },
+ { input: vec2(True, False), expected: False },
+ { input: vec2(False, True), expected: False },
+ { input: vec2(True, True), expected: True },
+ ],
+ },
+ vec3: {
+ type: TypeVec(3, TypeBool),
+ cases: [
+ { input: vec3(False, False, False), expected: False },
+ { input: vec3(True, False, False), expected: False },
+ { input: vec3(False, True, False), expected: False },
+ { input: vec3(True, True, False), expected: False },
+ { input: vec3(False, False, True), expected: False },
+ { input: vec3(True, False, True), expected: False },
+ { input: vec3(False, True, True), expected: False },
+ { input: vec3(True, True, True), expected: True },
+ ],
+ },
+ vec4: {
+ type: TypeVec(4, TypeBool),
+ cases: [
+ { input: vec4(False, False, False, False), expected: False },
+ { input: vec4(False, True, False, False), expected: False },
+ { input: vec4(False, False, True, False), expected: False },
+ { input: vec4(False, True, True, False), expected: False },
+ { input: vec4(False, False, False, True), expected: False },
+ { input: vec4(False, True, False, True), expected: False },
+ { input: vec4(False, False, True, True), expected: False },
+ { input: vec4(False, True, True, True), expected: False },
+ { input: vec4(True, False, False, False), expected: False },
+ { input: vec4(True, False, False, True), expected: False },
+ { input: vec4(True, False, True, False), expected: False },
+ { input: vec4(True, False, True, True), expected: False },
+ { input: vec4(True, True, False, False), expected: False },
+ { input: vec4(True, True, False, True), expected: False },
+ { input: vec4(True, True, True, False), expected: False },
+ { input: vec4(True, True, True, True), expected: True },
+ ],
+ },
+ };
+ const overload = overloads[t.params.overload];
+
+ await run(t, builtin('all'), [overload.type], TypeBool, t.params, overload.cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
new file mode 100644
index 0000000000..19ed7d186f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
@@ -0,0 +1,92 @@
+export const description = `
+Execution tests for the 'any' builtin function
+
+S is a bool
+T is S or vecN<S>
+@const fn all(e) -> bool
+Returns e if e is scalar.
+Returns true if any component of e is true if e is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import {
+ False,
+ True,
+ TypeBool,
+ TypeVec,
+ vec2,
+ vec3,
+ vec4,
+} from '../../../../../util/conversion.js';
+import { allInputSources, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('bool')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions')
+ .desc(`bool tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const)
+ )
+ .fn(async t => {
+ const overloads = {
+ scalar: {
+ type: TypeBool,
+ cases: [
+ { input: False, expected: False },
+ { input: True, expected: True },
+ ],
+ },
+ vec2: {
+ type: TypeVec(2, TypeBool),
+ cases: [
+ { input: vec2(False, False), expected: False },
+ { input: vec2(True, False), expected: True },
+ { input: vec2(False, True), expected: True },
+ { input: vec2(True, True), expected: True },
+ ],
+ },
+ vec3: {
+ type: TypeVec(3, TypeBool),
+ cases: [
+ { input: vec3(False, False, False), expected: False },
+ { input: vec3(True, False, False), expected: True },
+ { input: vec3(False, True, False), expected: True },
+ { input: vec3(True, True, False), expected: True },
+ { input: vec3(False, False, True), expected: True },
+ { input: vec3(True, False, True), expected: True },
+ { input: vec3(False, True, True), expected: True },
+ { input: vec3(True, True, True), expected: True },
+ ],
+ },
+ vec4: {
+ type: TypeVec(4, TypeBool),
+ cases: [
+ { input: vec4(False, False, False, False), expected: False },
+ { input: vec4(False, True, False, False), expected: True },
+ { input: vec4(False, False, True, False), expected: True },
+ { input: vec4(False, True, True, False), expected: True },
+ { input: vec4(False, False, False, True), expected: True },
+ { input: vec4(False, True, False, True), expected: True },
+ { input: vec4(False, False, True, True), expected: True },
+ { input: vec4(False, True, True, True), expected: True },
+ { input: vec4(True, False, False, False), expected: True },
+ { input: vec4(True, False, False, True), expected: True },
+ { input: vec4(True, False, True, False), expected: True },
+ { input: vec4(True, False, True, True), expected: True },
+ { input: vec4(True, True, False, False), expected: True },
+ { input: vec4(True, True, False, True), expected: True },
+ { input: vec4(True, True, True, False), expected: True },
+ { input: vec4(True, True, True, True), expected: True },
+ ],
+ },
+ };
+ const overload = overloads[t.params.overload];
+
+ await run(t, builtin('any'), [overload.type], TypeBool, t.params, overload.cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts
new file mode 100644
index 0000000000..0b1c62c773
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts
@@ -0,0 +1,16 @@
+export const description = `
+Execution tests for the 'arrayLength' builtin function
+
+fn arrayLength(e: ptr<storage,array<T>> ) -> u32
+Returns the number of elements in the runtime-sized array.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('array')
+ .specURL('https://www.w3.org/TR/WGSL/#array-builtin-functions')
+ .desc(`array length tests`)
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts
new file mode 100644
index 0000000000..6ff62d2431
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts
@@ -0,0 +1,61 @@
+export const description = `
+Execution tests for the 'asin' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn asin(e: T ) -> T
+Returns the arc sine of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { asinInterval } from '../../../../../util/f32_interval.js';
+import { linearRange, fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const inputs = [
+ ...linearRange(-1, 1, 100), // asin is defined on [-1, 1]
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('asin', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', asinInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', asinInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('asin'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts
new file mode 100644
index 0000000000..3c2b4fcc13
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'sinh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn asinh(e: T ) -> T
+Returns the hyperbolic arc sine of e.
+Computes the functional inverse of sinh.
+Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { asinhInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('asinh', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', asinhInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float test`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('asinh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
new file mode 100644
index 0000000000..218ccfc792
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
@@ -0,0 +1,76 @@
+export const description = `
+Execution tests for the 'atan' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn atan(e: T ) -> T
+Returns the arc tangent of e. Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { atanInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const inputs = [
+ // Known values
+ -Math.sqrt(3),
+ -1,
+ -1 / Math.sqrt(3),
+ 0,
+ 1,
+ 1 / Math.sqrt(3),
+ Math.sqrt(3),
+
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('atan', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', atanInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', atanInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('atan'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
new file mode 100644
index 0000000000..c1ba3f4c51
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
@@ -0,0 +1,71 @@
+export const description = `
+Execution tests for the 'atan2' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn atan2(e1: T ,e2: T ) -> T
+Returns the arc tangent of e1 over e2. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { atan2Interval } from '../../../../../util/f32_interval.js';
+import { linearRange, sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateBinaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('atan2', {
+ f32: () => {
+ // Using sparse, since there a N^2 cases being generated, but including extra values around 0, since that is where
+ // there is a discontinuity that implementations tend to behave badly at.
+ const numeric_range = [
+ ...sparseF32Range(),
+ ...linearRange(kValue.f32.negative.max, kValue.f32.positive.min, 10),
+ ];
+ return generateBinaryToF32IntervalCases(
+ numeric_range,
+ numeric_range,
+ 'unfiltered',
+ atan2Interval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('atan2'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts
new file mode 100644
index 0000000000..11267930c8
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts
@@ -0,0 +1,68 @@
+export const description = `
+Execution tests for the 'atanh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn atanh(e: T ) -> T
+Returns the hyperbolic arc tangent of e. The result is 0 when abs(e) ≥ 1.
+Computes the functional inverse of tanh.
+Component-wise when T is a vector.
+Note: The result is not mathematically meaningful when abs(e) >= 1.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { atanhInterval } from '../../../../../util/f32_interval.js';
+import { biasedRange, fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const inputs = [
+ ...biasedRange(kValue.f32.negative.less_than_one, -0.9, 20), // discontinuity at x = -1
+ -1,
+ ...biasedRange(kValue.f32.positive.less_than_one, 0.9, 20), // discontinuity at x = 1
+ 1,
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('atanh', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', atanhInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', atanhInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('atanh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts
new file mode 100644
index 0000000000..9dcae3a062
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, add and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by adding with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('add')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicAdd(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts
new file mode 100644
index 0000000000..3e896af780
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, and and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by anding with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('and')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicAnd(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts
new file mode 100644
index 0000000000..9172e3a638
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts
@@ -0,0 +1,49 @@
+export const description = `
+Performs the following steps atomically:
+ * Load the original value pointed to by atomic_ptr.
+ * Compare the original value to the value v using an equality operation.
+ * Store the value v only if the result of the equality comparison was true.
+
+Returns a two member structure, where the first member, old_value, is the original
+value of the atomic object and the second member, exchanged, is whether or not
+the comparison succeeded.
+
+Note: the equality comparison may spuriously fail on some implementations.
+That is, the second component of the result vector may be false even if the first
+component of the result vector equals cmp.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('exchange')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicCompareExchangeWeak(atomic_ptr: ptr<SC, atomic<T>, read_write>, cmp: T, v: T) -> __atomic_compare_exchange_result<T>
+
+struct __atomic_compare_exchange_result<T> {
+ old_value : T, // old value stored in the atomic
+ exchanged : bool, // true if the exchange was done
+}
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts
new file mode 100644
index 0000000000..cd7fa072c5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts
@@ -0,0 +1,33 @@
+export const description = `
+Atomically stores the value v in the atomic object pointed to atomic_ptr and returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('exchange')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicExchange(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts
new file mode 100644
index 0000000000..c2e6a7e3d8
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts
@@ -0,0 +1,34 @@
+export const description = `
+Returns the atomically loaded the value pointed to by atomic_ptr. It does not modify the object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-load')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('load')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-load')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicLoad(atomic_ptr: ptr<SC, atomic<T>, read_write>) -> T
+
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts
new file mode 100644
index 0000000000..b321524839
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, max and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by taking the max with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('max')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicMax(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts
new file mode 100644
index 0000000000..deea6a4105
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, min and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by take the min with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('min')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicMin(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts
new file mode 100644
index 0000000000..0b8f6cdc41
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, or and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by or'ing with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('or')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicOr(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts
new file mode 100644
index 0000000000..18b713ba2e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts
@@ -0,0 +1,33 @@
+export const description = `
+Atomically stores the value v in the atomic object pointed to by atomic_ptr.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-store')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('store')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-store')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicStore(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T)
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts
new file mode 100644
index 0000000000..cd150c59fb
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, subtract and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by subtracting with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('sub')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicSub(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts
new file mode 100644
index 0000000000..d8efd32f11
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts
@@ -0,0 +1,39 @@
+export const description = `
+Atomically read, xor and store value.
+
+ * Load the original value pointed to by atomic_ptr.
+ * Obtains a new value by xor'ing with the value v.
+ * Store the new value using atomic_ptr.
+
+Returns the original value stored in the atomic object.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+Atomic built-in functions must not be used in a vertex shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('xor')
+ .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw')
+ .desc(
+ `
+SC is storage or workgroup
+T is i32 or u32
+
+fn atomicXor(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T
+`
+ )
+ .params(u =>
+ u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts
new file mode 100644
index 0000000000..c54245b699
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts
@@ -0,0 +1,6 @@
+import { ExpressionBuilder } from '../../expression.js';
+
+/* @returns an ExpressionBuilder that calls the builtin with the given name */
+export function builtin(name: string): ExpressionBuilder {
+ return values => `${name}(${values.join(', ')})`;
+}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
new file mode 100644
index 0000000000..49626628ab
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
@@ -0,0 +1,72 @@
+export const description = `
+Execution tests for the 'ceil' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn ceil(e: T ) -> T
+Returns the ceiling of e. Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { ceilInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('ceil', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Small positive numbers
+ 0.1,
+ 0.9,
+ 1.0,
+ 1.1,
+ 1.9,
+ // Small negative numbers
+ -0.1,
+ -0.9,
+ -1.0,
+ -1.1,
+ -1.9,
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ ceilInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('ceil'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
new file mode 100644
index 0000000000..9411d667be
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
@@ -0,0 +1,172 @@
+export const description = `
+Execution tests for the 'clamp' builtin function
+
+S is AbstractInt, i32, or u32
+T is S or vecN<S>
+@const fn clamp(e: T , low: T, high: T) -> T
+Returns min(max(e,low),high). Component-wise when T is a vector.
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const clamp(e: T , low: T , high: T) -> T
+Returns either min(max(e,low),high), or the median of the three values e, low, high.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kBit } from '../../../../../util/constants.js';
+import {
+ i32,
+ i32Bits,
+ Scalar,
+ TypeF32,
+ TypeI32,
+ TypeU32,
+ u32,
+ u32Bits,
+} from '../../../../../util/conversion.js';
+import { clampIntervals } from '../../../../../util/f32_interval.js';
+import { sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, generateTernaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('clamp', {
+ u32: () => {
+ // This array must be strictly increasing, since that ordering determines
+ // the expected values.
+ const test_values: Array<Scalar> = [
+ u32Bits(kBit.u32.min),
+ u32(1),
+ u32(2),
+ u32(0x70000000),
+ u32(0x80000000),
+ u32Bits(kBit.u32.max),
+ ];
+
+ return generateIntegerTestCases(test_values);
+ },
+ i32: () => {
+ // This array must be strictly increasing, since that ordering determines
+ // the expected values.
+ const test_values: Array<Scalar> = [
+ i32Bits(kBit.i32.negative.min),
+ i32(-2),
+ i32(-1),
+ i32(0),
+ i32(1),
+ i32(2),
+ i32Bits(0x70000000),
+ i32Bits(kBit.i32.positive.max),
+ ];
+
+ return generateIntegerTestCases(test_values);
+ },
+ f32_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'f32-only',
+ ...clampIntervals
+ );
+ },
+ f32_non_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'unfiltered',
+ ...clampIntervals
+ );
+ },
+});
+
+/**
+ * Calculates clamp using the min-max formula.
+ * clamp(e, f, g) = min(max(e, f), g)
+ *
+ * Operates on indices of an ascending sorted array, instead of the actual
+ * values to avoid rounding issues.
+ *
+ * @returns the index of the clamped value
+ */
+function calculateMinMaxClamp(ei: number, fi: number, gi: number): number {
+ return Math.min(Math.max(ei, fi), gi);
+}
+
+/** @returns a set of clamp test cases from an ascending list of integer values */
+function generateIntegerTestCases(test_values: Array<Scalar>): Array<Case> {
+ const cases = new Array<Case>();
+ test_values.forEach((e, ei) => {
+ test_values.forEach((f, fi) => {
+ test_values.forEach((g, gi) => {
+ const expected_idx = calculateMinMaxClamp(ei, fi, gi);
+ const expected = test_values[expected_idx];
+ cases.push({ input: [e, f, g], expected });
+ });
+ });
+ });
+ return cases;
+}
+
+g.test('abstract_int')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`abstract int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('u32');
+ await run(t, builtin('clamp'), [TypeU32, TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('i32');
+ await run(t, builtin('clamp'), [TypeI32, TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('clamp'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
new file mode 100644
index 0000000000..c81b985dc5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
@@ -0,0 +1,68 @@
+export const description = `
+Execution tests for the 'cos' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn cos(e: T ) -> T
+Returns the cosine of e. Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { cosInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('cos', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Well defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 1000),
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ cosInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('cos'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
new file mode 100644
index 0000000000..2d2e0fdf80
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'cosh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn cosh(e: T ) -> T
+Returns the hyperbolic cosine of e. Component-wise when T is a vector
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { coshInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('cosh', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', coshInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', coshInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('cosh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
new file mode 100644
index 0000000000..cfae4bb6e0
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
@@ -0,0 +1,250 @@
+export const description = `
+Execution tests for the 'countLeadingZeros' builtin function
+
+S is i32 or u32
+T is S or vecN<S>
+@const fn countLeadingZeros(e: T ) -> T
+The number of consecutive 0 bits starting from the most significant bit of e,
+when T is a scalar type.
+Component-wise when T is a vector.
+Also known as "clz" in some languages.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countLeadingZeros'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) },
+
+ // One
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32(31) },
+
+ // 0's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32(30) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32(29) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32(28) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32(27) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32(26) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32(25) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32(24) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32(23) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32(22) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32(21) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32(20) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32(19) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32(18) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32(17) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32(16) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32(15) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32(14) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32(13) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32(12) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32(11) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32(10) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32(9) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32(8) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32(7) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32(6) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32(5) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32(4) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32(3) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32(2) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32(0) },
+
+ // 1's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000011), expected: u32(30) },
+ { input: u32Bits(0b00000000000000000000000000000111), expected: u32(29) },
+ { input: u32Bits(0b00000000000000000000000000001111), expected: u32(28) },
+ { input: u32Bits(0b00000000000000000000000000011111), expected: u32(27) },
+ { input: u32Bits(0b00000000000000000000000000111111), expected: u32(26) },
+ { input: u32Bits(0b00000000000000000000000001111111), expected: u32(25) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(24) },
+ { input: u32Bits(0b00000000000000000000000111111111), expected: u32(23) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(22) },
+ { input: u32Bits(0b00000000000000000000011111111111), expected: u32(21) },
+ { input: u32Bits(0b00000000000000000000111111111111), expected: u32(20) },
+ { input: u32Bits(0b00000000000000000001111111111111), expected: u32(19) },
+ { input: u32Bits(0b00000000000000000011111111111111), expected: u32(18) },
+ { input: u32Bits(0b00000000000000000111111111111111), expected: u32(17) },
+ { input: u32Bits(0b00000000000000001111111111111111), expected: u32(16) },
+ { input: u32Bits(0b00000000000000011111111111111111), expected: u32(15) },
+ { input: u32Bits(0b00000000000000111111111111111111), expected: u32(14) },
+ { input: u32Bits(0b00000000000001111111111111111111), expected: u32(13) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(12) },
+ { input: u32Bits(0b00000000000111111111111111111111), expected: u32(11) },
+ { input: u32Bits(0b00000000001111111111111111111111), expected: u32(10) },
+ { input: u32Bits(0b00000000011111111111111111111111), expected: u32(9) },
+ { input: u32Bits(0b00000000111111111111111111111111), expected: u32(8) },
+ { input: u32Bits(0b00000001111111111111111111111111), expected: u32(7) },
+ { input: u32Bits(0b00000011111111111111111111111111), expected: u32(6) },
+ { input: u32Bits(0b00000111111111111111111111111111), expected: u32(5) },
+ { input: u32Bits(0b00001111111111111111111111111111), expected: u32(4) },
+ { input: u32Bits(0b00011111111111111111111111111111), expected: u32(3) },
+ { input: u32Bits(0b00111111111111111111111111111111), expected: u32(2) },
+ { input: u32Bits(0b01111111111111111111111111111111), expected: u32(1) },
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) },
+
+ // random after leading 1
+ { input: u32Bits(0b00000000000000000000000000000110), expected: u32(29) },
+ { input: u32Bits(0b00000000000000000000000000001101), expected: u32(28) },
+ { input: u32Bits(0b00000000000000000000000000011101), expected: u32(27) },
+ { input: u32Bits(0b00000000000000000000000000111001), expected: u32(26) },
+ { input: u32Bits(0b00000000000000000000000001101111), expected: u32(25) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(24) },
+ { input: u32Bits(0b00000000000000000000000111101111), expected: u32(23) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(22) },
+ { input: u32Bits(0b00000000000000000000011111110001), expected: u32(21) },
+ { input: u32Bits(0b00000000000000000000111011011101), expected: u32(20) },
+ { input: u32Bits(0b00000000000000000001101101111111), expected: u32(19) },
+ { input: u32Bits(0b00000000000000000011111111011111), expected: u32(18) },
+ { input: u32Bits(0b00000000000000000101111001110101), expected: u32(17) },
+ { input: u32Bits(0b00000000000000001101111011110111), expected: u32(16) },
+ { input: u32Bits(0b00000000000000011111111111110011), expected: u32(15) },
+ { input: u32Bits(0b00000000000000111111111110111111), expected: u32(14) },
+ { input: u32Bits(0b00000000000001111111011111111111), expected: u32(13) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(12) },
+ { input: u32Bits(0b00000000000111110101011110111111), expected: u32(11) },
+ { input: u32Bits(0b00000000001111101111111111110111), expected: u32(10) },
+ { input: u32Bits(0b00000000011111111111010000101111), expected: u32(9) },
+ { input: u32Bits(0b00000000111111111111001111111011), expected: u32(8) },
+ { input: u32Bits(0b00000001111111011111101111111111), expected: u32(7) },
+ { input: u32Bits(0b00000011101011111011110111111011), expected: u32(6) },
+ { input: u32Bits(0b00000111111110111111111111111111), expected: u32(5) },
+ { input: u32Bits(0b00001111000000011011011110111111), expected: u32(4) },
+ { input: u32Bits(0b00011110101111011111111111111111), expected: u32(3) },
+ { input: u32Bits(0b00110110111111100111111110111101), expected: u32(2) },
+ { input: u32Bits(0b01010111111101111111011111011111), expected: u32(1) },
+ { input: u32Bits(0b11100010011110101101101110101111), expected: u32(0) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countLeadingZeros'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) },
+
+ // One
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32(31) },
+
+ // 0's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32(30) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32(29) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32(28) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32(27) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32(26) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32(25) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32(24) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32(23) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32(22) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32(21) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32(20) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32(19) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32(18) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32(17) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32(16) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32(15) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32(14) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32(13) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32(12) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32(11) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32(10) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32(9) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32(8) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32(7) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32(6) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32(5) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32(4) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32(3) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32(2) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32(0) },
+
+ // 1's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000011), expected: i32(30) },
+ { input: i32Bits(0b00000000000000000000000000000111), expected: i32(29) },
+ { input: i32Bits(0b00000000000000000000000000001111), expected: i32(28) },
+ { input: i32Bits(0b00000000000000000000000000011111), expected: i32(27) },
+ { input: i32Bits(0b00000000000000000000000000111111), expected: i32(26) },
+ { input: i32Bits(0b00000000000000000000000001111111), expected: i32(25) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(24) },
+ { input: i32Bits(0b00000000000000000000000111111111), expected: i32(23) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(22) },
+ { input: i32Bits(0b00000000000000000000011111111111), expected: i32(21) },
+ { input: i32Bits(0b00000000000000000000111111111111), expected: i32(20) },
+ { input: i32Bits(0b00000000000000000001111111111111), expected: i32(19) },
+ { input: i32Bits(0b00000000000000000011111111111111), expected: i32(18) },
+ { input: i32Bits(0b00000000000000000111111111111111), expected: i32(17) },
+ { input: i32Bits(0b00000000000000001111111111111111), expected: i32(16) },
+ { input: i32Bits(0b00000000000000011111111111111111), expected: i32(15) },
+ { input: i32Bits(0b00000000000000111111111111111111), expected: i32(14) },
+ { input: i32Bits(0b00000000000001111111111111111111), expected: i32(13) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(12) },
+ { input: i32Bits(0b00000000000111111111111111111111), expected: i32(11) },
+ { input: i32Bits(0b00000000001111111111111111111111), expected: i32(10) },
+ { input: i32Bits(0b00000000011111111111111111111111), expected: i32(9) },
+ { input: i32Bits(0b00000000111111111111111111111111), expected: i32(8) },
+ { input: i32Bits(0b00000001111111111111111111111111), expected: i32(7) },
+ { input: i32Bits(0b00000011111111111111111111111111), expected: i32(6) },
+ { input: i32Bits(0b00000111111111111111111111111111), expected: i32(5) },
+ { input: i32Bits(0b00001111111111111111111111111111), expected: i32(4) },
+ { input: i32Bits(0b00011111111111111111111111111111), expected: i32(3) },
+ { input: i32Bits(0b00111111111111111111111111111111), expected: i32(2) },
+ { input: i32Bits(0b01111111111111111111111111111111), expected: i32(1) },
+ { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) },
+
+ // random after leading 1
+ { input: i32Bits(0b00000000000000000000000000000110), expected: i32(29) },
+ { input: i32Bits(0b00000000000000000000000000001101), expected: i32(28) },
+ { input: i32Bits(0b00000000000000000000000000011101), expected: i32(27) },
+ { input: i32Bits(0b00000000000000000000000000111001), expected: i32(26) },
+ { input: i32Bits(0b00000000000000000000000001101111), expected: i32(25) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(24) },
+ { input: i32Bits(0b00000000000000000000000111101111), expected: i32(23) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(22) },
+ { input: i32Bits(0b00000000000000000000011111110001), expected: i32(21) },
+ { input: i32Bits(0b00000000000000000000111011011101), expected: i32(20) },
+ { input: i32Bits(0b00000000000000000001101101111111), expected: i32(19) },
+ { input: i32Bits(0b00000000000000000011111111011111), expected: i32(18) },
+ { input: i32Bits(0b00000000000000000101111001110101), expected: i32(17) },
+ { input: i32Bits(0b00000000000000001101111011110111), expected: i32(16) },
+ { input: i32Bits(0b00000000000000011111111111110011), expected: i32(15) },
+ { input: i32Bits(0b00000000000000111111111110111111), expected: i32(14) },
+ { input: i32Bits(0b00000000000001111111011111111111), expected: i32(13) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(12) },
+ { input: i32Bits(0b00000000000111110101011110111111), expected: i32(11) },
+ { input: i32Bits(0b00000000001111101111111111110111), expected: i32(10) },
+ { input: i32Bits(0b00000000011111111111010000101111), expected: i32(9) },
+ { input: i32Bits(0b00000000111111111111001111111011), expected: i32(8) },
+ { input: i32Bits(0b00000001111111011111101111111111), expected: i32(7) },
+ { input: i32Bits(0b00000011101011111011110111111011), expected: i32(6) },
+ { input: i32Bits(0b00000111111110111111111111111111), expected: i32(5) },
+ { input: i32Bits(0b00001111000000011011011110111111), expected: i32(4) },
+ { input: i32Bits(0b00011110101111011111111111111111), expected: i32(3) },
+ { input: i32Bits(0b00110110111111100111111110111101), expected: i32(2) },
+ { input: i32Bits(0b01010111111101111111011111011111), expected: i32(1) },
+ { input: i32Bits(0b11100010011110101101101110101111), expected: i32(0) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
new file mode 100644
index 0000000000..f0be916285
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
@@ -0,0 +1,249 @@
+export const description = `
+Execution tests for the 'countOneBits' builtin function
+
+S is i32 or u32
+T is S or vecN<S>
+@const fn countOneBits(e: T ) -> T
+The number of 1 bits in the representation of e.
+Also known as "population count".
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countOneBits'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32(0) },
+
+ // One
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32(1) },
+
+ // 0's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32(1) },
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32(1) },
+
+ // 1's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000011), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000000111), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000001111), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000011111), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000000111111), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000001111111), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000000111111111), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000011111111111), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000000111111111111), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000001111111111111), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000011111111111111), expected: u32(14) },
+ { input: u32Bits(0b00000000000000000111111111111111), expected: u32(15) },
+ { input: u32Bits(0b00000000000000001111111111111111), expected: u32(16) },
+ { input: u32Bits(0b00000000000000011111111111111111), expected: u32(17) },
+ { input: u32Bits(0b00000000000000111111111111111111), expected: u32(18) },
+ { input: u32Bits(0b00000000000001111111111111111111), expected: u32(19) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(20) },
+ { input: u32Bits(0b00000000000111111111111111111111), expected: u32(21) },
+ { input: u32Bits(0b00000000001111111111111111111111), expected: u32(22) },
+ { input: u32Bits(0b00000000011111111111111111111111), expected: u32(23) },
+ { input: u32Bits(0b00000000111111111111111111111111), expected: u32(24) },
+ { input: u32Bits(0b00000001111111111111111111111111), expected: u32(25) },
+ { input: u32Bits(0b00000011111111111111111111111111), expected: u32(26) },
+ { input: u32Bits(0b00000111111111111111111111111111), expected: u32(27) },
+ { input: u32Bits(0b00001111111111111111111111111111), expected: u32(28) },
+ { input: u32Bits(0b00011111111111111111111111111111), expected: u32(29) },
+ { input: u32Bits(0b00111111111111111111111111111111), expected: u32(30) },
+ { input: u32Bits(0b01111111111111111111111111111111), expected: u32(31) },
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32(32) },
+
+ // random after leading 1
+ { input: u32Bits(0b00000000000000000000000000000110), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001101), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000011101), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000111001), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000001101111), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000000111101111), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000011111110001), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000111011011101), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000001101101111111), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000011111111011111), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000101111001110101), expected: u32(10) },
+ { input: u32Bits(0b00000000000000001101111011110111), expected: u32(13) },
+ { input: u32Bits(0b00000000000000011111111111110011), expected: u32(15) },
+ { input: u32Bits(0b00000000000000111111111110111111), expected: u32(17) },
+ { input: u32Bits(0b00000000000001111111011111111111), expected: u32(18) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(20) },
+ { input: u32Bits(0b00000000000111110101011110111111), expected: u32(17) },
+ { input: u32Bits(0b00000000001111101111111111110111), expected: u32(20) },
+ { input: u32Bits(0b00000000011111111111010000101111), expected: u32(17) },
+ { input: u32Bits(0b00000000111111111111001111111011), expected: u32(21) },
+ { input: u32Bits(0b00000001111111011111101111111111), expected: u32(23) },
+ { input: u32Bits(0b00000011101011111011110111111011), expected: u32(21) },
+ { input: u32Bits(0b00000111111110111111111111111111), expected: u32(26) },
+ { input: u32Bits(0b00001111000000011011011110111111), expected: u32(18) },
+ { input: u32Bits(0b00011110101111011111111111111111), expected: u32(26) },
+ { input: u32Bits(0b00110110111111100111111110111101), expected: u32(24) },
+ { input: u32Bits(0b01010111111101111111011111011111), expected: u32(26) },
+ { input: u32Bits(0b11100010011110101101101110101111), expected: u32(21) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countOneBits'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32(0) },
+
+ // One
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32(1) },
+
+ // 0's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32(1) },
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32(1) },
+
+ // 1's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000011), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000000111), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000001111), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000011111), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000000111111), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000001111111), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000000111111111), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000011111111111), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000000111111111111), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000001111111111111), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000011111111111111), expected: i32(14) },
+ { input: i32Bits(0b00000000000000000111111111111111), expected: i32(15) },
+ { input: i32Bits(0b00000000000000001111111111111111), expected: i32(16) },
+ { input: i32Bits(0b00000000000000011111111111111111), expected: i32(17) },
+ { input: i32Bits(0b00000000000000111111111111111111), expected: i32(18) },
+ { input: i32Bits(0b00000000000001111111111111111111), expected: i32(19) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(20) },
+ { input: i32Bits(0b00000000000111111111111111111111), expected: i32(21) },
+ { input: i32Bits(0b00000000001111111111111111111111), expected: i32(22) },
+ { input: i32Bits(0b00000000011111111111111111111111), expected: i32(23) },
+ { input: i32Bits(0b00000000111111111111111111111111), expected: i32(24) },
+ { input: i32Bits(0b00000001111111111111111111111111), expected: i32(25) },
+ { input: i32Bits(0b00000011111111111111111111111111), expected: i32(26) },
+ { input: i32Bits(0b00000111111111111111111111111111), expected: i32(27) },
+ { input: i32Bits(0b00001111111111111111111111111111), expected: i32(28) },
+ { input: i32Bits(0b00011111111111111111111111111111), expected: i32(29) },
+ { input: i32Bits(0b00111111111111111111111111111111), expected: i32(30) },
+ { input: i32Bits(0b01111111111111111111111111111111), expected: i32(31) },
+ { input: i32Bits(0b11111111111111111111111111111111), expected: i32(32) },
+
+ // random after leading 1
+ { input: i32Bits(0b00000000000000000000000000000110), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001101), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000011101), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000111001), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000001101111), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000000111101111), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000011111110001), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000111011011101), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000001101101111111), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000011111111011111), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000101111001110101), expected: i32(10) },
+ { input: i32Bits(0b00000000000000001101111011110111), expected: i32(13) },
+ { input: i32Bits(0b00000000000000011111111111110011), expected: i32(15) },
+ { input: i32Bits(0b00000000000000111111111110111111), expected: i32(17) },
+ { input: i32Bits(0b00000000000001111111011111111111), expected: i32(18) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(20) },
+ { input: i32Bits(0b00000000000111110101011110111111), expected: i32(17) },
+ { input: i32Bits(0b00000000001111101111111111110111), expected: i32(20) },
+ { input: i32Bits(0b00000000011111111111010000101111), expected: i32(17) },
+ { input: i32Bits(0b00000000111111111111001111111011), expected: i32(21) },
+ { input: i32Bits(0b00000001111111011111101111111111), expected: i32(23) },
+ { input: i32Bits(0b00000011101011111011110111111011), expected: i32(21) },
+ { input: i32Bits(0b00000111111110111111111111111111), expected: i32(26) },
+ { input: i32Bits(0b00001111000000011011011110111111), expected: i32(18) },
+ { input: i32Bits(0b00011110101111011111111111111111), expected: i32(26) },
+ { input: i32Bits(0b00110110111111100111111110111101), expected: i32(24) },
+ { input: i32Bits(0b01010111111101111111011111011111), expected: i32(26) },
+ { input: i32Bits(0b11100010011110101101101110101111), expected: i32(21) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
new file mode 100644
index 0000000000..d0b3198f49
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
@@ -0,0 +1,250 @@
+export const description = `
+Execution tests for the 'countTrailingZeros' builtin function
+
+S is i32 or u32
+T is S or vecN<S>
+@const fn countTrailingZeros(e: T ) -> T
+The number of consecutive 0 bits starting from the least significant bit of e,
+when T is a scalar type.
+Component-wise when T is a vector.
+Also known as "ctz" in some languages.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countTrailingZeros'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) },
+
+ // High bit
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) },
+
+ // 0's before trailing 1
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) },
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) },
+
+ // 1's before trailing 1
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) },
+ { input: u32Bits(0b11111111111111111111111111111110), expected: u32(1) },
+ { input: u32Bits(0b11111111111111111111111111111100), expected: u32(2) },
+ { input: u32Bits(0b11111111111111111111111111111000), expected: u32(3) },
+ { input: u32Bits(0b11111111111111111111111111110000), expected: u32(4) },
+ { input: u32Bits(0b11111111111111111111111111100000), expected: u32(5) },
+ { input: u32Bits(0b11111111111111111111111111000000), expected: u32(6) },
+ { input: u32Bits(0b11111111111111111111111110000000), expected: u32(7) },
+ { input: u32Bits(0b11111111111111111111111100000000), expected: u32(8) },
+ { input: u32Bits(0b11111111111111111111111000000000), expected: u32(9) },
+ { input: u32Bits(0b11111111111111111111110000000000), expected: u32(10) },
+ { input: u32Bits(0b11111111111111111111100000000000), expected: u32(11) },
+ { input: u32Bits(0b11111111111111111111000000000000), expected: u32(12) },
+ { input: u32Bits(0b11111111111111111110000000000000), expected: u32(13) },
+ { input: u32Bits(0b11111111111111111100000000000000), expected: u32(14) },
+ { input: u32Bits(0b11111111111111111000000000000000), expected: u32(15) },
+ { input: u32Bits(0b11111111111111110000000000000000), expected: u32(16) },
+ { input: u32Bits(0b11111111111111100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b11111111111111000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b11111111111110000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b11111111111100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b11111111111000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b11111111110000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b11111111100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b11111111000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b11111110000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b11111100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b11111000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b11110000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b11100000000000000000000000000000), expected: u32(29) },
+ { input: u32Bits(0b11000000000000000000000000000000), expected: u32(30) },
+
+ // random before trailing 1
+ { input: u32Bits(0b11110000001111111101111010001111), expected: u32(0) },
+ { input: u32Bits(0b11011110111111100101110011110010), expected: u32(1) },
+ { input: u32Bits(0b11110111011011111111010000111100), expected: u32(2) },
+ { input: u32Bits(0b11010011011101111111010011101000), expected: u32(3) },
+ { input: u32Bits(0b11010111110111110001111110110000), expected: u32(4) },
+ { input: u32Bits(0b11111101111101111110101111100000), expected: u32(5) },
+ { input: u32Bits(0b11111001111011111001111011000000), expected: u32(6) },
+ { input: u32Bits(0b11001110110111110111111010000000), expected: u32(7) },
+ { input: u32Bits(0b11101111011111101110101100000000), expected: u32(8) },
+ { input: u32Bits(0b11111101111011111111111000000000), expected: u32(9) },
+ { input: u32Bits(0b10011111011101110110110000000000), expected: u32(10) },
+ { input: u32Bits(0b11111111101101111011100000000000), expected: u32(11) },
+ { input: u32Bits(0b11111011010110111011000000000000), expected: u32(12) },
+ { input: u32Bits(0b00111101010000111010000000000000), expected: u32(13) },
+ { input: u32Bits(0b11111011110001101100000000000000), expected: u32(14) },
+ { input: u32Bits(0b10111111010111111000000000000000), expected: u32(15) },
+ { input: u32Bits(0b11011101111010110000000000000000), expected: u32(16) },
+ { input: u32Bits(0b01110100110110100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b11100111001011000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b11111001110110000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b00110100100100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b11111010011000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b00000010110000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b11100111100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b00101101000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b11011010000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b11010100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b10111000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b01110000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b10100000000000000000000000000000), expected: u32(29) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('countTrailingZeros'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) },
+
+ // High bit
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32(31) },
+
+ // 0's before trailing 1
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) },
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) },
+
+ // 1's before trailing 1
+ { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) },
+ { input: i32Bits(0b11111111111111111111111111111110), expected: i32(1) },
+ { input: i32Bits(0b11111111111111111111111111111100), expected: i32(2) },
+ { input: i32Bits(0b11111111111111111111111111111000), expected: i32(3) },
+ { input: i32Bits(0b11111111111111111111111111110000), expected: i32(4) },
+ { input: i32Bits(0b11111111111111111111111111100000), expected: i32(5) },
+ { input: i32Bits(0b11111111111111111111111111000000), expected: i32(6) },
+ { input: i32Bits(0b11111111111111111111111110000000), expected: i32(7) },
+ { input: i32Bits(0b11111111111111111111111100000000), expected: i32(8) },
+ { input: i32Bits(0b11111111111111111111111000000000), expected: i32(9) },
+ { input: i32Bits(0b11111111111111111111110000000000), expected: i32(10) },
+ { input: i32Bits(0b11111111111111111111100000000000), expected: i32(11) },
+ { input: i32Bits(0b11111111111111111111000000000000), expected: i32(12) },
+ { input: i32Bits(0b11111111111111111110000000000000), expected: i32(13) },
+ { input: i32Bits(0b11111111111111111100000000000000), expected: i32(14) },
+ { input: i32Bits(0b11111111111111111000000000000000), expected: i32(15) },
+ { input: i32Bits(0b11111111111111110000000000000000), expected: i32(16) },
+ { input: i32Bits(0b11111111111111100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b11111111111111000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b11111111111110000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b11111111111100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b11111111111000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b11111111110000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b11111111100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b11111111000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b11111110000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b11111100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b11111000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b11110000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b11100000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b11000000000000000000000000000000), expected: i32(30) },
+
+ // random before trailing 1
+ { input: i32Bits(0b11110000001111111101111010001111), expected: i32(0) },
+ { input: i32Bits(0b11011110111111100101110011110010), expected: i32(1) },
+ { input: i32Bits(0b11110111011011111111010000111100), expected: i32(2) },
+ { input: i32Bits(0b11010011011101111111010011101000), expected: i32(3) },
+ { input: i32Bits(0b11010111110111110001111110110000), expected: i32(4) },
+ { input: i32Bits(0b11111101111101111110101111100000), expected: i32(5) },
+ { input: i32Bits(0b11111001111011111001111011000000), expected: i32(6) },
+ { input: i32Bits(0b11001110110111110111111010000000), expected: i32(7) },
+ { input: i32Bits(0b11101111011111101110101100000000), expected: i32(8) },
+ { input: i32Bits(0b11111101111011111111111000000000), expected: i32(9) },
+ { input: i32Bits(0b10011111011101110110110000000000), expected: i32(10) },
+ { input: i32Bits(0b11111111101101111011100000000000), expected: i32(11) },
+ { input: i32Bits(0b11111011010110111011000000000000), expected: i32(12) },
+ { input: i32Bits(0b00111101010000111010000000000000), expected: i32(13) },
+ { input: i32Bits(0b11111011110001101100000000000000), expected: i32(14) },
+ { input: i32Bits(0b10111111010111111000000000000000), expected: i32(15) },
+ { input: i32Bits(0b11011101111010110000000000000000), expected: i32(16) },
+ { input: i32Bits(0b01110100110110100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b11100111001011000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b11111001110110000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b00110100100100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b11111010011000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b00000010110000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b11100111100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b00101101000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b11011010000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b11010100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b10111000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b01110000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b10100000000000000000000000000000), expected: i32(29) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
new file mode 100644
index 0000000000..5bce44049e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
@@ -0,0 +1,66 @@
+export const description = `
+Execution tests for the 'cross' builtin function
+
+T is AbstractFloat, f32, or f16
+@const fn cross(e1: vec3<T> ,e2: vec3<T>) -> vec3<T>
+Returns the cross product of e1 and e2.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { crossInterval } from '../../../../../util/f32_interval.js';
+import { vectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateVectorPairToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('cross', {
+ f32_const: () => {
+ return generateVectorPairToVectorCases(
+ vectorF32Range(3),
+ vectorF32Range(3),
+ 'f32-only',
+ crossInterval
+ );
+ },
+ f32_non_const: () => {
+ return generateVectorPairToVectorCases(
+ vectorF32Range(3),
+ vectorF32Range(3),
+ 'unfiltered',
+ crossInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(
+ t,
+ builtin('cross'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
+ TypeVec(3, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts
new file mode 100644
index 0000000000..9b4346408b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'degrees' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<T>
+@const fn degrees(e1: T ) -> T
+Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when T is a vector
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { degreesInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('degrees', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', degreesInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', degreesInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('degrees'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts
new file mode 100644
index 0000000000..6ea24ceb6f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts
@@ -0,0 +1,32 @@
+export const description = `
+Execution tests for the 'determinant' builtin function
+
+T is AbstractFloat, f32, or f16
+@const determinant(e: matCxC<T> ) -> T
+Returns the determinant of e.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts
new file mode 100644
index 0000000000..f6c2239a02
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts
@@ -0,0 +1,172 @@
+export const description = `
+Execution tests for the 'distance' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn distance(e1: T ,e2: T ) -> f32
+Returns the distance between e1 and e2 (e.g. length(e1-e2)).
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { distanceInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, sparseVectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import {
+ allInputSources,
+ generateBinaryToF32IntervalCases,
+ generateVectorPairToF32IntervalCases,
+ run,
+} from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('distance', {
+ f32_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ distanceInterval
+ );
+ },
+ f32_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ distanceInterval
+ );
+ },
+ f32_vec2_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'f32-only',
+ distanceInterval
+ );
+ },
+ f32_vec2_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'unfiltered',
+ distanceInterval
+ );
+ },
+ f32_vec3_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'f32-only',
+ distanceInterval
+ );
+ },
+ f32_vec3_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'unfiltered',
+ distanceInterval
+ );
+ },
+ f32_vec4_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'f32-only',
+ distanceInterval
+ );
+ },
+ f32_vec4_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'unfiltered',
+ distanceInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('distance'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(
+ t,
+ builtin('distance'),
+ [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(
+ t,
+ builtin('distance'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(
+ t,
+ builtin('distance'),
+ [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts
new file mode 100644
index 0000000000..e76b4c56a9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts
@@ -0,0 +1,156 @@
+export const description = `
+Execution tests for the 'dot' builtin function
+
+T is AbstractInt, AbstractFloat, i32, u32, f32, or f16
+@const fn dot(e1: vecN<T>,e2: vecN<T>) -> T
+Returns the dot product of e1 and e2.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { dotInterval } from '../../../../../util/f32_interval.js';
+import { sparseVectorF32Range, vectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateVectorPairToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// vec3 and vec4 require calculating all possible permutations, so their runtime is much longer per test, so only using
+// sparse vectors for them
+export const d = makeCaseCache('dot', {
+ f32_vec2_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ vectorF32Range(2),
+ vectorF32Range(2),
+ 'f32-only',
+ dotInterval
+ );
+ },
+ f32_vec2_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ vectorF32Range(2),
+ vectorF32Range(2),
+ 'unfiltered',
+ dotInterval
+ );
+ },
+ f32_vec3_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'f32-only',
+ dotInterval
+ );
+ },
+ f32_vec3_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'unfiltered',
+ dotInterval
+ );
+ },
+ f32_vec4_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'f32-only',
+ dotInterval
+ );
+ },
+ f32_vec4_non_const: () => {
+ return generateVectorPairToF32IntervalCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'unfiltered',
+ dotInterval
+ );
+ },
+});
+
+g.test('abstract_int')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract int tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract float test`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(
+ t,
+ builtin('dot'),
+ [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(
+ t,
+ builtin('dot'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(
+ t,
+ builtin('dot'),
+ [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
+ TypeF32,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts
new file mode 100644
index 0000000000..287a51c699
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts
@@ -0,0 +1,23 @@
+export const description = `
+Execution tests for the 'dpdx' builtin function
+
+T is f32 or vecN<f32>
+fn dpdx(e:T) -> T
+Partial derivative of e with respect to window x coordinates.
+The result is the same as either dpdxFine(e) or dpdxCoarse(e).
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts
new file mode 100644
index 0000000000..67a75bb010
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts
@@ -0,0 +1,22 @@
+export const description = `
+Execution tests for the 'dpdxCoarse' builtin function
+
+T is f32 or vecN<f32>
+fn dpdxCoarse(e:T) ->T
+Returns the partial derivative of e with respect to window x coordinates using local differences.
+This may result in fewer unique positions that dpdxFine(e).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts
new file mode 100644
index 0000000000..91d65b990b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts
@@ -0,0 +1,21 @@
+export const description = `
+Execution tests for the 'dpdxFine' builtin function
+
+T is f32 or vecN<f32>
+fn dpdxFine(e:T) ->T
+Returns the partial derivative of e with respect to window x coordinates.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts
new file mode 100644
index 0000000000..0cd9cafdb9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts
@@ -0,0 +1,22 @@
+export const description = `
+Execution tests for the 'dpdy' builtin function
+
+T is f32 or vecN<f32>
+fn dpdy(e:T) ->T
+Partial derivative of e with respect to window y coordinates.
+The result is the same as either dpdyFine(e) or dpdyCoarse(e).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts
new file mode 100644
index 0000000000..f06869fdc2
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts
@@ -0,0 +1,22 @@
+export const description = `
+Execution tests for the 'dpdyCoarse' builtin function
+
+T is f32 or vecN<f32>
+fn dpdyCoarse(e:T) ->T
+Returns the partial derivative of e with respect to window y coordinates using local differences.
+This may result in fewer unique positions that dpdyFine(e).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 test`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts
new file mode 100644
index 0000000000..e09761de95
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts
@@ -0,0 +1,21 @@
+export const description = `
+Execution tests for the 'dpdyFine' builtin function
+
+T is f32 or vecN<f32>
+fn dpdyFine(e:T) ->T
+Returns the partial derivative of e with respect to window y coordinates.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
new file mode 100644
index 0000000000..1f43ac9d53
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
@@ -0,0 +1,68 @@
+export const description = `
+Execution tests for the 'exp' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn exp(e1: T ) -> T
+Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { expInterval } from '../../../../../util/f32_interval.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// floor(ln(max f32 value)) = 88, so exp(88) will be within range of a f32, but exp(89) will not
+// floor(ln(max f64 value)) = 709, so exp(709) can be handled by the testing framework, but exp(710) will misbehave
+const inputs = [
+ 0, // Returns 1 by definition
+ -89, // Returns subnormal value
+ kValue.f32.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f32.negative.max, -88, 100),
+ ...biasedRange(kValue.f32.positive.min, 88, 100),
+ ...linearRange(89, 709, 10), // Overflows f32, but not f64
+];
+
+export const d = makeCaseCache('exp', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', expInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', expInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('exp'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
new file mode 100644
index 0000000000..e0321387c1
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
@@ -0,0 +1,68 @@
+export const description = `
+Execution tests for the 'exp2' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn exp2(e: T ) -> T
+Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { exp2Interval } from '../../../../../util/f32_interval.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// floor(log2(max f32 value)) = 127, so exp2(127) will be within range of a f32, but exp2(128) will not
+// floor(ln(max f64 value)) = 1023, so exp2(1023) can be handled by the testing framework, but exp2(1024) will misbehave
+const inputs = [
+ 0, // Returns 1 by definition
+ -128, // Returns subnormal value
+ kValue.f32.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f32.negative.max, -127, 100),
+ ...biasedRange(kValue.f32.positive.min, 127, 100),
+ ...linearRange(128, 1023, 10), // Overflows f32, but not f64
+];
+
+export const d = makeCaseCache('exp2', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', exp2Interval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', exp2Interval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('exp2'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
new file mode 100644
index 0000000000..d535bf5d74
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
@@ -0,0 +1,337 @@
+export const description = `
+Execution tests for the 'extractBits' builtin function
+
+T is u32 or vecN<u32>
+@const fn extractBits(e: T, offset: u32, count: u32) -> T
+Reads bits from an integer, without sign extension.
+
+When T is a scalar type, then:
+ w is the bit width of T
+ o = min(offset,w)
+ c = min(count, w - o)
+
+The result is 0 if c is 0.
+Otherwise, bits 0..c-1 of the result are copied from bits o..o+c-1 of e.
+Other bits of the result are 0.
+Component-wise when T is a vector.
+
+
+T is i32 or vecN<i32>
+@const fn extractBits(e: T, offset: u32, count: u32) -> T
+Reads bits from an integer, with sign extension.
+
+When T is a scalar type, then:
+ w is the bit width of T
+ o = min(offset,w)
+ c = min(count, w - o)
+
+The result is 0 if c is 0.
+Otherwise, bits 0..c-1 of the result are copied from bits o..o+c-1 of e.
+Other bits of the result are the same as bit c-1 of the result.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import {
+ i32Bits,
+ TypeI32,
+ u32,
+ TypeU32,
+ u32Bits,
+ vec2,
+ vec3,
+ vec4,
+ TypeVec,
+} from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('width', [1, 2, 3, 4]))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const T = t.params.width === 1 ? TypeU32 : TypeVec(t.params.width, TypeU32);
+
+ const V = (x: number, y?: number, z?: number, w?: number) => {
+ y = y === undefined ? x : y;
+ z = z === undefined ? x : z;
+ w = w === undefined ? x : w;
+
+ switch (t.params.width) {
+ case 1:
+ return u32Bits(x);
+ case 2:
+ return vec2(u32Bits(x), u32Bits(y));
+ case 3:
+ return vec3(u32Bits(x), u32Bits(y), u32Bits(z));
+ default:
+ return vec4(u32Bits(x), u32Bits(y), u32Bits(z), u32Bits(w));
+ }
+ };
+
+ const all_1 = V(0b11111111111111111111111111111111);
+ const all_0 = V(0b00000000000000000000000000000000);
+ const low_1 = V(0b00000000000000000000000000000001);
+ const high_1 = V(0b10000000000000000000000000000000);
+ const pattern = V(
+ 0b00000000000111011100000000000000,
+ 0b11111111111000000011111111111111,
+ 0b00000000010101010101000000000000,
+ 0b00000000001010101010100000000000
+ );
+
+ const cases = [
+ { input: [all_0, u32(0), u32(32)], expected: all_0 },
+ { input: [all_0, u32(1), u32(10)], expected: all_0 },
+ { input: [all_0, u32(2), u32(5)], expected: all_0 },
+ { input: [all_0, u32(0), u32(1)], expected: all_0 },
+ { input: [all_0, u32(31), u32(1)], expected: all_0 },
+
+ { input: [all_1, u32(0), u32(32)], expected: all_1 },
+ {
+ input: [all_1, u32(1), u32(10)],
+ expected: V(0b00000000000000000000001111111111),
+ },
+ {
+ input: [all_1, u32(2), u32(5)],
+ expected: V(0b00000000000000000000000000011111),
+ },
+ { input: [all_1, u32(0), u32(1)], expected: low_1 },
+ { input: [all_1, u32(31), u32(1)], expected: low_1 },
+
+ // Patterns
+ { input: [pattern, u32(0), u32(32)], expected: pattern },
+ {
+ input: [pattern, u32(1), u32(31)],
+ expected: V(
+ 0b00000000000011101110000000000000,
+ 0b01111111111100000001111111111111,
+ 0b00000000001010101010100000000000,
+ 0b00000000000101010101010000000000
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(18)],
+ expected: V(
+ 0b00000000000000000000000001110111,
+ 0b00000000000000111111111110000000,
+ 0b00000000000000000000000101010101,
+ 0b00000000000000000000000010101010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(7)],
+ expected: V(
+ 0b00000000000000000000000001110111,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000001010101,
+ 0b00000000000000000000000000101010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(4)],
+ expected: V(
+ 0b00000000000000000000000000000111,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000101,
+ 0b00000000000000000000000000001010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(3)],
+ expected: V(
+ 0b00000000000000000000000000000111,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000101,
+ 0b00000000000000000000000000000010
+ ),
+ },
+ {
+ input: [pattern, u32(18), u32(3)],
+ expected: V(
+ 0b00000000000000000000000000000111,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000101,
+ 0b00000000000000000000000000000010
+ ),
+ },
+ { input: [low_1, u32(0), u32(1)], expected: low_1 },
+ { input: [high_1, u32(31), u32(1)], expected: low_1 },
+
+ // Zero count
+ { input: [all_1, u32(0), u32(0)], expected: all_0 },
+ { input: [all_0, u32(0), u32(0)], expected: all_0 },
+ { input: [low_1, u32(0), u32(0)], expected: all_0 },
+ { input: [high_1, u32(31), u32(0)], expected: all_0 },
+ { input: [pattern, u32(0), u32(0)], expected: all_0 },
+ ];
+
+ if (t.params.inputSource !== 'const') {
+ cases.push(
+ ...[
+ // End overflow
+ { input: [low_1, u32(0), u32(99)], expected: low_1 },
+ { input: [high_1, u32(31), u32(99)], expected: low_1 },
+ { input: [pattern, u32(0), u32(99)], expected: pattern },
+ {
+ input: [pattern, u32(14), u32(99)],
+ expected: V(
+ 0b00000000000000000000000001110111,
+ 0b00000000000000111111111110000000,
+ 0b00000000000000000000000101010101,
+ 0b00000000000000000000000010101010
+ ),
+ },
+ ]
+ );
+ }
+
+ await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('width', [1, 2, 3, 4]))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const T = t.params.width === 1 ? TypeI32 : TypeVec(t.params.width, TypeI32);
+
+ const V = (x: number, y?: number, z?: number, w?: number) => {
+ y = y === undefined ? x : y;
+ z = z === undefined ? x : z;
+ w = w === undefined ? x : w;
+
+ switch (t.params.width) {
+ case 1:
+ return i32Bits(x);
+ case 2:
+ return vec2(i32Bits(x), i32Bits(y));
+ case 3:
+ return vec3(i32Bits(x), i32Bits(y), i32Bits(z));
+ default:
+ return vec4(i32Bits(x), i32Bits(y), i32Bits(z), i32Bits(w));
+ }
+ };
+
+ const all_1 = V(0b11111111111111111111111111111111);
+ const all_0 = V(0b00000000000000000000000000000000);
+ const low_1 = V(0b00000000000000000000000000000001);
+ const high_1 = V(0b10000000000000000000000000000000);
+ const pattern = V(
+ 0b00000000000111011100000000000000,
+ 0b11111111111000000011111111111111,
+ 0b00000000010101010101000000000000,
+ 0b00000000001010101010100000000000
+ );
+
+ const cases = [
+ { input: [all_0, u32(0), u32(32)], expected: all_0 },
+ { input: [all_0, u32(1), u32(10)], expected: all_0 },
+ { input: [all_0, u32(2), u32(5)], expected: all_0 },
+ { input: [all_0, u32(0), u32(1)], expected: all_0 },
+ { input: [all_0, u32(31), u32(1)], expected: all_0 },
+
+ { input: [all_1, u32(0), u32(32)], expected: all_1 },
+ { input: [all_1, u32(1), u32(10)], expected: all_1 },
+ { input: [all_1, u32(2), u32(5)], expected: all_1 },
+ { input: [all_1, u32(0), u32(1)], expected: all_1 },
+ { input: [all_1, u32(31), u32(1)], expected: all_1 },
+
+ // Patterns
+ { input: [pattern, u32(0), u32(32)], expected: pattern },
+ {
+ input: [pattern, u32(1), u32(31)],
+ expected: V(
+ 0b00000000000011101110000000000000,
+ 0b11111111111100000001111111111111,
+ 0b00000000001010101010100000000000,
+ 0b00000000000101010101010000000000
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(18)],
+ expected: V(
+ 0b00000000000000000000000001110111,
+ 0b11111111111111111111111110000000,
+ 0b00000000000000000000000101010101,
+ 0b00000000000000000000000010101010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(7)],
+ expected: V(
+ 0b11111111111111111111111111110111,
+ 0b00000000000000000000000000000000,
+ 0b11111111111111111111111111010101,
+ 0b00000000000000000000000000101010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(4)],
+ expected: V(
+ 0b00000000000000000000000000000111,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000101,
+ 0b11111111111111111111111111111010
+ ),
+ },
+ {
+ input: [pattern, u32(14), u32(3)],
+ expected: V(
+ 0b11111111111111111111111111111111,
+ 0b00000000000000000000000000000000,
+ 0b11111111111111111111111111111101,
+ 0b00000000000000000000000000000010
+ ),
+ },
+ {
+ input: [pattern, u32(18), u32(3)],
+ expected: V(
+ 0b11111111111111111111111111111111,
+ 0b00000000000000000000000000000000,
+ 0b11111111111111111111111111111101,
+ 0b00000000000000000000000000000010
+ ),
+ },
+ { input: [low_1, u32(0), u32(1)], expected: all_1 },
+ { input: [high_1, u32(31), u32(1)], expected: all_1 },
+
+ // Zero count
+ { input: [all_1, u32(0), u32(0)], expected: all_0 },
+ { input: [all_0, u32(0), u32(0)], expected: all_0 },
+ { input: [low_1, u32(0), u32(0)], expected: all_0 },
+ { input: [high_1, u32(31), u32(0)], expected: all_0 },
+ { input: [pattern, u32(0), u32(0)], expected: all_0 },
+ ];
+
+ if (t.params.inputSource !== 'const') {
+ cases.push(
+ ...[
+ // End overflow
+ { input: [low_1, u32(0), u32(99)], expected: low_1 },
+ { input: [high_1, u32(31), u32(99)], expected: all_1 },
+ { input: [pattern, u32(0), u32(99)], expected: pattern },
+ {
+ input: [pattern, u32(14), u32(99)],
+ expected: V(
+ 0b00000000000000000000000001110111,
+ 0b11111111111111111111111110000000,
+ 0b00000000000000000000000101010101,
+ 0b00000000000000000000000010101010
+ ),
+ },
+ ]
+ );
+ }
+
+ await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
new file mode 100644
index 0000000000..22897f449a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
@@ -0,0 +1,201 @@
+export const description = `
+Execution tests for the 'faceForward' builtin function
+
+T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16>
+@const fn faceForward(e1: T ,e2: T ,e3: T ) -> T
+Returns e1 if dot(e2,e3) is negative, and -e1 otherwise.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { anyOf } from '../../../../../util/compare.js';
+import { f32, TypeF32, TypeVec, Vector } from '../../../../../util/conversion.js';
+import { F32Vector, faceForwardIntervals } from '../../../../../util/f32_interval.js';
+import { cartesianProduct, quantizeToF32, sparseVectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, IntervalFilter, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// Using a bespoke implementation of make*Case and generate*Cases here
+// since faceForwardIntervals is the only builtin with the API signature
+// (vec, vec, vec) -> vec
+//
+// Additionally faceForward has significant complexities around it due to the
+// fact that `dot` is calculated in it s operation, but the result of dot isn't
+// used to calculate the builtin's result.
+
+/**
+ * @returns a Case for `faceForward`
+ * @param x the `x` param for the case
+ * @param y the `y` param for the case
+ * @param z the `z` param for the case
+ * @param check what interval checking to apply
+ * */
+function makeCaseF32(
+ x: number[],
+ y: number[],
+ z: number[],
+ check: IntervalFilter
+): Case | undefined {
+ x = x.map(quantizeToF32);
+ y = y.map(quantizeToF32);
+ z = z.map(quantizeToF32);
+
+ const x_f32 = x.map(f32);
+ const y_f32 = y.map(f32);
+ const z_f32 = z.map(f32);
+
+ const results = faceForwardIntervals(x, y, z);
+ if (check === 'f32-only' && results.some(r => r === undefined)) {
+ return undefined;
+ }
+
+ // Stripping the undefined results, since undefined is used to signal that an OOB
+ // could occur within the calculation that isn't reflected in the result
+ // intervals.
+ const define_results = results.filter((r): r is F32Vector => r !== undefined);
+
+ return {
+ input: [new Vector(x_f32), new Vector(y_f32), new Vector(z_f32)],
+ expected: anyOf(...define_results),
+ };
+}
+
+/**
+ * @returns an array of Cases for `faceForward`
+ * @param xs array of inputs to try for the `x` param
+ * @param ys array of inputs to try for the `y` param
+ * @param zs array of inputs to try for the `z` param
+ * @param check what interval checking to apply
+ */
+function generateCasesF32(
+ xs: number[][],
+ ys: number[][],
+ zs: number[][],
+ check: IntervalFilter
+): Case[] {
+ // Cannot use `cartesianProduct` here due to heterogeneous param types
+ return cartesianProduct(xs, ys, zs)
+ .map(e => makeCaseF32(e[0], e[1], e[2], check))
+ .filter((c): c is Case => c !== undefined);
+}
+
+export const d = makeCaseCache('faceForward', {
+ f32_vec2_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'f32-only'
+ );
+ },
+ f32_vec2_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'unfiltered'
+ );
+ },
+ f32_vec3_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'f32-only'
+ );
+ },
+ f32_vec3_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'unfiltered'
+ );
+ },
+ f32_vec4_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'f32-only'
+ );
+ },
+ f32_vec4_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'unfiltered'
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(
+ t,
+ builtin('faceForward'),
+ [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
+ TypeVec(2, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(
+ t,
+ builtin('faceForward'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
+ TypeVec(3, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(
+ t,
+ builtin('faceForward'),
+ [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
+ TypeVec(4, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
new file mode 100644
index 0000000000..a04103304a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
@@ -0,0 +1,347 @@
+export const description = `
+Execution tests for the 'firstLeadingBit' builtin function
+
+T is u32 or vecN<u32>
+@const fn firstLeadingBit(e: T ) -> T
+For scalar T, the result is: T(-1) if e is zero.
+Otherwise the position of the most significant 1 bit in e.
+Component-wise when T is a vector.
+
+T is i32 or vecN<i32>
+@const fn firstLeadingBit(e: T ) -> T
+For scalar T, the result is: -1 if e is 0 or -1.
+Otherwise the position of the most significant bit in e that is different from e’s sign bit.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('firstLeadingBit'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) },
+
+ // One
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) },
+
+ // 0's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) },
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) },
+
+ // 1's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000011), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000000111), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001111), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000011111), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000111111), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000001111111), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000111111111), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000011111111111), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000111111111111), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000001111111111111), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000011111111111111), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000111111111111111), expected: u32(14) },
+ { input: u32Bits(0b00000000000000001111111111111111), expected: u32(15) },
+ { input: u32Bits(0b00000000000000011111111111111111), expected: u32(16) },
+ { input: u32Bits(0b00000000000000111111111111111111), expected: u32(17) },
+ { input: u32Bits(0b00000000000001111111111111111111), expected: u32(18) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(19) },
+ { input: u32Bits(0b00000000000111111111111111111111), expected: u32(20) },
+ { input: u32Bits(0b00000000001111111111111111111111), expected: u32(21) },
+ { input: u32Bits(0b00000000011111111111111111111111), expected: u32(22) },
+ { input: u32Bits(0b00000000111111111111111111111111), expected: u32(23) },
+ { input: u32Bits(0b00000001111111111111111111111111), expected: u32(24) },
+ { input: u32Bits(0b00000011111111111111111111111111), expected: u32(25) },
+ { input: u32Bits(0b00000111111111111111111111111111), expected: u32(26) },
+ { input: u32Bits(0b00001111111111111111111111111111), expected: u32(27) },
+ { input: u32Bits(0b00011111111111111111111111111111), expected: u32(28) },
+ { input: u32Bits(0b00111111111111111111111111111111), expected: u32(29) },
+ { input: u32Bits(0b01111111111111111111111111111111), expected: u32(30) },
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32(31) },
+
+ // random after leading 1
+ { input: u32Bits(0b00000000000000000000000000000110), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001101), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000011101), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000111001), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000001101111), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000111101111), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000011111110001), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000111011011101), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000001101101111111), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000011111111011111), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000101111001110101), expected: u32(14) },
+ { input: u32Bits(0b00000000000000001101111011110111), expected: u32(15) },
+ { input: u32Bits(0b00000000000000011111111111110011), expected: u32(16) },
+ { input: u32Bits(0b00000000000000111111111110111111), expected: u32(17) },
+ { input: u32Bits(0b00000000000001111111011111111111), expected: u32(18) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32(19) },
+ { input: u32Bits(0b00000000000111110101011110111111), expected: u32(20) },
+ { input: u32Bits(0b00000000001111101111111111110111), expected: u32(21) },
+ { input: u32Bits(0b00000000011111111111010000101111), expected: u32(22) },
+ { input: u32Bits(0b00000000111111111111001111111011), expected: u32(23) },
+ { input: u32Bits(0b00000001111111011111101111111111), expected: u32(24) },
+ { input: u32Bits(0b00000011101011111011110111111011), expected: u32(25) },
+ { input: u32Bits(0b00000111111110111111111111111111), expected: u32(26) },
+ { input: u32Bits(0b00001111000000011011011110111111), expected: u32(27) },
+ { input: u32Bits(0b00011110101111011111111111111111), expected: u32(28) },
+ { input: u32Bits(0b00110110111111100111111110111101), expected: u32(29) },
+ { input: u32Bits(0b01010111111101111111011111011111), expected: u32(30) },
+ { input: u32Bits(0b11100010011110101101101110101111), expected: u32(31) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('firstLeadingBit'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) },
+
+ // One
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) },
+
+ // Positive: 0's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) },
+
+ // Negative: 0's after leading 0
+ { input: i32Bits(0b11111111111111111111111111111110), expected: i32(0) },
+ { input: i32Bits(0b11111111111111111111111111111100), expected: i32(1) },
+ { input: i32Bits(0b11111111111111111111111111111000), expected: i32(2) },
+ { input: i32Bits(0b11111111111111111111111111110000), expected: i32(3) },
+ { input: i32Bits(0b11111111111111111111111111100000), expected: i32(4) },
+ { input: i32Bits(0b11111111111111111111111111000000), expected: i32(5) },
+ { input: i32Bits(0b11111111111111111111111110000000), expected: i32(6) },
+ { input: i32Bits(0b11111111111111111111111100000000), expected: i32(7) },
+ { input: i32Bits(0b11111111111111111111111000000000), expected: i32(8) },
+ { input: i32Bits(0b11111111111111111111110000000000), expected: i32(9) },
+ { input: i32Bits(0b11111111111111111111100000000000), expected: i32(10) },
+ { input: i32Bits(0b11111111111111111111000000000000), expected: i32(11) },
+ { input: i32Bits(0b11111111111111111110000000000000), expected: i32(12) },
+ { input: i32Bits(0b11111111111111111100000000000000), expected: i32(13) },
+ { input: i32Bits(0b11111111111111111000000000000000), expected: i32(14) },
+ { input: i32Bits(0b11111111111111110000000000000000), expected: i32(15) },
+ { input: i32Bits(0b11111111111111100000000000000000), expected: i32(16) },
+ { input: i32Bits(0b11111111111111000000000000000000), expected: i32(17) },
+ { input: i32Bits(0b11111111111110000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b11111111111100000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b11111111111000000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b11111111110000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b11111111100000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b11111111000000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b11111110000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b11111100000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b11111000000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b11110000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b11100000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b11000000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32(30) },
+
+ // Positive: 1's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000011), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000000111), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001111), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000011111), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000111111), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000001111111), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000111111111), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000011111111111), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000111111111111), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000001111111111111), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000011111111111111), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000111111111111111), expected: i32(14) },
+ { input: i32Bits(0b00000000000000001111111111111111), expected: i32(15) },
+ { input: i32Bits(0b00000000000000011111111111111111), expected: i32(16) },
+ { input: i32Bits(0b00000000000000111111111111111111), expected: i32(17) },
+ { input: i32Bits(0b00000000000001111111111111111111), expected: i32(18) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(19) },
+ { input: i32Bits(0b00000000000111111111111111111111), expected: i32(20) },
+ { input: i32Bits(0b00000000001111111111111111111111), expected: i32(21) },
+ { input: i32Bits(0b00000000011111111111111111111111), expected: i32(22) },
+ { input: i32Bits(0b00000000111111111111111111111111), expected: i32(23) },
+ { input: i32Bits(0b00000001111111111111111111111111), expected: i32(24) },
+ { input: i32Bits(0b00000011111111111111111111111111), expected: i32(25) },
+ { input: i32Bits(0b00000111111111111111111111111111), expected: i32(26) },
+ { input: i32Bits(0b00001111111111111111111111111111), expected: i32(27) },
+ { input: i32Bits(0b00011111111111111111111111111111), expected: i32(28) },
+ { input: i32Bits(0b00111111111111111111111111111111), expected: i32(29) },
+ { input: i32Bits(0b01111111111111111111111111111111), expected: i32(30) },
+
+ // Negative: 1's after leading 0
+ { input: i32Bits(0b11111111111111111111111111111101), expected: i32(1) },
+ { input: i32Bits(0b11111111111111111111111111111011), expected: i32(2) },
+ { input: i32Bits(0b11111111111111111111111111110111), expected: i32(3) },
+ { input: i32Bits(0b11111111111111111111111111101111), expected: i32(4) },
+ { input: i32Bits(0b11111111111111111111111111011111), expected: i32(5) },
+ { input: i32Bits(0b11111111111111111111111110111111), expected: i32(6) },
+ { input: i32Bits(0b11111111111111111111111101111111), expected: i32(7) },
+ { input: i32Bits(0b11111111111111111111111011111111), expected: i32(8) },
+ { input: i32Bits(0b11111111111111111111110111111111), expected: i32(9) },
+ { input: i32Bits(0b11111111111111111111101111111111), expected: i32(10) },
+ { input: i32Bits(0b11111111111111111111011111111111), expected: i32(11) },
+ { input: i32Bits(0b11111111111111111110111111111111), expected: i32(12) },
+ { input: i32Bits(0b11111111111111111101111111111111), expected: i32(13) },
+ { input: i32Bits(0b11111111111111111011111111111111), expected: i32(14) },
+ { input: i32Bits(0b11111111111111110111111111111111), expected: i32(15) },
+ { input: i32Bits(0b11111111111111101111111111111111), expected: i32(16) },
+ { input: i32Bits(0b11111111111111011111111111111111), expected: i32(17) },
+ { input: i32Bits(0b11111111111110111111111111111111), expected: i32(18) },
+ { input: i32Bits(0b11111111111101111111111111111111), expected: i32(19) },
+ { input: i32Bits(0b11111111111011111111111111111111), expected: i32(20) },
+ { input: i32Bits(0b11111111110111111111111111111111), expected: i32(21) },
+ { input: i32Bits(0b11111111101111111111111111111111), expected: i32(22) },
+ { input: i32Bits(0b11111111011111111111111111111111), expected: i32(23) },
+ { input: i32Bits(0b11111110111111111111111111111111), expected: i32(24) },
+ { input: i32Bits(0b11111101111111111111111111111111), expected: i32(25) },
+ { input: i32Bits(0b11111011111111111111111111111111), expected: i32(26) },
+ { input: i32Bits(0b11110111111111111111111111111111), expected: i32(27) },
+ { input: i32Bits(0b11101111111111111111111111111111), expected: i32(28) },
+ { input: i32Bits(0b11011111111111111111111111111111), expected: i32(29) },
+ { input: i32Bits(0b10111111111111111111111111111111), expected: i32(30) },
+
+ // Positive: random after leading 1
+ { input: i32Bits(0b00000000000000000000000000000110), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001101), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000011101), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000111001), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000001101111), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000111101111), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000011111110001), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000111011011101), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000001101101111111), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000011111111011111), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000101111001110101), expected: i32(14) },
+ { input: i32Bits(0b00000000000000001101111011110111), expected: i32(15) },
+ { input: i32Bits(0b00000000000000011111111111110011), expected: i32(16) },
+ { input: i32Bits(0b00000000000000111111111110111111), expected: i32(17) },
+ { input: i32Bits(0b00000000000001111111011111111111), expected: i32(18) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32(19) },
+ { input: i32Bits(0b00000000000111110101011110111111), expected: i32(20) },
+ { input: i32Bits(0b00000000001111101111111111110111), expected: i32(21) },
+ { input: i32Bits(0b00000000011111111111010000101111), expected: i32(22) },
+ { input: i32Bits(0b00000000111111111111001111111011), expected: i32(23) },
+ { input: i32Bits(0b00000001111111011111101111111111), expected: i32(24) },
+ { input: i32Bits(0b00000011101011111011110111111011), expected: i32(25) },
+ { input: i32Bits(0b00000111111110111111111111111111), expected: i32(26) },
+ { input: i32Bits(0b00001111000000011011011110111111), expected: i32(27) },
+ { input: i32Bits(0b00011110101111011111111111111111), expected: i32(28) },
+ { input: i32Bits(0b00110110111111100111111110111101), expected: i32(29) },
+ { input: i32Bits(0b01010111111101111111011111011111), expected: i32(30) },
+
+ // Negative: random after leading 0
+ { input: i32Bits(0b11111111111111111111111111111010), expected: i32(2) },
+ { input: i32Bits(0b11111111111111111111111111110110), expected: i32(3) },
+ { input: i32Bits(0b11111111111111111111111111101101), expected: i32(4) },
+ { input: i32Bits(0b11111111111111111111111111011101), expected: i32(5) },
+ { input: i32Bits(0b11111111111111111111111110111001), expected: i32(6) },
+ { input: i32Bits(0b11111111111111111111111101101111), expected: i32(7) },
+ { input: i32Bits(0b11111111111111111111111011111111), expected: i32(8) },
+ { input: i32Bits(0b11111111111111111111110111101111), expected: i32(9) },
+ { input: i32Bits(0b11111111111111111111101111111111), expected: i32(10) },
+ { input: i32Bits(0b11111111111111111111011111110001), expected: i32(11) },
+ { input: i32Bits(0b11111111111111111110111011011101), expected: i32(12) },
+ { input: i32Bits(0b11111111111111111101101101111111), expected: i32(13) },
+ { input: i32Bits(0b11111111111111111011111111011111), expected: i32(14) },
+ { input: i32Bits(0b11111111111111110101111001110101), expected: i32(15) },
+ { input: i32Bits(0b11111111111111101101111011110111), expected: i32(16) },
+ { input: i32Bits(0b11111111111111011111111111110011), expected: i32(17) },
+ { input: i32Bits(0b11111111111110111111111110111111), expected: i32(18) },
+ { input: i32Bits(0b11111111111101111111011111111111), expected: i32(19) },
+ { input: i32Bits(0b11111111111011111111111111111111), expected: i32(20) },
+ { input: i32Bits(0b11111111110111110101011110111111), expected: i32(21) },
+ { input: i32Bits(0b11111111101111101111111111110111), expected: i32(22) },
+ { input: i32Bits(0b11111111011111111111010000101111), expected: i32(23) },
+ { input: i32Bits(0b11111110111111111111001111111011), expected: i32(24) },
+ { input: i32Bits(0b11111101111111011111101111111111), expected: i32(25) },
+ { input: i32Bits(0b11111011101011111011110111111011), expected: i32(26) },
+ { input: i32Bits(0b11110111111110111111111111111111), expected: i32(27) },
+ { input: i32Bits(0b11101111000000011011011110111111), expected: i32(28) },
+ { input: i32Bits(0b11011110101111011111111111111111), expected: i32(29) },
+ { input: i32Bits(0b10110110111111100111111110111101), expected: i32(30) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
new file mode 100644
index 0000000000..5c65f59d28
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
@@ -0,0 +1,250 @@
+export const description = `
+Execution tests for the 'firstTrailingBit' builtin function
+
+S is i32, u32
+T is S or vecN<S>
+@const fn firstTrailingBit(e: T ) -> T
+For scalar T, the result is: T(-1) if e is zero.
+Otherwise the position of the least significant 1 bit in e.
+Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('firstTrailingBit'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) },
+
+ // High bit
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) },
+
+ // 0's before trailing 1
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) },
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) },
+
+ // 1's before trailing 1
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) },
+ { input: u32Bits(0b11111111111111111111111111111110), expected: u32(1) },
+ { input: u32Bits(0b11111111111111111111111111111100), expected: u32(2) },
+ { input: u32Bits(0b11111111111111111111111111111000), expected: u32(3) },
+ { input: u32Bits(0b11111111111111111111111111110000), expected: u32(4) },
+ { input: u32Bits(0b11111111111111111111111111100000), expected: u32(5) },
+ { input: u32Bits(0b11111111111111111111111111000000), expected: u32(6) },
+ { input: u32Bits(0b11111111111111111111111110000000), expected: u32(7) },
+ { input: u32Bits(0b11111111111111111111111100000000), expected: u32(8) },
+ { input: u32Bits(0b11111111111111111111111000000000), expected: u32(9) },
+ { input: u32Bits(0b11111111111111111111110000000000), expected: u32(10) },
+ { input: u32Bits(0b11111111111111111111100000000000), expected: u32(11) },
+ { input: u32Bits(0b11111111111111111111000000000000), expected: u32(12) },
+ { input: u32Bits(0b11111111111111111110000000000000), expected: u32(13) },
+ { input: u32Bits(0b11111111111111111100000000000000), expected: u32(14) },
+ { input: u32Bits(0b11111111111111111000000000000000), expected: u32(15) },
+ { input: u32Bits(0b11111111111111110000000000000000), expected: u32(16) },
+ { input: u32Bits(0b11111111111111100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b11111111111111000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b11111111111110000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b11111111111100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b11111111111000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b11111111110000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b11111111100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b11111111000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b11111110000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b11111100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b11111000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b11110000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b11100000000000000000000000000000), expected: u32(29) },
+ { input: u32Bits(0b11000000000000000000000000000000), expected: u32(30) },
+
+ // random before trailing 1
+ { input: u32Bits(0b11110000001111111101111010001111), expected: u32(0) },
+ { input: u32Bits(0b11011110111111100101110011110010), expected: u32(1) },
+ { input: u32Bits(0b11110111011011111111010000111100), expected: u32(2) },
+ { input: u32Bits(0b11010011011101111111010011101000), expected: u32(3) },
+ { input: u32Bits(0b11010111110111110001111110110000), expected: u32(4) },
+ { input: u32Bits(0b11111101111101111110101111100000), expected: u32(5) },
+ { input: u32Bits(0b11111001111011111001111011000000), expected: u32(6) },
+ { input: u32Bits(0b11001110110111110111111010000000), expected: u32(7) },
+ { input: u32Bits(0b11101111011111101110101100000000), expected: u32(8) },
+ { input: u32Bits(0b11111101111011111111111000000000), expected: u32(9) },
+ { input: u32Bits(0b10011111011101110110110000000000), expected: u32(10) },
+ { input: u32Bits(0b11111111101101111011100000000000), expected: u32(11) },
+ { input: u32Bits(0b11111011010110111011000000000000), expected: u32(12) },
+ { input: u32Bits(0b00111101010000111010000000000000), expected: u32(13) },
+ { input: u32Bits(0b11111011110001101100000000000000), expected: u32(14) },
+ { input: u32Bits(0b10111111010111111000000000000000), expected: u32(15) },
+ { input: u32Bits(0b11011101111010110000000000000000), expected: u32(16) },
+ { input: u32Bits(0b01110100110110100000000000000000), expected: u32(17) },
+ { input: u32Bits(0b11100111001011000000000000000000), expected: u32(18) },
+ { input: u32Bits(0b11111001110110000000000000000000), expected: u32(19) },
+ { input: u32Bits(0b00110100100100000000000000000000), expected: u32(20) },
+ { input: u32Bits(0b11111010011000000000000000000000), expected: u32(21) },
+ { input: u32Bits(0b00000010110000000000000000000000), expected: u32(22) },
+ { input: u32Bits(0b11100111100000000000000000000000), expected: u32(23) },
+ { input: u32Bits(0b00101101000000000000000000000000), expected: u32(24) },
+ { input: u32Bits(0b11011010000000000000000000000000), expected: u32(25) },
+ { input: u32Bits(0b11010100000000000000000000000000), expected: u32(26) },
+ { input: u32Bits(0b10111000000000000000000000000000), expected: u32(27) },
+ { input: u32Bits(0b01110000000000000000000000000000), expected: u32(28) },
+ { input: u32Bits(0b10100000000000000000000000000000), expected: u32(29) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ await run(t, builtin('firstTrailingBit'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) },
+
+ // High bit
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32(31) },
+
+ // 0's before trailing 1
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) },
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) },
+
+ // 1's before trailing 1
+ { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) },
+ { input: i32Bits(0b11111111111111111111111111111110), expected: i32(1) },
+ { input: i32Bits(0b11111111111111111111111111111100), expected: i32(2) },
+ { input: i32Bits(0b11111111111111111111111111111000), expected: i32(3) },
+ { input: i32Bits(0b11111111111111111111111111110000), expected: i32(4) },
+ { input: i32Bits(0b11111111111111111111111111100000), expected: i32(5) },
+ { input: i32Bits(0b11111111111111111111111111000000), expected: i32(6) },
+ { input: i32Bits(0b11111111111111111111111110000000), expected: i32(7) },
+ { input: i32Bits(0b11111111111111111111111100000000), expected: i32(8) },
+ { input: i32Bits(0b11111111111111111111111000000000), expected: i32(9) },
+ { input: i32Bits(0b11111111111111111111110000000000), expected: i32(10) },
+ { input: i32Bits(0b11111111111111111111100000000000), expected: i32(11) },
+ { input: i32Bits(0b11111111111111111111000000000000), expected: i32(12) },
+ { input: i32Bits(0b11111111111111111110000000000000), expected: i32(13) },
+ { input: i32Bits(0b11111111111111111100000000000000), expected: i32(14) },
+ { input: i32Bits(0b11111111111111111000000000000000), expected: i32(15) },
+ { input: i32Bits(0b11111111111111110000000000000000), expected: i32(16) },
+ { input: i32Bits(0b11111111111111100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b11111111111111000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b11111111111110000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b11111111111100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b11111111111000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b11111111110000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b11111111100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b11111111000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b11111110000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b11111100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b11111000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b11110000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b11100000000000000000000000000000), expected: i32(29) },
+ { input: i32Bits(0b11000000000000000000000000000000), expected: i32(30) },
+
+ // random before trailing 1
+ { input: i32Bits(0b11110000001111111101111010001111), expected: i32(0) },
+ { input: i32Bits(0b11011110111111100101110011110010), expected: i32(1) },
+ { input: i32Bits(0b11110111011011111111010000111100), expected: i32(2) },
+ { input: i32Bits(0b11010011011101111111010011101000), expected: i32(3) },
+ { input: i32Bits(0b11010111110111110001111110110000), expected: i32(4) },
+ { input: i32Bits(0b11111101111101111110101111100000), expected: i32(5) },
+ { input: i32Bits(0b11111001111011111001111011000000), expected: i32(6) },
+ { input: i32Bits(0b11001110110111110111111010000000), expected: i32(7) },
+ { input: i32Bits(0b11101111011111101110101100000000), expected: i32(8) },
+ { input: i32Bits(0b11111101111011111111111000000000), expected: i32(9) },
+ { input: i32Bits(0b10011111011101110110110000000000), expected: i32(10) },
+ { input: i32Bits(0b11111111101101111011100000000000), expected: i32(11) },
+ { input: i32Bits(0b11111011010110111011000000000000), expected: i32(12) },
+ { input: i32Bits(0b00111101010000111010000000000000), expected: i32(13) },
+ { input: i32Bits(0b11111011110001101100000000000000), expected: i32(14) },
+ { input: i32Bits(0b10111111010111111000000000000000), expected: i32(15) },
+ { input: i32Bits(0b11011101111010110000000000000000), expected: i32(16) },
+ { input: i32Bits(0b01110100110110100000000000000000), expected: i32(17) },
+ { input: i32Bits(0b11100111001011000000000000000000), expected: i32(18) },
+ { input: i32Bits(0b11111001110110000000000000000000), expected: i32(19) },
+ { input: i32Bits(0b00110100100100000000000000000000), expected: i32(20) },
+ { input: i32Bits(0b11111010011000000000000000000000), expected: i32(21) },
+ { input: i32Bits(0b00000010110000000000000000000000), expected: i32(22) },
+ { input: i32Bits(0b11100111100000000000000000000000), expected: i32(23) },
+ { input: i32Bits(0b00101101000000000000000000000000), expected: i32(24) },
+ { input: i32Bits(0b11011010000000000000000000000000), expected: i32(25) },
+ { input: i32Bits(0b11010100000000000000000000000000), expected: i32(26) },
+ { input: i32Bits(0b10111000000000000000000000000000), expected: i32(27) },
+ { input: i32Bits(0b01110000000000000000000000000000), expected: i32(28) },
+ { input: i32Bits(0b10100000000000000000000000000000), expected: i32(29) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
new file mode 100644
index 0000000000..227efff7ef
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
@@ -0,0 +1,71 @@
+export const description = `
+Execution tests for the 'floor' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn floor(e: T ) -> T
+Returns the floor of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { floorInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('floor', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Small positive numbers
+ 0.1,
+ 0.9,
+ 1.0,
+ 1.1,
+ 1.9,
+ // Small negative numbers
+ -0.1,
+ -0.9,
+ -1.0,
+ -1.1,
+ -1.9,
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ floorInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('floor'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
new file mode 100644
index 0000000000..f7488eb7b0
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
@@ -0,0 +1,68 @@
+export const description = `
+Execution tests for the 'fma' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn fma(e1: T ,e2: T ,e3: T ) -> T
+Returns e1 * e2 + e3. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { fmaInterval } from '../../../../../util/f32_interval.js';
+import { sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('fma', {
+ f32_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'f32-only',
+ fmaInterval
+ );
+ },
+ f32_non_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'unfiltered',
+ fmaInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('fma'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
new file mode 100644
index 0000000000..c7d0244b96
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
@@ -0,0 +1,73 @@
+export const description = `
+Execution tests for the 'fract' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn fract(e: T ) -> T
+Returns the fractional part of e, computed as e - floor(e).
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { fractInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('fract', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ 0.5, // 0.5 -> 0.5
+ 0.9, // ~0.9 -> ~0.9
+ 1, // 1 -> 0
+ 2, // 2 -> 0
+ 1.11, // ~1.11 -> ~0.11
+ 10.0001, // ~10.0001 -> ~0.0001
+ -0.1, // ~-0.1 -> ~0.9
+ -0.5, // -0.5 -> 0.5
+ -0.9, // ~-0.9 -> ~0.1
+ -1, // -1 -> 0
+ -2, // -2 -> 0
+ -1.11, // ~-1.11 -> ~0.89
+ -10.0001, // -10.0001 -> ~0.9999
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ fractInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('fract'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
new file mode 100644
index 0000000000..02f301f868
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
@@ -0,0 +1,80 @@
+export const description = `
+Execution tests for the 'frexp' builtin function
+
+S is f32 or f16
+T is S or vecN<S>
+
+@const fn frexp(e: T) -> result_struct
+
+Splits e into a significand and exponent of the form significand * 2^exponent.
+Returns the result_struct for the appropriate overload.
+
+
+The magnitude of the significand is in the range of [0.5, 1.0) or 0.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('scalar_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+struct __frexp_result {
+ sig : f32, // significand part
+ exp : i32 // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('scalar_f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f16 tests
+
+struct __frexp_result_f16 {
+ sig : f16, // significand part
+ exp : i32 // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('vector_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+vecN<f32>
+
+struct __frexp_result_vecN {
+ sig : vecN<f32>, // significand part
+ exp : vecN<i32> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('vector_f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+vecN<f16>
+
+struct __frexp_result_vecN_f16 {
+ sig : vecN<f16>, // significand part
+ exp : vecN<i32> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts
new file mode 100644
index 0000000000..7c6f0232a9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts
@@ -0,0 +1,21 @@
+export const description = `
+Execution tests for the 'fwidth' builtin function
+
+T is f32 or vecN<f32>
+fn fwidth(e:T) ->T
+Returns abs(dpdx(e)) + abs(dpdy(e)).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts
new file mode 100644
index 0000000000..9f93237934
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts
@@ -0,0 +1,21 @@
+export const description = `
+Execution tests for the 'fwidthCoarse' builtin function
+
+T is f32 or vecN<f32>
+fn fwidthCoarse(e:T) ->T
+Returns abs(dpdxCoarse(e)) + abs(dpdyCoarse(e)).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts
new file mode 100644
index 0000000000..b08c293228
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts
@@ -0,0 +1,21 @@
+export const description = `
+Execution tests for the 'fwidthFine' builtin function
+
+T is f32 or vecN<f32>
+fn fwidthFine(e:T) ->T
+Returns abs(dpdxFine(e)) + abs(dpdyFine(e)).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
new file mode 100644
index 0000000000..1068e76252
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
@@ -0,0 +1,386 @@
+export const description = `
+Execution tests for the 'insertBits' builtin function
+
+S is i32 or u32
+T is S or vecN<S>
+@const fn insertBits(e: T, newbits:T, offset: u32, count: u32) -> T Sets bits in an integer.
+
+When T is a scalar type, then:
+ w is the bit width of T
+ o = min(offset,w)
+ c = min(count, w - o)
+
+The result is e if c is 0.
+Otherwise, bits o..o+c-1 of the result are copied from bits 0..c-1 of newbits.
+Other bits of the result are copied from e.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import {
+ i32Bits,
+ TypeI32,
+ u32,
+ TypeU32,
+ u32Bits,
+ vec2,
+ vec3,
+ vec4,
+ TypeVec,
+} from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('integer')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`integer tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('signed', [false, true])
+ .combine('width', [1, 2, 3, 4])
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ const scalarType = t.params.signed ? TypeI32 : TypeU32;
+ const T = t.params.width === 1 ? scalarType : TypeVec(t.params.width, scalarType);
+
+ const V = (x: number, y?: number, z?: number, w?: number) => {
+ y = y === undefined ? x : y;
+ z = z === undefined ? x : z;
+ w = w === undefined ? x : w;
+
+ if (t.params.signed) {
+ switch (t.params.width) {
+ case 1:
+ return i32Bits(x);
+ case 2:
+ return vec2(i32Bits(x), i32Bits(y));
+ case 3:
+ return vec3(i32Bits(x), i32Bits(y), i32Bits(z));
+ default:
+ return vec4(i32Bits(x), i32Bits(y), i32Bits(z), i32Bits(w));
+ }
+ } else {
+ switch (t.params.width) {
+ case 1:
+ return u32Bits(x);
+ case 2:
+ return vec2(u32Bits(x), u32Bits(y));
+ case 3:
+ return vec3(u32Bits(x), u32Bits(y), u32Bits(z));
+ default:
+ return vec4(u32Bits(x), u32Bits(y), u32Bits(z), u32Bits(w));
+ }
+ }
+ };
+
+ const all_1 = V(0b11111111111111111111111111111111);
+ const all_0 = V(0b00000000000000000000000000000000);
+ const low_1 = V(0b00000000000000000000000000000001);
+ const low_0 = V(0b11111111111111111111111111111110);
+ const high_1 = V(0b10000000000000000000000000000000);
+ const high_0 = V(0b01111111111111111111111111111111);
+ const pattern = V(
+ 0b10001001010100100010010100100010,
+ 0b11001110001100111000110011100011,
+ 0b10101010101010101010101010101010,
+ 0b01010101010101010101010101010101
+ );
+
+ const cases = [
+ { input: [all_0, all_0, u32(0), u32(32)], expected: all_0 },
+ { input: [all_0, all_0, u32(1), u32(10)], expected: all_0 },
+ { input: [all_0, all_0, u32(2), u32(5)], expected: all_0 },
+ { input: [all_0, all_0, u32(0), u32(1)], expected: all_0 },
+ { input: [all_0, all_0, u32(31), u32(1)], expected: all_0 },
+
+ { input: [all_0, all_1, u32(0), u32(32)], expected: all_1 },
+ { input: [all_1, all_0, u32(0), u32(32)], expected: all_0 },
+ { input: [all_0, all_1, u32(0), u32(1)], expected: low_1 },
+ { input: [all_1, all_0, u32(0), u32(1)], expected: low_0 },
+ { input: [all_0, all_1, u32(31), u32(1)], expected: high_1 },
+ { input: [all_1, all_0, u32(31), u32(1)], expected: high_0 },
+ { input: [all_0, all_1, u32(1), u32(10)], expected: V(0b00000000000000000000011111111110) },
+ { input: [all_1, all_0, u32(1), u32(10)], expected: V(0b11111111111111111111100000000001) },
+ { input: [all_0, all_1, u32(2), u32(5)], expected: V(0b00000000000000000000000001111100) },
+ { input: [all_1, all_0, u32(2), u32(5)], expected: V(0b11111111111111111111111110000011) },
+
+ // Patterns
+ { input: [all_0, pattern, u32(0), u32(32)], expected: pattern },
+ { input: [all_1, pattern, u32(0), u32(32)], expected: pattern },
+ {
+ input: [all_0, pattern, u32(1), u32(31)],
+ expected: V(
+ 0b00010010101001000100101001000100,
+ 0b10011100011001110001100111000110,
+ 0b01010101010101010101010101010100,
+ 0b10101010101010101010101010101010
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(1), u32(31)],
+ expected: V(
+ 0b00010010101001000100101001000101,
+ 0b10011100011001110001100111000111,
+ 0b01010101010101010101010101010101,
+ 0b10101010101010101010101010101011
+ ),
+ },
+ {
+ input: [all_0, pattern, u32(14), u32(18)],
+ expected: V(
+ 0b10001001010010001000000000000000,
+ 0b11100011001110001100000000000000,
+ 0b10101010101010101000000000000000,
+ 0b01010101010101010100000000000000
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(14), u32(18)],
+ expected: V(
+ 0b10001001010010001011111111111111,
+ 0b11100011001110001111111111111111,
+ 0b10101010101010101011111111111111,
+ 0b01010101010101010111111111111111
+ ),
+ },
+ {
+ input: [all_0, pattern, u32(14), u32(7)],
+ expected: V(
+ 0b00000000000010001000000000000000,
+ 0b00000000000110001100000000000000,
+ 0b00000000000010101000000000000000,
+ 0b00000000000101010100000000000000
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(14), u32(7)],
+ expected: V(
+ 0b11111111111010001011111111111111,
+ 0b11111111111110001111111111111111,
+ 0b11111111111010101011111111111111,
+ 0b11111111111101010111111111111111
+ ),
+ },
+ {
+ input: [all_0, pattern, u32(14), u32(4)],
+ expected: V(
+ 0b00000000000000001000000000000000,
+ 0b00000000000000001100000000000000,
+ 0b00000000000000101000000000000000,
+ 0b00000000000000010100000000000000
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(14), u32(4)],
+ expected: V(
+ 0b11111111111111001011111111111111,
+ 0b11111111111111001111111111111111,
+ 0b11111111111111101011111111111111,
+ 0b11111111111111010111111111111111
+ ),
+ },
+ {
+ input: [all_0, pattern, u32(14), u32(3)],
+ expected: V(
+ 0b00000000000000001000000000000000,
+ 0b00000000000000001100000000000000,
+ 0b00000000000000001000000000000000,
+ 0b00000000000000010100000000000000
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(14), u32(3)],
+ expected: V(
+ 0b11111111111111101011111111111111,
+ 0b11111111111111101111111111111111,
+ 0b11111111111111101011111111111111,
+ 0b11111111111111110111111111111111
+ ),
+ },
+ {
+ input: [all_0, pattern, u32(18), u32(3)],
+ expected: V(
+ 0b00000000000010000000000000000000,
+ 0b00000000000011000000000000000000,
+ 0b00000000000010000000000000000000,
+ 0b00000000000101000000000000000000
+ ),
+ },
+ {
+ input: [all_1, pattern, u32(18), u32(3)],
+ expected: V(
+ 0b11111111111010111111111111111111,
+ 0b11111111111011111111111111111111,
+ 0b11111111111010111111111111111111,
+ 0b11111111111101111111111111111111
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(1), u32(31)],
+ expected: V(
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000001,
+ 0b00000000000000000000000000000000,
+ 0b00000000000000000000000000000001
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(1), u32(31)],
+ expected: V(
+ 0b11111111111111111111111111111110,
+ 0b11111111111111111111111111111111,
+ 0b11111111111111111111111111111110,
+ 0b11111111111111111111111111111111
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(14), u32(18)],
+ expected: V(
+ 0b00000000000000000010010100100010,
+ 0b00000000000000000000110011100011,
+ 0b00000000000000000010101010101010,
+ 0b00000000000000000001010101010101
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(14), u32(18)],
+ expected: V(
+ 0b11111111111111111110010100100010,
+ 0b11111111111111111100110011100011,
+ 0b11111111111111111110101010101010,
+ 0b11111111111111111101010101010101
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(14), u32(7)],
+ expected: V(
+ 0b10001001010000000010010100100010,
+ 0b11001110001000000000110011100011,
+ 0b10101010101000000010101010101010,
+ 0b01010101010000000001010101010101
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(14), u32(7)],
+ expected: V(
+ 0b10001001010111111110010100100010,
+ 0b11001110001111111100110011100011,
+ 0b10101010101111111110101010101010,
+ 0b01010101010111111101010101010101
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(14), u32(4)],
+ expected: V(
+ 0b10001001010100000010010100100010,
+ 0b11001110001100000000110011100011,
+ 0b10101010101010000010101010101010,
+ 0b01010101010101000001010101010101
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(14), u32(4)],
+ expected: V(
+ 0b10001001010100111110010100100010,
+ 0b11001110001100111100110011100011,
+ 0b10101010101010111110101010101010,
+ 0b01010101010101111101010101010101
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(14), u32(3)],
+ expected: V(
+ 0b10001001010100100010010100100010,
+ 0b11001110001100100000110011100011,
+ 0b10101010101010100010101010101010,
+ 0b01010101010101000001010101010101
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(14), u32(3)],
+ expected: V(
+ 0b10001001010100111110010100100010,
+ 0b11001110001100111100110011100011,
+ 0b10101010101010111110101010101010,
+ 0b01010101010101011101010101010101
+ ),
+ },
+ {
+ input: [pattern, all_0, u32(18), u32(3)],
+ expected: V(
+ 0b10001001010000100010010100100010,
+ 0b11001110001000111000110011100011,
+ 0b10101010101000101010101010101010,
+ 0b01010101010000010101010101010101
+ ),
+ },
+ {
+ input: [pattern, all_1, u32(18), u32(3)],
+ expected: V(
+ 0b10001001010111100010010100100010,
+ 0b11001110001111111000110011100011,
+ 0b10101010101111101010101010101010,
+ 0b01010101010111010101010101010101
+ ),
+ },
+ {
+ input: [pattern, pattern, u32(18), u32(3)],
+ expected: V(
+ 0b10001001010010100010010100100010,
+ 0b11001110001011111000110011100011,
+ 0b10101010101010101010101010101010,
+ 0b01010101010101010101010101010101
+ ),
+ },
+ {
+ input: [pattern, pattern, u32(14), u32(7)],
+ expected: V(
+ 0b10001001010010001010010100100010,
+ 0b11001110001110001100110011100011,
+ 0b10101010101010101010101010101010,
+ 0b01010101010101010101010101010101
+ ),
+ },
+
+ // Zero count
+ { input: [pattern, all_1, u32(0), u32(0)], expected: pattern },
+ { input: [pattern, all_1, u32(1), u32(0)], expected: pattern },
+ { input: [pattern, all_1, u32(2), u32(0)], expected: pattern },
+ { input: [pattern, all_1, u32(31), u32(0)], expected: pattern },
+ { input: [pattern, all_1, u32(32), u32(0)], expected: pattern },
+ { input: [pattern, all_1, u32(0), u32(0)], expected: pattern },
+ ];
+
+ if (t.params.inputSource !== 'const') {
+ cases.push(
+ ...[
+ // Start overflow
+ { input: [all_0, pattern, u32(50), u32(3)], expected: all_0 },
+ { input: [all_1, pattern, u32(50), u32(3)], expected: all_1 },
+ { input: [pattern, pattern, u32(50), u32(3)], expected: pattern },
+
+ // End overflow
+ { input: [all_0, pattern, u32(0), u32(99)], expected: pattern },
+ { input: [all_1, pattern, u32(0), u32(99)], expected: pattern },
+ { input: [all_0, low_1, u32(31), u32(99)], expected: high_1 },
+ {
+ input: [pattern, pattern, u32(20), u32(99)],
+ expected: V(
+ 0b01010010001000100010010100100010,
+ 0b11001110001100111000110011100011,
+ 0b10101010101010101010101010101010,
+ 0b01010101010101010101010101010101
+ ),
+ },
+ ]
+ );
+ }
+
+ await run(t, builtin('insertBits'), [T, T, TypeU32, TypeU32], T, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
new file mode 100644
index 0000000000..ec79aa3807
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
@@ -0,0 +1,63 @@
+export const description = `
+Execution tests for the 'inverseSqrt' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn inverseSqrt(e: T ) -> T
+Returns the reciprocal of sqrt(e). Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { inverseSqrtInterval } from '../../../../../util/f32_interval.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('inverseSqrt', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // 0 < x <= 1 linearly spread
+ ...linearRange(kValue.f32.positive.min, 1, 100),
+ // 1 <= x < 2^32, biased towards 1
+ ...biasedRange(1, 2 ** 32, 1000),
+ ],
+ 'unfiltered',
+ inverseSqrtInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('inverseSqrt'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
new file mode 100644
index 0000000000..b443aef4b6
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
@@ -0,0 +1,95 @@
+export const description = `
+Execution tests for the 'ldexp' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+
+K is AbstractInt, i32
+I is K or vecN<K>, where
+ I is a scalar if T is a scalar, or a vector when T is a vector
+
+@const fn ldexp(e1: T ,e2: I ) -> T
+Returns e1 * 2^e2. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { f32, i32, TypeF32, TypeI32 } from '../../../../../util/conversion.js';
+import { ldexpInterval } from '../../../../../util/f32_interval.js';
+import {
+ biasedRange,
+ fullF32Range,
+ fullI32Range,
+ quantizeToF32,
+ quantizeToI32,
+} from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const makeCase = (e1: number, e2: number): Case => {
+ // Due to the heterogeneous types of the params to ldexp (f32 & i32),
+ // makeBinaryToF32IntervalCase cannot be used here.
+ e1 = quantizeToF32(e1);
+ e2 = quantizeToI32(e2);
+ const expected = ldexpInterval(e1, e2);
+ return { input: [f32(e1), i32(e2)], expected };
+};
+
+export const d = makeCaseCache('ldexp', {
+ f32_non_const: () => {
+ const cases: Array<Case> = [];
+ fullF32Range().forEach(e1 => {
+ fullI32Range().forEach(e2 => {
+ cases.push(makeCase(e1, e2));
+ });
+ });
+ return cases;
+ },
+ f32_const: () => {
+ const cases: Array<Case> = [];
+ fullF32Range().forEach(e1 => {
+ biasedRange(-128, 128, 10).forEach(e2 => {
+ const val = e1 * Math.pow(2, e2);
+ if (val >= kValue.f32.negative.min && val <= kValue.f32.positive.max) {
+ cases.push(makeCase(e1, e2));
+ }
+ });
+ });
+ return cases;
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('ldexp'), [TypeF32, TypeI32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts
new file mode 100644
index 0000000000..1fb54f6fa0
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts
@@ -0,0 +1,107 @@
+export const description = `
+Execution tests for the 'length' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn length(e: T ) -> f32
+Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { lengthInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, vectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import {
+ allInputSources,
+ generateUnaryToF32IntervalCases,
+ generateVectorToF32IntervalCases,
+ run,
+} from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('length', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', lengthInterval);
+ },
+ f32_vec2_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(2), 'f32-only', lengthInterval);
+ },
+ f32_vec2_non_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(2), 'unfiltered', lengthInterval);
+ },
+ f32_vec3_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(3), 'f32-only', lengthInterval);
+ },
+ f32_vec3_non_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(3), 'unfiltered', lengthInterval);
+ },
+ f32_vec4_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(4), 'f32-only', lengthInterval);
+ },
+ f32_vec4_non_const: () => {
+ return generateVectorToF32IntervalCases(vectorF32Range(4), 'unfiltered', lengthInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('length'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(t, builtin('length'), [TypeVec(2, TypeF32)], TypeF32, t.params, cases);
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(t, builtin('length'), [TypeVec(3, TypeF32)], TypeF32, t.params, cases);
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(t, builtin('length'), [TypeVec(4, TypeF32)], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
new file mode 100644
index 0000000000..6d2b16f44e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
@@ -0,0 +1,71 @@
+export const description = `
+Execution tests for the 'log' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn log(e: T ) -> T
+Returns the natural logarithm of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { logInterval } from '../../../../../util/f32_interval.js';
+import { biasedRange, fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
+const inputs = [
+ ...linearRange(kValue.f32.positive.min, 0.5, 20),
+ ...linearRange(0.5, 2.0, 20),
+ ...biasedRange(2.0, 2 ** 32, 1000),
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('log', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', logInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', logInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('log'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
new file mode 100644
index 0000000000..4425fb7b1a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
@@ -0,0 +1,71 @@
+export const description = `
+Execution tests for the 'log2' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn log2(e: T ) -> T
+Returns the base-2 logarithm of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { log2Interval } from '../../../../../util/f32_interval.js';
+import { biasedRange, fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
+const inputs = [
+ ...linearRange(kValue.f32.positive.min, 0.5, 20),
+ ...linearRange(0.5, 2.0, 20),
+ ...biasedRange(2.0, 2 ** 32, 1000),
+ ...fullF32Range(),
+];
+
+export const d = makeCaseCache('log2', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'f32-only', log2Interval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(inputs, 'unfiltered', log2Interval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('log2'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
new file mode 100644
index 0000000000..7b8446b176
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
@@ -0,0 +1,123 @@
+export const description = `
+Execution tests for the 'max' builtin function
+
+S is AbstractInt, i32, or u32
+T is S or vecN<S>
+@const fn max(e1: T ,e2: T) -> T
+Returns e2 if e1 is less than e2, and e1 otherwise. Component-wise when T is a vector.
+
+S is AbstractFloat, f32, f16
+T is vecN<S>
+@const fn max(e1: T ,e2: T) -> T
+Returns e2 if e1 is less than e2, and e1 otherwise.
+If one operand is a NaN, the other is returned.
+If both operands are NaNs, a NaN is returned.
+Component-wise when T is a vector.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { i32, TypeF32, TypeI32, TypeU32, u32 } from '../../../../../util/conversion.js';
+import { maxInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, generateBinaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+/** Generate set of max test cases from list of interesting values */
+function generateTestCases(
+ values: Array<number>,
+ makeCase: (x: number, y: number) => Case
+): Array<Case> {
+ const cases = new Array<Case>();
+ values.forEach(e => {
+ values.forEach(f => {
+ cases.push(makeCase(e, f));
+ });
+ });
+ return cases;
+}
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('max', {
+ f32: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ maxInterval
+ );
+ },
+});
+
+g.test('abstract_int')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`abstract int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [u32(x), u32(y)], expected: u32(Math.max(x, y)) };
+ };
+
+ const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(t, builtin('max'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [i32(x), i32(y)], expected: i32(Math.max(x, y)) };
+ };
+
+ const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(t, builtin('max'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('max'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
new file mode 100644
index 0000000000..3b3e612a66
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
@@ -0,0 +1,122 @@
+export const description = `
+Execution tests for the 'min' builtin function
+
+S is AbstractInt, i32, or u32
+T is S or vecN<S>
+@const fn min(e1: T ,e2: T) -> T
+Returns e1 if e1 is less than e2, and e2 otherwise. Component-wise when T is a vector.
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn min(e1: T ,e2: T) -> T
+Returns e2 if e2 is less than e1, and e1 otherwise.
+If one operand is a NaN, the other is returned.
+If both operands are NaNs, a NaN is returned.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { i32, TypeF32, TypeI32, TypeU32, u32 } from '../../../../../util/conversion.js';
+import { minInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, generateBinaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('min', {
+ f32: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ minInterval
+ );
+ },
+});
+
+/** Generate set of min test cases from list of interesting values */
+function generateTestCases(
+ values: Array<number>,
+ makeCase: (x: number, y: number) => Case
+): Array<Case> {
+ const cases = new Array<Case>();
+ values.forEach(e => {
+ values.forEach(f => {
+ cases.push(makeCase(e, f));
+ });
+ });
+ return cases;
+}
+
+g.test('abstract_int')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`abstract int tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [u32(x), u32(y)], expected: u32(Math.min(x, y)) };
+ };
+
+ const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(t, builtin('min'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [i32(x), i32(y)], expected: i32(Math.min(x, y)) };
+ };
+
+ const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(t, builtin('min'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('min'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts
new file mode 100644
index 0000000000..8f0959ed08
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts
@@ -0,0 +1,93 @@
+export const description = `
+Execution tests for the 'mix' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn mix(e1: T, e2: T, e3: T) -> T
+Returns the linear blend of e1 and e2 (e.g. e1*(1-e3)+e2*e3). Component-wise when T is a vector.
+
+T is AbstractFloat, f32, or f16
+T2 is vecN<T>
+@const fn mix(e1: T2, e2: T2, e3: T) -> T2
+Returns the component-wise linear blend of e1 and e2, using scalar blending factor e3 for each component.
+Same as mix(e1,e2,T2(e3)).
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { mixIntervals } from '../../../../../util/f32_interval.js';
+import { sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('mix', {
+ f32_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'f32-only',
+ ...mixIntervals
+ );
+ },
+ f32_non_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'unfiltered',
+ ...mixIntervals
+ );
+ },
+});
+
+g.test('matching_abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests with matching params`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('matching_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 test with matching third param`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('mix'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('matching_f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests with matching third param`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('nonmatching_abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests with vector params and scalar third param`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('nonmatching_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests with vector params and scalar third param`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('monmatching_f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests with vector params and scalar third param`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts
new file mode 100644
index 0000000000..f82a297b6a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts
@@ -0,0 +1,356 @@
+export const description = `
+Execution tests for the 'modf' builtin function
+
+T is f32 or f16
+@const fn modf(e:T) -> result_struct
+Splits |e| into fractional and whole number parts.
+The whole part is (|e| % 1.0), and the fractional part is |e| minus the whole part.
+Returns the result_struct for the given type.
+
+S is f32 or f16
+T is vecN<S>
+@const fn modf(e:T) -> result_struct
+Splits the components of |e| into fractional and whole number parts.
+The |i|'th component of the whole and fractional parts equal the whole and fractional parts of modf(e[i]).
+Returns the result_struct for the given type.
+
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { f32, toVector, TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { modfInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, ExpressionBuilder, run } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+/* @returns an ExpressionBuilder that evaluates modf and returns .whole from the result structure */
+function wholeBuilder(): ExpressionBuilder {
+ return value => `modf(${value}).whole`;
+}
+
+/* @returns an ExpressionBuilder that evaluates modf and returns .fract from the result structure */
+function fractBuilder(): ExpressionBuilder {
+ return value => `modf(${value}).fract`;
+}
+
+/* @returns a fract Case for a given vector input */
+function makeVectorCaseFract(v: number[]): Case {
+ v = v.map(quantizeToF32);
+ const fs = v.map(e => {
+ return modfInterval(e).fract;
+ });
+
+ return { input: toVector(v, f32), expected: fs };
+}
+
+/* @returns a fract Case for a given vector input */
+function makeVectorCaseWhole(v: number[]): Case {
+ v = v.map(quantizeToF32);
+ const ws = v.map(e => {
+ return modfInterval(e).whole;
+ });
+
+ return { input: toVector(v, f32), expected: ws };
+}
+
+export const d = makeCaseCache('modf', {
+ f32_fract: () => {
+ const makeCase = (n: number): Case => {
+ n = quantizeToF32(n);
+ return { input: f32(n), expected: modfInterval(n).fract };
+ };
+ return fullF32Range().map(makeCase);
+ },
+ f32_whole: () => {
+ const makeCase = (n: number): Case => {
+ n = quantizeToF32(n);
+ return { input: f32(n), expected: modfInterval(n).whole };
+ };
+ return fullF32Range().map(makeCase);
+ },
+ f32_vec2_fract: () => {
+ return vectorF32Range(2).map(makeVectorCaseFract);
+ },
+ f32_vec2_whole: () => {
+ return vectorF32Range(2).map(makeVectorCaseWhole);
+ },
+ f32_vec3_fract: () => {
+ return vectorF32Range(3).map(makeVectorCaseFract);
+ },
+ f32_vec3_whole: () => {
+ return vectorF32Range(3).map(makeVectorCaseWhole);
+ },
+ f32_vec4_fract: () => {
+ return vectorF32Range(4).map(makeVectorCaseFract);
+ },
+ f32_vec4_whole: () => {
+ return vectorF32Range(4).map(makeVectorCaseWhole);
+ },
+});
+
+g.test('f32_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is f32
+
+struct __modf_result_f32 {
+ fract : f32, // fractional part
+ whole : f32 // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_fract');
+ await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f32_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is f32
+
+struct __modf_result_f32 {
+ fract : f32, // fractional part
+ whole : f32 // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_whole');
+ await run(t, wholeBuilder(), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f32_vec2_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<f32>
+
+struct __modf_result_vec2_f32 {
+ fract : vec2<f32>, // fractional part
+ whole : vec2<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec2_fract');
+ await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec2_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<f32>
+
+struct __modf_result_vec2_f32 {
+ fract : vec2<f32>, // fractional part
+ whole : vec2<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec2_whole');
+ await run(t, wholeBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec3_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<f32>
+
+struct __modf_result_vec3_f32 {
+ fract : vec3<f32>, // fractional part
+ whole : vec3<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec3_fract');
+ await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec3_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<f32>
+
+struct __modf_result_vec3_f32 {
+ fract : vec3<f32>, // fractional part
+ whole : vec3<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec3_whole');
+ await run(t, wholeBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec4_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<f32>
+
+struct __modf_result_vec4_f32 {
+ fract : vec4<f32>, // fractional part
+ whole : vec4<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec4_fract');
+ await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec4_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<f32>
+
+struct __modf_result_vec4_f32 {
+ fract : vec4<f32>, // fractional part
+ whole : vec4<f32> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('f32_vec4_whole');
+ await run(t, wholeBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ });
+
+g.test('f16_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is f16
+
+struct __modf_result_f16 {
+ fract : f16, // fractional part
+ whole : f16 // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is f16
+
+struct __modf_result_f16 {
+ fract : f16, // fractional part
+ whole : f16 // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec2_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<f16>
+
+struct __modf_result_vec2_f16 {
+ fract : vec2<f16>, // fractional part
+ whole : vec2<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec2_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<f16>
+
+struct __modf_result_vec2_f16 {
+ fract : vec2<f16>, // fractional part
+ whole : vec2<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec3_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<f16>
+
+struct __modf_result_vec3_f16 {
+ fract : vec3<f16>, // fractional part
+ whole : vec3<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec3_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<f16>
+
+struct __modf_result_vec3_f16 {
+ fract : vec3<f16>, // fractional part
+ whole : vec3<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec4_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<f16>
+
+struct __modf_result_vec4_f16 {
+ fract : vec4<f16>, // fractional part
+ whole : vec4<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
+
+g.test('f16_vec4_whole')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<f16>
+
+struct __modf_result_vec4_f16 {
+ fract : vec4<f16>, // fractional part
+ whole : vec4<f16> // whole part
+}
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts
new file mode 100644
index 0000000000..d1900795ad
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts
@@ -0,0 +1,89 @@
+export const description = `
+Execution tests for the 'normalize' builtin function
+
+T is AbstractFloat, f32, or f16
+@const fn normalize(e: vecN<T> ) -> vecN<T>
+Returns a unit vector in the same direction as e.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { normalizeInterval } from '../../../../../util/f32_interval.js';
+import { vectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateVectorToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('normalize', {
+ f32_vec2_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(2), 'f32-only', normalizeInterval);
+ },
+ f32_vec2_non_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(2), 'unfiltered', normalizeInterval);
+ },
+ f32_vec3_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(3), 'f32-only', normalizeInterval);
+ },
+ f32_vec3_non_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(3), 'unfiltered', normalizeInterval);
+ },
+ f32_vec4_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(4), 'f32-only', normalizeInterval);
+ },
+ f32_vec4_non_const: () => {
+ return generateVectorToVectorCases(vectorF32Range(4), 'unfiltered', normalizeInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(t, builtin('normalize'), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(t, builtin('normalize'), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(t, builtin('normalize'), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts
new file mode 100644
index 0000000000..790e54720c
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts
@@ -0,0 +1,88 @@
+export const description = `
+Converts two floating point values to half-precision floating point numbers, and then combines them into one u32 value.
+Component e[i] of the input is converted to a IEEE-754 binary16 value,
+which is then placed in bits 16 × i through 16 × i + 15 of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { anyOf, skipUndefined } from '../../../../../util/compare.js';
+import {
+ f32,
+ pack2x16float,
+ TypeF32,
+ TypeU32,
+ TypeVec,
+ u32,
+ vec2,
+} from '../../../../../util/conversion.js';
+import { cartesianProduct, fullF32Range, quantizeToF32 } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// pack2x16float has somewhat unusual behaviour, specifically around how it is
+// supposed to behave when values go OOB and when they are considered to have
+// gone OOB, so has its own bespoke implementation.
+
+/**
+ * @returns a Case for `pack2x16float`
+ * @param param0 first param for the case
+ * @param param1 second param for the case
+ * @param filter_undefined should inputs that cause an undefined expectation be
+ * filtered out, needed for const-eval
+ */
+function makeCase(param0: number, param1: number, filter_undefined: boolean): Case | undefined {
+ param0 = quantizeToF32(param0);
+ param1 = quantizeToF32(param1);
+
+ const results = pack2x16float(param0, param1);
+ if (filter_undefined && results.some(r => r === undefined)) {
+ return undefined;
+ }
+
+ return {
+ input: [vec2(f32(param0), f32(param1))],
+ expected: anyOf(
+ ...results.map(r => (r === undefined ? skipUndefined(undefined) : skipUndefined(u32(r))))
+ ),
+ };
+}
+
+/**
+ * @returns an array of Cases for `pack2x16float`
+ * @param param0s array of inputs to try for the first param
+ * @param param1s array of inputs to try for the second param
+ * @param filter_undefined should inputs that cause an undefined expectation be
+ * filtered out, needed for const-eval
+ */
+function generateCases(param0s: number[], param1s: number[], filter_undefined: boolean): Case[] {
+ return cartesianProduct(param0s, param1s)
+ .map(e => makeCase(e[0], e[1], filter_undefined))
+ .filter((c): c is Case => c !== undefined);
+}
+
+export const d = makeCaseCache('pack2x16float', {
+ f32_const: () => {
+ return generateCases(fullF32Range(), fullF32Range(), true);
+ },
+ f32_non_const: () => {
+ return generateCases(fullF32Range(), fullF32Range(), false);
+ },
+});
+
+g.test('pack')
+ .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
+ .desc(
+ `
+@const fn pack2x16float(e: vec2<f32>) -> u32
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('pack2x16float'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts
new file mode 100644
index 0000000000..54bb21f6c6
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts
@@ -0,0 +1,55 @@
+export const description = `
+Converts two normalized floating point values to 16-bit signed integers, and then combines them into one u32 value.
+Component e[i] of the input is converted to a 16-bit twos complement integer value
+⌊ 0.5 + 32767 × min(1, max(-1, e[i])) ⌋ which is then placed in
+bits 16 × i through 16 × i + 15 of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import {
+ f32,
+ pack2x16snorm,
+ TypeF32,
+ TypeU32,
+ TypeVec,
+ u32,
+ vec2,
+} from '../../../../../util/conversion.js';
+import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('pack')
+ .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
+ .desc(
+ `
+@const fn pack2x16snorm(e: vec2<f32>) -> u32
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ x = quantizeToF32(x);
+ y = quantizeToF32(y);
+ return { input: [vec2(f32(x), f32(y))], expected: u32(pack2x16snorm(x, y)) };
+ };
+
+ // Returns a value normalized to [-1, 1].
+ const normalizeF32 = (n: number): number => {
+ return n / kValue.f32.positive.max;
+ };
+
+ const cases: Array<Case> = vectorF32Range(2).flatMap(v => {
+ return [
+ makeCase(...(v as [number, number])),
+ makeCase(...(v.map(normalizeF32) as [number, number])),
+ ];
+ });
+
+ await run(t, builtin('pack2x16snorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts
new file mode 100644
index 0000000000..a875a9c7e1
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts
@@ -0,0 +1,55 @@
+export const description = `
+Converts two normalized floating point values to 16-bit unsigned integers, and then combines them into one u32 value.
+Component e[i] of the input is converted to a 16-bit unsigned integer value
+⌊ 0.5 + 65535 × min(1, max(0, e[i])) ⌋ which is then placed in
+bits 16 × i through 16 × i + 15 of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import {
+ f32,
+ pack2x16unorm,
+ TypeF32,
+ TypeU32,
+ TypeVec,
+ u32,
+ vec2,
+} from '../../../../../util/conversion.js';
+import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('pack')
+ .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
+ .desc(
+ `
+@const fn pack2x16unorm(e: vec2<f32>) -> u32
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const makeCase = (x: number, y: number): Case => {
+ x = quantizeToF32(x);
+ y = quantizeToF32(y);
+ return { input: [vec2(f32(x), f32(y))], expected: u32(pack2x16unorm(x, y)) };
+ };
+
+ // Returns a value normalized to [0, 1].
+ const normalizeF32 = (n: number): number => {
+ return n > 0 ? n / kValue.f32.positive.max : n / kValue.f32.negative.min;
+ };
+
+ const cases: Array<Case> = vectorF32Range(2).flatMap(v => {
+ return [
+ makeCase(...(v as [number, number])),
+ makeCase(...(v.map(normalizeF32) as [number, number])),
+ ];
+ });
+
+ await run(t, builtin('pack2x16unorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts
new file mode 100644
index 0000000000..de0463e9fc
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts
@@ -0,0 +1,60 @@
+export const description = `
+Converts four normalized floating point values to 8-bit signed integers, and then combines them into one u32 value.
+Component e[i] of the input is converted to an 8-bit twos complement integer value
+⌊ 0.5 + 127 × min(1, max(-1, e[i])) ⌋ which is then placed in
+bits 8 × i through 8 × i + 7 of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import {
+ f32,
+ pack4x8snorm,
+ Scalar,
+ TypeF32,
+ TypeU32,
+ TypeVec,
+ u32,
+ vec4,
+} from '../../../../../util/conversion.js';
+import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('pack')
+ .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
+ .desc(
+ `
+@const fn pack4x8snorm(e: vec4<f32>) -> u32
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const makeCase = (vals: [number, number, number, number]): Case => {
+ const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar];
+ for (const idx in vals) {
+ vals[idx] = quantizeToF32(vals[idx]);
+ vals_f32[idx] = f32(vals[idx]);
+ }
+
+ return { input: [vec4(...vals_f32)], expected: u32(pack4x8snorm(...vals)) };
+ };
+
+ // Returns a value normalized to [-1, 1].
+ const normalizeF32 = (n: number): number => {
+ return n / kValue.f32.positive.max;
+ };
+
+ const cases: Array<Case> = vectorF32Range(4).flatMap(v => {
+ return [
+ makeCase(v as [number, number, number, number]),
+ makeCase(v.map(normalizeF32) as [number, number, number, number]),
+ ];
+ });
+
+ await run(t, builtin('pack4x8snorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts
new file mode 100644
index 0000000000..b670e92fbb
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts
@@ -0,0 +1,60 @@
+export const description = `
+Converts four normalized floating point values to 8-bit unsigned integers, and then combines them into one u32 value.
+Component e[i] of the input is converted to an 8-bit unsigned integer value
+⌊ 0.5 + 255 × min(1, max(0, e[i])) ⌋ which is then placed in
+bits 8 × i through 8 × i + 7 of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import {
+ f32,
+ pack4x8unorm,
+ Scalar,
+ TypeF32,
+ TypeU32,
+ TypeVec,
+ u32,
+ vec4,
+} from '../../../../../util/conversion.js';
+import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('pack')
+ .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
+ .desc(
+ `
+@const fn pack4x8unorm(e: vec4<f32>) -> u32
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const makeCase = (vals: [number, number, number, number]): Case => {
+ const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar];
+ for (const idx in vals) {
+ vals[idx] = quantizeToF32(vals[idx]);
+ vals_f32[idx] = f32(vals[idx]);
+ }
+
+ return { input: [vec4(...vals_f32)], expected: u32(pack4x8unorm(...vals)) };
+ };
+
+ // Returns a value normalized to [0, 1].
+ const normalizeF32 = (n: number): number => {
+ return n > 0 ? n / kValue.f32.positive.max : n / kValue.f32.negative.min;
+ };
+
+ const cases: Array<Case> = vectorF32Range(4).flatMap(v => {
+ return [
+ makeCase(v as [number, number, number, number]),
+ makeCase(v.map(normalizeF32) as [number, number, number, number]),
+ ];
+ });
+
+ await run(t, builtin('pack4x8unorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts
new file mode 100644
index 0000000000..8f2b3c9a54
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts
@@ -0,0 +1,66 @@
+export const description = `
+Execution tests for the 'pow' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn pow(e1: T ,e2: T ) -> T
+Returns e1 raised to the power e2. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { powInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateBinaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('pow', {
+ f32_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ powInterval
+ );
+ },
+ f32_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ powInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('pow'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
new file mode 100644
index 0000000000..2c07f640be
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
@@ -0,0 +1,70 @@
+export const description = `
+Execution tests for the 'quantizeToF16' builtin function
+
+T is f32 or vecN<f32>
+@const fn quantizeToF16(e: T ) -> T
+Quantizes a 32-bit floating point value e as if e were converted to a IEEE 754
+binary16 value, and then converted back to a IEEE 754 binary32 value.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { kValue } from '../../../../../util/constants.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { quantizeToF16Interval } from '../../../../../util/f32_interval.js';
+import { fullF16Range, fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('quantizeToF16', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ kValue.f16.negative.min,
+ kValue.f16.negative.max,
+ kValue.f16.subnormal.negative.min,
+ kValue.f16.subnormal.negative.max,
+ kValue.f16.subnormal.positive.min,
+ kValue.f16.subnormal.positive.max,
+ kValue.f16.positive.min,
+ kValue.f16.positive.max,
+ ...fullF16Range(),
+ ],
+ 'f32-only',
+ quantizeToF16Interval
+ );
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ kValue.f16.negative.min,
+ kValue.f16.negative.max,
+ kValue.f16.subnormal.negative.min,
+ kValue.f16.subnormal.negative.max,
+ kValue.f16.subnormal.positive.min,
+ kValue.f16.subnormal.positive.max,
+ kValue.f16.positive.min,
+ kValue.f16.positive.max,
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ quantizeToF16Interval
+ );
+ },
+});
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('quantizeToF16'), [TypeF32], TypeF32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts
new file mode 100644
index 0000000000..9b53db29b9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts
@@ -0,0 +1,54 @@
+export const description = `
+Execution tests for the 'radians' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn radians(e1: T ) -> T
+Converts degrees to radians, approximating e1 * π / 180.
+Component-wise when T is a vector
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { radiansInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('radians', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', radiansInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('radians'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts
new file mode 100644
index 0000000000..a2865e1a85
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts
@@ -0,0 +1,137 @@
+export const description = `
+Execution tests for the 'reflect' builtin function
+
+T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16>
+@const fn reflect(e1: T, e2: T ) -> T
+For the incident vector e1 and surface orientation e2, returns the reflection
+direction e1-2*dot(e2,e1)*e2.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeVec } from '../../../../../util/conversion.js';
+import { reflectInterval } from '../../../../../util/f32_interval.js';
+import { sparseVectorF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateVectorPairToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('reflect', {
+ f32_vec2_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'f32-only',
+ reflectInterval
+ );
+ },
+ f32_vec2_non_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ 'unfiltered',
+ reflectInterval
+ );
+ },
+ f32_vec3_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'f32-only',
+ reflectInterval
+ );
+ },
+ f32_vec3_non_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ 'unfiltered',
+ reflectInterval
+ );
+ },
+ f32_vec4_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'f32-only',
+ reflectInterval
+ );
+ },
+ f32_vec4_non_const: () => {
+ return generateVectorPairToVectorCases(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ 'unfiltered',
+ reflectInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(
+ t,
+ builtin('reflect'),
+ [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
+ TypeVec(2, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(
+ t,
+ builtin('reflect'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
+ TypeVec(3, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(
+ t,
+ builtin('reflect'),
+ [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
+ TypeVec(4, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts
new file mode 100644
index 0000000000..96a0fbe02a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts
@@ -0,0 +1,196 @@
+export const description = `
+Execution tests for the 'refract' builtin function
+
+T is vecN<I>
+I is AbstractFloat, f32, or f16
+@const fn refract(e1: T ,e2: T ,e3: I ) -> T
+For the incident vector e1 and surface normal e2, and the ratio of indices of
+refraction e3, let k = 1.0 -e3*e3* (1.0 - dot(e2,e1) * dot(e2,e1)).
+If k < 0.0, returns the refraction vector 0.0, otherwise return the refraction
+vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { f32, TypeF32, TypeVec, Vector } from '../../../../../util/conversion.js';
+import { refractInterval } from '../../../../../util/f32_interval.js';
+import { sparseVectorF32Range, quantizeToF32, sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, IntervalFilter, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// Using a bespoke implementation of make*Case and generate*Cases here
+// since refract is the only builtin with the API signature
+// (vec, vec, scalar) -> vec
+
+/**
+ * @returns a Case for `refract`
+ * @param i the `i` param for the case
+ * @param s the `s` param for the case
+ * @param r the `r` param for the case
+ * @param check what interval checking to apply
+ * */
+function makeCaseF32(i: number[], s: number[], r: number, check: IntervalFilter): Case | undefined {
+ i = i.map(quantizeToF32);
+ s = s.map(quantizeToF32);
+ r = quantizeToF32(r);
+
+ const i_f32 = i.map(f32);
+ const s_f32 = s.map(f32);
+ const r_f32 = f32(r);
+
+ const vectors = refractInterval(i, s, r);
+ if (check === 'f32-only' && vectors.some(e => !e.isFinite())) {
+ return undefined;
+ }
+
+ return {
+ input: [new Vector(i_f32), new Vector(s_f32), r_f32],
+ expected: refractInterval(i, s, r),
+ };
+}
+
+/**
+ * @returns an array of Cases for `refract`
+ * @param param_is array of inputs to try for the `i` param
+ * @param param_ss array of inputs to try for the `s` param
+ * @param param_rs array of inputs to try for the `r` param
+ * @param check what interval checking to apply
+ */
+function generateCasesF32(
+ param_is: number[][],
+ param_ss: number[][],
+ param_rs: number[],
+ check: IntervalFilter
+): Case[] {
+ // Cannot use `cartesianProduct` here due to heterogeneous param types
+ return param_is
+ .flatMap(i => {
+ return param_ss.flatMap(s => {
+ return param_rs.map(r => {
+ return makeCaseF32(i, s, r, check);
+ });
+ });
+ })
+ .filter((c): c is Case => c !== undefined);
+}
+
+export const d = makeCaseCache('refract', {
+ f32_vec2_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ sparseF32Range(),
+ 'f32-only'
+ );
+ },
+ f32_vec2_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(2),
+ sparseVectorF32Range(2),
+ sparseF32Range(),
+ 'unfiltered'
+ );
+ },
+ f32_vec3_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ sparseF32Range(),
+ 'f32-only'
+ );
+ },
+ f32_vec3_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(3),
+ sparseVectorF32Range(3),
+ sparseF32Range(),
+ 'unfiltered'
+ );
+ },
+ f32_vec4_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ sparseF32Range(),
+ 'f32-only'
+ );
+ },
+ f32_vec4_non_const: () => {
+ return generateCasesF32(
+ sparseVectorF32Range(4),
+ sparseVectorF32Range(4),
+ sparseF32Range(),
+ 'unfiltered'
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(
+ t,
+ builtin('refract'),
+ [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32],
+ TypeVec(2, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec3s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
+ );
+ await run(
+ t,
+ builtin('refract'),
+ [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32],
+ TypeVec(3, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`f32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
+ );
+ await run(
+ t,
+ builtin('refract'),
+ [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32],
+ TypeVec(4, TypeF32),
+ t.params,
+ cases
+ );
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
new file mode 100644
index 0000000000..6acb359822
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
@@ -0,0 +1,250 @@
+export const description = `
+Execution tests for the 'reversBits' builtin function
+
+S is i32, u32
+T is S or vecN<S>
+@const fn reverseBits(e: T ) -> T
+Reverses the bits in e: The bit at position k of the result equals the bit at position 31-k of e.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeU32, u32Bits, TypeI32, i32Bits } from '../../../../../util/conversion.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('u32')
+ .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
+ .desc(`u32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ // prettier-ignore
+ await run(t, builtin('reverseBits'), [TypeU32], TypeU32, cfg, [
+ // Zero
+ { input: u32Bits(0b00000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000000) },
+
+ // One
+ { input: u32Bits(0b00000000000000000000000000000001), expected: u32Bits(0b10000000000000000000000000000000) },
+
+ // 0's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000010), expected: u32Bits(0b01000000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000000100), expected: u32Bits(0b00100000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000001000), expected: u32Bits(0b00010000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000010000), expected: u32Bits(0b00001000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000100000), expected: u32Bits(0b00000100000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000001000000), expected: u32Bits(0b00000010000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000010000000), expected: u32Bits(0b00000001000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000100000000), expected: u32Bits(0b00000000100000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000001000000000), expected: u32Bits(0b00000000010000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000010000000000), expected: u32Bits(0b00000000001000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000100000000000), expected: u32Bits(0b00000000000100000000000000000000) },
+ { input: u32Bits(0b00000000000000000001000000000000), expected: u32Bits(0b00000000000010000000000000000000) },
+ { input: u32Bits(0b00000000000000000010000000000000), expected: u32Bits(0b00000000000001000000000000000000) },
+ { input: u32Bits(0b00000000000000000100000000000000), expected: u32Bits(0b00000000000000100000000000000000) },
+ { input: u32Bits(0b00000000000000001000000000000000), expected: u32Bits(0b00000000000000010000000000000000) },
+ { input: u32Bits(0b00000000000000010000000000000000), expected: u32Bits(0b00000000000000001000000000000000) },
+ { input: u32Bits(0b00000000000000100000000000000000), expected: u32Bits(0b00000000000000000100000000000000) },
+ { input: u32Bits(0b00000000000001000000000000000000), expected: u32Bits(0b00000000000000000010000000000000) },
+ { input: u32Bits(0b00000000000010000000000000000000), expected: u32Bits(0b00000000000000000001000000000000) },
+ { input: u32Bits(0b00000000000100000000000000000000), expected: u32Bits(0b00000000000000000000100000000000) },
+ { input: u32Bits(0b00000000001000000000000000000000), expected: u32Bits(0b00000000000000000000010000000000) },
+ { input: u32Bits(0b00000000010000000000000000000000), expected: u32Bits(0b00000000000000000000001000000000) },
+ { input: u32Bits(0b00000000100000000000000000000000), expected: u32Bits(0b00000000000000000000000100000000) },
+ { input: u32Bits(0b00000001000000000000000000000000), expected: u32Bits(0b00000000000000000000000010000000) },
+ { input: u32Bits(0b00000010000000000000000000000000), expected: u32Bits(0b00000000000000000000000001000000) },
+ { input: u32Bits(0b00000100000000000000000000000000), expected: u32Bits(0b00000000000000000000000000100000) },
+ { input: u32Bits(0b00001000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000010000) },
+ { input: u32Bits(0b00010000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000001000) },
+ { input: u32Bits(0b00100000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000100) },
+ { input: u32Bits(0b01000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000010) },
+ { input: u32Bits(0b10000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000001) },
+
+ // 1's after leading 1
+ { input: u32Bits(0b00000000000000000000000000000011), expected: u32Bits(0b11000000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000000111), expected: u32Bits(0b11100000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000001111), expected: u32Bits(0b11110000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000011111), expected: u32Bits(0b11111000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000111111), expected: u32Bits(0b11111100000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000001111111), expected: u32Bits(0b11111110000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32Bits(0b11111111000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000111111111), expected: u32Bits(0b11111111100000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32Bits(0b11111111110000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000011111111111), expected: u32Bits(0b11111111111000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000111111111111), expected: u32Bits(0b11111111111100000000000000000000) },
+ { input: u32Bits(0b00000000000000000001111111111111), expected: u32Bits(0b11111111111110000000000000000000) },
+ { input: u32Bits(0b00000000000000000011111111111111), expected: u32Bits(0b11111111111111000000000000000000) },
+ { input: u32Bits(0b00000000000000000111111111111111), expected: u32Bits(0b11111111111111100000000000000000) },
+ { input: u32Bits(0b00000000000000001111111111111111), expected: u32Bits(0b11111111111111110000000000000000) },
+ { input: u32Bits(0b00000000000000011111111111111111), expected: u32Bits(0b11111111111111111000000000000000) },
+ { input: u32Bits(0b00000000000000111111111111111111), expected: u32Bits(0b11111111111111111100000000000000) },
+ { input: u32Bits(0b00000000000001111111111111111111), expected: u32Bits(0b11111111111111111110000000000000) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32Bits(0b11111111111111111111000000000000) },
+ { input: u32Bits(0b00000000000111111111111111111111), expected: u32Bits(0b11111111111111111111100000000000) },
+ { input: u32Bits(0b00000000001111111111111111111111), expected: u32Bits(0b11111111111111111111110000000000) },
+ { input: u32Bits(0b00000000011111111111111111111111), expected: u32Bits(0b11111111111111111111111000000000) },
+ { input: u32Bits(0b00000000111111111111111111111111), expected: u32Bits(0b11111111111111111111111100000000) },
+ { input: u32Bits(0b00000001111111111111111111111111), expected: u32Bits(0b11111111111111111111111110000000) },
+ { input: u32Bits(0b00000011111111111111111111111111), expected: u32Bits(0b11111111111111111111111111000000) },
+ { input: u32Bits(0b00000111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111100000) },
+ { input: u32Bits(0b00001111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111110000) },
+ { input: u32Bits(0b00011111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111000) },
+ { input: u32Bits(0b00111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111100) },
+ { input: u32Bits(0b01111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111110) },
+ { input: u32Bits(0b11111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111111) },
+
+ // random after leading 1
+ { input: u32Bits(0b00000000000000000000000000000110), expected: u32Bits(0b01100000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000001101), expected: u32Bits(0b10110000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000011101), expected: u32Bits(0b10111000000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000000111001), expected: u32Bits(0b10011100000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000001101111), expected: u32Bits(0b11110110000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000011111111), expected: u32Bits(0b11111111000000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000000111101111), expected: u32Bits(0b11110111100000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000001111111111), expected: u32Bits(0b11111111110000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000011111110001), expected: u32Bits(0b10001111111000000000000000000000) },
+ { input: u32Bits(0b00000000000000000000111011011101), expected: u32Bits(0b10111011011100000000000000000000) },
+ { input: u32Bits(0b00000000000000000001101101111111), expected: u32Bits(0b11111110110110000000000000000000) },
+ { input: u32Bits(0b00000000000000000011111111011111), expected: u32Bits(0b11111011111111000000000000000000) },
+ { input: u32Bits(0b00000000000000000101111001110101), expected: u32Bits(0b10101110011110100000000000000000) },
+ { input: u32Bits(0b00000000000000001101111011110111), expected: u32Bits(0b11101111011110110000000000000000) },
+ { input: u32Bits(0b00000000000000011111111111110011), expected: u32Bits(0b11001111111111111000000000000000) },
+ { input: u32Bits(0b00000000000000111111111110111111), expected: u32Bits(0b11111101111111111100000000000000) },
+ { input: u32Bits(0b00000000000001111111011111111111), expected: u32Bits(0b11111111111011111110000000000000) },
+ { input: u32Bits(0b00000000000011111111111111111111), expected: u32Bits(0b11111111111111111111000000000000) },
+ { input: u32Bits(0b00000000000111110101011110111111), expected: u32Bits(0b11111101111010101111100000000000) },
+ { input: u32Bits(0b00000000001111101111111111110111), expected: u32Bits(0b11101111111111110111110000000000) },
+ { input: u32Bits(0b00000000011111111111010000101111), expected: u32Bits(0b11110100001011111111111000000000) },
+ { input: u32Bits(0b00000000111111111111001111111011), expected: u32Bits(0b11011111110011111111111100000000) },
+ { input: u32Bits(0b00000001111111011111101111111111), expected: u32Bits(0b11111111110111111011111110000000) },
+ { input: u32Bits(0b00000011101011111011110111111011), expected: u32Bits(0b11011111101111011111010111000000) },
+ { input: u32Bits(0b00000111111110111111111111111111), expected: u32Bits(0b11111111111111111101111111100000) },
+ { input: u32Bits(0b00001111000000011011011110111111), expected: u32Bits(0b11111101111011011000000011110000) },
+ { input: u32Bits(0b00011110101111011111111111111111), expected: u32Bits(0b11111111111111111011110101111000) },
+ { input: u32Bits(0b00110110111111100111111110111101), expected: u32Bits(0b10111101111111100111111101101100) },
+ { input: u32Bits(0b01010111111101111111011111011111), expected: u32Bits(0b11111011111011111110111111101010) },
+ { input: u32Bits(0b11100010011110101101101110101111), expected: u32Bits(0b11110101110110110101111001000111) },
+ ]);
+ });
+
+g.test('i32')
+ .specURL('https://www.w3.org/TR/2021/WD-WGSL-20210929/#integer-builtin-functions')
+ .desc(`i32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cfg: Config = t.params;
+ // prettier-ignore
+ await run(t, builtin('reverseBits'), [TypeI32], TypeI32, cfg, [
+ // Zero
+ { input: i32Bits(0b00000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000000) },
+
+ // One
+ { input: i32Bits(0b00000000000000000000000000000001), expected: i32Bits(0b10000000000000000000000000000000) },
+
+ // 0's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000010), expected: i32Bits(0b01000000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000000100), expected: i32Bits(0b00100000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000001000), expected: i32Bits(0b00010000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000010000), expected: i32Bits(0b00001000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000100000), expected: i32Bits(0b00000100000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000001000000), expected: i32Bits(0b00000010000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000010000000), expected: i32Bits(0b00000001000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000100000000), expected: i32Bits(0b00000000100000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000001000000000), expected: i32Bits(0b00000000010000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000010000000000), expected: i32Bits(0b00000000001000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000100000000000), expected: i32Bits(0b00000000000100000000000000000000) },
+ { input: i32Bits(0b00000000000000000001000000000000), expected: i32Bits(0b00000000000010000000000000000000) },
+ { input: i32Bits(0b00000000000000000010000000000000), expected: i32Bits(0b00000000000001000000000000000000) },
+ { input: i32Bits(0b00000000000000000100000000000000), expected: i32Bits(0b00000000000000100000000000000000) },
+ { input: i32Bits(0b00000000000000001000000000000000), expected: i32Bits(0b00000000000000010000000000000000) },
+ { input: i32Bits(0b00000000000000010000000000000000), expected: i32Bits(0b00000000000000001000000000000000) },
+ { input: i32Bits(0b00000000000000100000000000000000), expected: i32Bits(0b00000000000000000100000000000000) },
+ { input: i32Bits(0b00000000000001000000000000000000), expected: i32Bits(0b00000000000000000010000000000000) },
+ { input: i32Bits(0b00000000000010000000000000000000), expected: i32Bits(0b00000000000000000001000000000000) },
+ { input: i32Bits(0b00000000000100000000000000000000), expected: i32Bits(0b00000000000000000000100000000000) },
+ { input: i32Bits(0b00000000001000000000000000000000), expected: i32Bits(0b00000000000000000000010000000000) },
+ { input: i32Bits(0b00000000010000000000000000000000), expected: i32Bits(0b00000000000000000000001000000000) },
+ { input: i32Bits(0b00000000100000000000000000000000), expected: i32Bits(0b00000000000000000000000100000000) },
+ { input: i32Bits(0b00000001000000000000000000000000), expected: i32Bits(0b00000000000000000000000010000000) },
+ { input: i32Bits(0b00000010000000000000000000000000), expected: i32Bits(0b00000000000000000000000001000000) },
+ { input: i32Bits(0b00000100000000000000000000000000), expected: i32Bits(0b00000000000000000000000000100000) },
+ { input: i32Bits(0b00001000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000010000) },
+ { input: i32Bits(0b00010000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000001000) },
+ { input: i32Bits(0b00100000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000100) },
+ { input: i32Bits(0b01000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000010) },
+ { input: i32Bits(0b10000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000001) },
+
+ // 1's after leading 1
+ { input: i32Bits(0b00000000000000000000000000000011), expected: i32Bits(0b11000000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000000111), expected: i32Bits(0b11100000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000001111), expected: i32Bits(0b11110000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000011111), expected: i32Bits(0b11111000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000111111), expected: i32Bits(0b11111100000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000001111111), expected: i32Bits(0b11111110000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32Bits(0b11111111000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000111111111), expected: i32Bits(0b11111111100000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32Bits(0b11111111110000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000011111111111), expected: i32Bits(0b11111111111000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000111111111111), expected: i32Bits(0b11111111111100000000000000000000) },
+ { input: i32Bits(0b00000000000000000001111111111111), expected: i32Bits(0b11111111111110000000000000000000) },
+ { input: i32Bits(0b00000000000000000011111111111111), expected: i32Bits(0b11111111111111000000000000000000) },
+ { input: i32Bits(0b00000000000000000111111111111111), expected: i32Bits(0b11111111111111100000000000000000) },
+ { input: i32Bits(0b00000000000000001111111111111111), expected: i32Bits(0b11111111111111110000000000000000) },
+ { input: i32Bits(0b00000000000000011111111111111111), expected: i32Bits(0b11111111111111111000000000000000) },
+ { input: i32Bits(0b00000000000000111111111111111111), expected: i32Bits(0b11111111111111111100000000000000) },
+ { input: i32Bits(0b00000000000001111111111111111111), expected: i32Bits(0b11111111111111111110000000000000) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32Bits(0b11111111111111111111000000000000) },
+ { input: i32Bits(0b00000000000111111111111111111111), expected: i32Bits(0b11111111111111111111100000000000) },
+ { input: i32Bits(0b00000000001111111111111111111111), expected: i32Bits(0b11111111111111111111110000000000) },
+ { input: i32Bits(0b00000000011111111111111111111111), expected: i32Bits(0b11111111111111111111111000000000) },
+ { input: i32Bits(0b00000000111111111111111111111111), expected: i32Bits(0b11111111111111111111111100000000) },
+ { input: i32Bits(0b00000001111111111111111111111111), expected: i32Bits(0b11111111111111111111111110000000) },
+ { input: i32Bits(0b00000011111111111111111111111111), expected: i32Bits(0b11111111111111111111111111000000) },
+ { input: i32Bits(0b00000111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111100000) },
+ { input: i32Bits(0b00001111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111110000) },
+ { input: i32Bits(0b00011111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111000) },
+ { input: i32Bits(0b00111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111100) },
+ { input: i32Bits(0b01111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111110) },
+ { input: i32Bits(0b11111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111111) },
+
+ // random after leading 1
+ { input: i32Bits(0b00000000000000000000000000000110), expected: i32Bits(0b01100000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000001101), expected: i32Bits(0b10110000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000011101), expected: i32Bits(0b10111000000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000000111001), expected: i32Bits(0b10011100000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000001101111), expected: i32Bits(0b11110110000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000011111111), expected: i32Bits(0b11111111000000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000000111101111), expected: i32Bits(0b11110111100000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000001111111111), expected: i32Bits(0b11111111110000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000011111110001), expected: i32Bits(0b10001111111000000000000000000000) },
+ { input: i32Bits(0b00000000000000000000111011011101), expected: i32Bits(0b10111011011100000000000000000000) },
+ { input: i32Bits(0b00000000000000000001101101111111), expected: i32Bits(0b11111110110110000000000000000000) },
+ { input: i32Bits(0b00000000000000000011111111011111), expected: i32Bits(0b11111011111111000000000000000000) },
+ { input: i32Bits(0b00000000000000000101111001110101), expected: i32Bits(0b10101110011110100000000000000000) },
+ { input: i32Bits(0b00000000000000001101111011110111), expected: i32Bits(0b11101111011110110000000000000000) },
+ { input: i32Bits(0b00000000000000011111111111110011), expected: i32Bits(0b11001111111111111000000000000000) },
+ { input: i32Bits(0b00000000000000111111111110111111), expected: i32Bits(0b11111101111111111100000000000000) },
+ { input: i32Bits(0b00000000000001111111011111111111), expected: i32Bits(0b11111111111011111110000000000000) },
+ { input: i32Bits(0b00000000000011111111111111111111), expected: i32Bits(0b11111111111111111111000000000000) },
+ { input: i32Bits(0b00000000000111110101011110111111), expected: i32Bits(0b11111101111010101111100000000000) },
+ { input: i32Bits(0b00000000001111101111111111110111), expected: i32Bits(0b11101111111111110111110000000000) },
+ { input: i32Bits(0b00000000011111111111010000101111), expected: i32Bits(0b11110100001011111111111000000000) },
+ { input: i32Bits(0b00000000111111111111001111111011), expected: i32Bits(0b11011111110011111111111100000000) },
+ { input: i32Bits(0b00000001111111011111101111111111), expected: i32Bits(0b11111111110111111011111110000000) },
+ { input: i32Bits(0b00000011101011111011110111111011), expected: i32Bits(0b11011111101111011111010111000000) },
+ { input: i32Bits(0b00000111111110111111111111111111), expected: i32Bits(0b11111111111111111101111111100000) },
+ { input: i32Bits(0b00001111000000011011011110111111), expected: i32Bits(0b11111101111011011000000011110000) },
+ { input: i32Bits(0b00011110101111011111111111111111), expected: i32Bits(0b11111111111111111011110101111000) },
+ { input: i32Bits(0b00110110111111100111111110111101), expected: i32Bits(0b10111101111111100111111101101100) },
+ { input: i32Bits(0b01010111111101111111011111011111), expected: i32Bits(0b11111011111011111110111111101010) },
+ { input: i32Bits(0b11100010011110101101101110101111), expected: i32Bits(0b11110101110110110101111001000111) },
+ ]);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts
new file mode 100644
index 0000000000..609ae629ad
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'round' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn round(e: T) -> T
+Result is the integer k nearest to e, as a floating point value.
+When e lies halfway between integers k and k+1, the result is k when k is even,
+and k+1 when k is odd.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { roundInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('round', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', roundInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('round'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts
new file mode 100644
index 0000000000..d1c83bbdd4
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts
@@ -0,0 +1,61 @@
+export const description = `
+Execution tests for the 'saturate' builtin function
+
+S is AbstractFloat, f32, or f16
+T is S or vecN<S>
+@const fn saturate(e: T) -> T
+Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { saturateInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('saturate', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Non-clamped values
+ ...linearRange(0.0, 1.0, 100),
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ saturateInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('saturate'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
new file mode 100644
index 0000000000..4e8c4c3799
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
@@ -0,0 +1,229 @@
+export const description = `
+Execution tests for the 'select' builtin function
+
+T is scalar, abstract numeric type, or vector
+@const fn select(f: T, t: T, cond: bool) -> T
+Returns t when cond is true, and f otherwise.
+
+T is scalar or abstract numeric type
+@const fn select(f: vecN<T>, t: vecN<T>, cond: vecN<bool>) -> vecN<T>
+Component-wise selection. Result component i is evaluated as select(f[i],t[i],cond[i]).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import {
+ VectorType,
+ TypeVec,
+ TypeBool,
+ TypeF32,
+ TypeI32,
+ TypeU32,
+ f32,
+ i32,
+ u32,
+ False,
+ True,
+ bool,
+ vec2,
+ vec3,
+ vec4,
+} from '../../../../../util/conversion.js';
+import { run, CaseList, allInputSources } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+function makeBool(n: number) {
+ return bool((n & 1) === 1);
+}
+
+type scalarKind = 'b' | 'f' | 'i' | 'u';
+
+const dataType = {
+ b: {
+ type: TypeBool,
+ constructor: makeBool,
+ },
+ f: {
+ type: TypeF32,
+ constructor: f32,
+ },
+ i: {
+ type: TypeI32,
+ constructor: i32,
+ },
+ u: {
+ type: TypeU32,
+ constructor: u32,
+ },
+};
+
+g.test('scalar')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions')
+ .desc(`scalar tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('component', ['b', 'f', 'i', 'u'] as const)
+ .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const)
+ )
+ .fn(async t => {
+ const componentType = dataType[t.params.component as scalarKind].type;
+ const cons = dataType[t.params.component as scalarKind].constructor;
+
+ // Create the scalar values that will be selected from, either as scalars
+ // or vectors.
+ //
+ // Each boolean will select between c[k] and c[k+4]. Those values must
+ // always compare as different. The tricky case is boolean, where the parity
+ // has to be different, i.e. c[k]-c[k+4] must be odd.
+ const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i));
+ // Now form vectors that will have different components from each other.
+ const v2a = vec2(c[0], c[1]);
+ const v2b = vec2(c[4], c[5]);
+ const v3a = vec3(c[0], c[1], c[2]);
+ const v3b = vec3(c[4], c[5], c[6]);
+ const v4a = vec4(c[0], c[1], c[2], c[3]);
+ const v4b = vec4(c[4], c[5], c[6], c[7]);
+
+ const overloads = {
+ scalar: {
+ type: componentType,
+ cases: [
+ { input: [c[0], c[1], False], expected: c[0] },
+ { input: [c[0], c[1], True], expected: c[1] },
+ ],
+ },
+ vec2: {
+ type: TypeVec(2, componentType),
+ cases: [
+ { input: [v2a, v2b, False], expected: v2a },
+ { input: [v2a, v2b, True], expected: v2b },
+ ],
+ },
+ vec3: {
+ type: TypeVec(3, componentType),
+ cases: [
+ { input: [v3a, v3b, False], expected: v3a },
+ { input: [v3a, v3b, True], expected: v3b },
+ ],
+ },
+ vec4: {
+ type: TypeVec(4, componentType),
+ cases: [
+ { input: [v4a, v4b, False], expected: v4a },
+ { input: [v4a, v4b, True], expected: v4b },
+ ],
+ },
+ };
+ const overload = overloads[t.params.overload];
+
+ await run(
+ t,
+ builtin('select'),
+ [overload.type, overload.type, TypeBool],
+ overload.type,
+ t.params,
+ overload.cases
+ );
+ });
+
+g.test('vector')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions')
+ .desc(`vector tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('component', ['b', 'f', 'i', 'u'] as const)
+ .combine('overload', ['vec2', 'vec3', 'vec4'] as const)
+ )
+ .fn(async t => {
+ const componentType = dataType[t.params.component as scalarKind].type;
+ const cons = dataType[t.params.component as scalarKind].constructor;
+
+ // Create the scalar values that will be selected from.
+ //
+ // Each boolean will select between c[k] and c[k+4]. Those values must
+ // always compare as different. The tricky case is boolean, where the parity
+ // has to be different, i.e. c[k]-c[k+4] must be odd.
+ const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i));
+ const T = True;
+ const F = False;
+
+ let tests: { dataType: VectorType; boolType: VectorType; cases: CaseList };
+
+ switch (t.params.overload) {
+ case 'vec2': {
+ const a = vec2(c[0], c[1]);
+ const b = vec2(c[4], c[5]);
+ tests = {
+ dataType: TypeVec(2, componentType),
+ boolType: TypeVec(2, TypeBool),
+ cases: [
+ { input: [a, b, vec2(F, F)], expected: vec2(a.x, a.y) },
+ { input: [a, b, vec2(F, T)], expected: vec2(a.x, b.y) },
+ { input: [a, b, vec2(T, F)], expected: vec2(b.x, a.y) },
+ { input: [a, b, vec2(T, T)], expected: vec2(b.x, b.y) },
+ ],
+ };
+ break;
+ }
+ case 'vec3': {
+ const a = vec3(c[0], c[1], c[2]);
+ const b = vec3(c[4], c[5], c[6]);
+ tests = {
+ dataType: TypeVec(3, componentType),
+ boolType: TypeVec(3, TypeBool),
+ cases: [
+ { input: [a, b, vec3(F, F, F)], expected: vec3(a.x, a.y, a.z) },
+ { input: [a, b, vec3(F, F, T)], expected: vec3(a.x, a.y, b.z) },
+ { input: [a, b, vec3(F, T, F)], expected: vec3(a.x, b.y, a.z) },
+ { input: [a, b, vec3(F, T, T)], expected: vec3(a.x, b.y, b.z) },
+ { input: [a, b, vec3(T, F, F)], expected: vec3(b.x, a.y, a.z) },
+ { input: [a, b, vec3(T, F, T)], expected: vec3(b.x, a.y, b.z) },
+ { input: [a, b, vec3(T, T, F)], expected: vec3(b.x, b.y, a.z) },
+ { input: [a, b, vec3(T, T, T)], expected: vec3(b.x, b.y, b.z) },
+ ],
+ };
+ break;
+ }
+ case 'vec4': {
+ const a = vec4(c[0], c[1], c[2], c[3]);
+ const b = vec4(c[4], c[5], c[6], c[7]);
+ tests = {
+ dataType: TypeVec(4, componentType),
+ boolType: TypeVec(4, TypeBool),
+ cases: [
+ { input: [a, b, vec4(F, F, F, F)], expected: vec4(a.x, a.y, a.z, a.w) },
+ { input: [a, b, vec4(F, F, F, T)], expected: vec4(a.x, a.y, a.z, b.w) },
+ { input: [a, b, vec4(F, F, T, F)], expected: vec4(a.x, a.y, b.z, a.w) },
+ { input: [a, b, vec4(F, F, T, T)], expected: vec4(a.x, a.y, b.z, b.w) },
+ { input: [a, b, vec4(F, T, F, F)], expected: vec4(a.x, b.y, a.z, a.w) },
+ { input: [a, b, vec4(F, T, F, T)], expected: vec4(a.x, b.y, a.z, b.w) },
+ { input: [a, b, vec4(F, T, T, F)], expected: vec4(a.x, b.y, b.z, a.w) },
+ { input: [a, b, vec4(F, T, T, T)], expected: vec4(a.x, b.y, b.z, b.w) },
+ { input: [a, b, vec4(T, F, F, F)], expected: vec4(b.x, a.y, a.z, a.w) },
+ { input: [a, b, vec4(T, F, F, T)], expected: vec4(b.x, a.y, a.z, b.w) },
+ { input: [a, b, vec4(T, F, T, F)], expected: vec4(b.x, a.y, b.z, a.w) },
+ { input: [a, b, vec4(T, F, T, T)], expected: vec4(b.x, a.y, b.z, b.w) },
+ { input: [a, b, vec4(T, T, F, F)], expected: vec4(b.x, b.y, a.z, a.w) },
+ { input: [a, b, vec4(T, T, F, T)], expected: vec4(b.x, b.y, a.z, b.w) },
+ { input: [a, b, vec4(T, T, T, F)], expected: vec4(b.x, b.y, b.z, a.w) },
+ { input: [a, b, vec4(T, T, T, T)], expected: vec4(b.x, b.y, b.z, b.w) },
+ ],
+ };
+ break;
+ }
+ }
+
+ await run(
+ t,
+ builtin('select'),
+ [tests.dataType, tests.dataType, tests.boolType],
+ tests.dataType,
+ t.params,
+ tests.cases
+ );
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts
new file mode 100644
index 0000000000..66f812dd66
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts
@@ -0,0 +1,53 @@
+export const description = `
+Execution tests for the 'sign' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn sign(e: T ) -> T
+Returns the sign of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { signInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('sign', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', signInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('sign'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
new file mode 100644
index 0000000000..802977b56f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
@@ -0,0 +1,67 @@
+export const description = `
+Execution tests for the 'sin' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn sin(e: T ) -> T
+Returns the sine of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { sinInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('sin', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Well defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 1000),
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ sinInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+f32 tests
+
+TODO(#792): Decide what the ground-truth is for these tests. [1]
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('sin'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts
new file mode 100644
index 0000000000..8f59ce1e4c
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'sinh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn sinh(e: T ) -> T
+Returns the hyperbolic sine of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { sinhInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('sinh', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', sinhInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', sinhInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('sinh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts
new file mode 100644
index 0000000000..2874f83262
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts
@@ -0,0 +1,70 @@
+export const description = `
+Execution tests for the 'smoothstep' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn smoothstep(low: T , high: T , x: T ) -> T
+Returns the smooth Hermite interpolation between 0 and 1.
+Component-wise when T is a vector.
+For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) / (high - low), 0.0, 1.0).
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { smoothStepInterval } from '../../../../../util/f32_interval.js';
+import { sparseF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('smoothstep', {
+ f32_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'f32-only',
+ smoothStepInterval
+ );
+ },
+ f32_non_const: () => {
+ return generateTernaryToF32IntervalCases(
+ sparseF32Range(),
+ sparseF32Range(),
+ sparseF32Range(),
+ 'unfiltered',
+ smoothStepInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('smoothstep'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts
new file mode 100644
index 0000000000..48980078a3
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'sqrt' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn sqrt(e: T ) -> T
+Returns the square root of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { sqrtInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('sqrt', {
+ f32_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', sqrtInterval);
+ },
+ f32_non_const: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', sqrtInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
+ await run(t, builtin('sqrt'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts
new file mode 100644
index 0000000000..24226c9463
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts
@@ -0,0 +1,85 @@
+export const description = `
+Execution tests for the 'step' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn step(edge: T ,x: T ) -> T
+Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { anyOf } from '../../../../../util/compare.js';
+import { f32, TypeF32 } from '../../../../../util/conversion.js';
+import { stepInterval, toF32Interval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, quantizeToF32 } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, Case, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('step', {
+ f32: () => {
+ const zeroInterval = toF32Interval(0);
+ const oneInterval = toF32Interval(1);
+
+ // stepInterval's return value isn't always interpreted as an acceptance
+ // interval, so makeBinaryToF32IntervalCase cannot be used here.
+ // See the comment block on stepInterval for more details
+ const makeCase = (edge: number, x: number): Case => {
+ edge = quantizeToF32(edge);
+ x = quantizeToF32(x);
+ const expected = stepInterval(edge, x);
+
+ // [0, 0], [1, 1], or [-∞, +∞] cases
+ if (expected.isPoint() || !expected.isFinite()) {
+ return { input: [f32(edge), f32(x)], expected };
+ }
+
+ // [0, 1] case
+ return {
+ input: [f32(edge), f32(x)],
+ expected: anyOf(zeroInterval, oneInterval),
+ };
+ };
+
+ const range = fullF32Range();
+ const cases: Array<Case> = [];
+ range.forEach(edge => {
+ range.forEach(x => {
+ cases.push(makeCase(edge, x));
+ });
+ });
+
+ return cases;
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('step'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts
new file mode 100644
index 0000000000..f376db4472
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts
@@ -0,0 +1,38 @@
+export const description = `
+'storageBarrier' affects memory and atomic operations in the storage address space.
+
+All synchronization functions execute a control barrier with Acquire/Release memory ordering.
+That is, all synchronization functions, and affected memory and atomic operations are ordered
+in program order relative to the synchronization function. Additionally, the affected memory
+and atomic operations program-ordered before the synchronization function must be visible to
+all other threads in the workgroup before any affected memory or atomic operation program-ordered
+after the synchronization function is executed by a member of the workgroup. All synchronization
+functions use the Workgroup memory scope. All synchronization functions have a Workgroup
+execution scope.
+
+All synchronization functions must only be used in the compute shader stage.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions')
+ .desc(
+ `
+All synchronization functions must only be used in the compute shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['vertex', 'fragment', 'compute'] as const))
+ .unimplemented();
+
+g.test('barrier')
+ .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions')
+ .desc(
+ `
+fn storageBarrier()
+`
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts
new file mode 100644
index 0000000000..c226cdd851
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts
@@ -0,0 +1,61 @@
+export const description = `
+Execution tests for the 'tan' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn tan(e: T ) -> T
+Returns the tangent of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { tanInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('tan', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(
+ [
+ // Defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 100),
+ ...fullF32Range(),
+ ],
+ 'unfiltered',
+ tanInterval
+ );
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('tan'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts
new file mode 100644
index 0000000000..d031331a04
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts
@@ -0,0 +1,53 @@
+export const description = `
+Execution tests for the 'tanh' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn tanh(e: T ) -> T
+Returns the hyperbolic tangent of e. Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { tanhInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('tanh', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', tanhInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('tanh'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts
new file mode 100644
index 0000000000..0ecb9964cf
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts
@@ -0,0 +1,160 @@
+export const description = `
+Execution tests for the 'textureDimension' builtin function
+
+The dimensions of the texture in texels.
+For textures based on cubes, the results are the dimensions of each face of the cube.
+Cube faces are square, so the x and y components of the result are equal.
+If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+T: f32, i32, u32
+
+fn textureDimensions(t: texture_1d<T>) -> u32
+fn textureDimensions(t: texture_1d<T>, level: u32) -> u32
+fn textureDimensions(t: texture_2d<T>) -> vec2<u32>
+fn textureDimensions(t: texture_2d<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_2d_array<T>) -> vec2<u32>
+fn textureDimensions(t: texture_2d_array<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_3d<T>) -> vec3<u32>
+fn textureDimensions(t: texture_3d<T>, level: u32) -> vec3<u32>
+fn textureDimensions(t: texture_cube<T>) -> vec2<u32>
+fn textureDimensions(t: texture_cube<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_cube_array<T>) -> vec2<u32>
+fn textureDimensions(t: texture_cube_array<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_multisampled_2d<T>)-> vec2<u32>
+
+Parameters:
+ * t: the sampled texture
+ * level:
+ - The mip level, with level 0 containing a full size version of the texture.
+ - If omitted, the dimensions of level 0 are returned.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', [
+ 'texture_1d',
+ 'texture_2d',
+ 'texture_2d_array',
+ 'texture_3d',
+ 'texture_cube',
+ 'texture_cube_array',
+ 'texture_multisampled_2d',
+ ] as const)
+ .beginSubcases()
+ .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const)
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('depth')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+fn textureDimensions(t: texture_depth_2d) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d_array) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube_array) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2<u32>
+
+Parameters:
+ * t: the depth or multisampled texture
+ * level:
+ - The mip level, with level 0 containing a full size version of the texture.
+ - If omitted, the dimensions of level 0 are returned.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', [
+ 'texture_depth_2d',
+ 'texture_depth_2d_array',
+ 'texture_depth_cube',
+ 'texture_depth_cube_array',
+ 'texture_depth_multisampled_2d',
+ ])
+ .beginSubcases()
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('storage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+F: rgba8unorm
+ rgba8snorm
+ rgba8uint
+ rgba8sint
+ rgba16uint
+ rgba16sint
+ rgba16float
+ r32uint
+ r32sint
+ r32float
+ rg32uint
+ rg32sint
+ rg32float
+ rgba32uint
+ rgba32sint
+ rgba32float
+A: read, write, read_write
+
+fn textureDimensions(t: texture_storage_1d<F,A>) -> u32
+fn textureDimensions(t: texture_storage_2d<F,A>) -> vec2<u32>
+fn textureDimensions(t: texture_storage_2d_array<F,A>) -> vec2<u32>
+fn textureDimensions(t: texture_storage_3d<F,A>) -> vec3<u32>
+
+Parameters:
+ * t: the storage texture
+`
+ )
+ .params(u =>
+ u
+ .combine('texel_format', [
+ 'rgba8unorm',
+ 'rgba8snorm',
+ 'rgba8uint',
+ 'rgba8sint',
+ 'rgba16uint',
+ 'rgba16sint',
+ 'rgba16float',
+ 'r32uint',
+ 'r32sint',
+ 'r32float',
+ 'rg32uint',
+ 'rg32sint',
+ 'rg32float',
+ 'rgba32uint',
+ 'rgba32sint',
+ 'rgba32float',
+ ] as const)
+ .beginSubcases()
+ .combine('access_mode', ['read', 'write', 'read_write'] as const)
+ )
+ .unimplemented();
+
+g.test('external')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+fn textureDimensions(t: texture_external) -> vec2<u32>
+
+Parameters:
+ * t: the external texture
+`
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts
new file mode 100644
index 0000000000..40b331efab
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts
@@ -0,0 +1,270 @@
+export const description = `
+Execution tests for the 'textureGather' builtin function
+
+A texture gather operation reads from a 2D, 2D array, cube, or cube array texture, computing a four-component vector as follows:
+ * Find the four texels that would be used in a sampling operation with linear filtering, from mip level 0:
+ - Use the specified coordinate, array index (when present), and offset (when present).
+ - The texels are adjacent, forming a square, when considering their texture space coordinates (u,v).
+ - Selected texels at the texture edge, cube face edge, or cube corners are handled as in ordinary texture sampling.
+ * For each texel, read one channel and convert it into a scalar value.
+ - For non-depth textures, a zero-based component parameter specifies the channel to use.
+ * If the texture format supports the specified channel, i.e. has more than component channels:
+ - Yield scalar value v[component] when the texel value is v.
+ * Otherwise:
+ - Yield 0.0 when component is 1 or 2.
+ - Yield 1.0 when component is 3 (the alpha channel).
+ - For depth textures, yield the texel value. (Depth textures only have one channel.)
+ * Yield the four-component vector, arranging scalars produced by the previous step into components according to the relative coordinates of the texels, as follows:
+ - Result component Relative texel coordinate
+ x (umin,vmax)
+ y (umax,vmax)
+ z (umax,vmin)
+ w (umin,vmin)
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+T: i32, u32, f32
+
+fn textureGather(component: C, t: texture_2d<T>, s: sampler, coords: vec2<f32>) -> vec4<T>
+fn textureGather(component: C, t: texture_2d<T>, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<T>
+
+Parameters:
+ * component:
+ - The index of the channel to read from the selected texels.
+ - When provided, the component expression must a creation-time expression (e.g. 1).
+ - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error.
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('T', ['f32-only', 'i32', 'u32'] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+T: i32, u32, f32
+
+fn textureGather(component: C, t: texture_cube<T>, s: sampler, coords: vec3<f32>) -> vec4<T>
+
+Parameters:
+ * component:
+ - The index of the channel to read from the selected texels.
+ - When provided, the component expression must a creation-time expression (e.g. 1).
+ - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error.
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('T', ['f32-only', 'i32', 'u32'] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ )
+ .unimplemented();
+
+g.test('sampled_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+T: i32, u32, f32
+
+fn textureGather(component: C, t: texture_2d_array<T>, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<T>
+fn textureGather(component: C, t: texture_2d_array<T>, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<T>
+
+Parameters:
+ * component:
+ - The index of the channel to read from the selected texels.
+ - When provided, the component expression must a creation-time expression (e.g. 1).
+ - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error.
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('T', ['f32-only', 'i32', 'u32'] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+T: i32, u32, f32
+
+fn textureGather(component: C, t: texture_cube_array<T>, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<T>
+
+Parameters:
+ * component:
+ - The index of the channel to read from the selected texels.
+ - When provided, the component expression must a creation-time expression (e.g. 1).
+ - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error.
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index
+`
+ )
+ .paramsSubcasesOnly(
+ u =>
+ u
+ .combine('T', ['f32-only', 'i32', 'u32'] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ )
+ .unimplemented();
+
+g.test('depth_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+fn textureGather(t: texture_depth_2d, s: sampler, coords: vec2<f32>) -> vec4<f32>
+fn textureGather(t: texture_depth_2d, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('depth_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+fn textureGather(t: texture_depth_cube, s: sampler, coords: vec3<f32>) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ )
+ .unimplemented();
+
+g.test('depth_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+
+fn textureGather(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<f32>
+fn textureGather(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('depth_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegather')
+ .desc(
+ `
+C: i32, u32
+
+fn textureGather(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index
+`
+ )
+ .paramsSubcasesOnly(
+ u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts
new file mode 100644
index 0000000000..c743883ce8
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts
@@ -0,0 +1,134 @@
+export const description = `
+Execution tests for the 'textureGatherCompare' builtin function
+
+A texture gather compare operation performs a depth comparison on four texels in a depth texture and collects the results into a single vector, as follows:
+ * Find the four texels that would be used in a depth sampling operation with linear filtering, from mip level 0:
+ - Use the specified coordinate, array index (when present), and offset (when present).
+ - The texels are adjacent, forming a square, when considering their texture space coordinates (u,v).
+ - Selected texels at the texture edge, cube face edge, or cube corners are handled as in ordinary texture sampling.
+ * For each texel, perform a comparison against the depth reference value, yielding a 0.0 or 1.0 value, as controlled by the comparison sampler parameters.
+ * Yield the four-component vector where the components are the comparison results with the texels with relative texel coordinates as follows:
+
+ Result component Relative texel coordinate
+ x (umin,vmax)
+ y (umax,vmax)
+ z (umax,vmin)
+ w (umin,vmin)
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
+ .desc(
+ `
+C: i32, u32
+
+fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> vec4<f32>
+fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler_comparison
+ * coords: The texture coordinates
+ * array_index: The 0-based array index.
+ * depth_ref: The reference value to compare the sampled depth value against
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
+ .desc(
+ `
+C: i32, u32
+
+fn textureGatherCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler_comparison
+ * coords: The texture coordinates
+ * array_index: The 0-based array index.
+ * depth_ref: The reference value to compare the sampled depth value against
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
+ .desc(
+ `
+fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32>
+fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler_comparison
+ * coords: The texture coordinates
+ * depth_ref: The reference value to compare the sampled depth value against
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
+ .desc(
+ `
+fn textureGatherCompare(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32>
+
+Parameters:
+ * t: The depth texture to read from
+ * s: The sampler_comparison
+ * coords: The texture coordinates
+ * depth_ref: The reference value to compare the sampled depth value against
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts
new file mode 100644
index 0000000000..30cc4fff52
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts
@@ -0,0 +1,185 @@
+export const description = `
+Execution tests for the 'textureLoad' builtin function
+
+Reads a single texel from a texture without sampling or filtering.
+
+Returns the unfiltered texel data.
+
+An out of bounds access occurs if:
+ * any element of coords is outside the range [0, textureDimensions(t, level)) for the corresponding element, or
+ * array_index is outside the range [0, textureNumLayers(t)), or
+ * level is outside the range [0, textureNumLevels(t))
+
+If an out of bounds access occurs, the built-in function returns one of:
+ * The data for some texel within bounds of the texture
+ * A vector (0,0,0,0) or (0,0,0,1) of the appropriate type for non-depth textures
+ * 0.0 for depth textures
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled_1d')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_1d<T>, coords: C, level: C) -> vec4<T>
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * level: The mip level, with level 0 containing a full size version of the texture
+`
+ )
+ .params(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(1))
+ .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_2d')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_2d<T>, coords: vec2<C>, level: C) -> vec4<T>
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * level: The mip level, with level 0 containing a full size version of the texture
+`
+ )
+ .params(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_3d')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_3d<T>, coords: vec3<C>, level: C) -> vec4<T>
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * level: The mip level, with level 0 containing a full size version of the texture
+`
+ )
+ .params(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const)
+ )
+ .unimplemented();
+
+g.test('multisampled')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_multisampled_2d<T>, coords: vec2<C>, sample_index: C)-> vec4<T>
+fn textureLoad(t: texture_depth_multisampled_2d, coords: vec2<C>, sample_index: C)-> f32
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * sample_index: The 0-based sample index of the multisampled texture
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', [
+ 'texture_multisampled_2d',
+ 'texture_depth_multisampled_2d',
+ ] as const)
+ .beginSubcases()
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('sample_index', [-1, 0, `sampleCount-1`, `sampleCount`] as const)
+ )
+ .unimplemented();
+
+g.test('depth')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_depth_2d, coords: vec2<C>, level: C) -> f32
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * level: The mip level, with level 0 containing a full size version of the texture
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const)
+ )
+ .unimplemented();
+
+g.test('external')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_external, coords: vec2<C>) -> vec4<f32>
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u.combine('C', ['i32', 'u32'] as const).combine('coords', generateCoordBoundaries(2))
+ )
+ .unimplemented();
+
+g.test('arrayed')
+ .specURL('https://www.w3.org/TR/WGSL/#textureload')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureLoad(t: texture_2d_array<T>, coords: vec2<C>, array_index: C, level: C) -> vec4<T>
+fn textureLoad(t: texture_depth_2d_array, coords: vec2<C>, array_index: C, level: C) -> f32
+
+Parameters:
+ * t: The sampled texture to read from
+ * coords: The 0-based texel coordinate
+ * array_index: The 0-based texture array index
+ * level: The mip level, with level 0 containing a full size version of the texture
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_2d_array', 'texture_depth_2d_array'] as const)
+ .beginSubcases()
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('array_index', [-1, 0, `numlayers-1`, `numlayers`] as const)
+ .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts
new file mode 100644
index 0000000000..b845301161
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts
@@ -0,0 +1,100 @@
+export const description = `
+Execution tests for the 'textureNumLayers' builtin function
+
+Returns the number of layers (elements) of an array texture.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers')
+ .desc(
+ `
+T, a sampled type.
+
+fn textureNumLayers(t: texture_2d_array<T>) -> u32
+fn textureNumLayers(t: texture_cube_array<T>) -> u32
+
+Parameters
+ * t The sampled array texture.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_2d_array', 'texture_cube_array'] as const)
+ .beginSubcases()
+ .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const)
+ )
+ .unimplemented();
+
+g.test('arrayed')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers')
+ .desc(
+ `
+fn textureNumLayers(t: texture_depth_2d_array) -> u32
+fn textureNumLayers(t: texture_depth_cube_array) -> u32
+
+Parameters
+ * t The depth array texture.
+`
+ )
+ .params(u =>
+ u.combine('texture_type', ['texture_depth_2d_array', 'texture_depth_cube_array'] as const)
+ )
+ .unimplemented();
+
+g.test('storage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers')
+ .desc(
+ `
+F: rgba8unorm
+ rgba8snorm
+ rgba8uint
+ rgba8sint
+ rgba16uint
+ rgba16sint
+ rgba16float
+ r32uint
+ r32sint
+ r32float
+ rg32uint
+ rg32sint
+ rg32float
+ rgba32uint
+ rgba32sint
+ rgba32float
+A: read, write, read_write
+
+fn textureNumLayers(t: texture_storage_2d_array<F,A>) -> u32
+
+Parameters
+ * t The sampled storage array texture.
+`
+ )
+ .params(u =>
+ u
+ .beginSubcases()
+ .combine('texel_format', [
+ 'rgba8unorm',
+ 'rgba8snorm',
+ 'rgba8uint',
+ 'rgba8sint',
+ 'rgba16uint',
+ 'rgba16sint',
+ 'rgba16float',
+ 'r32uint',
+ 'r32sint',
+ 'r32float',
+ 'rg32uint',
+ 'rg32sint',
+ 'rg32float',
+ 'rgba32uint',
+ 'rgba32sint',
+ 'rgba32float',
+ ] as const)
+ .combine('access_mode', ['read', 'write', 'read_write'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts
new file mode 100644
index 0000000000..4204397b23
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts
@@ -0,0 +1,65 @@
+export const description = `
+Execution tests for the 'textureNumLevels' builtin function
+
+Returns the number of mip levels of a texture.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumlevels')
+ .desc(
+ `
+T, a sampled type.
+
+fn textureNumLevels(t: texture_1d<T>) -> u32
+fn textureNumLevels(t: texture_2d<T>) -> u32
+fn textureNumLevels(t: texture_2d_array<T>) -> u32
+fn textureNumLevels(t: texture_3d<T>) -> u32
+fn textureNumLevels(t: texture_cube<T>) -> u32
+fn textureNumLevels(t: texture_cube_array<T>) -> u32
+
+Parameters
+ * t The sampled array texture.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', [
+ 'texture_1d',
+ 'texture_2d',
+ 'texture_2d_array',
+ 'texture_3d',
+ 'texture_cube',
+ 'texture_cube_array`',
+ ] as const)
+ .beginSubcases()
+ .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const)
+ )
+ .unimplemented();
+
+g.test('depth')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumlevels')
+ .desc(
+ `
+fn textureNumLevels(t: texture_depth_2d) -> u32
+fn textureNumLevels(t: texture_depth_2d_array) -> u32
+fn textureNumLevels(t: texture_depth_cube) -> u32
+fn textureNumLevels(t: texture_depth_cube_array) -> u32
+
+Parameters
+ * t The depth array texture.
+`
+ )
+ .params(u =>
+ u.combine('texture_type', [
+ 'texture_depth_2d',
+ 'texture_depth_2d_array',
+ 'texture_depth_cube',
+ 'texture_depth_cube_array',
+ ] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts
new file mode 100644
index 0000000000..26bda6cd48
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts
@@ -0,0 +1,37 @@
+export const description = `
+Execution tests for the 'textureNumSamples' builtin function
+
+Returns the number samples per texel in a multisampled texture.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumsamples')
+ .desc(
+ `
+T, a sampled type.
+
+fn textureNumSamples(t: texture_multisampled_2d<T>) -> u32
+
+Parameters
+ * t The multisampled texture.
+`
+ )
+ .params(u => u.beginSubcases().combine('sampled_type', ['f32-only', 'i32', 'u32'] as const))
+ .unimplemented();
+
+g.test('depth')
+ .specURL('https://www.w3.org/TR/WGSL/#texturenumsamples')
+ .desc(
+ `
+fn textureNumSamples(t: texture_depth_multisampled_2d) -> u32
+
+Parameters
+ * t The multisampled texture.
+`
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts
new file mode 100644
index 0000000000..f5b01dfc63
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts
@@ -0,0 +1,273 @@
+export const description = `
+Samples a texture.
+
+Must only be used in a fragment shader stage.
+Must only be invoked in uniform control flow.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+Tests that 'textureSample' can only be called in 'fragment' shaders.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('control_flow')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+Tests that 'textureSample' can only be called in uniform control flow.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('sampled_1d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_1d<f32>, s: sampler, coords: f32) -> vec4<f32>
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(1))
+ )
+ .unimplemented();
+
+g.test('sampled_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>) -> vec4<f32>
+fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_3d<f32>, s: sampler, coords: vec3<f32>) -> vec4<f32>
+fn textureSample(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, offset: vec3<i32>) -> vec4<f32>
+fn textureSample(t: texture_cube<f32>, s: sampler, coords: vec3<f32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_3d', 'texture_cube'] as const)
+ .beginSubcases()
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('offset', generateOffsets(3))
+ )
+ .unimplemented();
+
+g.test('depth_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_depth_2d, s: sampler, coords: vec2<f32>) -> f32
+fn textureSample(t: texture_depth_2d, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSample(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<f32>
+fn textureSample(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSample(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<f32>
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+`
+ )
+ .paramsSubcasesOnly(
+ u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ )
+ .unimplemented();
+
+g.test('depth_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_depth_cube, s: sampler, coords: vec3<f32>) -> f32
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ )
+ .unimplemented();
+
+g.test('depth_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSample(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C) -> f32
+fn textureSample(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('depth_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSample(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C) -> f32
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+`
+ )
+ .paramsSubcasesOnly(
+ u =>
+ u
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts
new file mode 100644
index 0000000000..786bce4830
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts
@@ -0,0 +1,163 @@
+export const description = `
+Execution tests for the 'textureSampleBias' builtin function
+
+Samples a texture with a bias to the mip level.
+Must only be used in a fragment shader stage.
+Must only be invoked in uniform control flow.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+Tests that 'textureSampleBias' can only be called in 'fragment' shaders.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('control_flow')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+Tests that 'textureSampleBias' can only be called in uniform control flow.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('sampled_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+fn textureSampleBias(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32>
+fn textureSampleBias(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, bias: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99.
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+fn textureSampleBias(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
+fn textureSampleBias(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, bias: f32, offset: vec3<i32>) -> vec4<f32>
+fn textureSampleBias(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
+
+Parameters:
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99.
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_3d', 'texture_cube'] as const)
+ .beginSubcases()
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const)
+ .combine('offset', generateOffsets(3))
+ )
+ .unimplemented();
+
+g.test('arrayed_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+C: i32, u32
+
+fn textureSampleBias(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, bias: f32) -> vec4<f32>
+fn textureSampleBias(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, bias: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index to sample.
+ * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99.
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('arrayed_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
+ .desc(
+ `
+C: i32, u32
+
+fn textureSampleBias(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, bias: f32) -> vec4<f32>
+
+Parameters:
+ * t: The sampled texture to read from
+ * s: The sampler type
+ * coords: The texture coordinates
+ * array_index: The 0-based texture array index to sample.
+ * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99.
+ * offset:
+ - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ This offset is applied before applying any texture wrapping modes.
+ - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ - Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts
new file mode 100644
index 0000000000..9f723fac2e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts
@@ -0,0 +1,145 @@
+export const description = `
+Samples a depth texture and compares the sampled depth values against a reference value.
+
+Must only be used in a fragment shader stage.
+Must only be invoked in uniform control flow.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+Tests that 'textureSampleCompare' can only be called in 'fragment' shaders.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('control_flow')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+Tests that 'textureSampleCompare' can only be called in uniform control flow.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32
+fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * depth_ref The reference value to compare the sampled depth value against.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+fn textureSampleCompare(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * depth_ref The reference value to compare the sampled depth value against.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
+
+g.test('arrayed_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> f32
+fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * array_index: The 0-based texture array index to sample.
+ * depth_ref The reference value to compare the sampled depth value against.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('arrayed_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * array_index: The 0-based texture array index to sample.
+ * depth_ref The reference value to compare the sampled depth value against.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts
new file mode 100644
index 0000000000..500df8a6ec
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts
@@ -0,0 +1,149 @@
+export const description = `
+Samples a depth texture and compares the sampled depth values against a reference value.
+
+The textureSampleCompareLevel function is the same as textureSampleCompare, except that:
+
+ * textureSampleCompareLevel always samples texels from mip level 0.
+ * The function does not compute derivatives.
+ * There is no requirement for textureSampleCompareLevel to be invoked in uniform control flow.
+ * textureSampleCompareLevel may be invoked in any shader stage.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+Tests that 'textureSampleCompareLevel' maybe called in any shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('control_flow')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+Tests that 'textureSampleCompareLevel' maybe called in non-uniform control flow.
+`
+ )
+ .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
+ .unimplemented();
+
+g.test('2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+fn textureSampleCompareLevel(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32
+fn textureSampleCompareLevel(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * depth_ref The reference value to compare the sampled depth value against.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+fn textureSampleCompareLevel(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * depth_ref The reference value to compare the sampled depth value against.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
+
+g.test('arrayed_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> f32
+fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * array_index: The 0-based texture array index to sample.
+ * depth_ref The reference value to compare the sampled depth value against.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('arrayed_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleCompareLevel(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> f32
+
+Parameters:
+ * t The depth texture to sample.
+ * s The sampler_comparision type.
+ * coords The texture coordinates used for sampling.
+ * array_index: The 0-based texture array index to sample.
+ * depth_ref The reference value to compare the sampled depth value against.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts
new file mode 100644
index 0000000000..e0d754ece3
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts
@@ -0,0 +1,136 @@
+export const description = `
+Samples a texture using explicit gradients.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad')
+ .desc(
+ `
+fn textureSampleGrad(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32>
+fn textureSampleGrad(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled texture.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * ddx The x direction derivative vector used to compute the sampling locations
+ * ddy The y direction derivative vector used to compute the sampling locations
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad')
+ .desc(
+ `
+fn textureSampleGrad(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32>
+fn textureSampleGrad(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>, offset: vec3<i32>) -> vec4<f32>
+fn textureSampleGrad(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled texture.
+ * s The sampler type.
+ * ddx The x direction derivative vector used to compute the sampling locations
+ * ddy The y direction derivative vector used to compute the sampling locations
+ * coords The texture coordinates used for sampling.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('offset', generateOffsets(3))
+ )
+ .unimplemented();
+
+g.test('sampled_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleGrad(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32>
+fn textureSampleGrad(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, ddx: vec2<f32>, ddy: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled texture.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * ddx The x direction derivative vector used to compute the sampling locations
+ * ddy The y direction derivative vector used to compute the sampling locations
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('offset', generateOffsets(2))
+ )
+ .unimplemented();
+
+g.test('sampled_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleGrad(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled texture.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * ddx The x direction derivative vector used to compute the sampling locations
+ * ddy The y direction derivative vector used to compute the sampling locations
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts
new file mode 100644
index 0000000000..f8073c65d6
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts
@@ -0,0 +1,274 @@
+export const description = `
+Samples a texture.
+
+Must only be used in a fragment shader stage.
+Must only be invoked in uniform control flow.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+import { generateCoordBoundaries, generateOffsets } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('sampled_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+fn textureSampleLevel(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, level: f32) -> vec4<f32>
+fn textureSampleLevel(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, level: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleLevel(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, level: f32) -> vec4<f32>
+fn textureSampleLevel(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, level: f32, offset: vec2<i32>) -> vec4<f32>
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+fn textureSampleLevel(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, level: f32) -> vec4<f32>
+fn textureSampleLevel(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, level: f32, offset: vec3<i32>) -> vec4<f32>
+fn textureSampleLevel(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, level: f32) -> vec4<f32>
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_3d', 'texture_cube'] as const)
+ .beginSubcases()
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('offset', generateOffsets(3))
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('sampled_array_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleLevel(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, level: f32) -> vec4<f32>
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * array_index The 0-based texture array index to sample.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('offset', generateOffsets(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('depth_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleLevel(t: texture_depth_2d, s: sampler, coords: vec2<f32>, level: C) -> f32
+fn textureSampleLevel(t: texture_depth_2d, s: sampler, coords: vec2<f32>, level: C, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('depth_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleLevel(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, level: C) -> f32
+fn textureSampleLevel(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, level: C, offset: vec2<i32>) -> f32
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * array_index The 0-based texture array index to sample.
+ * coords The texture coordinates used for sampling.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .paramsSubcasesOnly(u =>
+ u
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('offset', generateOffsets(2))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
+
+g.test('depth_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureSampleLevel(t: texture_depth_cube, s: sampler, coords: vec3<f32>, level: C) -> f32
+fn textureSampleLevel(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C, level: C) -> f32
+
+Parameters:
+ * t The sampled or depth texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * level
+ * The mip level, with level 0 containing a full size version of the texture.
+ * For the functions where level is a f32, fractional values may interpolate between
+ two levels if the format is filterable according to the Texture Format Capabilities.
+ * When not specified, mip level 0 is sampled.
+ * offset
+ * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
+ * This offset is applied before applying any texture wrapping modes.
+ * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
+ * Each offset component must be at least -8 and at most 7.
+ Values outside of this range will result in a shader-creation error.
+`
+ )
+ .params(u =>
+ u
+ .combine('texture_type', ['texture_depth_cube', 'texture_depth_cube_array'] as const)
+ .beginSubcases()
+ .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'])
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ .combine('coords', generateCoordBoundaries(3))
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts
new file mode 100644
index 0000000000..efef971e24
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts
@@ -0,0 +1,122 @@
+export const description = `
+Writes a single texel to a texture.
+
+The channel format T depends on the storage texel format F.
+See the texel format table for the mapping of texel format to channel format.
+
+Note: An out-of-bounds access occurs if:
+ * any element of coords is outside the range [0, textureDimensions(t)) for the corresponding element, or
+ * array_index is outside the range of [0, textureNumLayers(t))
+
+If an out-of-bounds access occurs, the built-in function may do any of the following:
+ * not be executed
+ * store value to some in bounds texel
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TexelFormats } from '../../../../types.js';
+
+import { generateCoordBoundaries } from './utils.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('store_1d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturestore')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureStore(t: texture_storage_1d<F,write>, coords: C, value: vec4<T>)
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * value The new texel value
+`
+ )
+ .params(u =>
+ u
+ .combineWithParams(TexelFormats)
+ .beginSubcases()
+ .combine('coords', generateCoordBoundaries(1))
+ .combine('C', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
+
+g.test('store_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturestore')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureStore(t: texture_storage_2d<F,write>, coords: vec2<C>, value: vec4<T>)
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * value The new texel value
+`
+ )
+ .params(u =>
+ u
+ .combineWithParams(TexelFormats)
+ .beginSubcases()
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('C', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
+
+g.test('store_array_2d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturestore')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureStore(t: texture_storage_2d_array<F,write>, coords: vec2<C>, array_index: C, value: vec4<T>)
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * array_index The 0-based texture array index
+ * coords The texture coordinates used for sampling.
+ * value The new texel value
+`
+ )
+ .params(
+ u =>
+ u
+ .combineWithParams(TexelFormats)
+ .beginSubcases()
+ .combine('coords', generateCoordBoundaries(2))
+ .combine('C', ['i32', 'u32'] as const)
+ .combine('C_value', [-1, 0, 1, 2, 3, 4] as const)
+ /* array_index not param'd as out-of-bounds is implementation specific */
+ )
+ .unimplemented();
+
+g.test('store_3d_coords')
+ .specURL('https://www.w3.org/TR/WGSL/#texturestore')
+ .desc(
+ `
+C is i32 or u32
+
+fn textureStore(t: texture_storage_3d<F,write>, coords: vec3<C>, value: vec4<T>)
+
+Parameters:
+ * t The sampled, depth, or external texture to sample.
+ * s The sampler type.
+ * coords The texture coordinates used for sampling.
+ * value The new texel value
+`
+ )
+ .params(u =>
+ u
+ .combineWithParams(TexelFormats)
+ .beginSubcases()
+ .combine('coords', generateCoordBoundaries(3))
+ .combine('C', ['i32', 'u32'] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts
new file mode 100644
index 0000000000..707b74a38b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts
@@ -0,0 +1,46 @@
+export const description = `
+Execution tests for the 'transpose' builtin function
+
+T is AbstractFloat, f32, or f16
+@const transpose(e: matRxC<T> ) -> matCxR<T>
+Returns the transpose of e.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { allInputSources } from '../../expression.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('rows', [2, 3, 4] as const)
+ .combine('cols', [2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('rows', [2, 3, 4] as const)
+ .combine('cols', [2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', allInputSources)
+ .combine('rows', [2, 3, 4] as const)
+ .combine('cols', [2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts
new file mode 100644
index 0000000000..c743af3230
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts
@@ -0,0 +1,54 @@
+export const description = `
+Execution tests for the 'trunc' builtin function
+
+S is AbstractFloat, f32, f16
+T is S or vecN<S>
+@const fn trunc(e: T ) -> T
+Returns the nearest whole number whose absolute value is less than or equal to e.
+Component-wise when T is a vector.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32 } from '../../../../../util/conversion.js';
+import { truncInterval } from '../../../../../util/f32_interval.js';
+import { fullF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('trunc', {
+ f32: () => {
+ return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', truncInterval);
+ },
+});
+
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
+
+g.test('f32')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f32 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('f32');
+ await run(t, builtin('trunc'), [TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('f16')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`f16 tests`)
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts
new file mode 100644
index 0000000000..990b957aaf
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts
@@ -0,0 +1,40 @@
+export const description = `
+Decomposes a 32-bit value into two 16-bit chunks, and reinterpets each chunk as
+a floating point value.
+Component i of the result is the f32 representation of v, where v is the
+interpretation of bits 16×i through 16×i+15 of e as an IEEE-754 binary16 value.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
+import { unpack2x16floatInterval } from '../../../../../util/f32_interval.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('unpack2x16float', {
+ u32_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16floatInterval);
+ },
+ u32_non_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16floatInterval);
+ },
+});
+
+g.test('unpack')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
+ .desc(
+ `
+@const fn unpack2x16float(e: u32) -> vec2<f32>
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
+ await run(t, builtin('unpack2x16float'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts
new file mode 100644
index 0000000000..6b003ad5d2
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts
@@ -0,0 +1,40 @@
+export const description = `
+Decomposes a 32-bit value into two 16-bit chunks, then reinterprets each chunk
+as a signed normalized floating point value.
+Component i of the result is max(v ÷ 32767, -1), where v is the interpretation
+of bits 16×i through 16×i+15 of e as a twos-complement signed integer.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
+import { unpack2x16snormInterval } from '../../../../../util/f32_interval.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('unpack2x16snorm', {
+ u32_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16snormInterval);
+ },
+ u32_non_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16snormInterval);
+ },
+});
+
+g.test('unpack')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
+ .desc(
+ `
+@const fn unpack2x16snorm(e: u32) -> vec2<f32>
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
+ await run(t, builtin('unpack2x16snorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts
new file mode 100644
index 0000000000..f79047668e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts
@@ -0,0 +1,40 @@
+export const description = `
+Decomposes a 32-bit value into two 16-bit chunks, then reinterprets each chunk
+as an unsigned normalized floating point value.
+Component i of the result is v ÷ 65535, where v is the interpretation of bits
+16×i through 16×i+15 of e as an unsigned integer.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
+import { unpack2x16unormInterval } from '../../../../../util/f32_interval.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('unpack2x16unorm', {
+ u32_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16unormInterval);
+ },
+ u32_non_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16unormInterval);
+ },
+});
+
+g.test('unpack')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
+ .desc(
+ `
+@const fn unpack2x16unorm(e: u32) -> vec2<f32>
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
+ await run(t, builtin('unpack2x16unorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts
new file mode 100644
index 0000000000..8f425a46f4
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts
@@ -0,0 +1,40 @@
+export const description = `
+Decomposes a 32-bit value into four 8-bit chunks, then reinterprets each chunk
+as a signed normalized floating point value.
+Component i of the result is max(v ÷ 127, -1), where v is the interpretation of
+bits 8×i through 8×i+7 of e as a twos-complement signed integer.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
+import { unpack4x8snormInterval } from '../../../../../util/f32_interval.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('unpack4x8snorm', {
+ u32_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack4x8snormInterval);
+ },
+ u32_non_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack4x8snormInterval);
+ },
+});
+
+g.test('unpack')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
+ .desc(
+ `
+@const fn unpack4x8snorm(e: u32) -> vec4<f32>
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
+ await run(t, builtin('unpack4x8snorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts
new file mode 100644
index 0000000000..01d3db47c9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts
@@ -0,0 +1,40 @@
+export const description = `
+Decomposes a 32-bit value into four 8-bit chunks, then reinterprets each chunk
+as an unsigned normalized floating point value.
+Component i of the result is v ÷ 255, where v is the interpretation of bits 8×i
+through 8×i+7 of e as an unsigned integer.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
+import { unpack4x8unormInterval } from '../../../../../util/f32_interval.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('unpack4x8unorm', {
+ u32_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack4x8unormInterval);
+ },
+ u32_non_const: () => {
+ return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack4x8unormInterval);
+ },
+});
+
+g.test('unpack')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
+ .desc(
+ `
+@const fn unpack4x8unorm(e: u32) -> vec4<f32>
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
+ await run(t, builtin('unpack4x8unorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts
new file mode 100644
index 0000000000..9cbee00939
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts
@@ -0,0 +1,45 @@
+/**
+ * Generates the boundary entries for the given number of dimensions
+ *
+ * @param numDimensions: The number of dimensions to generate for
+ * @returns an array of generated coord boundaries
+ */
+export function generateCoordBoundaries(numDimensions: number) {
+ const ret = ['in-bounds'];
+
+ if (numDimensions < 1 || numDimensions > 3) {
+ throw new Error(`invalid numDimensions: ${numDimensions}`);
+ }
+
+ const name = 'xyz';
+ for (let i = 0; i < numDimensions; ++i) {
+ for (const j of ['min', 'max']) {
+ for (const k of ['wrap', 'boundary']) {
+ ret.push(`${name[i]}-${j}-${k}`);
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Generates a set of offset values to attempt in the range [-9, 8].
+ *
+ * @param numDimensions: The number of dimensions to generate for
+ * @return an array of generated offset values
+ */
+export function generateOffsets(numDimensions: number) {
+ if (numDimensions < 2 || numDimensions > 3) {
+ throw new Error(`generateOffsets: invalid numDimensions: ${numDimensions}`);
+ }
+ const ret: Array<undefined | Array<number>> = [undefined];
+ for (const val of [-9, -8, 0, 1, 7, 8]) {
+ const v = [];
+ for (let i = 0; i < numDimensions; ++i) {
+ v.push(val);
+ }
+ ret.push(v);
+ }
+ return ret;
+}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts
new file mode 100644
index 0000000000..74e0f12325
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts
@@ -0,0 +1,38 @@
+export const description = `
+'workgroupBarrier' affects memory and atomic operations in the workgroup address space.
+
+All synchronization functions execute a control barrier with Acquire/Release memory ordering.
+That is, all synchronization functions, and affected memory and atomic operations are ordered
+in program order relative to the synchronization function. Additionally, the affected memory
+and atomic operations program-ordered before the synchronization function must be visible to
+all other threads in the workgroup before any affected memory or atomic operation program-ordered
+after the synchronization function is executed by a member of the workgroup. All synchronization
+functions use the Workgroup memory scope. All synchronization functions have a Workgroup
+execution scope.
+
+All synchronization functions must only be used in the compute shader stage.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('stage')
+ .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions')
+ .desc(
+ `
+All synchronization functions must only be used in the compute shader stage.
+`
+ )
+ .params(u => u.combine('stage', ['vertex', 'fragment', 'compute'] as const))
+ .unimplemented();
+
+g.test('barrier')
+ .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions')
+ .desc(
+ `
+fn workgroupBarrier()
+`
+ )
+ .unimplemented();