From 6f7b221c0778a267dea0c0eed549040514577e3b Mon Sep 17 00:00:00 2001 From: Natalie Chouinard Date: Thu, 26 Feb 2026 10:26:50 -0500 Subject: [PATCH 1/2] Test compound assignment order of eval Test that the lhs is evaluated before the rhs in a compound assignment statement. --- src/webgpu/listing_meta.json | 1 + .../execution/statement/compound.spec.ts | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 24e1c6c81639..57d60ee2d9e0 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2040,6 +2040,7 @@ "webgpu:shader,execution,stage:basic_compute:*": { "subcaseMS": 1.000 }, "webgpu:shader,execution,stage:basic_render:*": { "subcaseMS": 1.000 }, "webgpu:shader,execution,statement,compound:decl:*": { "subcaseMS": 29.767 }, + "webgpu:shader,execution,statement,compound:eval_order:*": { "subcaseMS": 54.311 }, "webgpu:shader,execution,statement,discard:all:*": { "subcaseMS": 36.094 }, "webgpu:shader,execution,statement,discard:continuing:*": { "subcaseMS": 276.268 }, "webgpu:shader,execution,statement,discard:derivatives:*": { "subcaseMS": 15.287 }, diff --git a/src/webgpu/shader/execution/statement/compound.spec.ts b/src/webgpu/shader/execution/statement/compound.spec.ts index 1e3bc906fd3f..1774ef6512cd 100644 --- a/src/webgpu/shader/execution/statement/compound.spec.ts +++ b/src/webgpu/shader/execution/statement/compound.spec.ts @@ -6,6 +6,7 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { keysOf } from '../../../../common/util/data_tables.js'; import { TypedArrayBufferView } from '../../../../common/util/util.js'; import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../gpu_test.js'; +import { runFlowControlTest } from '../flow_control/harness.js'; export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest); @@ -135,3 +136,33 @@ g.test('decl') kTests[t.params.case].src ); }); + +g.test('eval_order') + .desc('Tests evaluation order of compound assignment, lhs is evaluated before rhs') + .fn(t => { + runFlowControlTest(t, f => ({ + entrypoint: ` +arr[0] = 41; +${f.expect_order(0)} +arr[idx()] += foo(); +${f.expect_order(3)} +if (arr[0] == 42) { + ${f.expect_order(4)} +} else { + ${f.expect_not_reached()} +} +`, + extra: ` +var arr : array; +fn idx() -> u32 { + ${f.expect_order(1)} + return 0; +} +fn foo() -> u32 { + ${f.expect_order(2)} + arr[0] = 10; + return 1; +} +`, + })); + }); From a72a5dd97bb6b46a511b5a492882a2ee76d3af3d Mon Sep 17 00:00:00 2001 From: Natalie Chouinard Date: Thu, 26 Feb 2026 16:56:47 -0500 Subject: [PATCH 2/2] Test swizzle assignment eval order Test that the lhs swizzle's vector pointer is evaluated before the rhs, and that it's value as a swizzle is loaded before rhs. Then check that it is re-loaded after rhs evaluation for use in the store of the whole swizzle assignment vector, because side effects from the rhs on non-swizzled components should persist. --- src/webgpu/listing_meta.json | 2 + .../statement/swizzle_assignment.spec.ts | 74 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 57d60ee2d9e0..47fc70d51e56 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2066,6 +2066,8 @@ "webgpu:shader,execution,statement,increment_decrement:vec4_element_decrement:*": { "subcaseMS": 5.300 }, "webgpu:shader,execution,statement,increment_decrement:vec4_element_increment:*": { "subcaseMS": 6.300 }, "webgpu:shader,execution,statement,phony:executes:*": { "subcaseMS": 129.949 }, + "webgpu:shader,execution,statement,swizzle_assignment:compound_eval_order:*": { "subcaseMS": 16.651 }, + "webgpu:shader,execution,statement,swizzle_assignment:eval_order:*": { "subcaseMS": 17.193 }, "webgpu:shader,execution,statement,swizzle_assignment:swizzle_assignment_vars:*": { "subcaseMS": 1200.970 }, "webgpu:shader,execution,statement,swizzle_assignment:swizzle_compound_assignment:*": { "subcaseMS": 0.091 }, "webgpu:shader,execution,value_init:array,nested:*": { "subcaseMS": 3004.523 }, diff --git a/src/webgpu/shader/execution/statement/swizzle_assignment.spec.ts b/src/webgpu/shader/execution/statement/swizzle_assignment.spec.ts index 421240f99133..b66f6bc2929e 100644 --- a/src/webgpu/shader/execution/statement/swizzle_assignment.spec.ts +++ b/src/webgpu/shader/execution/statement/swizzle_assignment.spec.ts @@ -7,6 +7,7 @@ import { keysOf } from '../../../../common/util/data_tables.js'; import { TypedArrayBufferView } from '../../../../common/util/util.js'; import { Float16Array } from '../../../../external/petamoriken/float16/float16.js'; import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../gpu_test.js'; +import { runFlowControlTest } from '../flow_control/harness.js'; export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest); @@ -420,3 +421,76 @@ fn main() { }`; runSwizzleAssignmentTest(t, elemType, expected, wgsl); }); + +g.test('eval_order') + .desc( + 'Tests that the vec pointer on the lhs of a swizzle assignment is evaluated before the rhs, and the load of the lhs vec happens after rhs.' + ) + .fn(t => { + t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); + runFlowControlTest(t, f => ({ + entrypoint: ` + arr[0] = vec4u(1, 1, 1, 1); + ${f.expect_order(0)} + arr[foo()].xy = bar(); + ${f.expect_order(3)} + if (all(arr[0] == vec4u(4, 5, 3, 8))) { + ${f.expect_order(4)} + } else { + ${f.expect_not_reached()} + } +`, + extra: ` +var arr : array; +fn foo() -> u32 { + ${f.expect_order(1)} + arr[0].x = 6; // overwritten by swizzle + arr[0].z = 7; // overwritten by bar() + arr[0].w = 8; // persists + return 0; +} +fn bar() -> vec2u { + ${f.expect_order(2)} + arr[0].z = 3; // persists + return vec2u(4, 5); // will set x,y +} +`, + })); + }); + +g.test('compound_eval_order') + .desc( + 'Tests that the lhs of a swizzle compound assignment is evaluated before the rhs, and another load of the lhs vec happens after rhs evaluation, without re-evaluating the pointer to the lhs vec.' + ) + .fn(t => { + t.skipIfLanguageFeatureNotSupported('swizzle_assignment'); + runFlowControlTest(t, f => ({ + entrypoint: ` + arr[0] = vec4u(1, 1, 1, 1); + ${f.expect_order(0)} + arr[foo()].xy += bar(); + ${f.expect_order(3)} + if (all(arr[0] == vec4u(10, 6, 3, 8))) { + ${f.expect_order(4)} + } else { + ${f.expect_not_reached()} + } +`, + extra: ` +var arr : array; +fn foo() -> u32 { + ${f.expect_order(1)} + arr[0].x = 6; // modifies x before add + arr[0].z = 7; // overwritten by bar() + arr[0].w = 8; // persists + return 0; +} +fn bar() -> vec2u { + ${f.expect_order(2)} + arr[0].x = 2; // no visible effect + arr[0].z = 3; // persists + return vec2u(4, 5); // will add to x,y +} +`, + })); + });