[spirv-reader] use sem::Load to translate atomics
This comprehensively captures all of the expressions that may invoke
the load-rule and therefore need to have an `atomicLoad()`.
Fixed: tint:2010
Change-Id: Ie51370b4febcb6489bc5c406206e054f22f86968
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/152223
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/reader/ast_lower/atomics.cc b/src/tint/lang/spirv/reader/ast_lower/atomics.cc
index 0d3b3fc..bd2aee4 100644
--- a/src/tint/lang/spirv/reader/ast_lower/atomics.cc
+++ b/src/tint/lang/spirv/reader/ast_lower/atomics.cc
@@ -28,6 +28,7 @@
#include "src/tint/lang/wgsl/sem/block_statement.h"
#include "src/tint/lang/wgsl/sem/function.h"
#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
+#include "src/tint/lang/wgsl/sem/load.h"
#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
#include "src/tint/lang/wgsl/sem/statement.h"
#include "src/tint/utils/containers/map.h"
@@ -247,48 +248,27 @@
return false;
};
- // Look for loads and stores via assignments and decls of atomic variables we've collected
- // so far, and replace them with atomicLoad and atomicStore.
- for (auto* atomic_var : atomic_variables) {
- for (auto* vu : atomic_var->Users()) {
- Switch(
- vu->Stmt()->Declaration(),
- [&](const ast::AssignmentStatement* assign) {
- auto* sem_lhs = ctx.src->Sem().GetVal(assign->lhs);
- if (is_ref_to_atomic_var(sem_lhs)) {
- ctx.Replace(assign, [=] {
- auto* lhs = ctx.CloneWithoutTransform(assign->lhs);
- auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
- auto* call = b.Call(core::str(core::Function::kAtomicStore),
- b.AddressOf(lhs), rhs);
- return b.CallStmt(call);
- });
- return;
- }
-
- auto sem_rhs = ctx.src->Sem().GetVal(assign->rhs);
- if (is_ref_to_atomic_var(sem_rhs->UnwrapLoad())) {
- ctx.Replace(assign->rhs, [=] {
- auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
- return b.Call(core::str(core::Function::kAtomicLoad),
- b.AddressOf(rhs));
- });
- return;
- }
- },
- [&](const ast::VariableDeclStatement* decl) {
- auto* var = decl->variable;
- if (auto* sem_init = ctx.src->Sem().GetVal(var->initializer)) {
- if (is_ref_to_atomic_var(sem_init->UnwrapLoad())) {
- ctx.Replace(var->initializer, [=] {
- auto* rhs = ctx.CloneWithoutTransform(var->initializer);
- return b.Call(core::str(core::Function::kAtomicLoad),
- b.AddressOf(rhs));
- });
- return;
- }
- }
+ // Look for loads and stores of atomic variables we've collected so far, and replace them
+ // with atomicLoad and atomicStore.
+ for (auto* node : ctx.src->ASTNodes().Objects()) {
+ if (auto* load = ctx.src->Sem().Get<sem::Load>(node)) {
+ if (is_ref_to_atomic_var(load->Reference())) {
+ ctx.Replace(load->Reference()->Declaration(), [=] {
+ auto* expr = ctx.CloneWithoutTransform(load->Reference()->Declaration());
+ return b.Call(core::str(core::Function::kAtomicLoad), b.AddressOf(expr));
});
+ }
+ } else if (auto* assign = node->As<ast::AssignmentStatement>()) {
+ auto* sem_lhs = ctx.src->Sem().GetVal(assign->lhs);
+ if (is_ref_to_atomic_var(sem_lhs)) {
+ ctx.Replace(assign, [=] {
+ auto* lhs = ctx.CloneWithoutTransform(assign->lhs);
+ auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
+ auto* call =
+ b.Call(core::str(core::Function::kAtomicStore), b.AddressOf(lhs), rhs);
+ return b.CallStmt(call);
+ });
+ }
}
}
}
diff --git a/src/tint/lang/spirv/reader/ast_lower/atomics_test.cc b/src/tint/lang/spirv/reader/ast_lower/atomics_test.cc
index 9bef265..8898c52 100644
--- a/src/tint/lang/spirv/reader/ast_lower/atomics_test.cc
+++ b/src/tint/lang/spirv/reader/ast_lower/atomics_test.cc
@@ -1376,5 +1376,76 @@
EXPECT_EQ(expect, str(got));
}
+
+TEST_F(AtomicsTest, ReplaceBitcastArgument_Scaler) {
+ auto* src = R"(
+var<workgroup> wg : u32;
+
+fn f() {
+ stub_atomicAdd_u32(wg, 1u);
+
+ wg = 0u;
+ var b : f32;
+ b = bitcast<f32>(wg);
+}
+)";
+
+ auto* expect = R"(
+var<workgroup> wg : atomic<u32>;
+
+fn f() {
+ atomicAdd(&(wg), 1u);
+ atomicStore(&(wg), 0u);
+ var b : f32;
+ b = bitcast<f32>(atomicLoad(&(wg)));
+}
+)";
+
+ auto got = Run(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(AtomicsTest, ReplaceBitcastArgument_Struct) {
+ auto* src = R"(
+struct S {
+ a : u32,
+}
+
+var<workgroup> wg : S;
+
+fn f() {
+ stub_atomicAdd_u32(wg.a, 1u);
+
+ wg.a = 0u;
+ var b : f32;
+ b = bitcast<f32>(wg.a);
+}
+)";
+
+ auto* expect = R"(
+struct S_atomic {
+ a : atomic<u32>,
+}
+
+struct S {
+ a : u32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f() {
+ atomicAdd(&(wg.a), 1u);
+ atomicStore(&(wg.a), 0u);
+ var b : f32;
+ b = bitcast<f32>(atomicLoad(&(wg.a)));
+}
+)";
+
+ auto got = Run(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace tint::spirv::reader
diff --git a/test/tint/bug/tint/2010.spvasm b/test/tint/bug/tint/2010.spvasm
new file mode 100644
index 0000000..ca245f0
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm
@@ -0,0 +1,191 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Google Shaderc over Glslang; 10
+; Bound: 134
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main" %gl_LocalInvocationID
+ OpExecutionMode %2 LocalSize 32 1 1
+ OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+ OpMemberDecorate %_struct_4 0 Offset 0
+ OpMemberDecorate %_struct_5 0 Offset 0
+ OpDecorate %_struct_5 Block
+ OpDecorate %6 DescriptorSet 0
+ OpDecorate %6 Binding 1
+ OpDecorate %_runtimearr_v4float ArrayStride 16
+ OpMemberDecorate %_struct_8 0 NonWritable
+ OpMemberDecorate %_struct_8 0 Offset 0
+ OpDecorate %_struct_8 BufferBlock
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 2
+ OpDecorate %_runtimearr_v4float_0 ArrayStride 16
+ OpMemberDecorate %_struct_11 0 NonReadable
+ OpMemberDecorate %_struct_11 0 Offset 0
+ OpDecorate %_struct_11 BufferBlock
+ OpDecorate %12 DescriptorSet 0
+ OpDecorate %12 Binding 3
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %15 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v2float = OpTypeVector %float 2
+ %uint = OpTypeInt 32 0
+ %int = OpTypeInt 32 1
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %_struct_24 = OpTypeStruct %v2float %uint
+ %uint_4096 = OpConstant %uint 4096
+%_arr__struct_24_uint_4096 = OpTypeArray %_struct_24 %uint_4096
+%_ptr_Workgroup__arr__struct_24_uint_4096 = OpTypePointer Workgroup %_arr__struct_24_uint_4096
+ %28 = OpVariable %_ptr_Workgroup__arr__struct_24_uint_4096 Workgroup
+ %int_0 = OpConstant %int 0
+%_ptr_Workgroup_v2float = OpTypePointer Workgroup %v2float
+ %bool = OpTypeBool
+ %v2uint = OpTypeVector %uint 2
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+ %34 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %35 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %36 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %37 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %uint_32 = OpConstant %uint 32
+ %uint_264 = OpConstant %uint 264
+ %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%_ptr_Input_uint = OpTypePointer Input %uint
+ %_struct_4 = OpTypeStruct %uint
+ %_struct_5 = OpTypeStruct %_struct_4
+%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
+ %6 = OpVariable %_ptr_Uniform__struct_5 Uniform
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_runtimearr_v4float = OpTypeRuntimeArray %v4float
+ %_struct_8 = OpTypeStruct %_runtimearr_v4float
+%_ptr_Uniform__struct_8 = OpTypePointer Uniform %_struct_8
+ %9 = OpVariable %_ptr_Uniform__struct_8 Uniform
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+ %float_0_5 = OpConstant %float 0.5
+%_ptr_Workgroup__struct_24 = OpTypePointer Workgroup %_struct_24
+%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float
+ %_struct_11 = OpTypeStruct %_runtimearr_v4float_0
+%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11
+ %12 = OpVariable %_ptr_Uniform__struct_11 Uniform
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+ %2 = OpFunction %void None %15
+ %50 = OpLabel
+ %51 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0
+ %52 = OpLoad %uint %51
+ OpBranch %53
+ %53 = OpLabel
+ %54 = OpPhi %uint %uint_0 %50 %55 %56
+ %57 = OpAccessChain %_ptr_Uniform_uint %6 %int_0 %int_0
+ %58 = OpLoad %uint %57
+ %59 = OpULessThan %bool %54 %58
+ OpLoopMerge %60 %56 None
+ OpBranchConditional %59 %61 %60
+ %61 = OpLabel
+ %62 = OpIAdd %uint %54 %52
+ %63 = OpUGreaterThanEqual %bool %62 %58
+ OpSelectionMerge %64 None
+ OpBranchConditional %63 %65 %64
+ %65 = OpLabel
+ %66 = OpAccessChain %_ptr_Uniform_v4float %9 %int_0 %62
+ %67 = OpLoad %v4float %66
+ %68 = OpVectorShuffle %v2float %67 %67 0 1
+ %69 = OpVectorShuffle %v2float %67 %67 2 3
+ %70 = OpFAdd %v2float %68 %69
+ %71 = OpVectorTimesScalar %v2float %70 %float_0_5
+ %72 = OpCompositeConstruct %_struct_24 %71 %62
+ %73 = OpAccessChain %_ptr_Workgroup__struct_24 %28 %62
+ OpStore %73 %72
+ OpBranch %64
+ %64 = OpLabel
+ OpBranch %56
+ %56 = OpLabel
+ %55 = OpIAdd %uint %54 %uint_32
+ OpBranch %53
+ %60 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %74 = OpBitcast %int %58
+ %75 = OpAccessChain %_ptr_Workgroup_v2float %28 %int_0 %int_0
+ %76 = OpLoad %v2float %75
+ %77 = OpIEqual %bool %52 %uint_0
+ OpSelectionMerge %78 None
+ OpBranchConditional %77 %79 %78
+ %79 = OpLabel
+ %80 = OpBitcast %v2uint %76
+ %81 = OpCompositeExtract %uint %80 0
+ OpStore %34 %81
+ %82 = OpCompositeExtract %uint %80 1
+ OpStore %35 %82
+ OpStore %36 %81
+ OpStore %37 %82
+ OpBranch %78
+ %78 = OpLabel
+ %83 = OpVectorShuffle %v4float %76 %76 0 1 0 1
+ OpBranch %84
+ %84 = OpLabel
+ %85 = OpPhi %v4float %83 %78 %86 %87
+ %88 = OpPhi %uint %uint_1 %78 %89 %87
+ %90 = OpBitcast %uint %74
+ %91 = OpULessThan %bool %88 %90
+ OpLoopMerge %92 %87 None
+ OpBranchConditional %91 %93 %92
+ %93 = OpLabel
+ %94 = OpIAdd %uint %88 %52
+ %95 = OpUGreaterThanEqual %bool %94 %90
+ OpSelectionMerge %96 None
+ OpBranchConditional %95 %97 %96
+ %97 = OpLabel
+ %98 = OpAccessChain %_ptr_Workgroup_v2float %28 %94 %int_0
+ %99 = OpLoad %v2float %98
+ %100 = OpVectorShuffle %v2float %85 %85 0 1
+ %101 = OpExtInst %v2float %1 FMin %100 %99
+ %102 = OpCompositeExtract %float %101 0
+ %103 = OpCompositeInsert %v4float %102 %85 0
+ %104 = OpCompositeExtract %float %101 1
+ %105 = OpCompositeInsert %v4float %104 %103 1
+ %106 = OpVectorShuffle %v2float %105 %105 2 3
+ %107 = OpExtInst %v2float %1 FMax %106 %99
+ %108 = OpCompositeExtract %float %107 0
+ %109 = OpCompositeInsert %v4float %108 %105 2
+ %110 = OpCompositeExtract %float %107 1
+ %111 = OpCompositeInsert %v4float %110 %109 3
+ OpBranch %96
+ %96 = OpLabel
+ %86 = OpPhi %v4float %85 %93 %111 %97
+ OpBranch %87
+ %87 = OpLabel
+ %89 = OpIAdd %uint %88 %uint_32
+ OpBranch %84
+ %92 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %112 = OpCompositeExtract %float %85 0
+ %113 = OpBitcast %uint %112
+ %114 = OpAtomicUMin %uint %34 %uint_1 %uint_0 %113
+ %115 = OpCompositeExtract %float %85 1
+ %116 = OpBitcast %uint %115
+ %117 = OpAtomicUMin %uint %35 %uint_1 %uint_0 %116
+ %118 = OpCompositeExtract %float %85 2
+ %119 = OpBitcast %uint %118
+ %120 = OpAtomicUMax %uint %36 %uint_1 %uint_0 %119
+ %121 = OpCompositeExtract %float %85 3
+ %122 = OpBitcast %uint %121
+ %123 = OpAtomicUMax %uint %37 %uint_1 %uint_0 %122
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %124 = OpLoad %uint %34
+ %125 = OpBitcast %float %124
+ %126 = OpLoad %uint %35
+ %127 = OpBitcast %float %126
+ %128 = OpLoad %uint %36
+ %129 = OpBitcast %float %128
+ %130 = OpLoad %uint %37
+ %131 = OpBitcast %float %130
+ %132 = OpCompositeConstruct %v4float %125 %127 %129 %131
+ %133 = OpAccessChain %_ptr_Uniform_v4float %12 %int_0 %int_0
+ OpStore %133 %132
+ OpReturn
+ OpFunctionEnd
diff --git a/test/tint/bug/tint/2010.spvasm.expected.fxc.hlsl b/test/tint/bug/tint/2010.spvasm.expected.fxc.hlsl
new file mode 100644
index 0000000..1634493
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm.expected.fxc.hlsl
@@ -0,0 +1,159 @@
+SKIP: FAILED
+
+struct S {
+ float2 field0;
+ uint field1;
+};
+
+groupshared S x_28[4096];
+groupshared uint x_34;
+groupshared uint x_35;
+groupshared uint x_36;
+groupshared uint x_37;
+static uint3 x_3 = uint3(0u, 0u, 0u);
+cbuffer cbuffer_x_6 : register(b1) {
+ uint4 x_6[1];
+};
+ByteAddressBuffer x_9 : register(t2);
+RWByteAddressBuffer x_12 : register(u3);
+
+void main_1() {
+ uint x_54 = 0u;
+ uint x_58 = 0u;
+ float4 x_85 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ uint x_88 = 0u;
+ const uint x_52 = x_3.x;
+ x_54 = 0u;
+ while (true) {
+ uint x_55 = 0u;
+ x_58 = x_6[0].x;
+ if ((x_54 < x_58)) {
+ } else {
+ break;
+ }
+ const uint x_62 = (x_54 + x_52);
+ if ((x_62 >= x_58)) {
+ const float4 x_67 = asfloat(x_9.Load4((16u * x_62)));
+ const S tint_symbol_2 = {((x_67.xy + x_67.zw) * 0.5f), x_62};
+ x_28[x_62] = tint_symbol_2;
+ }
+ {
+ x_55 = (x_54 + 32u);
+ x_54 = x_55;
+ }
+ }
+ GroupMemoryBarrierWithGroupSync();
+ const int x_74 = asint(x_58);
+ const float2 x_76 = x_28[0].field0;
+ if ((x_52 == 0u)) {
+ const uint2 x_80 = asuint(x_76);
+ const uint x_81 = x_80.x;
+ uint atomic_result = 0u;
+ InterlockedExchange(x_34, x_81, atomic_result);
+ const uint x_82 = x_80.y;
+ uint atomic_result_1 = 0u;
+ InterlockedExchange(x_35, x_82, atomic_result_1);
+ uint atomic_result_2 = 0u;
+ InterlockedExchange(x_36, x_81, atomic_result_2);
+ uint atomic_result_3 = 0u;
+ InterlockedExchange(x_37, x_82, atomic_result_3);
+ }
+ x_85 = x_76.xyxy;
+ x_88 = 1u;
+ while (true) {
+ float4 x_111 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ float4 x_86 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ uint x_89 = 0u;
+ const uint x_90 = asuint(x_74);
+ if ((x_88 < x_90)) {
+ } else {
+ break;
+ }
+ const uint x_94 = (x_88 + x_52);
+ x_86 = x_85;
+ if ((x_94 >= x_90)) {
+ const float2 x_99 = x_28[x_94].field0;
+ const float2 x_101 = min(x_85.xy, x_99);
+ float4 x_103_1 = x_85;
+ x_103_1.x = x_101.x;
+ const float4 x_103 = x_103_1;
+ float4 x_105_1 = x_103;
+ x_105_1.y = x_101.y;
+ const float4 x_105 = x_105_1;
+ const float2 x_107 = max(x_105_1.zw, x_99);
+ float4 x_109_1 = x_105;
+ x_109_1.z = x_107.x;
+ x_111 = x_109_1;
+ x_111.w = x_107.y;
+ x_86 = x_111;
+ }
+ {
+ x_89 = (x_88 + 32u);
+ x_85 = x_86;
+ x_88 = x_89;
+ }
+ }
+ GroupMemoryBarrierWithGroupSync();
+ uint atomic_result_4 = 0u;
+ InterlockedMin(x_34, asuint(x_85.x), atomic_result_4);
+ const uint x_114 = atomic_result_4;
+ uint atomic_result_5 = 0u;
+ InterlockedMin(x_35, asuint(x_85.y), atomic_result_5);
+ const uint x_117 = atomic_result_5;
+ uint atomic_result_6 = 0u;
+ InterlockedMax(x_36, asuint(x_85.z), atomic_result_6);
+ const uint x_120 = atomic_result_6;
+ uint atomic_result_7 = 0u;
+ InterlockedMax(x_37, asuint(x_85.w), atomic_result_7);
+ const uint x_123 = atomic_result_7;
+ GroupMemoryBarrierWithGroupSync();
+ uint atomic_result_8 = 0u;
+ InterlockedOr(x_34, 0, atomic_result_8);
+ uint atomic_result_9 = 0u;
+ InterlockedOr(x_35, 0, atomic_result_9);
+ uint atomic_result_10 = 0u;
+ InterlockedOr(x_36, 0, atomic_result_10);
+ uint atomic_result_11 = 0u;
+ InterlockedOr(x_37, 0, atomic_result_11);
+ x_12.Store4(0u, asuint(float4(asfloat(atomic_result_8), asfloat(atomic_result_9), asfloat(atomic_result_10), asfloat(atomic_result_11))));
+ return;
+}
+
+struct tint_symbol_1 {
+ uint3 x_3_param : SV_GroupThreadID;
+ uint local_invocation_index : SV_GroupIndex;
+};
+
+void main_inner(uint3 x_3_param, uint local_invocation_index) {
+ if ((local_invocation_index < 1u)) {
+ uint atomic_result_12 = 0u;
+ InterlockedExchange(x_34, 0u, atomic_result_12);
+ uint atomic_result_13 = 0u;
+ InterlockedExchange(x_35, 0u, atomic_result_13);
+ uint atomic_result_14 = 0u;
+ InterlockedExchange(x_36, 0u, atomic_result_14);
+ uint atomic_result_15 = 0u;
+ InterlockedExchange(x_37, 0u, atomic_result_15);
+ }
+ {
+ for(uint idx = local_invocation_index; (idx < 4096u); idx = (idx + 32u)) {
+ const uint i = idx;
+ const S tint_symbol_3 = (S)0;
+ x_28[i] = tint_symbol_3;
+ }
+ }
+ GroupMemoryBarrierWithGroupSync();
+ x_3 = x_3_param;
+ main_1();
+}
+
+[numthreads(32, 1, 1)]
+void main(tint_symbol_1 tint_symbol) {
+ main_inner(tint_symbol.x_3_param, tint_symbol.local_invocation_index);
+ return;
+}
+
+FXC validation failure:
+ T:\tmp\dawn-temp\dawn-src\test\tint\Shader@0x00000239714DEA80(116,3-139): error X3694: race condition writing to shared resource detected, consider making this write conditional.
+ T:\tmp\dawn-temp\dawn-src\test\tint\Shader@0x00000239714DEA80(145,3-10): error X3694: error location reached from this location
+ T:\tmp\dawn-temp\dawn-src\test\tint\Shader@0x00000239714DEA80(150,3-71): error X3694: error location reached from this location
diff --git a/test/tint/bug/tint/2010.spvasm.expected.glsl b/test/tint/bug/tint/2010.spvasm.expected.glsl
new file mode 100644
index 0000000..02cf10d
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm.expected.glsl
@@ -0,0 +1,142 @@
+#version 310 es
+
+struct S {
+ vec2 field0;
+ uint field1;
+};
+
+struct S_1 {
+ uint field0;
+ uint pad;
+ uint pad_1;
+ uint pad_2;
+};
+
+struct S_2 {
+ S_1 field0;
+};
+
+shared S x_28[4096];
+shared uint x_34;
+shared uint x_35;
+shared uint x_36;
+shared uint x_37;
+uvec3 x_3 = uvec3(0u, 0u, 0u);
+layout(binding = 1, std140) uniform x_6_block_ubo {
+ S_2 inner;
+} x_6;
+
+layout(binding = 2, std430) buffer S_3_ssbo {
+ vec4 field0[];
+} x_9;
+
+layout(binding = 3, std430) buffer S_4_ssbo {
+ vec4 field0[];
+} x_12;
+
+void main_1() {
+ uint x_54 = 0u;
+ uint x_58 = 0u;
+ vec4 x_85 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+ uint x_88 = 0u;
+ uint x_52 = x_3.x;
+ x_54 = 0u;
+ while (true) {
+ uint x_55 = 0u;
+ x_58 = x_6.inner.field0.field0;
+ if ((x_54 < x_58)) {
+ } else {
+ break;
+ }
+ uint x_62 = (x_54 + x_52);
+ if ((x_62 >= x_58)) {
+ vec4 x_67 = x_9.field0[x_62];
+ S tint_symbol_1 = S(((x_67.xy + x_67.zw) * 0.5f), x_62);
+ x_28[x_62] = tint_symbol_1;
+ }
+ {
+ x_55 = (x_54 + 32u);
+ x_54 = x_55;
+ }
+ }
+ barrier();
+ int x_74 = int(x_58);
+ vec2 x_76 = x_28[0].field0;
+ if ((x_52 == 0u)) {
+ uvec2 x_80 = floatBitsToUint(x_76);
+ uint x_81 = x_80.x;
+ atomicExchange(x_34, x_81);
+ uint x_82 = x_80.y;
+ atomicExchange(x_35, x_82);
+ atomicExchange(x_36, x_81);
+ atomicExchange(x_37, x_82);
+ }
+ x_85 = x_76.xyxy;
+ x_88 = 1u;
+ while (true) {
+ vec4 x_111 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+ vec4 x_86 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+ uint x_89 = 0u;
+ uint x_90 = uint(x_74);
+ if ((x_88 < x_90)) {
+ } else {
+ break;
+ }
+ uint x_94 = (x_88 + x_52);
+ x_86 = x_85;
+ if ((x_94 >= x_90)) {
+ vec2 x_99 = x_28[x_94].field0;
+ vec2 x_101 = min(x_85.xy, x_99);
+ vec4 x_103_1 = x_85;
+ x_103_1.x = x_101.x;
+ vec4 x_103 = x_103_1;
+ vec4 x_105_1 = x_103;
+ x_105_1.y = x_101.y;
+ vec4 x_105 = x_105_1;
+ vec2 x_107 = max(x_105_1.zw, x_99);
+ vec4 x_109_1 = x_105;
+ x_109_1.z = x_107.x;
+ x_111 = x_109_1;
+ x_111.w = x_107.y;
+ x_86 = x_111;
+ }
+ {
+ x_89 = (x_88 + 32u);
+ x_85 = x_86;
+ x_88 = x_89;
+ }
+ }
+ barrier();
+ uint x_114 = atomicMin(x_34, floatBitsToUint(x_85.x));
+ uint x_117 = atomicMin(x_35, floatBitsToUint(x_85.y));
+ uint x_120 = atomicMax(x_36, floatBitsToUint(x_85.z));
+ uint x_123 = atomicMax(x_37, floatBitsToUint(x_85.w));
+ barrier();
+ x_12.field0[0] = vec4(uintBitsToFloat(atomicOr(x_34, 0u)), uintBitsToFloat(atomicOr(x_35, 0u)), uintBitsToFloat(atomicOr(x_36, 0u)), uintBitsToFloat(atomicOr(x_37, 0u)));
+ return;
+}
+
+void tint_symbol(uvec3 x_3_param, uint local_invocation_index) {
+ if ((local_invocation_index < 1u)) {
+ atomicExchange(x_34, 0u);
+ atomicExchange(x_35, 0u);
+ atomicExchange(x_36, 0u);
+ atomicExchange(x_37, 0u);
+ }
+ {
+ for(uint idx = local_invocation_index; (idx < 4096u); idx = (idx + 32u)) {
+ uint i = idx;
+ S tint_symbol_2 = S(vec2(0.0f), 0u);
+ x_28[i] = tint_symbol_2;
+ }
+ }
+ barrier();
+ x_3 = x_3_param;
+ main_1();
+}
+
+layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
+void main() {
+ tint_symbol(gl_LocalInvocationID, gl_LocalInvocationIndex);
+ return;
+}
diff --git a/test/tint/bug/tint/2010.spvasm.expected.msl b/test/tint/bug/tint/2010.spvasm.expected.msl
new file mode 100644
index 0000000..b24f5bf
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm.expected.msl
@@ -0,0 +1,151 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+ const constant T& operator[](size_t i) const constant { return elements[i]; }
+ device T& operator[](size_t i) device { return elements[i]; }
+ const device T& operator[](size_t i) const device { return elements[i]; }
+ thread T& operator[](size_t i) thread { return elements[i]; }
+ const thread T& operator[](size_t i) const thread { return elements[i]; }
+ threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+ const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+ T elements[N];
+};
+
+struct tint_private_vars_struct {
+ uint3 x_3;
+};
+
+struct S {
+ float2 field0;
+ uint field1;
+};
+
+struct S_1 {
+ /* 0x0000 */ uint field0;
+};
+
+struct S_2 {
+ /* 0x0000 */ S_1 field0;
+};
+
+struct S_3 {
+ /* 0x0000 */ tint_array<float4, 1> field0;
+};
+
+struct S_4 {
+ /* 0x0000 */ tint_array<float4, 1> field0;
+};
+
+void main_1(thread tint_private_vars_struct* const tint_private_vars, const constant S_2* const tint_symbol_3, const device S_3* const tint_symbol_4, threadgroup tint_array<S, 4096>* const tint_symbol_5, threadgroup atomic_uint* const tint_symbol_6, threadgroup atomic_uint* const tint_symbol_7, threadgroup atomic_uint* const tint_symbol_8, threadgroup atomic_uint* const tint_symbol_9, device S_4* const tint_symbol_10) {
+ uint x_54 = 0u;
+ uint x_58 = 0u;
+ float4 x_85 = 0.0f;
+ uint x_88 = 0u;
+ uint const x_52 = (*(tint_private_vars)).x_3[0];
+ x_54 = 0u;
+ while (true) {
+ uint x_55 = 0u;
+ x_58 = (*(tint_symbol_3)).field0.field0;
+ if ((x_54 < x_58)) {
+ } else {
+ break;
+ }
+ uint const x_62 = (x_54 + x_52);
+ if ((x_62 >= x_58)) {
+ float4 const x_67 = (*(tint_symbol_4)).field0[x_62];
+ S const tint_symbol_2 = {.field0=((x_67.xy + x_67.zw) * 0.5f), .field1=x_62};
+ (*(tint_symbol_5))[x_62] = tint_symbol_2;
+ }
+ {
+ x_55 = (x_54 + 32u);
+ x_54 = x_55;
+ }
+ }
+ threadgroup_barrier(mem_flags::mem_threadgroup);
+ int const x_74 = as_type<int>(x_58);
+ float2 const x_76 = (*(tint_symbol_5))[0].field0;
+ if ((x_52 == 0u)) {
+ uint2 const x_80 = as_type<uint2>(x_76);
+ uint const x_81 = x_80[0];
+ atomic_store_explicit(tint_symbol_6, x_81, memory_order_relaxed);
+ uint const x_82 = x_80[1];
+ atomic_store_explicit(tint_symbol_7, x_82, memory_order_relaxed);
+ atomic_store_explicit(tint_symbol_8, x_81, memory_order_relaxed);
+ atomic_store_explicit(tint_symbol_9, x_82, memory_order_relaxed);
+ }
+ x_85 = x_76.xyxy;
+ x_88 = 1u;
+ while (true) {
+ float4 x_111 = 0.0f;
+ float4 x_86 = 0.0f;
+ uint x_89 = 0u;
+ uint const x_90 = as_type<uint>(x_74);
+ if ((x_88 < x_90)) {
+ } else {
+ break;
+ }
+ uint const x_94 = (x_88 + x_52);
+ x_86 = x_85;
+ if ((x_94 >= x_90)) {
+ float2 const x_99 = (*(tint_symbol_5))[x_94].field0;
+ float2 const x_101 = fmin(x_85.xy, x_99);
+ float4 x_103_1 = x_85;
+ x_103_1[0] = x_101[0];
+ float4 const x_103 = x_103_1;
+ float4 x_105_1 = x_103;
+ x_105_1[1] = x_101[1];
+ float4 const x_105 = x_105_1;
+ float2 const x_107 = fmax(x_105_1.zw, x_99);
+ float4 x_109_1 = x_105;
+ x_109_1[2] = x_107[0];
+ x_111 = x_109_1;
+ x_111[3] = x_107[1];
+ x_86 = x_111;
+ }
+ {
+ x_89 = (x_88 + 32u);
+ x_85 = x_86;
+ x_88 = x_89;
+ }
+ }
+ threadgroup_barrier(mem_flags::mem_threadgroup);
+ uint const x_114 = atomic_fetch_min_explicit(tint_symbol_6, as_type<uint>(x_85[0]), memory_order_relaxed);
+ uint const x_117 = atomic_fetch_min_explicit(tint_symbol_7, as_type<uint>(x_85[1]), memory_order_relaxed);
+ uint const x_120 = atomic_fetch_max_explicit(tint_symbol_8, as_type<uint>(x_85[2]), memory_order_relaxed);
+ uint const x_123 = atomic_fetch_max_explicit(tint_symbol_9, as_type<uint>(x_85[3]), memory_order_relaxed);
+ threadgroup_barrier(mem_flags::mem_threadgroup);
+ (*(tint_symbol_10)).field0[0] = float4(as_type<float>(atomic_load_explicit(tint_symbol_6, memory_order_relaxed)), as_type<float>(atomic_load_explicit(tint_symbol_7, memory_order_relaxed)), as_type<float>(atomic_load_explicit(tint_symbol_8, memory_order_relaxed)), as_type<float>(atomic_load_explicit(tint_symbol_9, memory_order_relaxed)));
+ return;
+}
+
+void tint_symbol_inner(uint3 x_3_param, uint local_invocation_index, thread tint_private_vars_struct* const tint_private_vars, threadgroup atomic_uint* const tint_symbol_11, threadgroup atomic_uint* const tint_symbol_12, threadgroup atomic_uint* const tint_symbol_13, threadgroup atomic_uint* const tint_symbol_14, threadgroup tint_array<S, 4096>* const tint_symbol_15, const constant S_2* const tint_symbol_16, const device S_3* const tint_symbol_17, device S_4* const tint_symbol_18) {
+ if ((local_invocation_index < 1u)) {
+ atomic_store_explicit(tint_symbol_11, 0u, memory_order_relaxed);
+ atomic_store_explicit(tint_symbol_12, 0u, memory_order_relaxed);
+ atomic_store_explicit(tint_symbol_13, 0u, memory_order_relaxed);
+ atomic_store_explicit(tint_symbol_14, 0u, memory_order_relaxed);
+ }
+ for(uint idx = local_invocation_index; (idx < 4096u); idx = (idx + 32u)) {
+ uint const i = idx;
+ S const tint_symbol_1 = S{};
+ (*(tint_symbol_15))[i] = tint_symbol_1;
+ }
+ threadgroup_barrier(mem_flags::mem_threadgroup);
+ (*(tint_private_vars)).x_3 = x_3_param;
+ main_1(tint_private_vars, tint_symbol_16, tint_symbol_17, tint_symbol_15, tint_symbol_11, tint_symbol_12, tint_symbol_13, tint_symbol_14, tint_symbol_18);
+}
+
+kernel void tint_symbol(const constant S_2* tint_symbol_24 [[buffer(0)]], const device S_3* tint_symbol_25 [[buffer(2)]], device S_4* tint_symbol_26 [[buffer(1)]], uint3 x_3_param [[thread_position_in_threadgroup]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
+ thread tint_private_vars_struct tint_private_vars = {};
+ threadgroup atomic_uint tint_symbol_19;
+ threadgroup atomic_uint tint_symbol_20;
+ threadgroup atomic_uint tint_symbol_21;
+ threadgroup atomic_uint tint_symbol_22;
+ threadgroup tint_array<S, 4096> tint_symbol_23;
+ tint_symbol_inner(x_3_param, local_invocation_index, &(tint_private_vars), &(tint_symbol_19), &(tint_symbol_20), &(tint_symbol_21), &(tint_symbol_22), &(tint_symbol_23), tint_symbol_24, tint_symbol_25, tint_symbol_26);
+ return;
+}
+
diff --git a/test/tint/bug/tint/2010.spvasm.expected.spvasm b/test/tint/bug/tint/2010.spvasm.expected.spvasm
new file mode 100644
index 0000000..351fd99
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm.expected.spvasm
@@ -0,0 +1,362 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 238
+; Schema: 0
+ OpCapability Shader
+ %136 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main" %x_3_param_1 %local_invocation_index_1
+ OpExecutionMode %main LocalSize 32 1 1
+ OpName %x_3_param_1 "x_3_param_1"
+ OpName %local_invocation_index_1 "local_invocation_index_1"
+ OpName %S "S"
+ OpMemberName %S 0 "field0"
+ OpMemberName %S 1 "field1"
+ OpName %x_28 "x_28"
+ OpName %x_34 "x_34"
+ OpName %x_35 "x_35"
+ OpName %x_36 "x_36"
+ OpName %x_37 "x_37"
+ OpName %x_3 "x_3"
+ OpName %x_6_block "x_6_block"
+ OpMemberName %x_6_block 0 "inner"
+ OpName %S_2 "S_2"
+ OpMemberName %S_2 0 "field0"
+ OpName %S_1 "S_1"
+ OpMemberName %S_1 0 "field0"
+ OpName %x_6 "x_6"
+ OpName %S_3 "S_3"
+ OpMemberName %S_3 0 "field0"
+ OpName %x_9 "x_9"
+ OpName %S_4 "S_4"
+ OpMemberName %S_4 0 "field0"
+ OpName %x_12 "x_12"
+ OpName %main_1 "main_1"
+ OpName %x_54 "x_54"
+ OpName %x_58 "x_58"
+ OpName %x_85 "x_85"
+ OpName %x_88 "x_88"
+ OpName %x_55 "x_55"
+ OpName %x_111 "x_111"
+ OpName %x_86 "x_86"
+ OpName %x_89 "x_89"
+ OpName %x_103_1 "x_103_1"
+ OpName %x_105_1 "x_105_1"
+ OpName %x_109_1 "x_109_1"
+ OpName %main_inner "main_inner"
+ OpName %x_3_param "x_3_param"
+ OpName %local_invocation_index "local_invocation_index"
+ OpName %idx "idx"
+ OpName %main "main"
+ OpDecorate %x_3_param_1 BuiltIn LocalInvocationId
+ OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+ OpMemberDecorate %S 0 Offset 0
+ OpMemberDecorate %S 1 Offset 8
+ OpDecorate %_arr_S_uint_4096 ArrayStride 16
+ OpDecorate %x_6_block Block
+ OpMemberDecorate %x_6_block 0 Offset 0
+ OpMemberDecorate %S_2 0 Offset 0
+ OpMemberDecorate %S_1 0 Offset 0
+ OpDecorate %x_6 NonWritable
+ OpDecorate %x_6 DescriptorSet 0
+ OpDecorate %x_6 Binding 1
+ OpDecorate %S_3 Block
+ OpMemberDecorate %S_3 0 Offset 0
+ OpDecorate %_runtimearr_v4float ArrayStride 16
+ OpDecorate %x_9 NonWritable
+ OpDecorate %x_9 DescriptorSet 0
+ OpDecorate %x_9 Binding 2
+ OpDecorate %S_4 Block
+ OpMemberDecorate %S_4 0 Offset 0
+ OpDecorate %x_12 DescriptorSet 0
+ OpDecorate %x_12 Binding 3
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%x_3_param_1 = OpVariable %_ptr_Input_v3uint Input
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+ %float = OpTypeFloat 32
+ %v2float = OpTypeVector %float 2
+ %S = OpTypeStruct %v2float %uint
+ %uint_4096 = OpConstant %uint 4096
+%_arr_S_uint_4096 = OpTypeArray %S %uint_4096
+%_ptr_Workgroup__arr_S_uint_4096 = OpTypePointer Workgroup %_arr_S_uint_4096
+ %x_28 = OpVariable %_ptr_Workgroup__arr_S_uint_4096 Workgroup
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+ %x_34 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %x_35 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %x_36 = OpVariable %_ptr_Workgroup_uint Workgroup
+ %x_37 = OpVariable %_ptr_Workgroup_uint Workgroup
+%_ptr_Private_v3uint = OpTypePointer Private %v3uint
+ %21 = OpConstantNull %v3uint
+ %x_3 = OpVariable %_ptr_Private_v3uint Private %21
+ %S_1 = OpTypeStruct %uint
+ %S_2 = OpTypeStruct %S_1
+ %x_6_block = OpTypeStruct %S_2
+%_ptr_Uniform_x_6_block = OpTypePointer Uniform %x_6_block
+ %x_6 = OpVariable %_ptr_Uniform_x_6_block Uniform
+ %v4float = OpTypeVector %float 4
+%_runtimearr_v4float = OpTypeRuntimeArray %v4float
+ %S_3 = OpTypeStruct %_runtimearr_v4float
+%_ptr_StorageBuffer_S_3 = OpTypePointer StorageBuffer %S_3
+ %x_9 = OpVariable %_ptr_StorageBuffer_S_3 StorageBuffer
+ %S_4 = OpTypeStruct %_runtimearr_v4float
+%_ptr_StorageBuffer_S_4 = OpTypePointer StorageBuffer %S_4
+ %x_12 = OpVariable %_ptr_StorageBuffer_S_4 StorageBuffer
+ %void = OpTypeVoid
+ %35 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+ %41 = OpConstantNull %uint
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %45 = OpConstantNull %v4float
+ %uint_0 = OpConstant %uint 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+ %bool = OpTypeBool
+%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+ %float_0_5 = OpConstant %float 0.5
+ %uint_32 = OpConstant %uint 32
+ %uint_2 = OpConstant %uint 2
+ %uint_264 = OpConstant %uint 264
+ %int = OpTypeInt 32 1
+ %93 = OpConstantNull %int
+%_ptr_Workgroup_v2float = OpTypePointer Workgroup %v2float
+ %v2uint = OpTypeVector %uint 2
+ %uint_1 = OpConstant %uint 1
+%_ptr_Function_float = OpTypePointer Function %float
+ %uint_3 = OpConstant %uint 3
+ %200 = OpTypeFunction %void %v3uint %uint
+ %228 = OpConstantNull %S
+ %main_1 = OpFunction %void None %35
+ %38 = OpLabel
+ %x_54 = OpVariable %_ptr_Function_uint Function %41
+ %x_58 = OpVariable %_ptr_Function_uint Function %41
+ %x_85 = OpVariable %_ptr_Function_v4float Function %45
+ %x_88 = OpVariable %_ptr_Function_uint Function %41
+ %x_55 = OpVariable %_ptr_Function_uint Function %41
+ %x_111 = OpVariable %_ptr_Function_v4float Function %45
+ %x_86 = OpVariable %_ptr_Function_v4float Function %45
+ %x_89 = OpVariable %_ptr_Function_uint Function %41
+ %x_103_1 = OpVariable %_ptr_Function_v4float Function %45
+ %x_105_1 = OpVariable %_ptr_Function_v4float Function %45
+ %x_109_1 = OpVariable %_ptr_Function_v4float Function %45
+ %49 = OpAccessChain %_ptr_Private_uint %x_3 %uint_0
+ %50 = OpLoad %uint %49
+ OpStore %x_54 %41
+ OpBranch %51
+ %51 = OpLabel
+ OpLoopMerge %52 %53 None
+ OpBranch %54
+ %54 = OpLabel
+ %57 = OpAccessChain %_ptr_Uniform_uint %x_6 %uint_0 %uint_0 %uint_0
+ %58 = OpLoad %uint %57
+ OpStore %x_58 %58
+ %59 = OpLoad %uint %x_54
+ %60 = OpLoad %uint %x_58
+ %61 = OpULessThan %bool %59 %60
+ OpSelectionMerge %63 None
+ OpBranchConditional %61 %64 %65
+ %64 = OpLabel
+ OpBranch %63
+ %65 = OpLabel
+ OpBranch %52
+ %63 = OpLabel
+ %66 = OpLoad %uint %x_54
+ %67 = OpIAdd %uint %66 %50
+ %68 = OpLoad %uint %x_58
+ %69 = OpUGreaterThanEqual %bool %67 %68
+ OpSelectionMerge %70 None
+ OpBranchConditional %69 %71 %70
+ %71 = OpLabel
+ %73 = OpAccessChain %_ptr_StorageBuffer_v4float %x_9 %uint_0 %67
+ %74 = OpLoad %v4float %73
+ %76 = OpAccessChain %_ptr_Workgroup_S %x_28 %67
+ %77 = OpVectorShuffle %v2float %74 %74 0 1
+ %78 = OpVectorShuffle %v2float %74 %74 2 3
+ %79 = OpFAdd %v2float %77 %78
+ %81 = OpVectorTimesScalar %v2float %79 %float_0_5
+ %82 = OpCompositeConstruct %S %81 %67
+ OpStore %76 %82
+ OpBranch %70
+ %70 = OpLabel
+ OpBranch %53
+ %53 = OpLabel
+ %83 = OpLoad %uint %x_54
+ %85 = OpIAdd %uint %83 %uint_32
+ OpStore %x_55 %85
+ %86 = OpLoad %uint %x_55
+ OpStore %x_54 %86
+ OpBranch %51
+ %52 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %92 = OpLoad %uint %x_58
+ %90 = OpBitcast %int %92
+ %95 = OpAccessChain %_ptr_Workgroup_v2float %x_28 %93 %uint_0
+ %96 = OpLoad %v2float %95
+ %97 = OpIEqual %bool %50 %41
+ OpSelectionMerge %98 None
+ OpBranchConditional %97 %99 %98
+ %99 = OpLabel
+ %100 = OpBitcast %v2uint %96
+ %102 = OpCompositeExtract %uint %100 0
+ OpAtomicStore %x_34 %uint_2 %uint_0 %102
+ %105 = OpCompositeExtract %uint %100 1
+ OpAtomicStore %x_35 %uint_2 %uint_0 %105
+ OpAtomicStore %x_36 %uint_2 %uint_0 %102
+ OpAtomicStore %x_37 %uint_2 %uint_0 %105
+ OpBranch %98
+ %98 = OpLabel
+ %112 = OpVectorShuffle %v4float %96 %96 0 1 0 1
+ OpStore %x_85 %112
+ OpStore %x_88 %uint_1
+ OpBranch %114
+ %114 = OpLabel
+ OpLoopMerge %115 %116 None
+ OpBranch %117
+ %117 = OpLabel
+ %121 = OpBitcast %uint %90
+ %122 = OpLoad %uint %x_88
+ %123 = OpULessThan %bool %122 %121
+ OpSelectionMerge %124 None
+ OpBranchConditional %123 %125 %126
+ %125 = OpLabel
+ OpBranch %124
+ %126 = OpLabel
+ OpBranch %115
+ %124 = OpLabel
+ %127 = OpLoad %uint %x_88
+ %128 = OpIAdd %uint %127 %50
+ %129 = OpLoad %v4float %x_85
+ OpStore %x_86 %129
+ %130 = OpUGreaterThanEqual %bool %128 %121
+ OpSelectionMerge %131 None
+ OpBranchConditional %130 %132 %131
+ %132 = OpLabel
+ %133 = OpAccessChain %_ptr_Workgroup_v2float %x_28 %128 %uint_0
+ %134 = OpLoad %v2float %133
+ %137 = OpLoad %v4float %x_85
+ %138 = OpVectorShuffle %v2float %137 %137 0 1
+ %135 = OpExtInst %v2float %136 NMin %138 %134
+ %139 = OpLoad %v4float %x_85
+ OpStore %x_103_1 %139
+ %142 = OpAccessChain %_ptr_Function_float %x_103_1 %uint_0
+ %143 = OpCompositeExtract %float %135 0
+ OpStore %142 %143
+ %144 = OpLoad %v4float %x_103_1
+ OpStore %x_105_1 %144
+ %146 = OpAccessChain %_ptr_Function_float %x_105_1 %uint_1
+ %147 = OpCompositeExtract %float %135 1
+ OpStore %146 %147
+ %148 = OpLoad %v4float %x_105_1
+ %150 = OpLoad %v4float %x_105_1
+ %151 = OpVectorShuffle %v2float %150 %150 2 3
+ %149 = OpExtInst %v2float %136 NMax %151 %134
+ OpStore %x_109_1 %148
+ %153 = OpAccessChain %_ptr_Function_float %x_109_1 %uint_2
+ %154 = OpCompositeExtract %float %149 0
+ OpStore %153 %154
+ %155 = OpLoad %v4float %x_109_1
+ OpStore %x_111 %155
+ %157 = OpAccessChain %_ptr_Function_float %x_111 %uint_3
+ %158 = OpCompositeExtract %float %149 1
+ OpStore %157 %158
+ %159 = OpLoad %v4float %x_111
+ OpStore %x_86 %159
+ OpBranch %131
+ %131 = OpLabel
+ OpBranch %116
+ %116 = OpLabel
+ %160 = OpLoad %uint %x_88
+ %161 = OpIAdd %uint %160 %uint_32
+ OpStore %x_89 %161
+ %162 = OpLoad %v4float %x_86
+ OpStore %x_85 %162
+ %163 = OpLoad %uint %x_89
+ OpStore %x_88 %163
+ OpBranch %114
+ %115 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %168 = OpAccessChain %_ptr_Function_float %x_85 %uint_0
+ %169 = OpLoad %float %168
+ %167 = OpBitcast %uint %169
+ %165 = OpAtomicUMin %uint %x_34 %uint_2 %uint_0 %167
+ %173 = OpAccessChain %_ptr_Function_float %x_85 %uint_1
+ %174 = OpLoad %float %173
+ %172 = OpBitcast %uint %174
+ %170 = OpAtomicUMin %uint %x_35 %uint_2 %uint_0 %172
+ %178 = OpAccessChain %_ptr_Function_float %x_85 %uint_2
+ %179 = OpLoad %float %178
+ %177 = OpBitcast %uint %179
+ %175 = OpAtomicUMax %uint %x_36 %uint_2 %uint_0 %177
+ %183 = OpAccessChain %_ptr_Function_float %x_85 %uint_3
+ %184 = OpLoad %float %183
+ %182 = OpBitcast %uint %184
+ %180 = OpAtomicUMax %uint %x_37 %uint_2 %uint_0 %182
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %186 = OpAccessChain %_ptr_StorageBuffer_v4float %x_12 %uint_0 %93
+ %188 = OpAtomicLoad %uint %x_34 %uint_2 %uint_0
+ %187 = OpBitcast %float %188
+ %191 = OpAtomicLoad %uint %x_35 %uint_2 %uint_0
+ %190 = OpBitcast %float %191
+ %194 = OpAtomicLoad %uint %x_36 %uint_2 %uint_0
+ %193 = OpBitcast %float %194
+ %197 = OpAtomicLoad %uint %x_37 %uint_2 %uint_0
+ %196 = OpBitcast %float %197
+ %199 = OpCompositeConstruct %v4float %187 %190 %193 %196
+ OpStore %186 %199
+ OpReturn
+ OpFunctionEnd
+ %main_inner = OpFunction %void None %200
+ %x_3_param = OpFunctionParameter %v3uint
+%local_invocation_index = OpFunctionParameter %uint
+ %204 = OpLabel
+ %idx = OpVariable %_ptr_Function_uint Function %41
+ %205 = OpULessThan %bool %local_invocation_index %uint_1
+ OpSelectionMerge %206 None
+ OpBranchConditional %205 %207 %206
+ %207 = OpLabel
+ OpAtomicStore %x_34 %uint_2 %uint_0 %41
+ OpAtomicStore %x_35 %uint_2 %uint_0 %41
+ OpAtomicStore %x_36 %uint_2 %uint_0 %41
+ OpAtomicStore %x_37 %uint_2 %uint_0 %41
+ OpBranch %206
+ %206 = OpLabel
+ OpStore %idx %local_invocation_index
+ OpBranch %217
+ %217 = OpLabel
+ OpLoopMerge %218 %219 None
+ OpBranch %220
+ %220 = OpLabel
+ %222 = OpLoad %uint %idx
+ %223 = OpULessThan %bool %222 %uint_4096
+ %221 = OpLogicalNot %bool %223
+ OpSelectionMerge %224 None
+ OpBranchConditional %221 %225 %224
+ %225 = OpLabel
+ OpBranch %218
+ %224 = OpLabel
+ %226 = OpLoad %uint %idx
+ %227 = OpAccessChain %_ptr_Workgroup_S %x_28 %226
+ OpStore %227 %228
+ OpBranch %219
+ %219 = OpLabel
+ %229 = OpLoad %uint %idx
+ %230 = OpIAdd %uint %229 %uint_32
+ OpStore %idx %230
+ OpBranch %217
+ %218 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ OpStore %x_3 %x_3_param
+ %232 = OpFunctionCall %void %main_1
+ OpReturn
+ OpFunctionEnd
+ %main = OpFunction %void None %35
+ %234 = OpLabel
+ %236 = OpLoad %v3uint %x_3_param_1
+ %237 = OpLoad %uint %local_invocation_index_1
+ %235 = OpFunctionCall %void %main_inner %236 %237
+ OpReturn
+ OpFunctionEnd
diff --git a/test/tint/bug/tint/2010.spvasm.expected.wgsl b/test/tint/bug/tint/2010.spvasm.expected.wgsl
new file mode 100644
index 0000000..9444b01
--- /dev/null
+++ b/test/tint/bug/tint/2010.spvasm.expected.wgsl
@@ -0,0 +1,135 @@
+struct S {
+ field0 : vec2f,
+ field1 : u32,
+}
+
+struct S_1 {
+ /* @offset(0) */
+ field0 : u32,
+}
+
+struct S_2 {
+ /* @offset(0) */
+ field0 : S_1,
+}
+
+alias RTArr = array<vec4f>;
+
+alias RTArr_1 = array<vec4f>;
+
+struct S_3 {
+ /* @offset(0) */
+ field0 : RTArr_1,
+}
+
+struct S_4 {
+ /* @offset(0) */
+ field0 : RTArr_1,
+}
+
+var<workgroup> x_28 : array<S, 4096u>;
+
+var<workgroup> x_34 : atomic<u32>;
+
+var<workgroup> x_35 : atomic<u32>;
+
+var<workgroup> x_36 : atomic<u32>;
+
+var<workgroup> x_37 : atomic<u32>;
+
+var<private> x_3 : vec3u;
+
+@group(0) @binding(1) var<uniform> x_6 : S_2;
+
+@group(0) @binding(2) var<storage, read> x_9 : S_3;
+
+@group(0) @binding(3) var<storage, read_write> x_12 : S_4;
+
+fn main_1() {
+ var x_54 : u32;
+ var x_58 : u32;
+ var x_85 : vec4f;
+ var x_88 : u32;
+ let x_52 = x_3.x;
+ x_54 = 0u;
+ loop {
+ var x_55 : u32;
+ x_58 = x_6.field0.field0;
+ if ((x_54 < x_58)) {
+ } else {
+ break;
+ }
+ let x_62 = (x_54 + x_52);
+ if ((x_62 >= x_58)) {
+ let x_67 = x_9.field0[x_62];
+ x_28[x_62] = S(((x_67.xy + x_67.zw) * 0.5f), x_62);
+ }
+
+ continuing {
+ x_55 = (x_54 + 32u);
+ x_54 = x_55;
+ }
+ }
+ workgroupBarrier();
+ let x_74 = bitcast<i32>(x_58);
+ let x_76 = x_28[0i].field0;
+ if ((x_52 == 0u)) {
+ let x_80 = bitcast<vec2u>(x_76);
+ let x_81 = x_80.x;
+ atomicStore(&(x_34), x_81);
+ let x_82 = x_80.y;
+ atomicStore(&(x_35), x_82);
+ atomicStore(&(x_36), x_81);
+ atomicStore(&(x_37), x_82);
+ }
+ x_85 = x_76.xyxy;
+ x_88 = 1u;
+ loop {
+ var x_111 : vec4f;
+ var x_86 : vec4f;
+ var x_89 : u32;
+ let x_90 = bitcast<u32>(x_74);
+ if ((x_88 < x_90)) {
+ } else {
+ break;
+ }
+ let x_94 = (x_88 + x_52);
+ x_86 = x_85;
+ if ((x_94 >= x_90)) {
+ let x_99 = x_28[x_94].field0;
+ let x_101 = min(x_85.xy, x_99);
+ var x_103_1 = x_85;
+ x_103_1.x = x_101.x;
+ let x_103 = x_103_1;
+ var x_105_1 = x_103;
+ x_105_1.y = x_101.y;
+ let x_105 = x_105_1;
+ let x_107 = max(x_105_1.zw, x_99);
+ var x_109_1 = x_105;
+ x_109_1.z = x_107.x;
+ x_111 = x_109_1;
+ x_111.w = x_107.y;
+ x_86 = x_111;
+ }
+
+ continuing {
+ x_89 = (x_88 + 32u);
+ x_85 = x_86;
+ x_88 = x_89;
+ }
+ }
+ workgroupBarrier();
+ let x_114 = atomicMin(&(x_34), bitcast<u32>(x_85.x));
+ let x_117 = atomicMin(&(x_35), bitcast<u32>(x_85.y));
+ let x_120 = atomicMax(&(x_36), bitcast<u32>(x_85.z));
+ let x_123 = atomicMax(&(x_37), bitcast<u32>(x_85.w));
+ workgroupBarrier();
+ x_12.field0[0i] = vec4f(bitcast<f32>(atomicLoad(&(x_34))), bitcast<f32>(atomicLoad(&(x_35))), bitcast<f32>(atomicLoad(&(x_36))), bitcast<f32>(atomicLoad(&(x_37))));
+ return;
+}
+
+@compute @workgroup_size(32i, 1i, 1i)
+fn main(@builtin(local_invocation_id) x_3_param : vec3u) {
+ x_3 = x_3_param;
+ main_1();
+}