summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts484
1 files changed, 484 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts
new file mode 100644
index 0000000000..0dfdd81165
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/evaluation_order.spec.ts
@@ -0,0 +1,484 @@
+export const description = `
+Execution Tests for evaluation order of expressions
+`;
+
+import { makeTestGroup } from '../../../common/framework/test_group.js';
+import { GPUTest } from '../../gpu_test.js';
+import { TypeI32 } from '../../util/conversion.js';
+
+export const g = makeTestGroup(GPUTest);
+
+const common_source = `
+var<private> a : i32 = 2;
+var<private> b : i32 = 3;
+var<private> c : i32 = 4;
+
+var<private> arr2D : array<array<i32, 10>, 10> = array<array<i32, 10>, 10>(
+ array<i32, 10>( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
+ array<i32, 10>(10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
+ array<i32, 10>(20, 22, 22, 23, 24, 25, 26, 27, 28, 29),
+ array<i32, 10>(30, 32, 32, 33, 34, 35, 36, 37, 38, 33),
+ array<i32, 10>(40, 42, 42, 43, 44, 45, 46, 47, 48, 44),
+ array<i32, 10>(50, 52, 52, 53, 54, 55, 56, 57, 58, 55),
+ array<i32, 10>(60, 62, 62, 63, 64, 65, 66, 67, 68, 66),
+ array<i32, 10>(70, 72, 72, 73, 74, 75, 76, 77, 78, 77),
+ array<i32, 10>(80, 82, 82, 83, 84, 85, 86, 87, 88, 98),
+ array<i32, 10>(90, 92, 92, 93, 94, 95, 96, 97, 98, 99),
+);
+
+var<private> arr1D_zero : array<i32, 50>;
+var<private> arr2D_zero : array<array<i32, 50>, 50>;
+var<private> arr3D_zero : array<array<array<i32, 50>, 50>, 50>;
+
+var<private> vec4_zero : vec4<i32>;
+
+struct S {
+ x : i32,
+ y : i32,
+ z : i32,
+}
+
+fn mul(p1 : ptr<private, i32>, multiplicand : i32) -> i32 {
+ *p1 = *p1 * multiplicand;
+ return *p1;
+}
+
+fn add(p1 : ptr<private, i32>, addend : i32) -> i32 {
+ *p1 = *p1 + addend;
+ return *p1;
+}
+
+fn sub_mul3(a : i32, a_mul : i32, b : i32, b_mul : i32, c : i32, c_mul : i32) -> i32 {
+ return a * a_mul - b * b_mul - c * c_mul;
+}
+
+fn sub_mul4(a : i32, a_mul : i32, b : i32, b_mul : i32, c : i32, c_mul : i32, d : i32, d_mul : i32) -> i32 {
+ return a * a_mul - b * b_mul - c * c_mul - d * d_mul;
+}
+
+fn set_vec4_x(p : ptr<private, vec4<i32>>, v : i32) -> i32 {
+ (*p).x = v;
+ return 0;
+}
+
+fn make_S(init : i32) -> S {
+ return S(init, init, init);
+}
+
+fn mul3_ret0(p1 : ptr<private, i32>, p2 : ptr<private, i32>, p3 : ptr<private, i32>, multiplicand : i32) -> i32 {
+ *p1 = *p1 * multiplicand;
+ *p2 = *p2 * multiplicand;
+ *p3 = *p3 * multiplicand;
+ return 0;
+}
+`;
+
+g.test('binary_arith')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr')
+ .desc('Tests order of evaluation of arithmetic binary expressions.')
+ .paramsSimple([
+ {
+ name: 'BothSE', // SE = Side Effects
+ _body: 'return mul(&a, 10) - mul(&a, 10);',
+ _result: 20 - 200,
+ },
+ {
+ name: 'LeftSE', //
+ _body: 'return mul(&a, 10) - a;',
+ _result: 20 - 20,
+ },
+ {
+ name: 'RightSE', //
+ _body: 'return a - mul(&a, 10);',
+ _result: 2 - 20,
+ },
+ {
+ name: 'ThreeSE',
+ _body: 'return mul(&a, 10) - mul(&a, 10) - mul(&a, 10);',
+ _result: 20 - 200 - 2000,
+ },
+ {
+ name: 'LeftmostSE',
+ _body: 'return mul3_ret0(&a, &b, &c, 10) - a - b - c;',
+ _result: 0 - 20 - 30 - 40,
+ },
+ {
+ name: 'RightmostSE', //
+ _body: 'return a - b - c - mul3_ret0(&a, &b, &c, 10);',
+ _result: 2 - 3 - 4 - 0,
+ },
+ {
+ name: 'MiddleSE', //
+ _body: 'return a - b - mul3_ret0(&a, &b, &c, 10) - c;',
+ _result: 2 - 3 - 0 - 40,
+ },
+ {
+ name: 'LiteralAndSEAndVar', //
+ _body: 'return 1 - mul(&a, 10) - a;',
+ _result: 1 - 20 - 20,
+ },
+ {
+ name: 'VarAndSEAndLiteral', //
+ _body: 'return a - mul(&a, 10) - 1;',
+ _result: 2 - 20 - 1,
+ },
+ {
+ name: 'SEAndVarAndLiteral', //
+ _body: 'return mul(&a, 10) - a - 1;',
+ _result: 20 - 20 - 1,
+ },
+ {
+ name: 'VarAndLiteralAndSE', //
+ _body: 'return a - 1 - mul(&a, 10);',
+ _result: 2 - 1 - 20,
+ },
+ {
+ name: 'MemberAccessAndSE',
+ _body: 'return vec4_zero.x + set_vec4_x(&vec4_zero, 42);',
+ _result: 0,
+ },
+ {
+ name: 'SEAndMemberAccess',
+ _body: 'return set_vec4_x(&vec4_zero, 42) + vec4_zero.x;',
+ _result: 42,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('binary_logical')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#logical-expr')
+ .desc('Tests order of evaluation of logical binary expressions.')
+ .paramsSimple([
+ {
+ name: 'BothSE',
+ _body:
+ 'let r = (add(&a, 1) == 3) && (add(&a, 1) == 4);' + //
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'LeftSE',
+ _body:
+ 'let r = (add(&a, 1) == 3) && (a == 3);' + //
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'RightSE',
+ _body:
+ 'let r = (a == 2) && (add(&a, 1) == 3);' + //
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'LeftmostSE',
+ _body:
+ 'let r = (mul3_ret0(&a, &b, &c, 10) == 0) && (a == 20) && (b == 30) && (c == 40);' +
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'RightmostSE',
+ _body:
+ 'let r = (a == 2) && (b == 3) && (c == 4) && (mul3_ret0(&a, &b, &c, 10) == 0);' +
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'MiddleSE',
+ _body:
+ 'let r = (a == 2) && (b == 3) && (mul3_ret0(&a, &b, &c, 10) == 0) && (c == 40);' +
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'ShortCircuit_And_LhsOnly',
+ _body:
+ // rhs should not execute
+ 'let t = (a != 2) && (mul(&a, 10) == 20);' + //
+ 'return a;',
+ _result: 2,
+ },
+ {
+ name: 'ShortCircuit_And_LhsAndRhs',
+ _body:
+ // rhs should execute
+ 'let t = (a == 2) && (mul(&a, 10) == 20);' + //
+ 'return a;',
+ _result: 20,
+ },
+ {
+ name: 'ShortCircuit_Or_LhsOnly',
+ _body:
+ // rhs should not execute
+ 'let t = (a == 2) || (mul(&a, 10) == 20);' + //
+ 'let r = (a == 2);' +
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'ShortCircuit_Or_LhsAndRhs',
+ _body:
+ // rhs should execute
+ 'let t = (a != 2) || (mul(&a, 10) == 20);' + //
+ 'let r = (a == 20);' +
+ 'return i32(r);',
+ _result: 1,
+ },
+ {
+ name: 'NoShortCircuit_And',
+ _body:
+ // rhs should execute
+ 'let t = (a != 2) & (mul(&a, 10) == 20);' + //
+ 'return a;',
+ _result: 20,
+ },
+ {
+ name: 'NoShortCircuit_Or',
+ _body:
+ // rhs should execute
+ 'let t = (a == 2) | (mul(&a, 10) == 20);' + //
+ 'return a;',
+ _result: 20,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('binary_mixed')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#arithmetic-expr')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#logical-expr')
+ .desc('Tests order of evaluation of both arithmetic and logical binary expressions.')
+ .paramsSimple([
+ {
+ name: 'ArithAndLogical',
+ _body: 'return mul(&a, 10) - i32(mul(&a, 10) == 200 && mul(&a, 10) == 2000);',
+ _result: 20 - 1,
+ },
+ {
+ name: 'LogicalAndArith',
+ _body: 'return i32(mul(&a, 10) == 20 && mul(&a, 10) == 200) - mul(&a, 10);',
+ _result: 1 - 2000,
+ },
+ {
+ name: 'ArithAndLogical_ShortCircuit',
+ _body: 'return mul(&a, 10) - i32(mul(&a, 10) != 200 && mul(&a, 10) == 2000);',
+ _result: 20 - 0,
+ },
+ {
+ name: 'LogicalAndArith_ShortCircuit',
+ _body: 'return i32(mul(&a, 10) != 20 && mul(&a, 10) == 200) - mul(&a, 10);',
+ _result: 0 - 200,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('call')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#function-calls')
+ .desc('Tests order of evaluation of call expressions.')
+ .paramsSimple([
+ {
+ name: 'OneSE', //
+ _body: 'return sub_mul3(mul(&a, 2), 2, a, 3, 3, 4);',
+ _result: 4 * 2 - 4 * 3 - 3 * 4,
+ },
+ {
+ name: 'AllSE',
+ _body: 'return sub_mul3(mul(&a, 2), 2, mul(&a, 2), 3, mul(&a, 2), 4);',
+ _result: 4 * 2 - 8 * 3 - 16 * 4,
+ },
+ {
+ name: 'MiddleNotSE',
+ _body: 'return sub_mul3(mul(&a, 2), 2, a, 3, mul(&a, 2), 4);',
+ _result: 4 * 2 - 4 * 3 - 8 * 4,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('index_accessor')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#array-access-expr')
+ .desc('Tests order of evaluation of index accessor expressions.')
+ .paramsSimple([
+ {
+ name: 'LeftSE', //
+ _body: 'return arr2D[mul(&a, 2)][a];',
+ _result: 4 * 10 + 4,
+ },
+ {
+ name: 'RightSE', //
+ _body: 'return arr2D[a][mul(&a, 2)];',
+ _result: 2 * 10 + 4,
+ },
+ {
+ name: 'BothSE',
+ _body: 'return arr2D[mul(&a, 2)][mul(&a, 2)];',
+ _result: 4 * 10 + 8,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('assignment')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#assignment')
+ .desc('Tests order of evaluation of assignment statements.')
+ .paramsSimple([
+ {
+ name: 'ToArray1D',
+ _body:
+ 'arr1D_zero[mul(&a, 2)] = mul(&a, 2);' + //
+ 'return arr1D_zero[8];',
+ _result: 4,
+ },
+ {
+ name: 'ToArray2D',
+ _body:
+ 'arr2D_zero[mul(&a, 2)][mul(&a, 2)] = mul(&a, 2);' + //
+ 'return arr2D_zero[8][16];',
+ _result: 4,
+ },
+ {
+ name: 'ToArrayFromArray',
+ _body:
+ 'arr2D_zero[4][8] = 123;' +
+ 'arr1D_zero[mul(&a, 2)] = arr2D_zero[mul(&a, 2)][mul(&a, 2)];' +
+ 'return arr1D_zero[16];',
+ _result: 123,
+ },
+ {
+ name: 'ToArrayIndexedByArrayIndexedBySE',
+ _body:
+ 'var arr1 = arr1D_zero;' +
+ 'var arr2 = arr1D_zero;' +
+ 'arr2[8] = 3;' +
+ 'arr1[arr2[mul(&a, 2)]] = mul(&a, 2);' +
+ 'return arr1[3];',
+ _result: 4,
+ },
+ {
+ name: 'ToVec_BothSE',
+ _body:
+ 'a = 0;' +
+ 'vec4_zero[add(&a, 1)] = add(&a, 1);' + //
+ 'return vec4_zero[2];',
+ _result: 1,
+ },
+ {
+ name: 'ToVec_LeftSE',
+ _body:
+ 'vec4_zero[add(&a, 1)] = a;' + //
+ 'return vec4_zero[3];',
+ _result: 2,
+ },
+ {
+ name: 'ToVec_RightSE',
+ _body:
+ 'vec4_zero[a] = add(&a, 1);' + //
+ 'return vec4_zero[3];',
+ _result: 3,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('type_constructor')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr')
+ .desc('Tests order of evaluation of type constructor expressions.')
+ .paramsSimple([
+ {
+ name: 'Struct',
+ _body:
+ 'let r = S(mul(&a, 2), mul(&a, 2), mul(&a, 2));' + //
+ 'return sub_mul3(r.x, 2, r.y, 3, r.z, 4);',
+ _result: 4 * 2 - 8 * 3 - 16 * 4,
+ },
+ {
+ name: 'Array1D',
+ _body:
+ 'let r = array<i32, 3>(mul(&a, 2), mul(&a, 2), mul(&a, 2));' + //
+ 'return sub_mul3(r[0], 2, r[1], 3, r[2], 4);',
+ _result: 4 * 2 - 8 * 3 - 16 * 4,
+ },
+ {
+ name: 'Array2D',
+ _body:
+ 'let r = array<array<i32, 2>, 2>(array<i32, 2>(mul(&a, 2), mul(&a, 2)), array<i32, 2>(mul(&a, 2), mul(&a, 2)));' +
+ 'return sub_mul4(r[0][0], 2, r[0][1], 3, r[1][0], 4, r[1][1], 5);',
+ _result: 4 * 2 - 8 * 3 - 16 * 4 - 32 * 5,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+g.test('member_accessor')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#struct-access-expr')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#vector-access-expr')
+ .desc('Tests order of evaluation of member accessor expressions.')
+ .paramsSimple([
+ {
+ name: 'Vec',
+ _body: 'return vec3(mul(&a, 2)).x - vec3(mul(&a, 3)).x;',
+ _result: 4 - 12,
+ },
+ {
+ name: 'Struct',
+ _body: 'return make_S(mul(&a, 2)).x - make_S(mul(&a, 3)).x;',
+ _result: 4 - 12,
+ },
+ ])
+ .fn(t => run(t, t.params._body, t.params._result));
+
+function run(t: GPUTest, body: string, result: number) {
+ // WGSL source
+ const source =
+ common_source +
+ `
+fn test_body() -> i32 {
+ ${body}
+}
+
+@group(0) @binding(0) var<storage, read_write> output : i32;
+
+@compute @workgroup_size(1)
+fn main() {
+ output = test_body();
+}
+`;
+
+ // Construct a buffer to hold the results of the expression tests
+ const outputBufferSize = 4; // result : i32
+ const outputBuffer = t.device.createBuffer({
+ size: outputBufferSize,
+ usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE,
+ });
+
+ const module = t.device.createShaderModule({ code: source });
+ const pipeline = t.device.createComputePipeline({
+ layout: 'auto',
+ compute: { module, entryPoint: 'main' },
+ });
+
+ const group = t.device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [{ binding: 0, resource: { buffer: outputBuffer } }],
+ });
+
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginComputePass();
+ pass.setPipeline(pipeline);
+ pass.setBindGroup(0, group);
+ pass.dispatchWorkgroups(1);
+ pass.end();
+
+ t.queue.submit([encoder.finish()]);
+
+ const checkExpectation = (outputData: Uint8Array) => {
+ const output = TypeI32.read(outputData, 0);
+ const got = output.value;
+ const expected = result;
+ if (got !== expected) {
+ return new Error(`returned: ${got}, expected: ${expected}`);
+ }
+ return undefined;
+ };
+
+ t.expectGPUBufferValuesPassCheck(outputBuffer, checkExpectation, {
+ type: Uint8Array,
+ typedLength: outputBufferSize,
+ });
+}