diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts')
-rw-r--r-- | dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts new file mode 100644 index 0000000000..15f7ad4a97 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/validation/expression/precedence.spec.ts @@ -0,0 +1,188 @@ +export const description = ` +Validation tests for operator precedence. +`; + +import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { keysOf } from '../../../../common/util/data_tables.js'; +import { ShaderValidationTest } from '../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +// Bit set for the binary operator groups. +const kMultiplicative = 1 << 0; +const kAdditive = 1 << 1; +const kShift = 1 << 2; +const kRelational = 1 << 3; +const kBinaryAnd = 1 << 4; +const kBinaryXor = 1 << 5; +const kBinaryOr = 1 << 6; +const kLogical = 1 << 7; + +// Set of other operators that each operator can precede without any parentheses. +const kCanPrecedeWithoutParens: Record<number, number> = {}; +kCanPrecedeWithoutParens[kMultiplicative] = kMultiplicative | kAdditive | kRelational; +kCanPrecedeWithoutParens[kAdditive] = kMultiplicative | kAdditive | kRelational; +kCanPrecedeWithoutParens[kShift] = kRelational | kLogical; +kCanPrecedeWithoutParens[kRelational] = kMultiplicative | kAdditive | kShift | kLogical; +kCanPrecedeWithoutParens[kBinaryAnd] = kBinaryAnd; +kCanPrecedeWithoutParens[kBinaryXor] = kBinaryXor; +kCanPrecedeWithoutParens[kBinaryOr] = kBinaryOr; +kCanPrecedeWithoutParens[kLogical] = kRelational; + +// The list of binary operators. +interface BinaryOperatorInfo { + op: string; + group: number; +} +const kBinaryOperators: Record<string, BinaryOperatorInfo> = { + mul: { op: '*', group: kMultiplicative }, + div: { op: '/', group: kMultiplicative }, + mod: { op: '%', group: kMultiplicative }, + + add: { op: '+', group: kAdditive }, + sub: { op: '-', group: kAdditive }, + + shl: { op: '<<', group: kShift }, + shr: { op: '>>', group: kShift }, + + lt: { op: '<', group: kRelational }, + gt: { op: '>', group: kRelational }, + le: { op: '<=', group: kRelational }, + ge: { op: '>=', group: kRelational }, + eq: { op: '==', group: kRelational }, + ne: { op: '!=', group: kRelational }, + + bin_and: { op: '&', group: kBinaryAnd }, + bin_xor: { op: '^', group: kBinaryXor }, + bin_or: { op: '|', group: kBinaryOr }, + + log_and: { op: '&&', group: kLogical }, + log_or: { op: '||', group: kLogical }, +}; + +g.test('binary_requires_parentheses') + .desc( + ` + Validates that certain binary operators require parentheses to bind correctly. + ` + ) + .params(u => + u + .combine('op1', keysOf(kBinaryOperators)) + .combine('op2', keysOf(kBinaryOperators)) + .filter(p => { + // Skip expressions that would parse as template lists. + if (p.op1 === 'lt' && ['gt', 'ge', 'shr'].includes(p.op2)) { + return false; + } + // Only combine logical operators with relational operators. + if (kBinaryOperators[p.op1].group === kLogical) { + return kBinaryOperators[p.op2].group === kRelational; + } + if (kBinaryOperators[p.op2].group === kLogical) { + return kBinaryOperators[p.op1].group === kRelational; + } + return true; + }) + ) + .fn(t => { + const op1 = kBinaryOperators[t.params.op1]; + const op2 = kBinaryOperators[t.params.op2]; + const code = ` +var<private> a : ${op1.group === kLogical ? 'bool' : 'u32'}; +var<private> b : u32; +var<private> c : ${op2.group === kLogical ? 'bool' : 'u32'}; +fn foo() { + let foo = a ${op1.op} b ${op2.op} c; +} +`; + + const valid = (kCanPrecedeWithoutParens[op1.group] & op2.group) !== 0; + t.expectCompileResult(valid, code); + }); + +g.test('mixed_logical_requires_parentheses') + .desc( + ` + Validates that mixed logical operators require parentheses to bind correctly. + ` + ) + .params(u => + u + .combine('op1', keysOf(kBinaryOperators)) + .combine('op2', keysOf(kBinaryOperators)) + .combine('parens', ['none', 'left', 'right']) + .filter(p => { + const group1 = kBinaryOperators[p.op1].group; + const group2 = kBinaryOperators[p.op2].group; + return group1 === kLogical && group2 === kLogical; + }) + ) + .fn(t => { + const op1 = kBinaryOperators[t.params.op1]; + const op2 = kBinaryOperators[t.params.op2]; + let expr = `a ${op1.op} b ${op2.op} c;`; + if (t.params.parens === 'left') { + expr = `(a ${op1.op} b) ${op2.op} c;`; + } else if (t.params.parens === 'right') { + expr = `a ${op1.op} (b ${op2.op} c);`; + } + const code = ` +var<private> a : bool; +var<private> b : bool; +var<private> c : bool; +fn foo() { + let bar = ${expr}; +} +`; + const valid = t.params.parens !== 'none' || t.params.op1 === t.params.op2; + t.expectCompileResult(valid, code); + }); + +// The list of miscellaneous other test cases. +interface Expression { + expr: string; + result: boolean; +} +const kExpressions: Record<string, Expression> = { + neg_member: { expr: '- str . a', result: true }, + comp_member: { expr: '~ str . a', result: true }, + addr_member: { expr: '& str . a', result: true }, + log_and_member: { expr: 'false && str . b', result: true }, + log_or_member: { expr: 'false || str . b', result: true }, + and_addr: { expr: ' v & &str .a', result: false }, + and_addr_paren: { expr: 'v & (&str).a', result: true }, + deref_member: { expr: ' * ptr_str . a', result: false }, + deref_member_paren: { expr: '(* ptr_str) . a', result: true }, + deref_idx: { expr: ' * ptr_vec [0]', result: false }, + deref_idx_paren: { expr: '(* ptr_vec) [1]', result: true }, +}; + +g.test('other') + .desc( + ` + Test that other operator precedence rules are correctly implemented. + ` + ) + .params(u => u.combine('expr', keysOf(kExpressions))) + .fn(t => { + const expr = kExpressions[t.params.expr]; + const wgsl = ` + struct S { + a: i32, + b: bool, + } + + fn main() { + var v = 42; + var vec = vec4(); + var str = S(42, false); + let ptr_vec = &vec; + let ptr_str = &str; + + let foo = ${expr.expr}; + } + `; + + t.expectCompileResult(expr.result, wgsl); + }); |