From 249807acccbc1a2dafc5e99670c44919ca0c1903 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Fri, 15 Mar 2024 13:37:48 -0400 Subject: [PATCH] Add validation tests for statement behavior analysis (#3506) --- src/webgpu/listing_meta.json | 4 + .../parse/statement_behavior.spec.ts | 143 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/webgpu/shader/validation/parse/statement_behavior.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 2df3f2a36222..b2bf6582c590 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2119,6 +2119,10 @@ "webgpu:shader,validation,parse,source:empty:*": { "subcaseMS": 1.101 }, "webgpu:shader,validation,parse,source:invalid_source:*": { "subcaseMS": 1.100 }, "webgpu:shader,validation,parse,source:valid_source:*": { "subcaseMS": 1.101 }, + "webgpu:shader,validation,parse,statement_behavior:invalid_functions:*": { "subcaseMS": 1.107 }, + "webgpu:shader,validation,parse,statement_behavior:invalid_statements:*": { "subcaseMS": 80.699 }, + "webgpu:shader,validation,parse,statement_behavior:valid_functions:*": { "subcaseMS": 0.978 }, + "webgpu:shader,validation,parse,statement_behavior:valid_statements:*": { "subcaseMS": 15.781 }, "webgpu:shader,validation,parse,unary_ops:all:*": { "subcaseMS": 1.000 }, "webgpu:shader,validation,parse,var_and_let:initializer_type:*": { "subcaseMS": 0.900 }, "webgpu:shader,validation,parse,var_and_let:var_access_mode_bad_other_template_contents:*": { "subcaseMS": 4.071 }, diff --git a/src/webgpu/shader/validation/parse/statement_behavior.spec.ts b/src/webgpu/shader/validation/parse/statement_behavior.spec.ts new file mode 100644 index 000000000000..1acfd01d8b90 --- /dev/null +++ b/src/webgpu/shader/validation/parse/statement_behavior.spec.ts @@ -0,0 +1,143 @@ +export const description = ` +Test statement behavior analysis. + +Functions must have a behavior of {Return}, {Next}, or {Return, Next}. +Functions with a return type must have a behavior of {Return}. + +Each statement in the function must be valid according to the table. +`; + +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); + +const kInvalidStatements = { + break: `break`, + break_if: `break if true`, + continue: `continue`, + loop1: `loop { }`, + loop2: `loop { continuing { } }`, + loop3: `loop { continue; continuing { } }`, + loop4: `loop { continuing { break; } }`, + loop5: `loop { continuing { continue; } }`, + loop6: `loop { continuing { return; } }`, + loop7: `loop { continue; break; }`, + loop8: `loop { continuing { break if true; return; } }`, + for1: `for (;;) { }`, + for2: `for (var i = 0; ; i++) { }`, + for3: `for (;; break) { }`, + for4: `for (;; continue ) { }`, + for5: `for (;; return ) { }`, + for6: `for (;;) { continue; break; }`, + // while loops always have break in their behaviors. + switch1: `switch (1) { case 1 { } }`, + sequence1: `return; loop { }`, + compound1: `{ loop { } }`, +}; + +g.test('invalid_statements') + .desc('Test statements with invalid behaviors') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#behaviors-rules') + .params(u => u.combine('body', keysOf(kInvalidStatements))) + .fn(t => { + const body = kInvalidStatements[t.params.body]; + const code = `fn foo() { + ${body}; + }`; + t.expectCompileResult(false, code); + }); + +const kValidStatements = { + empty: ``, + const_assert: `const_assert true`, + let: `let x = 1`, + var1: `var x = 1`, + var2: `var x : i32`, + assign: `v = 1`, + phony_assign: `_ = 1`, + compound_assign: `v += 1`, + return: `return`, + discard: `discard`, + function_call1: `bar()`, + function_call2: `workgroupBarrier()`, + + if1: `if true { } else { }`, + if2: `if true { }`, + + break1: `loop { break; }`, + break2: `loop { if false { break; } }`, + break_if: `loop { continuing { break if false; } }`, + + continue1: `loop { continue; continuing { break if true; } }`, + + loop1: `loop { break; }`, + loop2: `loop { break; continuing { } }`, + loop3: `loop { continue; continuing { break if true; } }`, + loop4: `loop { break; continue; }`, + + for1: `for (; true; ) { }`, + for2: `for (;;) { break; }`, + for3: `for (;true;) { continue; }`, + + while1: `while true { }`, + while2: `while true { continue; }`, + while3: `while true { continue; break; }`, + + switch1: `switch 1 { default { } }`, + swtich2: `switch 1 { case 1 { } default { } }`, + switch3: `switch 1 { default { break; } }`, + switch4: `switch 1 { default { } case 1 { break; } }`, + + sequence1: `return; let x = 1`, + sequence2: `if true { } let x = 1`, + sequence3: `switch 1 { default { break; return; } }`, + + compound1: `{ }`, + compound2: `{ loop { break; } if true { return; } }`, +}; + +g.test('valid_statements') + .desc('Test statements with valid behaviors') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#behaviors-rules') + .params(u => u.combine('body', keysOf(kValidStatements))) + .fn(t => { + const body = kValidStatements[t.params.body]; + const code = ` + var v : i32; + fn bar() { } + fn foo() { + ${body}; + }`; + t.expectCompileResult(true, code); + }); + +const kInvalidFunctions = { + next_for_type: `fn foo() -> bool { }`, + next_return_for_type: `fn foo() -> bool { if true { return true; } }`, +}; + +g.test('invalid_functions') + .desc('Test functions with invalid behaviors') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#behaviors-rules') + .params(u => u.combine('function', keysOf(kInvalidFunctions))) + .fn(t => { + const func = kInvalidFunctions[t.params.function]; + t.expectCompileResult(false, func); + }); + +const kValidFunctions = { + empty: `fn foo() { }`, + next_return: `fn foo() { if true { return; } }`, + no_final_return: `fn foo() -> bool { if true { return true; } else { return false; } }`, +}; + +g.test('valid_functions') + .desc('Test functions with valid behaviors') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#behaviors-rules') + .params(u => u.combine('function', keysOf(kValidFunctions))) + .fn(t => { + const func = kValidFunctions[t.params.function]; + t.expectCompileResult(true, func); + });