summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/binary.ts9
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts220
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts143
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_arithmetic.spec.ts194
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_logical.spec.ts260
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts156
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts213
7 files changed, 1195 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/binary.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/binary.ts
new file mode 100644
index 0000000000..dcfc2b2a3f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/binary.ts
@@ -0,0 +1,9 @@
+import { ExpressionBuilder } from '../expression.js';
+
+/* @returns an ExpressionBuilder that evaluates a binary operation */
+export function binary(op: string): ExpressionBuilder {
+ return values => {
+ const values_str = values.map(v => `(${v})`);
+ return `(${values_str.join(op)})`;
+ };
+}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts
new file mode 100644
index 0000000000..6a708564d9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts
@@ -0,0 +1,220 @@
+export const description = `
+Execution Tests for the bitwise binary expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { i32, scalarType, u32 } from '../../../../util/conversion.js';
+import { allInputSources, run } from '../expression.js';
+
+import { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('bitwise_or')
+ .specURL('https://www.w3.org/TR/WGSL/#bit-expr')
+ .desc(
+ `
+e1 | e2: T
+T is i32, u32, vecN<i32>, or vecN<u32>
+
+Bitwise-or. Component-wise when T is a vector.
+`
+ )
+ .params(u =>
+ u
+ .combine('type', ['i32', 'u32'] as const)
+ .combine('inputSource', allInputSources)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const type = scalarType(t.params.type);
+ const V = t.params.type === 'i32' ? i32 : u32;
+ const cases = [
+ // Static patterns
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b00000000000000000000000000000000)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b00000000000000000000000000000000)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b11111111111111111111111111111111)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b11111111111111111111111111111111)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b10100100010010100100010010100100), V(0b00000000000000000000000000000000)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b10100100010010100100010010100100)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b01010010001001010010001001010010), V(0b10100100010010100100010010100100)],
+ expected: V(0b11110110011011110110011011110110),
+ },
+ ];
+ // Permute all combinations of a single bit being set for the LHS and RHS
+ for (let i = 0; i < 32; i++) {
+ const lhs = 1 << i;
+ for (let j = 0; j < 32; j++) {
+ const rhs = 1 << j;
+ cases.push({
+ input: [V(lhs), V(rhs)],
+ expected: V(lhs | rhs),
+ });
+ }
+ }
+ await run(t, binary('|'), [type, type], type, t.params, cases);
+ });
+
+g.test('bitwise_and')
+ .specURL('https://www.w3.org/TR/WGSL/#bit-expr')
+ .desc(
+ `
+e1 & e2: T
+T is i32, u32, vecN<i32>, or vecN<u32>
+
+Bitwise-and. Component-wise when T is a vector.
+`
+ )
+ .params(u =>
+ u
+ .combine('type', ['i32', 'u32'] as const)
+ .combine('inputSource', allInputSources)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const type = scalarType(t.params.type);
+ const V = t.params.type === 'i32' ? i32 : u32;
+ const cases = [
+ // Static patterns
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b00000000000000000000000000000000)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b00000000000000000000000000000000)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b11111111111111111111111111111111)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b11111111111111111111111111111111)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b10100100010010100100010010100100), V(0b00000000000000000000000000000000)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b10100100010010100100010010100100), V(0b11111111111111111111111111111111)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b10100100010010100100010010100100)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b10100100010010100100010010100100)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b01010010001001010010001001010010), V(0b01011011101101011011101101011011)],
+ expected: V(0b01010010001001010010001001010010),
+ },
+ ];
+ // Permute all combinations of a single bit being set for the LHS and all but one bit set for the RHS
+ for (let i = 0; i < 32; i++) {
+ const lhs = 1 << i;
+ for (let j = 0; j < 32; j++) {
+ const rhs = 0xffffffff ^ (1 << j);
+ cases.push({
+ input: [V(lhs), V(rhs)],
+ expected: V(lhs & rhs),
+ });
+ }
+ }
+ await run(t, binary('&'), [type, type], type, t.params, cases);
+ });
+
+g.test('bitwise_exclusive_or')
+ .specURL('https://www.w3.org/TR/WGSL/#bit-expr')
+ .desc(
+ `
+e1 ^ e2: T
+T is i32, u32, vecN<i32>, or vecN<u32>
+
+Bitwise-exclusive-or. Component-wise when T is a vector.
+`
+ )
+ .params(u =>
+ u
+ .combine('type', ['i32', 'u32'] as const)
+ .combine('inputSource', allInputSources)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const type = scalarType(t.params.type);
+ const V = t.params.type === 'i32' ? i32 : u32;
+ const cases = [
+ // Static patterns
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b00000000000000000000000000000000)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b00000000000000000000000000000000)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b11111111111111111111111111111111)],
+ expected: V(0b11111111111111111111111111111111),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b11111111111111111111111111111111)],
+ expected: V(0b00000000000000000000000000000000),
+ },
+ {
+ input: [V(0b10100100010010100100010010100100), V(0b00000000000000000000000000000000)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b10100100010010100100010010100100), V(0b11111111111111111111111111111111)],
+ expected: V(0b01011011101101011011101101011011),
+ },
+ {
+ input: [V(0b00000000000000000000000000000000), V(0b10100100010010100100010010100100)],
+ expected: V(0b10100100010010100100010010100100),
+ },
+ {
+ input: [V(0b11111111111111111111111111111111), V(0b10100100010010100100010010100100)],
+ expected: V(0b01011011101101011011101101011011),
+ },
+ {
+ input: [V(0b01010010001001010010001001010010), V(0b01011011101101011011101101011011)],
+ expected: V(0b00001001100100001001100100001001),
+ },
+ ];
+ // Permute all combinations of a single bit being set for the LHS and all but one bit set for the RHS
+ for (let i = 0; i < 32; i++) {
+ const lhs = 1 << i;
+ for (let j = 0; j < 32; j++) {
+ const rhs = 0xffffffff ^ (1 << j);
+ cases.push({
+ input: [V(lhs), V(rhs)],
+ expected: V(lhs ^ rhs),
+ });
+ }
+ }
+ await run(t, binary('^'), [type, type], type, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts
new file mode 100644
index 0000000000..d3c426920a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts
@@ -0,0 +1,143 @@
+export const description = `
+Execution Tests for the boolean binary logical expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { bool, TypeBool } from '../../../../util/conversion.js';
+import { allInputSources, run } from '../expression.js';
+
+import { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+// Short circuiting vs no short circuiting is not tested here, it is covered in
+// src/webgpu/shader/execution/evaluation_order.spec.ts
+
+g.test('and')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 & e2
+Logical "and". Component-wise when T is a vector. Evaluates both e1 and e2.
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(false) },
+ { input: [bool(true), bool(false)], expected: bool(false) },
+ { input: [bool(false), bool(true)], expected: bool(false) },
+ { input: [bool(true), bool(true)], expected: bool(true) },
+ ];
+
+ await run(t, binary('&'), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
+
+g.test('and_short_circuit')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 && e2
+short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 only if e1 is true.
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(false) },
+ { input: [bool(true), bool(false)], expected: bool(false) },
+ { input: [bool(false), bool(true)], expected: bool(false) },
+ { input: [bool(true), bool(true)], expected: bool(true) },
+ ];
+
+ await run(t, binary('&&'), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
+
+g.test('or')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 | e2
+Logical "or". Component-wise when T is a vector. Evaluates both e1 and e2.
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(false) },
+ { input: [bool(true), bool(false)], expected: bool(true) },
+ { input: [bool(false), bool(true)], expected: bool(true) },
+ { input: [bool(true), bool(true)], expected: bool(true) },
+ ];
+
+ await run(t, binary('|'), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
+
+g.test('or_short_circuit')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 || e2
+short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 only if e1 is true.
+`
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(false) },
+ { input: [bool(true), bool(false)], expected: bool(true) },
+ { input: [bool(false), bool(true)], expected: bool(true) },
+ { input: [bool(true), bool(true)], expected: bool(true) },
+ ];
+
+ await run(t, binary('||'), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
+
+g.test('equals')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 == e2
+Equality. Component-wise when T is a vector.
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(true) },
+ { input: [bool(true), bool(false)], expected: bool(false) },
+ { input: [bool(false), bool(true)], expected: bool(false) },
+ { input: [bool(true), bool(true)], expected: bool(true) },
+ ];
+
+ await run(t, binary('=='), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
+
+g.test('not_equals')
+ .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
+ .desc(
+ `
+Expression: e1 != e2
+Equality. Component-wise when T is a vector.
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = [
+ { input: [bool(false), bool(false)], expected: bool(false) },
+ { input: [bool(true), bool(false)], expected: bool(true) },
+ { input: [bool(false), bool(true)], expected: bool(true) },
+ { input: [bool(true), bool(true)], expected: bool(false) },
+ ];
+
+ await run(t, binary('!='), [TypeBool, TypeBool], TypeBool, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_arithmetic.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_arithmetic.spec.ts
new file mode 100644
index 0000000000..84d32191bc
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_arithmetic.spec.ts
@@ -0,0 +1,194 @@
+export const description = `
+Execution Tests for the f32 arithmetic binary expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { TypeF32 } from '../../../../util/conversion.js';
+import {
+ additionInterval,
+ divisionInterval,
+ multiplicationInterval,
+ remainderInterval,
+ subtractionInterval,
+} 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 { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('binary/f32_arithmetic', {
+ addition_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ additionInterval
+ );
+ },
+ addition_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ additionInterval
+ );
+ },
+ subtraction_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ subtractionInterval
+ );
+ },
+ subtraction_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ subtractionInterval
+ );
+ },
+ multiplication_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ multiplicationInterval
+ );
+ },
+ multiplication_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ multiplicationInterval
+ );
+ },
+ division_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ divisionInterval
+ );
+ },
+ division_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ divisionInterval
+ );
+ },
+ remainder_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'f32-only',
+ remainderInterval
+ );
+ },
+ remainder_non_const: () => {
+ return generateBinaryToF32IntervalCases(
+ fullF32Range(),
+ fullF32Range(),
+ 'unfiltered',
+ remainderInterval
+ );
+ },
+});
+
+g.test('addition')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x + y
+Accuracy: Correctly rounded
+`
+ )
+ .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' ? 'addition_const' : 'addition_non_const'
+ );
+ await run(t, binary('+'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('subtraction')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x - y
+Accuracy: Correctly rounded
+`
+ )
+ .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' ? 'subtraction_const' : 'subtraction_non_const'
+ );
+ await run(t, binary('-'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('multiplication')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x * y
+Accuracy: Correctly rounded
+`
+ )
+ .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' ? 'multiplication_const' : 'multiplication_non_const'
+ );
+ await run(t, binary('*'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('division')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x / y
+Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126]
+`
+ )
+ .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' ? 'division_const' : 'division_non_const'
+ );
+ await run(t, binary('/'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
+
+g.test('remainder')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x % y
+Accuracy: Derived from x - y * trunc(x/y)
+`
+ )
+ .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' ? 'remainder_const' : 'remainder_non_const'
+ );
+ await run(t, binary('%'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_logical.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_logical.spec.ts
new file mode 100644
index 0000000000..21f2810d01
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/f32_logical.spec.ts
@@ -0,0 +1,260 @@
+export const description = `
+Execution Tests for the f32 logical binary expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { anyOf } from '../../../../util/compare.js';
+import { bool, f32, Scalar, TypeBool, TypeF32 } from '../../../../util/conversion.js';
+import { flushSubnormalScalarF32, vectorF32Range } from '../../../../util/math.js';
+import { makeCaseCache } from '../case_cache.js';
+import { allInputSources, Case, run } from '../expression.js';
+
+import { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+/**
+ * @returns a test case for the provided left hand & right hand values and truth function.
+ * Handles quantization and subnormals.
+ */
+function makeCase(
+ lhs: number,
+ rhs: number,
+ truthFunc: (lhs: Scalar, rhs: Scalar) => boolean
+): Case {
+ const f32_lhs = f32(lhs);
+ const f32_rhs = f32(rhs);
+ const lhs_options = new Set([f32_lhs, flushSubnormalScalarF32(f32_lhs)]);
+ const rhs_options = new Set([f32_rhs, flushSubnormalScalarF32(f32_rhs)]);
+ const expected: Array<Scalar> = [];
+ lhs_options.forEach(l => {
+ rhs_options.forEach(r => {
+ const result = bool(truthFunc(l, r));
+ if (!expected.includes(result)) {
+ expected.push(result);
+ }
+ });
+ });
+
+ return { input: [f32_lhs, f32_rhs], expected: anyOf(...expected) };
+}
+
+export const d = makeCaseCache('binary/f32_logical', {
+ equals_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) === (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ equals_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) === (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ not_equals_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) !== (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ not_equals_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) !== (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ less_than_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) < (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ less_than_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) < (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ less_equals_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) <= (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ less_equals_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) <= (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ greater_than_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) > (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ greater_than_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) > (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ greater_equals_non_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) >= (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+ greater_equals_const: () => {
+ const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => {
+ return (lhs.value as number) >= (rhs.value as number);
+ };
+
+ return vectorF32Range(2).map(v => {
+ return makeCase(v[0], v[1], truthFunc);
+ });
+ },
+});
+
+g.test('equals')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x == y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'equals_const' : 'equals_non_const'
+ );
+ await run(t, binary('=='), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
+
+g.test('not_equals')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x != y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'not_equals_const' : 'not_equals_non_const'
+ );
+ await run(t, binary('!='), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
+
+g.test('less_than')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x < y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'less_than_const' : 'less_than_non_const'
+ );
+ await run(t, binary('<'), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
+
+g.test('less_equals')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x <= y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'less_equals_const' : 'less_equals_non_const'
+ );
+ await run(t, binary('<='), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
+
+g.test('greater_than')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x > y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'greater_than_const' : 'greater_than_non_const'
+ );
+ await run(t, binary('>'), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
+
+g.test('greater_equals')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x >= y
+Accuracy: Correct result
+`
+ )
+ .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' ? 'greater_equals_const' : 'greater_equals_non_const'
+ );
+ await run(t, binary('>='), [TypeF32, TypeF32], TypeBool, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts
new file mode 100644
index 0000000000..bbdb260a2d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts
@@ -0,0 +1,156 @@
+export const description = `
+Execution Tests for the i32 arithmetic binary expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { kValue } from '../../../../util/constants.js';
+import { TypeI32 } from '../../../../util/conversion.js';
+import { fullI32Range } from '../../../../util/math.js';
+import { makeCaseCache } from '../case_cache.js';
+import { allInputSources, generateBinaryToI32Cases, run } from '../expression.js';
+
+import { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('binary/i32_arithmetic', {
+ addition: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ return x + y;
+ });
+ },
+ subtraction: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ return x - y;
+ });
+ },
+ multiplication: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ division_non_const: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ if (y === 0) {
+ return x;
+ }
+ if (x === kValue.i32.negative.min && y === -1) {
+ return x;
+ }
+ return x / y;
+ });
+ },
+ division_const: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ if (y === 0) {
+ return undefined;
+ }
+ if (x === kValue.i32.negative.min && y === -1) {
+ return undefined;
+ }
+ return x / y;
+ });
+ },
+ remainder_non_const: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ if (y === 0) {
+ return 0;
+ }
+ if (x === kValue.i32.negative.min && y === -1) {
+ return 0;
+ }
+ return x % y;
+ });
+ },
+ remainder_const: () => {
+ return generateBinaryToI32Cases(fullI32Range(), fullI32Range(), (x, y) => {
+ if (y === 0) {
+ return undefined;
+ }
+ if (x === kValue.i32.negative.min && y === -1) {
+ return undefined;
+ }
+ return x % y;
+ });
+ },
+});
+
+g.test('addition')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x + y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('addition');
+ await run(t, binary('+'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('subtraction')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x - y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('subtraction');
+ await run(t, binary('-'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('multiplication')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x * y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('multiplication');
+ await run(t, binary('*'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('division')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x / y
+`
+ )
+ .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' ? 'division_const' : 'division_non_const'
+ );
+ await run(t, binary('/'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
+
+g.test('remainder')
+ .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation')
+ .desc(
+ `
+Expression: x % y
+`
+ )
+ .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' ? 'remainder_const' : 'remainder_non_const'
+ );
+ await run(t, binary('%'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts
new file mode 100644
index 0000000000..bf7789a635
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts
@@ -0,0 +1,213 @@
+export const description = `
+Execution Tests for the u32 arithmetic binary expression operations
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../gpu_test.js';
+import { TypeU32, TypeVec } from '../../../../util/conversion.js';
+import { fullU32Range, sparseU32Range, vectorU32Range } from '../../../../util/math.js';
+import { makeCaseCache } from '../case_cache.js';
+import {
+ allInputSources,
+ generateBinaryToU32Cases,
+ generateU32VectorBinaryToVectorCases,
+ generateVectorU32BinaryToVectorCases,
+ run,
+} from '../expression.js';
+
+import { binary } from './binary.js';
+
+export const g = makeTestGroup(GPUTest);
+
+export const d = makeCaseCache('binary/u32_arithmetic', {
+ addition: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ return x + y;
+ });
+ },
+ subtraction: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ return x - y;
+ });
+ },
+ multiplication: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ division_non_const: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ if (y === 0) {
+ return x;
+ }
+ return x / y;
+ });
+ },
+ division_const: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ if (y === 0) {
+ return undefined;
+ }
+ return x / y;
+ });
+ },
+ remainder_non_const: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ if (y === 0) {
+ return 0;
+ }
+ return x % y;
+ });
+ },
+ remainder_const: () => {
+ return generateBinaryToU32Cases(fullU32Range(), fullU32Range(), (x, y) => {
+ if (y === 0) {
+ return undefined;
+ }
+ return x % y;
+ });
+ },
+ multiplication_scalar_vector2: () => {
+ return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ multiplication_scalar_vector3: () => {
+ return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ multiplication_scalar_vector4: () => {
+ return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ multiplication_vector2_scalar: () => {
+ return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ multiplication_vector3_scalar: () => {
+ return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+ multiplication_vector4_scalar: () => {
+ return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), (x, y) => {
+ return Math.imul(x, y);
+ });
+ },
+});
+
+g.test('addition')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x + y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('addition');
+ await run(t, binary('+'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('subtraction')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x - y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('subtraction');
+ await run(t, binary('-'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('multiplication')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x * y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = await d.get('multiplication');
+ await run(t, binary('*'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('division')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x / y
+`
+ )
+ .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' ? 'division_const' : 'division_non_const'
+ );
+ await run(t, binary('/'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('remainder')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x % y
+`
+ )
+ .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' ? 'remainder_const' : 'remainder_non_const'
+ );
+ await run(t, binary('%'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ });
+
+g.test('multiplication_scalar_vector')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x * y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize_rhs', [2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const vec_size = t.params.vectorize_rhs;
+ const vec_type = TypeVec(vec_size, TypeU32);
+ const cases = await d.get(`multiplication_scalar_vector${vec_size}`);
+ await run(t, binary('*'), [TypeU32, vec_type], vec_type, t.params, cases);
+ });
+
+g.test('multiplication_vector_scalar')
+ .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr')
+ .desc(
+ `
+Expression: x * y
+`
+ )
+ .params(u =>
+ u.combine('inputSource', allInputSources).combine('vectorize_lhs', [2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const vec_size = t.params.vectorize_lhs;
+ const vec_type = TypeVec(vec_size, TypeU32);
+ const cases = await d.get(`multiplication_vector${vec_size}_scalar`);
+ await run(t, binary('*'), [vec_type, TypeU32], vec_type, t.params, cases);
+ });