Skip to content

Commit 0e32299

Browse files
committed
shader/execution: Add 'preventValueOptimizations' case parameterization
This controls the behaviour of `f.value()`. When enabled, the value is placed into a storage buffer and loaded, preventing the compiler from seeing the constant value. When disabled, the value is simply placed into the shader as a literal. This was previously enabled by default.
1 parent ec5ad7d commit 0e32299

File tree

8 files changed

+73
-21
lines changed

8 files changed

+73
-21
lines changed

src/webgpu/shader/execution/flow_control/call.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('call_basic')
1313
.desc('Test that flow control enters a called function')
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(t, f => ({
1617
entrypoint: `
@@ -27,6 +28,7 @@ fn f() {
2728

2829
g.test('call_nested')
2930
.desc('Test that flow control enters a nested function calls')
31+
.params(u => u.combine('preventValueOptimizations', [true, false]))
3032
.fn(t => {
3133
runFlowControlTest(t, f => ({
3234
entrypoint: `
@@ -53,6 +55,7 @@ fn c() {
5355

5456
g.test('call_repeated')
5557
.desc('Test that flow control enters a nested function calls')
58+
.params(u => u.combine('preventValueOptimizations', [true, false]))
5659
.fn(t => {
5760
runFlowControlTest(t, f => ({
5861
entrypoint: `

src/webgpu/shader/execution/flow_control/for.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('for_basic')
1313
.desc('Test that flow control executes a for-loop body the correct number of times')
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(
1617
t,
@@ -27,6 +28,7 @@ g.test('for_basic')
2728

2829
g.test('for_break')
2930
.desc('Test that flow control exits a for-loop when reaching a break statement')
31+
.params(u => u.combine('preventValueOptimizations', [true, false]))
3032
.fn(t => {
3133
runFlowControlTest(
3234
t,
@@ -48,6 +50,7 @@ g.test('for_break')
4850

4951
g.test('for_continue')
5052
.desc('Test flow control for a for-loop continue statement')
53+
.params(u => u.combine('preventValueOptimizations', [true, false]))
5154
.fn(t => {
5255
runFlowControlTest(
5356
t,
@@ -69,6 +72,7 @@ g.test('for_continue')
6972

7073
g.test('for_initalizer')
7174
.desc('Test flow control for a for-loop initializer')
75+
.params(u => u.combine('preventValueOptimizations', [true, false]))
7276
.fn(t => {
7377
runFlowControlTest(t, f => ({
7478
entrypoint: `
@@ -79,7 +83,7 @@ g.test('for_initalizer')
7983
${f.expect_order(5)}
8084
`,
8185
extra: `
82-
fn initializer() -> u32 {
86+
fn initializer() -> i32 {
8387
${f.expect_order(1)}
8488
return ${f.value(0)};
8589
}
@@ -89,6 +93,7 @@ fn initializer() -> u32 {
8993

9094
g.test('for_condition')
9195
.desc('Test flow control for a for-loop condition')
96+
.params(u => u.combine('preventValueOptimizations', [true, false]))
9297
.fn(t => {
9398
runFlowControlTest(t, f => ({
9499
entrypoint: `
@@ -99,7 +104,7 @@ g.test('for_condition')
99104
${f.expect_order(8)}
100105
`,
101106
extra: `
102-
fn condition(i : u32) -> bool {
107+
fn condition(i : i32) -> bool {
103108
${f.expect_order(1, 3, 5, 7)}
104109
return i < ${f.value(3)};
105110
}
@@ -109,6 +114,7 @@ fn condition(i : u32) -> bool {
109114

110115
g.test('for_continuing')
111116
.desc('Test flow control for a for-loop continuing statement')
117+
.params(u => u.combine('preventValueOptimizations', [true, false]))
112118
.fn(t => {
113119
runFlowControlTest(t, f => ({
114120
entrypoint: `
@@ -119,7 +125,7 @@ g.test('for_continuing')
119125
${f.expect_order(7)}
120126
`,
121127
extra: `
122-
fn cont(i : u32) -> u32 {
128+
fn cont(i : i32) -> i32 {
123129
${f.expect_order(2, 4, 6)}
124130
return i + 1;
125131
}

src/webgpu/shader/execution/flow_control/harness.ts

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
import { Colors } from '../../../../common/util/colors.js';
22
import { GPUTest } from '../../../gpu_test.js';
33

4+
/**
5+
* Options for runFlowControlTest()
6+
*/
7+
interface FlowControlTest extends GPUTest {
8+
params: {
9+
/**
10+
* If true, then constant values will be placed into a storage buffer,
11+
* preventing the shader compiler from knowing the value at compile time.
12+
* This can prevent constant folding, loop unrolling, dead-code
13+
* optimizations etc, which would could all affect the tests.
14+
*/
15+
preventValueOptimizations?: boolean;
16+
};
17+
}
18+
419
/**
520
* The builder interface for the runFlowControlTest() callback.
6-
* This interface is indented to be used to inject WGSL logic into the test shader.
21+
* This interface is indented to be used to inject WGSL logic into the test
22+
* shader.
723
* @see runFlowControlTest
824
*/
925
interface FlowControlTestBuilder {
1026
/**
11-
* Emits an expression to load the given value from a storage buffer, preventing the shader
12-
* compiler from knowing the value at compile time. This can prevent constant folding, loop
13-
* unrolling, dead-code optimizations etc, which would could all affect the tests.
27+
* Emits a value into the shader.
28+
* If the FlowControlTest.params.preventValueOptimizations flag is enabled,
29+
* then value() emits an expression to load the given value from a storage
30+
* buffer, preventing the shader compiler from knowing the value at compile
31+
* time. This can prevent constant folding, loop unrolling, dead-code
32+
* optimizations etc, which would could all affect the tests.
1433
*/
1534
value(v: number | boolean): string;
1635

1736
/**
18-
* Emits an expectation that the statement will be executed at the given chronological events.
37+
* Emits an expectation that the statement will be executed at the given
38+
* chronological events.
1939
* @param event one or more chronological events, the first being 0.
2040
*/
2141
expect_order(...event: number[]): string;
@@ -55,12 +75,13 @@ interface FlowControlTestBuilder {
5575
* ```
5676
*
5777
* @param t The test object
58-
* @param builder The shader builder function that takes a FlowControlTestBuilder as the single
59-
* argument, and returns either a WGSL string which is embedded into the WGSL entrypoint function,
60-
* or a structure with entrypoint-scoped WGSL code and extra module-scope WGSL code.
78+
* @param builder The shader builder function that takes a
79+
* FlowControlTestBuilder as the single argument, and returns either a WGSL
80+
* string which is embedded into the WGSL entrypoint function, or a structure
81+
* with entrypoint-scoped WGSL code and extra module-scope WGSL code.
6182
*/
6283
export function runFlowControlTest(
63-
t: GPUTest,
84+
t: FlowControlTest,
6485
build_wgsl: (builder: FlowControlTestBuilder) => string | { entrypoint: string; extra: string }
6586
) {
6687
const inputData = new Array<number>();
@@ -80,12 +101,16 @@ export function runFlowControlTest(
80101

81102
const build_wgsl_result = build_wgsl({
82103
value: v => {
83-
if (typeof v === 'boolean') {
84-
inputData.push(v ? 1 : 0);
85-
return `inputs[${inputData.length - 1}] != 0`;
104+
if (t.params.preventValueOptimizations) {
105+
if (typeof v === 'boolean') {
106+
inputData.push(v ? 1 : 0);
107+
return `inputs[${inputData.length - 1}] != 0`;
108+
}
109+
inputData.push(v);
110+
return `inputs[${inputData.length - 1}]`;
111+
} else {
112+
return `${v}`;
86113
}
87-
inputData.push(v);
88-
return `inputs[${inputData.length - 1}]`;
89114
},
90115
expect_order: (...expected) => {
91116
expectations.push({
@@ -117,7 +142,7 @@ struct Outputs {
117142
count : u32,
118143
data : array<u32>,
119144
};
120-
@group(0) @binding(0) var<storage, read> inputs : array<u32>;
145+
@group(0) @binding(0) var<storage, read> inputs : array<i32>;
121146
@group(0) @binding(1) var<storage, read_write> outputs : Outputs;
122147
123148
fn push_output(value : u32) {
@@ -142,7 +167,8 @@ ${main_wgsl.extra}
142167
},
143168
});
144169

145-
// If there are no inputs, just put a single value in the buffer to keep makeBufferWithContents() happy.
170+
// If there are no inputs, just put a single value in the buffer to keep
171+
// makeBufferWithContents() happy.
146172
if (inputData.length === 0) {
147173
inputData.push(0);
148174
}
@@ -191,8 +217,8 @@ ${main_wgsl.extra}
191217
// returns an Error with the given message and WGSL source
192218
const fail = (err: string) => Error(`${err}\nWGSL:\n${Colors.dim(Colors.blue(wgsl))}`);
193219

194-
// returns a colorized string of the expect_order() call, highlighting the event number that
195-
// caused an error.
220+
// returns a colorized string of the expect_order() call, highlighting
221+
// the event number that caused an error.
196222
const expect_order_err = (expectation: ExpectedEvents, err_idx: number) => {
197223
let out = 'expect_order(';
198224
for (let i = 0; i < expectation.values.length; i++) {

src/webgpu/shader/execution/flow_control/if.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ g.test('if_true')
1313
.desc(
1414
"Test that flow control executes the 'true' block of an if statement and not the 'false' block"
1515
)
16+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1617
.fn(t => {
1718
runFlowControlTest(
1819
t,
@@ -32,6 +33,7 @@ g.test('if_false')
3233
.desc(
3334
"Test that flow control executes the 'false' block of an if statement and not the 'true' block"
3435
)
36+
.params(u => u.combine('preventValueOptimizations', [true, false]))
3537
.fn(t => {
3638
runFlowControlTest(
3739
t,
@@ -49,6 +51,7 @@ g.test('if_false')
4951

5052
g.test('else_if')
5153
.desc("Test that flow control executes the correct 'else if' block of an if statement")
54+
.params(u => u.combine('preventValueOptimizations', [true, false]))
5255
.fn(t => {
5356
runFlowControlTest(
5457
t,

src/webgpu/shader/execution/flow_control/loop.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('loop_break')
1313
.desc('Test that flow control exits a loop when reaching a break statement')
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(
1617
t,
@@ -33,6 +34,7 @@ g.test('loop_break')
3334

3435
g.test('loop_continue')
3536
.desc('Test flow control for a loop continue statement')
37+
.params(u => u.combine('preventValueOptimizations', [true, false]))
3638
.fn(t => {
3739
runFlowControlTest(
3840
t,
@@ -60,6 +62,7 @@ g.test('loop_continue')
6062

6163
g.test('loop_continuing_basic')
6264
.desc('Test basic flow control for a loop continuing block')
65+
.params(u => u.combine('preventValueOptimizations', [true, false]))
6366
.fn(t => {
6467
runFlowControlTest(
6568
t,

src/webgpu/shader/execution/flow_control/return.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('return')
1313
.desc("Test that flow control does not execute after a 'return' statement")
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(
1617
t,
@@ -24,6 +25,7 @@ g.test('return')
2425

2526
g.test('return_conditional_true')
2627
.desc("Test that flow control does not execute after a 'return' statement in a if (true) block")
28+
.params(u => u.combine('preventValueOptimizations', [true, false]))
2729
.fn(t => {
2830
runFlowControlTest(
2931
t,
@@ -39,6 +41,7 @@ g.test('return_conditional_true')
3941

4042
g.test('return_conditional_false')
4143
.desc("Test that flow control does not execute after a 'return' statement in a if (false) block")
44+
.params(u => u.combine('preventValueOptimizations', [true, false]))
4245
.fn(t => {
4346
runFlowControlTest(
4447
t,

src/webgpu/shader/execution/flow_control/switch.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('switch')
1313
.desc('Test that flow control executes the correct switch case block')
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(
1617
t,
@@ -43,6 +44,7 @@ g.test('switch_multiple_case')
4344
.desc(
4445
'Test that flow control executes the correct switch case block with multiple cases per block'
4546
)
47+
.params(u => u.combine('preventValueOptimizations', [true, false]))
4648
.fn(t => {
4749
runFlowControlTest(
4850
t,
@@ -71,6 +73,7 @@ g.test('switch_multiple_case_default')
7173
.desc(
7274
'Test that flow control executes the correct switch case block with multiple cases per block (combined with default)'
7375
)
76+
.params(u => u.combine('preventValueOptimizations', [true, false]))
7477
.fn(t => {
7578
runFlowControlTest(
7679
t,
@@ -92,6 +95,7 @@ g.test('switch_multiple_case_default')
9295
});
9396
g.test('switch_default')
9497
.desc('Test that flow control executes the switch default block')
98+
.params(u => u.combine('preventValueOptimizations', [true, false]))
9599
.fn(t => {
96100
runFlowControlTest(
97101
t,
@@ -122,6 +126,7 @@ ${f.expect_order(2)}
122126

123127
g.test('switch_default_only')
124128
.desc('Test that flow control executes the switch default block, which is the only case')
129+
.params(u => u.combine('preventValueOptimizations', [true, false]))
125130
.fn(t => {
126131
runFlowControlTest(
127132
t,

src/webgpu/shader/execution/flow_control/while.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const g = makeTestGroup(GPUTest);
1111

1212
g.test('while_basic')
1313
.desc('Test that flow control executes a while-loop body the correct number of times')
14+
.params(u => u.combine('preventValueOptimizations', [true, false]))
1415
.fn(t => {
1516
runFlowControlTest(
1617
t,
@@ -29,6 +30,7 @@ g.test('while_basic')
2930

3031
g.test('while_break')
3132
.desc('Test that flow control exits a while-loop when reaching a break statement')
33+
.params(u => u.combine('preventValueOptimizations', [true, false]))
3234
.fn(t => {
3335
runFlowControlTest(
3436
t,
@@ -52,6 +54,7 @@ g.test('while_break')
5254

5355
g.test('while_continue')
5456
.desc('Test flow control for a while-loop continue statement')
57+
.params(u => u.combine('preventValueOptimizations', [true, false]))
5558
.fn(t => {
5659
runFlowControlTest(
5760
t,

0 commit comments

Comments
 (0)