resolver: Enable support of out of order declarations
Allow module-scope declarations to be made in any order.
Bug: tint:1266
Change-Id: Ib2607b6c33fad7c83e2c36f85b0a965eac922ec5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79769
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/resolver/call_test.cc b/src/resolver/call_test.cc
index 3448726..9a3cc85 100644
--- a/src/resolver/call_test.cc
+++ b/src/resolver/call_test.cc
@@ -101,6 +101,18 @@
EXPECT_EQ(call->Target(), Sem().Get(func));
}
+TEST_F(ResolverCallTest, OutOfOrder) {
+ auto* call_expr = Call("b");
+ Func("a", {}, ty.void_(), {CallStmt(call_expr)});
+ auto* b = Func("b", {}, ty.void_(), {});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* call = Sem().Get(call_expr);
+ EXPECT_NE(call, nullptr);
+ EXPECT_EQ(call->Target(), Sem().Get(b));
+}
+
} // namespace
} // namespace resolver
} // namespace tint
diff --git a/src/resolver/dependency_graph.cc b/src/resolver/dependency_graph.cc
index d520cd7..7aa5c52 100644
--- a/src/resolver/dependency_graph.cc
+++ b/src/resolver/dependency_graph.cc
@@ -474,7 +474,7 @@
/// Performs global dependency analysis on the module, emitting any errors to
/// #diagnostics.
/// @returns true if analysis found no errors, otherwise false.
- bool Run(const ast::Module& module, bool allow_out_of_order_decls) {
+ bool Run(const ast::Module& module) {
// Collect all the named globals from the AST module
GatherGlobals(module);
@@ -487,11 +487,6 @@
// Dump the dependency graph if TINT_DUMP_DEPENDENCY_GRAPH is non-zero
DumpDependencyGraph();
- if (!allow_out_of_order_decls) {
- // Prevent out-of-order declarations.
- ErrorOnOutOfOrderDeclarations();
- }
-
graph_.ordered_globals = std::move(sorted_);
return !diagnostics_.contains_errors();
@@ -668,36 +663,6 @@
return {};
}
- // TODO(crbug.com/tint/1266): Errors if there are any uses of globals before
- // their declaration. Out-of-order declarations was added to the WGSL
- // specification with https://github.com/gpuweb/gpuweb/pull/2244, but Mozilla
- // have objections to this change so this feature is currently disabled via
- // this function.
- void ErrorOnOutOfOrderDeclarations() {
- if (diagnostics_.contains_errors()) {
- // Might have already errored about cyclic dependencies. No need to report
- // out-of-order errors as well.
- return;
- }
- std::unordered_set<const Global*> seen;
- for (auto* global : declaration_order_) {
- for (auto* dep : global->deps) {
- if (!seen.count(dep)) {
- auto info = DepInfoFor(global, dep);
- auto name = NameOf(dep->node);
- AddError(diagnostics_,
- KindOf(dep->node) + " '" + name +
- "' used before it has been declared",
- info.source);
- AddNote(diagnostics_,
- KindOf(dep->node) + " '" + name + "' declared here",
- dep->node->source);
- }
- }
- seen.emplace(global);
- }
- }
-
/// CyclicDependencyFound() emits an error diagnostic for a cyclic dependency.
/// @param root is the global that starts the cyclic dependency, which must be
/// found in `stack`.
@@ -789,10 +754,9 @@
bool DependencyGraph::Build(const ast::Module& module,
const SymbolTable& symbols,
diag::List& diagnostics,
- DependencyGraph& output,
- bool allow_out_of_order_decls) {
+ DependencyGraph& output) {
DependencyAnalysis da{symbols, diagnostics, output};
- return da.Run(module, allow_out_of_order_decls);
+ return da.Run(module);
}
} // namespace resolver
diff --git a/src/resolver/dependency_graph.h b/src/resolver/dependency_graph.h
index 6235ad7..294786d 100644
--- a/src/resolver/dependency_graph.h
+++ b/src/resolver/dependency_graph.h
@@ -40,14 +40,11 @@
/// @param symbols the symbol table
/// @param diagnostics the diagnostic list to populate with errors / warnings
/// @param output the resulting DependencyGraph
- /// @param allow_out_of_order_decls if true, then out-of-order declarations
- /// are not considered an error
/// @returns true on success, false on error
static bool Build(const ast::Module& module,
const SymbolTable& symbols,
diag::List& diagnostics,
- DependencyGraph& output,
- bool allow_out_of_order_decls);
+ DependencyGraph& output);
/// All globals in dependency-sorted order.
std::vector<const ast::Node*> ordered_globals;
diff --git a/src/resolver/dependency_graph_test.cc b/src/resolver/dependency_graph_test.cc
index 0b9080c..2329d86 100644
--- a/src/resolver/dependency_graph_test.cc
+++ b/src/resolver/dependency_graph_test.cc
@@ -29,13 +29,10 @@
template <typename T>
class ResolverDependencyGraphTestWithParam : public ResolverTestWithParam<T> {
public:
- bool allow_out_of_order_decls = true;
-
DependencyGraph Build(std::string expected_error = "") {
DependencyGraph graph;
auto result = DependencyGraph::Build(this->AST(), this->Symbols(),
- this->Diagnostics(), graph,
- allow_out_of_order_decls);
+ this->Diagnostics(), graph);
if (expected_error.empty()) {
EXPECT_TRUE(result) << this->Diagnostics().str();
} else {
@@ -659,13 +656,7 @@
////////////////////////////////////////////////////////////////////////////////
namespace used_before_decl_tests {
-class ResolverDependencyGraphUsedBeforeDeclTest
- : public ResolverDependencyGraphTest {
- public:
- ResolverDependencyGraphUsedBeforeDeclTest() {
- allow_out_of_order_decls = false;
- }
-};
+using ResolverDependencyGraphUsedBeforeDeclTest = ResolverDependencyGraphTest;
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, FuncCall) {
// fn A() { B(); }
@@ -674,8 +665,7 @@
Func("A", {}, ty.void_(), {CallStmt(Call(Expr(Source{{12, 34}}, "B")))});
Func(Source{{56, 78}}, "B", {}, ty.void_(), {Return()});
- Build(R"(12:34 error: function 'B' used before it has been declared
-56:78 note: function 'B' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeConstructed) {
@@ -688,8 +678,7 @@
{Block(Ignore(Construct(ty.type_name(Source{{12, 34}}, "T"))))});
Alias(Source{{56, 78}}, "T", ty.i32());
- Build(R"(12:34 error: alias 'T' used before it has been declared
-56:78 note: alias 'T' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByLocal) {
@@ -702,8 +691,7 @@
{Block(Decl(Var("v", ty.type_name(Source{{12, 34}}, "T"))))});
Alias(Source{{56, 78}}, "T", ty.i32());
- Build(R"(12:34 error: alias 'T' used before it has been declared
-56:78 note: alias 'T' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByParam) {
@@ -713,8 +701,7 @@
Func("F", {Param("p", ty.type_name(Source{{12, 34}}, "T"))}, ty.void_(), {});
Alias(Source{{56, 78}}, "T", ty.i32());
- Build(R"(12:34 error: alias 'T' used before it has been declared
-56:78 note: alias 'T' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedAsReturnType) {
@@ -724,8 +711,7 @@
Func("F", {}, ty.type_name(Source{{12, 34}}, "T"), {});
Alias(Source{{56, 78}}, "T", ty.i32());
- Build(R"(12:34 error: alias 'T' used before it has been declared
-56:78 note: alias 'T' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeByStructMember) {
@@ -735,8 +721,7 @@
Structure("S", {Member("m", ty.type_name(Source{{12, 34}}, "T"))});
Alias(Source{{56, 78}}, "T", ty.i32());
- Build(R"(12:34 error: alias 'T' used before it has been declared
-56:78 note: alias 'T' declared here)");
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, VarUsed) {
@@ -751,10 +736,7 @@
Global(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate,
Expr(2.1f));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: var 'G' used before it has been declared
-56:78 note: var 'G' declared here)");
+ Build();
}
} // namespace used_before_decl_tests
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 2a77c96..623af60 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -95,8 +95,7 @@
}
if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(),
- builder_->Diagnostics(), dependencies_,
- /* allow_out_of_order_decls*/ false)) {
+ builder_->Diagnostics(), dependencies_)) {
return false;
}
@@ -118,9 +117,8 @@
bool Resolver::ResolveInternal() {
Mark(&builder_->AST());
- // Process everything else in the order they appear in the module. This is
- // necessary for validation of use-before-declaration.
- for (auto* decl : builder_->AST().GlobalDeclarations()) {
+ // Process all module-scope declarations in dependency order.
+ for (auto* decl : dependencies_.ordered_globals) {
if (auto* td = decl->As<ast::TypeDecl>()) {
Mark(td);
if (!TypeDecl(td)) {
diff --git a/src/transform/add_spirv_block_attribute_test.cc b/src/transform/add_spirv_block_attribute_test.cc
index 21dbe19..256e8b7 100644
--- a/src/transform/add_spirv_block_attribute_test.cc
+++ b/src/transform/add_spirv_block_attribute_test.cc
@@ -548,6 +548,68 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(AddSpirvBlockAttributeTest,
+ Aliases_Nested_OuterBuffer_InnerBuffer_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main() {
+ let f0 = u0.i.f;
+ let f1 = u1.f;
+}
+
+@group(0) @binding(1)
+var<uniform> u1 : MyInner;
+
+type MyInner = Inner;
+
+@group(0) @binding(0)
+var<uniform> u0 : MyOuter;
+
+type MyOuter = Outer;
+
+struct Outer {
+ i : MyInner;
+};
+
+struct Inner {
+ f : f32;
+};
+)";
+ auto* expect = R"(
+@stage(fragment)
+fn main() {
+ let f0 = u0.i.f;
+ let f1 = u1.inner.f;
+}
+
+@internal(spirv_block)
+struct u1_block {
+ inner : Inner;
+}
+
+@group(0) @binding(1) var<uniform> u1 : u1_block;
+
+type MyInner = Inner;
+
+@group(0) @binding(0) var<uniform> u0 : MyOuter;
+
+type MyOuter = Outer;
+
+@internal(spirv_block)
+struct Outer {
+ i : MyInner;
+}
+
+struct Inner {
+ f : f32;
+}
+)";
+
+ auto got = Run<AddSpirvBlockAttribute>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/array_length_from_uniform.cc b/src/transform/array_length_from_uniform.cc
index 9270fd9..9735317 100644
--- a/src/transform/array_length_from_uniform.cc
+++ b/src/transform/array_length_from_uniform.cc
@@ -154,10 +154,8 @@
buffer_size_ubo = ctx.dst->Global(
ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct),
ast::StorageClass::kUniform,
- ast::AttributeList{
- ctx.dst->create<ast::GroupAttribute>(cfg->ubo_binding.group),
- ctx.dst->create<ast::BindingAttribute>(
- cfg->ubo_binding.binding)});
+ ast::AttributeList{ctx.dst->GroupAndBinding(
+ cfg->ubo_binding.group, cfg->ubo_binding.binding)});
}
return buffer_size_ubo;
};
diff --git a/src/transform/array_length_from_uniform_test.cc b/src/transform/array_length_from_uniform_test.cc
index a4f2fa9..1ae41ba 100644
--- a/src/transform/array_length_from_uniform_test.cc
+++ b/src/transform/array_length_from_uniform_test.cc
@@ -536,6 +536,54 @@
got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
}
+TEST_F(ArrayLengthFromUniformTest, OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var len : u32 = arrayLength(&sb.arr);
+}
+
+@group(0) @binding(0) var<storage, read> sb : SB;
+
+struct SB {
+ x : i32;
+ arr : array<i32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ buffer_size : array<vec4<u32>, 1u>;
+}
+
+@group(0) @binding(30) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var len : u32 = ((tint_symbol_1.buffer_size[0u][0u] - 4u) / 4u);
+}
+
+@group(0) @binding(0) var<storage, read> sb : SB;
+
+struct SB {
+ x : i32;
+ arr : array<i32>;
+}
+)";
+
+ ArrayLengthFromUniform::Config cfg({0, 30u});
+ cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0);
+
+ DataMap data;
+ data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
+
+ auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+ EXPECT_EQ(std::unordered_set<uint32_t>({0}),
+ got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/calculate_array_length.cc b/src/transform/calculate_array_length.cc
index ac57650..b745bd2 100644
--- a/src/transform/calculate_array_length.cc
+++ b/src/transform/calculate_array_length.cc
@@ -101,7 +101,7 @@
auto* type = CreateASTTypeFor(ctx, buffer_type);
auto* disable_validation = ctx.dst->Disable(
ast::DisabledValidation::kIgnoreConstructibleFunctionParameter);
- auto* func = ctx.dst->create<ast::Function>(
+ ctx.dst->AST().AddFunction(ctx.dst->create<ast::Function>(
name,
ast::VariableList{
// Note: The buffer parameter requires the kStorage StorageClass
@@ -118,20 +118,8 @@
ast::AttributeList{
ctx.dst->ASTNodes().Create<BufferSizeIntrinsic>(ctx.dst->ID()),
},
- ast::AttributeList{});
- // Insert the intrinsic function after the structure or array structure
- // element type. TODO(crbug.com/tint/1266): Once we allow out-of-order
- // declarations, this can be simplified.
- auto* insert_after = buffer_type;
- while (auto* arr = insert_after->As<sem::Array>()) {
- insert_after = arr->ElemType();
- }
- if (auto* str = insert_after->As<sem::Struct>()) {
- ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), str->Declaration(),
- func);
- } else {
- ctx.InsertFront(ctx.src->AST().GlobalDeclarations(), func);
- }
+ ast::AttributeList{}));
+
return name;
});
};
diff --git a/src/transform/calculate_array_length_test.cc b/src/transform/calculate_array_length_test.cc
index fcf7001..fa1f274 100644
--- a/src/transform/calculate_array_length_test.cc
+++ b/src/transform/calculate_array_length_test.cc
@@ -111,14 +111,14 @@
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
+
struct SB {
x : i32;
arr : array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> sb : SB;
@stage(compute) @workgroup_size(1)
@@ -149,13 +149,13 @@
}
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<S>, result : ptr<function, u32>)
+
struct S {
f : f32;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<S>, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> arr : array<S>;
@stage(compute) @workgroup_size(1)
@@ -186,13 +186,13 @@
}
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<array<S, 4u>>, result : ptr<function, u32>)
+
struct S {
f : f32;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<array<S, 4u>>, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> arr : array<array<S, 4>>;
@stage(compute) @workgroup_size(1)
@@ -261,14 +261,14 @@
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
+
struct SB {
x : i32;
arr : array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> sb : SB;
@stage(compute) @workgroup_size(1)
@@ -334,15 +334,15 @@
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
+
struct SB {
x : i32;
y : f32;
arr : @stride(64) array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> sb : SB;
@stage(compute) @workgroup_size(1)
@@ -381,14 +381,14 @@
)";
auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
+
struct SB {
x : i32;
arr : array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> sb : SB;
@stage(compute) @workgroup_size(1)
@@ -443,6 +443,12 @@
auto* expect = R"(
@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr<function, u32>)
+
+@internal(intrinsic_buffer_size)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr<function, u32>)
+
+@internal(intrinsic_buffer_size)
fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<i32>, result : ptr<function, u32>)
struct SB1 {
@@ -450,17 +456,11 @@
arr1 : array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr<function, u32>)
-
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> sb1 : SB1;
@group(0) @binding(1) var<storage, read> sb2 : SB2;
@@ -512,14 +512,14 @@
auto* expect =
R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
+
struct SB {
x : i32;
arr : array<i32>;
}
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr<function, u32>)
-
@group(0) @binding(0) var<storage, read> a : SB;
@group(0) @binding(1) var<storage, read> b : SB;
@@ -544,6 +544,82 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CalculateArrayLengthTest, OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var len1 : u32 = arrayLength(&(sb1.arr1));
+ var len2 : u32 = arrayLength(&(sb2.arr2));
+ var len3 : u32 = arrayLength(&sb3);
+ var x : u32 = (len1 + len2 + len3);
+}
+
+@group(0) @binding(0) var<storage, read> sb1 : SB1;
+
+struct SB1 {
+ x : i32;
+ arr1 : array<i32>;
+};
+
+@group(0) @binding(1) var<storage, read> sb2 : SB2;
+
+struct SB2 {
+ x : i32;
+ arr2 : array<vec4<f32>>;
+};
+
+@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_buffer_size)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr<function, u32>)
+
+@internal(intrinsic_buffer_size)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr<function, u32>)
+
+@internal(intrinsic_buffer_size)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array<i32>, result : ptr<function, u32>)
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var tint_symbol_1 : u32 = 0u;
+ tint_symbol(sb1, &(tint_symbol_1));
+ let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
+ var tint_symbol_4 : u32 = 0u;
+ tint_symbol_3(sb2, &(tint_symbol_4));
+ let tint_symbol_5 : u32 = ((tint_symbol_4 - 16u) / 16u);
+ var tint_symbol_7 : u32 = 0u;
+ tint_symbol_6(sb3, &(tint_symbol_7));
+ let tint_symbol_8 : u32 = (tint_symbol_7 / 4u);
+ var len1 : u32 = tint_symbol_2;
+ var len2 : u32 = tint_symbol_5;
+ var len3 : u32 = tint_symbol_8;
+ var x : u32 = ((len1 + len2) + len3);
+}
+
+@group(0) @binding(0) var<storage, read> sb1 : SB1;
+
+struct SB1 {
+ x : i32;
+ arr1 : array<i32>;
+}
+
+@group(0) @binding(1) var<storage, read> sb2 : SB2;
+
+struct SB2 {
+ x : i32;
+ arr2 : array<vec4<f32>>;
+}
+
+@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
+)";
+
+ auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/canonicalize_entry_point_io_test.cc b/src/transform/canonicalize_entry_point_io_test.cc
index 23030cb..990a0c5 100644
--- a/src/transform/canonicalize_entry_point_io_test.cc
+++ b/src/transform/canonicalize_entry_point_io_test.cc
@@ -203,6 +203,42 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(@location(1) loc1 : myf32) {
+ var x : myf32 = loc1;
+}
+
+type myf32 = f32;
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(1)
+ loc1 : f32;
+}
+
+fn frag_main_inner(loc1 : myf32) {
+ var x : myf32 = loc1;
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol : tint_symbol_1) {
+ frag_main_inner(tint_symbol.loc1);
+}
+
+type myf32 = f32;
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Spirv) {
auto* src = R"(
struct FragBuiltins {
@@ -257,6 +293,60 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Spirv_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(@location(0) loc0 : f32,
+ locations : FragLocations,
+ builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+struct FragBuiltins {
+ @builtin(position) coord : vec4<f32>;
+};
+struct FragLocations {
+ @location(1) loc1 : f32;
+ @location(2) @interpolate(flat) loc2 : vec4<u32>;
+};
+)";
+
+ auto* expect = R"(
+@location(0) @internal(disable_validation__ignore_storage_class) var<in> loc0_1 : f32;
+
+@location(1) @internal(disable_validation__ignore_storage_class) var<in> loc1_1 : f32;
+
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> loc2_1 : vec4<u32>;
+
+@builtin(position) @internal(disable_validation__ignore_storage_class) var<in> coord_1 : vec4<f32>;
+
+fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+@stage(fragment)
+fn frag_main() {
+ frag_main_inner(loc0_1, FragLocations(loc1_1, loc2_1), FragBuiltins(coord_1));
+}
+
+struct FragBuiltins {
+ coord : vec4<f32>;
+}
+
+struct FragLocations {
+ loc1 : f32;
+ loc2 : vec4<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, StructParameters_kMsl) {
auto* src = R"(
struct FragBuiltins {
@@ -312,6 +402,61 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, StructParameters_kMsl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(@location(0) loc0 : f32,
+ locations : FragLocations,
+ builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+struct FragBuiltins {
+ @builtin(position) coord : vec4<f32>;
+};
+struct FragLocations {
+ @location(1) loc1 : f32;
+ @location(2) @interpolate(flat) loc2 : vec4<u32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ loc0 : f32;
+ @location(1)
+ loc1 : f32;
+ @location(2) @interpolate(flat)
+ loc2 : vec4<u32>;
+}
+
+fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+@stage(fragment)
+fn frag_main(@builtin(position) coord : vec4<f32>, tint_symbol : tint_symbol_1) {
+ frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(coord));
+}
+
+struct FragBuiltins {
+ coord : vec4<f32>;
+}
+
+struct FragLocations {
+ loc1 : f32;
+ loc2 : vec4<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl) {
auto* src = R"(
struct FragBuiltins {
@@ -369,6 +514,63 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(@location(0) loc0 : f32,
+ locations : FragLocations,
+ builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+struct FragBuiltins {
+ @builtin(position) coord : vec4<f32>;
+};
+struct FragLocations {
+ @location(1) loc1 : f32;
+ @location(2) @interpolate(flat) loc2 : vec4<u32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ loc0 : f32;
+ @location(1)
+ loc1 : f32;
+ @location(2) @interpolate(flat)
+ loc2 : vec4<u32>;
+ @builtin(position)
+ coord : vec4<f32>;
+}
+
+fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
+ var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol : tint_symbol_1) {
+ frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(tint_symbol.coord));
+}
+
+struct FragBuiltins {
+ coord : vec4<f32>;
+}
+
+struct FragLocations {
+ loc1 : f32;
+ loc2 : vec4<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Spirv) {
auto* src = R"(
@stage(fragment)
@@ -525,6 +727,62 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Spirv_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+struct FragOutput {
+ @location(0) color : vec4<f32>;
+ @builtin(frag_depth) depth : f32;
+ @builtin(sample_mask) mask : u32;
+};
+)";
+
+ auto* expect = R"(
+@location(0) @internal(disable_validation__ignore_storage_class) var<out> color_1 : vec4<f32>;
+
+@builtin(frag_depth) @internal(disable_validation__ignore_storage_class) var<out> depth_1 : f32;
+
+@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> mask_1 : array<u32, 1>;
+
+fn frag_main_inner() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+@stage(fragment)
+fn frag_main() {
+ let inner_result = frag_main_inner();
+ color_1 = inner_result.color;
+ depth_1 = inner_result.depth;
+ mask_1[0] = inner_result.mask;
+}
+
+struct FragOutput {
+ color : vec4<f32>;
+ depth : f32;
+ mask : u32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Msl) {
auto* src = R"(
struct FragOutput {
@@ -586,6 +844,67 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Msl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+struct FragOutput {
+ @location(0) color : vec4<f32>;
+ @builtin(frag_depth) depth : f32;
+ @builtin(sample_mask) mask : u32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @location(0)
+ color : vec4<f32>;
+ @builtin(frag_depth)
+ depth : f32;
+ @builtin(sample_mask)
+ mask : u32;
+}
+
+fn frag_main_inner() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+@stage(fragment)
+fn frag_main() -> tint_symbol {
+ let inner_result = frag_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.color = inner_result.color;
+ wrapper_result.depth = inner_result.depth;
+ wrapper_result.mask = inner_result.mask;
+ return wrapper_result;
+}
+
+struct FragOutput {
+ color : vec4<f32>;
+ depth : f32;
+ mask : u32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl) {
auto* src = R"(
struct FragOutput {
@@ -647,6 +966,67 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+struct FragOutput {
+ @location(0) color : vec4<f32>;
+ @builtin(frag_depth) depth : f32;
+ @builtin(sample_mask) mask : u32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @location(0)
+ color : vec4<f32>;
+ @builtin(frag_depth)
+ depth : f32;
+ @builtin(sample_mask)
+ mask : u32;
+}
+
+fn frag_main_inner() -> FragOutput {
+ var output : FragOutput;
+ output.depth = 1.0;
+ output.mask = 7u;
+ output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
+ return output;
+}
+
+@stage(fragment)
+fn frag_main() -> tint_symbol {
+ let inner_result = frag_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.color = inner_result.color;
+ wrapper_result.depth = inner_result.depth;
+ wrapper_result.mask = inner_result.mask;
+ return wrapper_result;
+}
+
+struct FragOutput {
+ color : vec4<f32>;
+ depth : f32;
+ mask : u32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest,
StructParameters_SharedDeviceFunction_Spirv) {
auto* src = R"(
@@ -716,6 +1096,74 @@
}
TEST_F(CanonicalizeEntryPointIOTest,
+ StructParameters_SharedDeviceFunction_Spirv_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main1(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return x.value * x.mul;
+}
+
+struct FragmentInput {
+ @location(0) value : f32;
+ @location(1) mul : f32;
+};
+)";
+
+ auto* expect = R"(
+@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_1 : f32;
+
+@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_1 : f32;
+
+@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_2 : f32;
+
+@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_2 : f32;
+
+fn frag_main1_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main1() {
+ frag_main1_inner(FragmentInput(value_1, mul_1));
+}
+
+fn frag_main2_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2() {
+ frag_main2_inner(FragmentInput(value_2, mul_2));
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return (x.value * x.mul);
+}
+
+struct FragmentInput {
+ value : f32;
+ mul : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(CanonicalizeEntryPointIOTest,
StructParameters_SharedDeviceFunction_Msl) {
auto* src = R"(
struct FragmentInput {
@@ -790,6 +1238,80 @@
}
TEST_F(CanonicalizeEntryPointIOTest,
+ StructParameters_SharedDeviceFunction_Msl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main1(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return x.value * x.mul;
+}
+
+struct FragmentInput {
+ @location(0) value : f32;
+ @location(1) mul : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ value : f32;
+ @location(1)
+ mul : f32;
+}
+
+fn frag_main1_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main1(tint_symbol : tint_symbol_1) {
+ frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
+}
+
+struct tint_symbol_3 {
+ @location(0)
+ value : f32;
+ @location(1)
+ mul : f32;
+}
+
+fn frag_main2_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2(tint_symbol_2 : tint_symbol_3) {
+ frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return (x.value * x.mul);
+}
+
+struct FragmentInput {
+ value : f32;
+ mul : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(CanonicalizeEntryPointIOTest,
StructParameters_SharedDeviceFunction_Hlsl) {
auto* src = R"(
struct FragmentInput {
@@ -863,6 +1385,80 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ StructParameters_SharedDeviceFunction_Hlsl_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main1(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return x.value * x.mul;
+}
+
+struct FragmentInput {
+ @location(0) value : f32;
+ @location(1) mul : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ value : f32;
+ @location(1)
+ mul : f32;
+}
+
+fn frag_main1_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main1(tint_symbol : tint_symbol_1) {
+ frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
+}
+
+struct tint_symbol_3 {
+ @location(0)
+ value : f32;
+ @location(1)
+ mul : f32;
+}
+
+fn frag_main2_inner(inputs : FragmentInput) {
+ var x : f32 = foo(inputs);
+}
+
+@stage(fragment)
+fn frag_main2(tint_symbol_2 : tint_symbol_3) {
+ frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
+}
+
+fn foo(x : FragmentInput) -> f32 {
+ return (x.value * x.mul);
+}
+
+struct FragmentInput {
+ value : f32;
+ mul : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) {
auto* src = R"(
struct FragmentInput {
@@ -931,6 +1527,74 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main1(inputs : FragmentInput) {
+ global_inputs = inputs;
+ var r : f32 = foo();
+ var g : f32 = bar();
+}
+
+fn foo() -> f32 {
+ return global_inputs.col1 * 0.5;
+}
+
+fn bar() -> f32 {
+ return global_inputs.col2 * 2.0;
+}
+
+var<private> global_inputs : FragmentInput;
+
+struct FragmentInput {
+ @location(0) col1 : f32;
+ @location(1) col2 : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ col1 : f32;
+ @location(1)
+ col2 : f32;
+}
+
+fn frag_main1_inner(inputs : FragmentInput) {
+ global_inputs = inputs;
+ var r : f32 = foo();
+ var g : f32 = bar();
+}
+
+@stage(fragment)
+fn frag_main1(tint_symbol : tint_symbol_1) {
+ frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
+}
+
+fn foo() -> f32 {
+ return (global_inputs.col1 * 0.5);
+}
+
+fn bar() -> f32 {
+ return (global_inputs.col2 * 2.0);
+}
+
+var<private> global_inputs : FragmentInput;
+
+struct FragmentInput {
+ col1 : f32;
+ col2 : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) {
auto* src = R"(
type myf32 = f32;
@@ -1018,6 +1682,93 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
+ var x : myf32 = foo(inputs);
+ return MyFragmentOutput(x, inputs.col2);
+}
+
+type MyFragmentInput = FragmentInput;
+
+type MyFragmentOutput = FragmentOutput;
+
+fn foo(x : MyFragmentInput) -> myf32 {
+ return x.col1;
+}
+
+struct FragmentInput {
+ @location(0) col1 : myf32;
+ @location(1) col2 : myf32;
+};
+
+struct FragmentOutput {
+ @location(0) col1 : myf32;
+ @location(1) col2 : myf32;
+};
+
+type myf32 = f32;
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ col1 : f32;
+ @location(1)
+ col2 : f32;
+}
+
+struct tint_symbol_2 {
+ @location(0)
+ col1 : f32;
+ @location(1)
+ col2 : f32;
+}
+
+fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
+ var x : myf32 = foo(inputs);
+ return MyFragmentOutput(x, inputs.col2);
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.col1 = inner_result.col1;
+ wrapper_result.col2 = inner_result.col2;
+ return wrapper_result;
+}
+
+type MyFragmentInput = FragmentInput;
+
+type MyFragmentOutput = FragmentOutput;
+
+fn foo(x : MyFragmentInput) -> myf32 {
+ return x.col1;
+}
+
+struct FragmentInput {
+ col1 : myf32;
+ col2 : myf32;
+}
+
+struct FragmentOutput {
+ col1 : myf32;
+ col2 : myf32;
+}
+
+type myf32 = f32;
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) {
auto* src = R"(
struct VertexOut {
@@ -1110,6 +1861,98 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(inputs : FragmentIn,
+ @location(3) @interpolate(perspective, centroid) loc3 : f32) {
+ let x = inputs.loc1 + inputs.loc2 + loc3;
+}
+
+@stage(vertex)
+fn vert_main() -> VertexOut {
+ return VertexOut();
+}
+
+struct VertexOut {
+ @builtin(position) pos : vec4<f32>;
+ @location(1) @interpolate(flat) loc1: f32;
+ @location(2) @interpolate(linear, sample) loc2 : f32;
+ @location(3) @interpolate(perspective, centroid) loc3 : f32;
+};
+
+struct FragmentIn {
+ @location(1) @interpolate(flat) loc1: f32;
+ @location(2) @interpolate(linear, sample) loc2 : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(1) @interpolate(flat)
+ loc1 : f32;
+ @location(2) @interpolate(linear, sample)
+ loc2 : f32;
+ @location(3) @interpolate(perspective, centroid)
+ loc3 : f32;
+}
+
+fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
+ let x = ((inputs.loc1 + inputs.loc2) + loc3);
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol : tint_symbol_1) {
+ frag_main_inner(FragmentIn(tint_symbol.loc1, tint_symbol.loc2), tint_symbol.loc3);
+}
+
+struct tint_symbol_2 {
+ @location(1) @interpolate(flat)
+ loc1 : f32;
+ @location(2) @interpolate(linear, sample)
+ loc2 : f32;
+ @location(3) @interpolate(perspective, centroid)
+ loc3 : f32;
+ @builtin(position)
+ pos : vec4<f32>;
+}
+
+fn vert_main_inner() -> VertexOut {
+ return VertexOut();
+}
+
+@stage(vertex)
+fn vert_main() -> tint_symbol_2 {
+ let inner_result = vert_main_inner();
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.pos = inner_result.pos;
+ wrapper_result.loc1 = inner_result.loc1;
+ wrapper_result.loc2 = inner_result.loc2;
+ wrapper_result.loc3 = inner_result.loc3;
+ return wrapper_result;
+}
+
+struct VertexOut {
+ pos : vec4<f32>;
+ loc1 : f32;
+ loc2 : f32;
+ loc3 : f32;
+}
+
+struct FragmentIn {
+ loc1 : f32;
+ loc2 : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes_Integers_Spirv) {
// Test that we add a Flat attribute to integers that are vertex outputs and
// fragment inputs, but not vertex inputs or fragment outputs.
@@ -1241,6 +2084,138 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ InterpolateAttributes_Integers_Spirv_OutOfOrder) {
+ // Test that we add a Flat attribute to integers that are vertex outputs and
+ // fragment inputs, but not vertex inputs or fragment outputs.
+ auto* src = R"(
+@stage(vertex)
+fn vert_main(in : VertexIn) -> VertexOut {
+ return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
+}
+
+@stage(fragment)
+fn frag_main(inputs : FragmentInterface) -> FragmentInterface {
+ return inputs;
+}
+
+struct VertexIn {
+ @location(0) i : i32;
+ @location(1) u : u32;
+ @location(2) vi : vec4<i32>;
+ @location(3) vu : vec4<u32>;
+};
+
+struct VertexOut {
+ @location(0) @interpolate(flat) i : i32;
+ @location(1) @interpolate(flat) u : u32;
+ @location(2) @interpolate(flat) vi : vec4<i32>;
+ @location(3) @interpolate(flat) vu : vec4<u32>;
+ @builtin(position) pos : vec4<f32>;
+};
+
+struct FragmentInterface {
+ @location(0) @interpolate(flat) i : i32;
+ @location(1) @interpolate(flat) u : u32;
+ @location(2) @interpolate(flat) vi : vec4<i32>;
+ @location(3) @interpolate(flat) vu : vec4<u32>;
+};
+)";
+
+ auto* expect =
+ R"(
+@location(0) @internal(disable_validation__ignore_storage_class) var<in> i_1 : i32;
+
+@location(1) @internal(disable_validation__ignore_storage_class) var<in> u_1 : u32;
+
+@location(2) @internal(disable_validation__ignore_storage_class) var<in> vi_1 : vec4<i32>;
+
+@location(3) @internal(disable_validation__ignore_storage_class) var<in> vu_1 : vec4<u32>;
+
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_2 : i32;
+
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_2 : u32;
+
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_2 : vec4<i32>;
+
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_2 : vec4<u32>;
+
+@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> i_3 : i32;
+
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> u_3 : u32;
+
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vi_3 : vec4<i32>;
+
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
+
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
+
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
+
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
+
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
+
+fn vert_main_inner(in : VertexIn) -> VertexOut {
+ return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
+}
+
+@stage(vertex)
+fn vert_main() {
+ let inner_result = vert_main_inner(VertexIn(i_1, u_1, vi_1, vu_1));
+ i_2 = inner_result.i;
+ u_2 = inner_result.u;
+ vi_2 = inner_result.vi;
+ vu_2 = inner_result.vu;
+ pos_1 = inner_result.pos;
+}
+
+fn frag_main_inner(inputs : FragmentInterface) -> FragmentInterface {
+ return inputs;
+}
+
+@stage(fragment)
+fn frag_main() {
+ let inner_result_1 = frag_main_inner(FragmentInterface(i_3, u_3, vi_3, vu_3));
+ i_4 = inner_result_1.i;
+ u_4 = inner_result_1.u;
+ vi_4 = inner_result_1.vi;
+ vu_4 = inner_result_1.vu;
+}
+
+struct VertexIn {
+ i : i32;
+ u : u32;
+ vi : vec4<i32>;
+ vu : vec4<u32>;
+}
+
+struct VertexOut {
+ i : i32;
+ u : u32;
+ vi : vec4<i32>;
+ vu : vec4<u32>;
+ pos : vec4<f32>;
+}
+
+struct FragmentInterface {
+ i : i32;
+ u : u32;
+ vi : vec4<i32>;
+ vu : vec4<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) {
auto* src = R"(
struct VertexOut {
@@ -1306,6 +2281,71 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn main1() -> VertexOut {
+ return VertexOut();
+}
+
+@stage(vertex)
+fn main2() -> [[builtin(position), invariant]] vec4<f32> {
+ return vec4<f32>();
+}
+
+struct VertexOut {
+ [[builtin(position), invariant]] pos : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @builtin(position) @invariant
+ pos : vec4<f32>;
+}
+
+fn main1_inner() -> VertexOut {
+ return VertexOut();
+}
+
+@stage(vertex)
+fn main1() -> tint_symbol {
+ let inner_result = main1_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.pos = inner_result.pos;
+ return wrapper_result;
+}
+
+struct tint_symbol_1 {
+ @builtin(position) @invariant
+ value : vec4<f32>;
+}
+
+fn main2_inner() -> vec4<f32> {
+ return vec4<f32>();
+}
+
+@stage(vertex)
+fn main2() -> tint_symbol_1 {
+ let inner_result_1 = main2_inner();
+ var wrapper_result_1 : tint_symbol_1;
+ wrapper_result_1.value = inner_result_1;
+ return wrapper_result_1;
+}
+
+struct VertexOut {
+ pos : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes) {
auto* src = R"(
struct FragmentInput {
@@ -1374,6 +2414,74 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main(inputs : FragmentInput) -> FragmentOutput {
+ return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
+}
+
+struct FragmentInput {
+ @size(16) @location(1) value : f32;
+ @builtin(position) @align(32) coord : vec4<f32>;
+ @location(0) @interpolate(linear, sample) @align(128) loc0 : f32;
+};
+
+struct FragmentOutput {
+ @size(16) @location(1) @interpolate(flat) value : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0) @interpolate(linear, sample)
+ loc0 : f32;
+ @location(1)
+ value : f32;
+ @builtin(position)
+ coord : vec4<f32>;
+}
+
+struct tint_symbol_2 {
+ @location(1) @interpolate(flat)
+ value : f32;
+}
+
+fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
+ return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, tint_symbol.coord, tint_symbol.loc0));
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.value = inner_result.value;
+ return wrapper_result;
+}
+
+struct FragmentInput {
+ @size(16)
+ value : f32;
+ @align(32)
+ coord : vec4<f32>;
+ @align(128)
+ loc0 : f32;
+}
+
+struct FragmentOutput {
+ @size(16)
+ value : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
auto* src = R"(
struct VertexOutput {
@@ -1479,6 +2587,111 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, SortedMembers_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main() -> VertexOutput {
+ return VertexOutput();
+}
+
+@stage(fragment)
+fn frag_main(@builtin(front_facing) ff : bool,
+ @location(2) @interpolate(flat) c : i32,
+ inputs : FragmentInputExtra,
+ @location(1) @interpolate(flat) b : u32) {
+}
+
+struct VertexOutput {
+ @location(1) @interpolate(flat) b : u32;
+ @builtin(position) pos : vec4<f32>;
+ @location(3) @interpolate(flat) d : u32;
+ @location(0) a : f32;
+ @location(2) @interpolate(flat) c : i32;
+};
+
+struct FragmentInputExtra {
+ @location(3) @interpolate(flat) d : u32;
+ @builtin(position) pos : vec4<f32>;
+ @location(0) a : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @location(0)
+ a : f32;
+ @location(1) @interpolate(flat)
+ b : u32;
+ @location(2) @interpolate(flat)
+ c : i32;
+ @location(3) @interpolate(flat)
+ d : u32;
+ @builtin(position)
+ pos : vec4<f32>;
+}
+
+fn vert_main_inner() -> VertexOutput {
+ return VertexOutput();
+}
+
+@stage(vertex)
+fn vert_main() -> tint_symbol {
+ let inner_result = vert_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.b = inner_result.b;
+ wrapper_result.pos = inner_result.pos;
+ wrapper_result.d = inner_result.d;
+ wrapper_result.a = inner_result.a;
+ wrapper_result.c = inner_result.c;
+ return wrapper_result;
+}
+
+struct tint_symbol_2 {
+ @location(0)
+ a : f32;
+ @location(1) @interpolate(flat)
+ b : u32;
+ @location(2) @interpolate(flat)
+ c : i32;
+ @location(3) @interpolate(flat)
+ d : u32;
+ @builtin(position)
+ pos : vec4<f32>;
+ @builtin(front_facing)
+ ff : bool;
+}
+
+fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
+}
+
+@stage(fragment)
+fn frag_main(tint_symbol_1 : tint_symbol_2) {
+ frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, tint_symbol_1.pos, tint_symbol_1.a), tint_symbol_1.b);
+}
+
+struct VertexOutput {
+ b : u32;
+ pos : vec4<f32>;
+ d : u32;
+ a : f32;
+ c : i32;
+}
+
+struct FragmentInputExtra {
+ d : u32;
+ pos : vec4<f32>;
+ a : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) {
auto* src = R"(
@stage(fragment)
@@ -1704,6 +2917,60 @@
}
TEST_F(CanonicalizeEntryPointIOTest,
+ FixedSampleMask_StructWithAuthoredMask_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main() -> Output {
+ return Output(0.5, 7u, 1.0);
+}
+
+struct Output {
+ @builtin(frag_depth) depth : f32;
+ @builtin(sample_mask) mask : u32;
+ @location(0) value : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @location(0)
+ value : f32;
+ @builtin(frag_depth)
+ depth : f32;
+ @builtin(sample_mask)
+ mask : u32;
+}
+
+fn frag_main_inner() -> Output {
+ return Output(0.5, 7u, 1.0);
+}
+
+@stage(fragment)
+fn frag_main() -> tint_symbol {
+ let inner_result = frag_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.depth = inner_result.depth;
+ wrapper_result.mask = (inner_result.mask & 3u);
+ wrapper_result.value = inner_result.value;
+ return wrapper_result;
+}
+
+struct Output {
+ depth : f32;
+ mask : u32;
+ value : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(CanonicalizeEntryPointIOTest,
FixedSampleMask_StructWithoutAuthoredMask) {
auto* src = R"(
struct Output {
@@ -1755,6 +3022,58 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ FixedSampleMask_StructWithoutAuthoredMask_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn frag_main() -> Output {
+ return Output(0.5, 1.0);
+}
+
+struct Output {
+ @builtin(frag_depth) depth : f32;
+ @location(0) value : f32;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @location(0)
+ value : f32;
+ @builtin(frag_depth)
+ depth : f32;
+ @builtin(sample_mask)
+ fixed_sample_mask : u32;
+}
+
+fn frag_main_inner() -> Output {
+ return Output(0.5, 1.0);
+}
+
+@stage(fragment)
+fn frag_main() -> tint_symbol {
+ let inner_result = frag_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.depth = inner_result.depth;
+ wrapper_result.value = inner_result.value;
+ wrapper_result.fixed_sample_mask = 3u;
+ return wrapper_result;
+}
+
+struct Output {
+ depth : f32;
+ value : f32;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_MultipleShaders) {
auto* src = R"(
@stage(fragment)
@@ -2009,6 +3328,48 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ EmitVertexPointSize_ReturnStruct_Spirv_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main() -> VertOut {
+ return VertOut();
+}
+
+struct VertOut {
+ @builtin(position) pos : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+
+@builtin(pointsize) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size : f32;
+
+fn vert_main_inner() -> VertOut {
+ return VertOut();
+}
+
+@stage(vertex)
+fn vert_main() {
+ let inner_result = vert_main_inner();
+ pos_1 = inner_result.pos;
+ vertex_point_size = 1.0;
+}
+
+struct VertOut {
+ pos : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_ReturnStruct_Msl) {
auto* src = R"(
struct VertOut {
@@ -2055,6 +3416,53 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ EmitVertexPointSize_ReturnStruct_Msl_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main() -> VertOut {
+ return VertOut();
+}
+
+struct VertOut {
+ @builtin(position) pos : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ @builtin(position)
+ pos : vec4<f32>;
+ @builtin(pointsize)
+ vertex_point_size : f32;
+}
+
+fn vert_main_inner() -> VertOut {
+ return VertOut();
+}
+
+@stage(vertex)
+fn vert_main() -> tint_symbol {
+ let inner_result = vert_main_inner();
+ var wrapper_result : tint_symbol;
+ wrapper_result.pos = inner_result.pos;
+ wrapper_result.vertex_point_size = 1.0;
+ return wrapper_result;
+}
+
+struct VertOut {
+ pos : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Spirv) {
auto* src = R"(
var<private> vertex_point_size : f32;
@@ -2133,6 +3541,85 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ EmitVertexPointSize_AvoidNameClash_Spirv_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
+ return VertOut();
+}
+
+struct VertIn1 {
+ @location(0) collide : f32;
+};
+
+struct VertIn2 {
+ @location(1) collide : f32;
+};
+
+var<private> vertex_point_size : f32;
+var<private> vertex_point_size_1 : f32;
+var<private> vertex_point_size_2 : f32;
+
+struct VertOut {
+ @location(0) vertex_point_size : f32;
+ @builtin(position) vertex_point_size_1 : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+@location(0) @internal(disable_validation__ignore_storage_class) var<in> collide_2 : f32;
+
+@location(1) @internal(disable_validation__ignore_storage_class) var<in> collide_3 : f32;
+
+@location(0) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_3 : f32;
+
+@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_1_1 : vec4<f32>;
+
+@builtin(pointsize) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_4 : f32;
+
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
+ return VertOut();
+}
+
+@stage(vertex)
+fn vert_main() {
+ let inner_result = vert_main_inner(VertIn1(collide_2), VertIn2(collide_3));
+ vertex_point_size_3 = inner_result.vertex_point_size;
+ vertex_point_size_1_1 = inner_result.vertex_point_size_1;
+ vertex_point_size_4 = 1.0;
+}
+
+struct VertIn1 {
+ collide : f32;
+}
+
+struct VertIn2 {
+ collide : f32;
+}
+
+var<private> vertex_point_size : f32;
+
+var<private> vertex_point_size_1 : f32;
+
+var<private> vertex_point_size_2 : f32;
+
+struct VertOut {
+ vertex_point_size : f32;
+ vertex_point_size_1 : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Msl) {
auto* src = R"(
struct VertIn1 {
@@ -2209,6 +3696,83 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ EmitVertexPointSize_AvoidNameClash_Msl_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
+ return VertOut();
+}
+
+struct VertIn1 {
+ @location(0) collide : f32;
+};
+
+struct VertIn2 {
+ @location(1) collide : f32;
+};
+
+struct VertOut {
+ @location(0) vertex_point_size : vec4<f32>;
+ @builtin(position) vertex_point_size_1 : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ collide : f32;
+ @location(1)
+ collide_2 : f32;
+}
+
+struct tint_symbol_2 {
+ @location(0)
+ vertex_point_size : vec4<f32>;
+ @builtin(position)
+ vertex_point_size_1 : vec4<f32>;
+ @builtin(pointsize)
+ vertex_point_size_2 : f32;
+}
+
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
+ return VertOut();
+}
+
+@stage(vertex)
+fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.vertex_point_size = inner_result.vertex_point_size;
+ wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
+ wrapper_result.vertex_point_size_2 = 1.0;
+ return wrapper_result;
+}
+
+struct VertIn1 {
+ collide : f32;
+}
+
+struct VertIn2 {
+ collide : f32;
+}
+
+struct VertOut {
+ vertex_point_size : vec4<f32>;
+ vertex_point_size_1 : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Hlsl) {
auto* src = R"(
struct VertIn1 {
@@ -2285,6 +3849,83 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest,
+ EmitVertexPointSize_AvoidNameClash_Hlsl_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
+ return VertOut();
+}
+
+struct VertIn1 {
+ @location(0) collide : f32;
+};
+
+struct VertIn2 {
+ @location(1) collide : f32;
+};
+
+struct VertOut {
+ @location(0) vertex_point_size : vec4<f32>;
+ @builtin(position) vertex_point_size_1 : vec4<f32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ @location(0)
+ collide : f32;
+ @location(1)
+ collide_2 : f32;
+}
+
+struct tint_symbol_2 {
+ @location(0)
+ vertex_point_size : vec4<f32>;
+ @builtin(position)
+ vertex_point_size_1 : vec4<f32>;
+ @builtin(pointsize)
+ vertex_point_size_2 : f32;
+}
+
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
+ return VertOut();
+}
+
+@stage(vertex)
+fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.vertex_point_size = inner_result.vertex_point_size;
+ wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
+ wrapper_result.vertex_point_size_2 = 1.0;
+ return wrapper_result;
+}
+
+struct VertIn1 {
+ collide : f32;
+}
+
+struct VertIn2 {
+ collide : f32;
+}
+
+struct VertOut {
+ vertex_point_size : vec4<f32>;
+ vertex_point_size_1 : vec4<f32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl, 0xFFFFFFFF, true);
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, SpirvSampleMaskBuiltins) {
auto* src = R"(
@stage(fragment)
diff --git a/src/transform/combine_samplers_test.cc b/src/transform/combine_samplers_test.cc
index f0c3a33..481ef3d 100644
--- a/src/transform/combine_samplers_test.cc
+++ b/src/transform/combine_samplers_test.cc
@@ -65,6 +65,34 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, SimplePair_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d<f32>;
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t_s, placeholder_sampler, vec2<f32>(1.0, 2.0));
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, SimplePairInAFunction) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
@@ -101,6 +129,42 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, SimplePairInAFunction_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return sample(t, s, vec2<f32>(1.0, 2.0));
+}
+
+fn sample(t : texture_2d<f32>, s : sampler, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t, s, coords);
+}
+
+@group(0) @binding(1) var s : sampler;
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return sample(t_s, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn sample(t_s_1 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t_s_1, placeholder_sampler, coords);
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, SimplePairRename) {
auto* src = R"(
@group(0) @binding(1) var t : texture_2d<f32>;
@@ -212,6 +276,45 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, AliasedTypes_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return sample(t, s, vec2<f32>(1.0, 2.0));
+}
+
+fn sample(t : Tex2d, s : sampler, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t, s, coords);
+}
+
+@group(0) @binding(0) var t : Tex2d;
+@group(0) @binding(1) var s : sampler;
+
+type Tex2d = texture_2d<f32>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return sample(t_s, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn sample(t_s_1 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t_s_1, placeholder_sampler, coords);
+}
+
+type Tex2d = texture_2d<f32>;
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, SimplePairInTwoFunctions) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
@@ -256,6 +359,49 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, SimplePairInTwoFunctions_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return f(t, s, vec2<f32>(1.0, 2.0));
+}
+
+fn f(t : texture_2d<f32>, s : sampler, coords : vec2<f32>) -> vec4<f32> {
+ return g(t, s, coords);
+}
+
+fn g(t : texture_2d<f32>, s : sampler, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t, s, coords);
+}
+
+@group(0) @binding(1) var s : sampler;
+@group(0) @binding(0) var t : texture_2d<f32>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return f(t_s, vec2<f32>(1.0, 2.0));
+}
+
+fn f(t_s_1 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return g(t_s_1, coords);
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn g(t_s_2 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t_s_2, placeholder_sampler, coords);
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, TwoFunctionsGenerateSamePair) {
auto* src = R"(
@group(1) @binding(0) var tex : texture_2d<f32>;
@@ -500,6 +646,44 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, GlobalTextureLocalSampler_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return f(samp1, samp2, vec2<f32>(1.0, 2.0));
+}
+
+fn f(s1 : sampler, s2 : sampler, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(tex, s1, coords) + textureSample(tex, s2, coords);
+}
+
+@group(0) @binding(1) var samp1 : sampler;
+@group(0) @binding(2) var samp2 : sampler;
+@group(0) @binding(0) var tex : texture_2d<f32>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp1 : texture_2d<f32>;
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp2 : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return f(tex_samp1, tex_samp2, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn f(tex_s1 : texture_2d<f32>, tex_s2 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return (textureSample(tex_s1, placeholder_sampler, coords) + textureSample(tex_s2, placeholder_sampler, coords));
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, LocalTextureGlobalSampler) {
auto* src = R"(
@group(0) @binding(0) var tex1 : texture_2d<f32>;
@@ -540,6 +724,44 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, LocalTextureGlobalSampler_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return f(tex1, tex2, vec2<f32>(1.0, 2.0));
+}
+
+fn f(t1 : texture_2d<f32>, t2 : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return textureSample(t1, samp, coords) + textureSample(t2, samp, coords);
+}
+
+@group(0) @binding(2) var samp : sampler;
+@group(0) @binding(0) var tex1 : texture_2d<f32>;
+@group(0) @binding(1) var tex2 : texture_2d<f32>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex1_samp : texture_2d<f32>;
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex2_samp : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return f(tex1_samp, tex2_samp, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn f(t1_samp : texture_2d<f32>, t2_samp : texture_2d<f32>, coords : vec2<f32>) -> vec4<f32> {
+ return (textureSample(t1_samp, placeholder_sampler, coords) + textureSample(t2_samp, placeholder_sampler, coords));
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, TextureLoadNoSampler) {
auto* src = R"(
@group(0) @binding(0) var tex : texture_2d<f32>;
@@ -687,6 +909,41 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, TextureSampleCompareInAFunction_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return vec4<f32>(f(tex, samp, vec2<f32>(1.0, 2.0)));
+}
+
+fn f(t : texture_depth_2d, s : sampler_comparison, coords : vec2<f32>) -> f32 {
+ return textureSampleCompare(t, s, coords, 5.0f);
+}
+
+@group(0) @binding(0) var tex : texture_depth_2d;
+@group(0) @binding(1) var samp : sampler_comparison;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp : texture_depth_2d;
+
+fn main() -> vec4<f32> {
+ return vec4<f32>(f(tex_samp, vec2<f32>(1.0, 2.0)));
+}
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_comparison_sampler : sampler_comparison;
+
+fn f(t_s : texture_depth_2d, coords : vec2<f32>) -> f32 {
+ return textureSampleCompare(t_s, placeholder_comparison_sampler, coords, 5.0);
+}
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CombineSamplersTest, BindingPointCollision) {
auto* src = R"(
@group(1) @binding(0) var tex : texture_2d<f32>;
@@ -719,6 +976,37 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CombineSamplersTest, BindingPointCollision_OutOfOrder) {
+ auto* src = R"(
+fn main() -> vec4<f32> {
+ return textureSample(tex, samp, gcoords);
+}
+
+@group(1) @binding(1) var samp : sampler;
+@group(0) @binding(0) var<uniform> gcoords : vec2<f32>;
+@group(1) @binding(0) var tex : texture_2d<f32>;
+
+)";
+ auto* expect = R"(
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp : texture_2d<f32>;
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(tex_samp, placeholder_sampler, gcoords);
+}
+
+@internal(disable_validation__binding_point_collision) @group(0) @binding(0) var<uniform> gcoords : vec2<f32>;
+)";
+
+ DataMap data;
+ data.Add<CombineSamplers::BindingInfo>(CombineSamplers::BindingMap(),
+ sem::BindingPoint());
+ auto got = Run<CombineSamplers>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/decompose_memory_access_test.cc b/src/transform/decompose_memory_access_test.cc
index dffb3f2..2f8ed59 100644
--- a/src/transform/decompose_memory_access_test.cc
+++ b/src/transform/decompose_memory_access_test.cc
@@ -246,6 +246,202 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, SB_BasicLoad_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var a : i32 = sb.a;
+ var b : u32 = sb.b;
+ var c : f32 = sb.c;
+ var d : vec2<i32> = sb.d;
+ var e : vec2<u32> = sb.e;
+ var f : vec2<f32> = sb.f;
+ var g : vec3<i32> = sb.g;
+ var h : vec3<u32> = sb.h;
+ var i : vec3<f32> = sb.i;
+ var j : vec4<i32> = sb.j;
+ var k : vec4<u32> = sb.k;
+ var l : vec4<f32> = sb.l;
+ var m : mat2x2<f32> = sb.m;
+ var n : mat2x3<f32> = sb.n;
+ var o : mat2x4<f32> = sb.o;
+ var p : mat3x2<f32> = sb.p;
+ var q : mat3x3<f32> = sb.q;
+ var r : mat3x4<f32> = sb.r;
+ var s : mat4x2<f32> = sb.s;
+ var t : mat4x3<f32> = sb.t;
+ var u : mat4x4<f32> = sb.u;
+ var v : array<vec3<f32>, 2> = sb.v;
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> i32
+
+@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> u32
+
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> f32
+
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<i32>
+
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<f32>
+
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<i32>
+
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<f32>
+
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<f32>
+
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x2<f32> {
+ return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x3<f32> {
+ return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x4<f32> {
+ return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x2<f32> {
+ return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x3<f32> {
+ return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x4<f32> {
+ return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x2<f32> {
+ return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x3<f32> {
+ return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x4<f32> {
+ return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> array<vec3<f32>, 2u> {
+ var arr : array<vec3<f32>, 2u>;
+ for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+ arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+ }
+ return arr;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var a : i32 = tint_symbol(sb, 0u);
+ var b : u32 = tint_symbol_1(sb, 4u);
+ var c : f32 = tint_symbol_2(sb, 8u);
+ var d : vec2<i32> = tint_symbol_3(sb, 16u);
+ var e : vec2<u32> = tint_symbol_4(sb, 24u);
+ var f : vec2<f32> = tint_symbol_5(sb, 32u);
+ var g : vec3<i32> = tint_symbol_6(sb, 48u);
+ var h : vec3<u32> = tint_symbol_7(sb, 64u);
+ var i : vec3<f32> = tint_symbol_8(sb, 80u);
+ var j : vec4<i32> = tint_symbol_9(sb, 96u);
+ var k : vec4<u32> = tint_symbol_10(sb, 112u);
+ var l : vec4<f32> = tint_symbol_11(sb, 128u);
+ var m : mat2x2<f32> = tint_symbol_12(sb, 144u);
+ var n : mat2x3<f32> = tint_symbol_13(sb, 160u);
+ var o : mat2x4<f32> = tint_symbol_14(sb, 192u);
+ var p : mat3x2<f32> = tint_symbol_15(sb, 224u);
+ var q : mat3x3<f32> = tint_symbol_16(sb, 256u);
+ var r : mat3x4<f32> = tint_symbol_17(sb, 304u);
+ var s : mat4x2<f32> = tint_symbol_18(sb, 352u);
+ var t : mat4x3<f32> = tint_symbol_19(sb, 384u);
+ var u : mat4x4<f32> = tint_symbol_20(sb, 448u);
+ var v : array<vec3<f32>, 2> = tint_symbol_21(sb, 512u);
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad) {
auto* src = R"(
struct UB {
@@ -442,6 +638,202 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var a : i32 = ub.a;
+ var b : u32 = ub.b;
+ var c : f32 = ub.c;
+ var d : vec2<i32> = ub.d;
+ var e : vec2<u32> = ub.e;
+ var f : vec2<f32> = ub.f;
+ var g : vec3<i32> = ub.g;
+ var h : vec3<u32> = ub.h;
+ var i : vec3<f32> = ub.i;
+ var j : vec4<i32> = ub.j;
+ var k : vec4<u32> = ub.k;
+ var l : vec4<f32> = ub.l;
+ var m : mat2x2<f32> = ub.m;
+ var n : mat2x3<f32> = ub.n;
+ var o : mat2x4<f32> = ub.o;
+ var p : mat3x2<f32> = ub.p;
+ var q : mat3x3<f32> = ub.q;
+ var r : mat3x4<f32> = ub.r;
+ var s : mat4x2<f32> = ub.s;
+ var t : mat4x3<f32> = ub.t;
+ var u : mat4x4<f32> = ub.u;
+ var v : array<vec3<f32>, 2> = ub.v;
+}
+
+@group(0) @binding(0) var<uniform> ub : UB;
+
+struct UB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> i32
+
+@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> u32
+
+@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> f32
+
+@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec2<i32>
+
+@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec2<f32>
+
+@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec3<i32>
+
+@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec3<f32>
+
+@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> vec4<f32>
+
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat2x2<f32> {
+ return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat2x3<f32> {
+ return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat2x4<f32> {
+ return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat3x2<f32> {
+ return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat3x3<f32> {
+ return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat3x4<f32> {
+ return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat4x2<f32> {
+ return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat4x3<f32> {
+ return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat4x4<f32> {
+ return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> array<vec3<f32>, 2u> {
+ var arr : array<vec3<f32>, 2u>;
+ for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+ arr[i_1] = tint_symbol_8(buffer, (offset + (i_1 * 16u)));
+ }
+ return arr;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var a : i32 = tint_symbol(ub, 0u);
+ var b : u32 = tint_symbol_1(ub, 4u);
+ var c : f32 = tint_symbol_2(ub, 8u);
+ var d : vec2<i32> = tint_symbol_3(ub, 16u);
+ var e : vec2<u32> = tint_symbol_4(ub, 24u);
+ var f : vec2<f32> = tint_symbol_5(ub, 32u);
+ var g : vec3<i32> = tint_symbol_6(ub, 48u);
+ var h : vec3<u32> = tint_symbol_7(ub, 64u);
+ var i : vec3<f32> = tint_symbol_8(ub, 80u);
+ var j : vec4<i32> = tint_symbol_9(ub, 96u);
+ var k : vec4<u32> = tint_symbol_10(ub, 112u);
+ var l : vec4<f32> = tint_symbol_11(ub, 128u);
+ var m : mat2x2<f32> = tint_symbol_12(ub, 144u);
+ var n : mat2x3<f32> = tint_symbol_13(ub, 160u);
+ var o : mat2x4<f32> = tint_symbol_14(ub, 192u);
+ var p : mat3x2<f32> = tint_symbol_15(ub, 224u);
+ var q : mat3x3<f32> = tint_symbol_16(ub, 256u);
+ var r : mat3x4<f32> = tint_symbol_17(ub, 304u);
+ var s : mat4x2<f32> = tint_symbol_18(ub, 352u);
+ var t : mat4x3<f32> = tint_symbol_19(ub, 384u);
+ var u : mat4x4<f32> = tint_symbol_20(ub, 448u);
+ var v : array<vec3<f32>, 2> = tint_symbol_21(ub, 512u);
+}
+
+@group(0) @binding(0) var<uniform> ub : UB;
+
+struct UB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, SB_BasicStore) {
auto* src = R"(
struct SB {
@@ -655,6 +1047,219 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, SB_BasicStore_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ sb.a = i32();
+ sb.b = u32();
+ sb.c = f32();
+ sb.d = vec2<i32>();
+ sb.e = vec2<u32>();
+ sb.f = vec2<f32>();
+ sb.g = vec3<i32>();
+ sb.h = vec3<u32>();
+ sb.i = vec3<f32>();
+ sb.j = vec4<i32>();
+ sb.k = vec4<u32>();
+ sb.l = vec4<f32>();
+ sb.m = mat2x2<f32>();
+ sb.n = mat2x3<f32>();
+ sb.o = mat2x4<f32>();
+ sb.p = mat3x2<f32>();
+ sb.q = mat3x3<f32>();
+ sb.r = mat3x4<f32>();
+ sb.s = mat4x2<f32>();
+ sb.t = mat4x3<f32>();
+ sb.u = mat4x4<f32>();
+ sb.v = array<vec3<f32>, 2>();
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : i32)
+
+@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : u32)
+
+@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : f32)
+
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<i32>)
+
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<f32>)
+
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<i32>)
+
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<f32>)
+
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<f32>)
+
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x2<f32>) {
+ tint_symbol_5(buffer, (offset + 0u), value[0u]);
+ tint_symbol_5(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x3<f32>) {
+ tint_symbol_8(buffer, (offset + 0u), value[0u]);
+ tint_symbol_8(buffer, (offset + 16u), value[1u]);
+}
+
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x4<f32>) {
+ tint_symbol_11(buffer, (offset + 0u), value[0u]);
+ tint_symbol_11(buffer, (offset + 16u), value[1u]);
+}
+
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x2<f32>) {
+ tint_symbol_5(buffer, (offset + 0u), value[0u]);
+ tint_symbol_5(buffer, (offset + 8u), value[1u]);
+ tint_symbol_5(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x3<f32>) {
+ tint_symbol_8(buffer, (offset + 0u), value[0u]);
+ tint_symbol_8(buffer, (offset + 16u), value[1u]);
+ tint_symbol_8(buffer, (offset + 32u), value[2u]);
+}
+
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x4<f32>) {
+ tint_symbol_11(buffer, (offset + 0u), value[0u]);
+ tint_symbol_11(buffer, (offset + 16u), value[1u]);
+ tint_symbol_11(buffer, (offset + 32u), value[2u]);
+}
+
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x2<f32>) {
+ tint_symbol_5(buffer, (offset + 0u), value[0u]);
+ tint_symbol_5(buffer, (offset + 8u), value[1u]);
+ tint_symbol_5(buffer, (offset + 16u), value[2u]);
+ tint_symbol_5(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x3<f32>) {
+ tint_symbol_8(buffer, (offset + 0u), value[0u]);
+ tint_symbol_8(buffer, (offset + 16u), value[1u]);
+ tint_symbol_8(buffer, (offset + 32u), value[2u]);
+ tint_symbol_8(buffer, (offset + 48u), value[3u]);
+}
+
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x4<f32>) {
+ tint_symbol_11(buffer, (offset + 0u), value[0u]);
+ tint_symbol_11(buffer, (offset + 16u), value[1u]);
+ tint_symbol_11(buffer, (offset + 32u), value[2u]);
+ tint_symbol_11(buffer, (offset + 48u), value[3u]);
+}
+
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : array<vec3<f32>, 2u>) {
+ var array = value;
+ for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+ tint_symbol_8(buffer, (offset + (i_1 * 16u)), array[i_1]);
+ }
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ tint_symbol(sb, 0u, i32());
+ tint_symbol_1(sb, 4u, u32());
+ tint_symbol_2(sb, 8u, f32());
+ tint_symbol_3(sb, 16u, vec2<i32>());
+ tint_symbol_4(sb, 24u, vec2<u32>());
+ tint_symbol_5(sb, 32u, vec2<f32>());
+ tint_symbol_6(sb, 48u, vec3<i32>());
+ tint_symbol_7(sb, 64u, vec3<u32>());
+ tint_symbol_8(sb, 80u, vec3<f32>());
+ tint_symbol_9(sb, 96u, vec4<i32>());
+ tint_symbol_10(sb, 112u, vec4<u32>());
+ tint_symbol_11(sb, 128u, vec4<f32>());
+ tint_symbol_12(sb, 144u, mat2x2<f32>());
+ tint_symbol_13(sb, 160u, mat2x3<f32>());
+ tint_symbol_14(sb, 192u, mat2x4<f32>());
+ tint_symbol_15(sb, 224u, mat3x2<f32>());
+ tint_symbol_16(sb, 256u, mat3x3<f32>());
+ tint_symbol_17(sb, 304u, mat3x4<f32>());
+ tint_symbol_18(sb, 352u, mat4x2<f32>());
+ tint_symbol_19(sb, 384u, mat4x3<f32>());
+ tint_symbol_20(sb, 448u, mat4x4<f32>());
+ tint_symbol_21(sb, 512u, array<vec3<f32>, 2>());
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, LoadStructure) {
auto* src = R"(
struct SB {
@@ -813,6 +1418,164 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, LoadStructure_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var x : SB = sb;
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> i32
+
+@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> u32
+
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> f32
+
+@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<i32>
+
+@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<u32>
+
+@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec2<f32>
+
+@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<i32>
+
+@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<u32>
+
+@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec3<f32>
+
+@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<i32>
+
+@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<u32>
+
+@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> vec4<f32>
+
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x2<f32> {
+ return mat2x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)));
+}
+
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x3<f32> {
+ return mat2x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x4<f32> {
+ return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x2<f32> {
+ return mat3x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)));
+}
+
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x3<f32> {
+ return mat3x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat3x4<f32> {
+ return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
+}
+
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x2<f32> {
+ return mat4x2<f32>(tint_symbol_6(buffer, (offset + 0u)), tint_symbol_6(buffer, (offset + 8u)), tint_symbol_6(buffer, (offset + 16u)), tint_symbol_6(buffer, (offset + 24u)));
+}
+
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x3<f32> {
+ return mat4x3<f32>(tint_symbol_9(buffer, (offset + 0u)), tint_symbol_9(buffer, (offset + 16u)), tint_symbol_9(buffer, (offset + 32u)), tint_symbol_9(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat4x4<f32> {
+ return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
+}
+
+fn tint_symbol_22(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> array<vec3<f32>, 2u> {
+ var arr : array<vec3<f32>, 2u>;
+ for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+ arr[i_1] = tint_symbol_9(buffer, (offset + (i_1 * 16u)));
+ }
+ return arr;
+}
+
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> SB {
+ return SB(tint_symbol_1(buffer, (offset + 0u)), tint_symbol_2(buffer, (offset + 4u)), tint_symbol_3(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)), tint_symbol_6(buffer, (offset + 32u)), tint_symbol_7(buffer, (offset + 48u)), tint_symbol_8(buffer, (offset + 64u)), tint_symbol_9(buffer, (offset + 80u)), tint_symbol_10(buffer, (offset + 96u)), tint_symbol_11(buffer, (offset + 112u)), tint_symbol_12(buffer, (offset + 128u)), tint_symbol_13(buffer, (offset + 144u)), tint_symbol_14(buffer, (offset + 160u)), tint_symbol_15(buffer, (offset + 192u)), tint_symbol_16(buffer, (offset + 224u)), tint_symbol_17(buffer, (offset + 256u)), tint_symbol_18(buffer, (offset + 304u)), tint_symbol_19(buffer, (offset + 352u)), tint_symbol_20(buffer, (offset + 384u)), tint_symbol_21(buffer, (offset + 448u)), tint_symbol_22(buffer, (offset + 512u)));
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var x : SB = tint_symbol(sb, 0u);
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, StoreStructure) {
auto* src = R"(
struct SB {
@@ -1009,6 +1772,202 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, StoreStructure_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ sb = SB();
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : i32)
+
+@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : u32)
+
+@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : f32)
+
+@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<i32>)
+
+@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<u32>)
+
+@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec2<f32>)
+
+@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<i32>)
+
+@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<u32>)
+
+@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec3<f32>)
+
+@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<i32>)
+
+@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<u32>)
+
+@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : vec4<f32>)
+
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x2<f32>) {
+ tint_symbol_6(buffer, (offset + 0u), value[0u]);
+ tint_symbol_6(buffer, (offset + 8u), value[1u]);
+}
+
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x3<f32>) {
+ tint_symbol_9(buffer, (offset + 0u), value[0u]);
+ tint_symbol_9(buffer, (offset + 16u), value[1u]);
+}
+
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x4<f32>) {
+ tint_symbol_12(buffer, (offset + 0u), value[0u]);
+ tint_symbol_12(buffer, (offset + 16u), value[1u]);
+}
+
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x2<f32>) {
+ tint_symbol_6(buffer, (offset + 0u), value[0u]);
+ tint_symbol_6(buffer, (offset + 8u), value[1u]);
+ tint_symbol_6(buffer, (offset + 16u), value[2u]);
+}
+
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x3<f32>) {
+ tint_symbol_9(buffer, (offset + 0u), value[0u]);
+ tint_symbol_9(buffer, (offset + 16u), value[1u]);
+ tint_symbol_9(buffer, (offset + 32u), value[2u]);
+}
+
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat3x4<f32>) {
+ tint_symbol_12(buffer, (offset + 0u), value[0u]);
+ tint_symbol_12(buffer, (offset + 16u), value[1u]);
+ tint_symbol_12(buffer, (offset + 32u), value[2u]);
+}
+
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x2<f32>) {
+ tint_symbol_6(buffer, (offset + 0u), value[0u]);
+ tint_symbol_6(buffer, (offset + 8u), value[1u]);
+ tint_symbol_6(buffer, (offset + 16u), value[2u]);
+ tint_symbol_6(buffer, (offset + 24u), value[3u]);
+}
+
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x3<f32>) {
+ tint_symbol_9(buffer, (offset + 0u), value[0u]);
+ tint_symbol_9(buffer, (offset + 16u), value[1u]);
+ tint_symbol_9(buffer, (offset + 32u), value[2u]);
+ tint_symbol_9(buffer, (offset + 48u), value[3u]);
+}
+
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat4x4<f32>) {
+ tint_symbol_12(buffer, (offset + 0u), value[0u]);
+ tint_symbol_12(buffer, (offset + 16u), value[1u]);
+ tint_symbol_12(buffer, (offset + 32u), value[2u]);
+ tint_symbol_12(buffer, (offset + 48u), value[3u]);
+}
+
+fn tint_symbol_22(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : array<vec3<f32>, 2u>) {
+ var array = value;
+ for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
+ tint_symbol_9(buffer, (offset + (i_1 * 16u)), array[i_1]);
+ }
+}
+
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : SB) {
+ tint_symbol_1(buffer, (offset + 0u), value.a);
+ tint_symbol_2(buffer, (offset + 4u), value.b);
+ tint_symbol_3(buffer, (offset + 8u), value.c);
+ tint_symbol_4(buffer, (offset + 16u), value.d);
+ tint_symbol_5(buffer, (offset + 24u), value.e);
+ tint_symbol_6(buffer, (offset + 32u), value.f);
+ tint_symbol_7(buffer, (offset + 48u), value.g);
+ tint_symbol_8(buffer, (offset + 64u), value.h);
+ tint_symbol_9(buffer, (offset + 80u), value.i);
+ tint_symbol_10(buffer, (offset + 96u), value.j);
+ tint_symbol_11(buffer, (offset + 112u), value.k);
+ tint_symbol_12(buffer, (offset + 128u), value.l);
+ tint_symbol_13(buffer, (offset + 144u), value.m);
+ tint_symbol_14(buffer, (offset + 160u), value.n);
+ tint_symbol_15(buffer, (offset + 192u), value.o);
+ tint_symbol_16(buffer, (offset + 224u), value.p);
+ tint_symbol_17(buffer, (offset + 256u), value.q);
+ tint_symbol_18(buffer, (offset + 304u), value.r);
+ tint_symbol_19(buffer, (offset + 352u), value.s);
+ tint_symbol_20(buffer, (offset + 384u), value.t);
+ tint_symbol_21(buffer, (offset + 448u), value.u);
+ tint_symbol_22(buffer, (offset + 512u), value.v);
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ tint_symbol(sb, 0u, SB());
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ a : i32;
+ b : u32;
+ c : f32;
+ d : vec2<i32>;
+ e : vec2<u32>;
+ f : vec2<f32>;
+ g : vec3<i32>;
+ h : vec3<u32>;
+ i : vec3<f32>;
+ j : vec4<i32>;
+ k : vec4<u32>;
+ l : vec4<f32>;
+ m : mat2x2<f32>;
+ n : mat2x3<f32>;
+ o : mat2x4<f32>;
+ p : mat3x2<f32>;
+ q : mat3x3<f32>;
+ r : mat3x4<f32>;
+ s : mat4x2<f32>;
+ t : mat4x3<f32>;
+ u : mat4x4<f32>;
+ v : array<vec3<f32>, 2>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain) {
auto* src = R"(
struct S1 {
@@ -1079,6 +2038,76 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var x : f32 = sb.b[4].b[1].b.z;
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : @stride(256) array<S2>;
+};
+
+struct S2 {
+ a : i32;
+ b : @stride(32) array<S1, 3>;
+ c : i32;
+};
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+};
+)";
+
+ // sb.b[4].b[1].b.z
+ // ^ ^ ^ ^ ^ ^
+ // | | | | | |
+ // 128 | |1200| 1224
+ // | | |
+ // 1152 1168 1216
+
+ auto* expect = R"(
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> f32
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var x : f32 = tint_symbol(sb, 1224u);
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : @stride(256) array<S2>;
+}
+
+struct S2 {
+ a : i32;
+ b : @stride(32) array<S1, 3>;
+ c : i32;
+}
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChain) {
auto* src = R"(
struct S1 {
@@ -1148,6 +2177,75 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChain_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var i : i32 = 4;
+ var j : u32 = 1u;
+ var k : i32 = 2;
+ var x : f32 = sb.b[i].b[j].b[k];
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : @stride(256) array<S2>;
+};
+
+struct S2 {
+ a : i32;
+ b : @stride(32) array<S1, 3>;
+ c : i32;
+};
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> f32
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var i : i32 = 4;
+ var j : u32 = 1u;
+ var k : i32 = 2;
+ var x : f32 = tint_symbol(sb, (((((128u + (256u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : @stride(256) array<S2>;
+}
+
+struct S2 {
+ a : i32;
+ b : @stride(32) array<S1, 3>;
+ c : i32;
+}
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChainWithAliases) {
auto* src = R"(
struct S1 {
@@ -1233,6 +2331,92 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest,
+ ComplexDynamicAccessChainWithAliases_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var i : i32 = 4;
+ var j : u32 = 1u;
+ var k : i32 = 2;
+ var x : f32 = sb.b[i].b[j].b[k];
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : A2_Array;
+};
+
+type A2_Array = @stride(256) array<S2>;
+
+type A2 = S2;
+
+struct S2 {
+ a : i32;
+ b : A1_Array;
+ c : i32;
+};
+
+type A1 = S1;
+
+type A1_Array = @stride(32) array<S1, 3>;
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> f32
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var i : i32 = 4;
+ var j : u32 = 1u;
+ var k : i32 = 2;
+ var x : f32 = tint_symbol(sb, (((((128u + (256u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ @size(128)
+ a : i32;
+ b : A2_Array;
+}
+
+type A2_Array = @stride(256) array<S2>;
+
+type A2 = S2;
+
+struct S2 {
+ a : i32;
+ b : A1_Array;
+ c : i32;
+}
+
+type A1 = S1;
+
+type A1_Array = @stride(32) array<S1, 3>;
+
+struct S1 {
+ a : i32;
+ b : vec3<f32>;
+ c : i32;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, StorageBufferAtomics) {
auto* src = R"(
struct SB {
@@ -1378,6 +2562,151 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, StorageBufferAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ atomicStore(&sb.a, 123);
+ atomicLoad(&sb.a);
+ atomicAdd(&sb.a, 123);
+ atomicSub(&sb.a, 123);
+ atomicMax(&sb.a, 123);
+ atomicMin(&sb.a, 123);
+ atomicAnd(&sb.a, 123);
+ atomicOr(&sb.a, 123);
+ atomicXor(&sb.a, 123);
+ atomicExchange(&sb.a, 123);
+ atomicCompareExchangeWeak(&sb.a, 123, 345);
+
+ atomicStore(&sb.b, 123u);
+ atomicLoad(&sb.b);
+ atomicAdd(&sb.b, 123u);
+ atomicSub(&sb.b, 123u);
+ atomicMax(&sb.b, 123u);
+ atomicMin(&sb.b, 123u);
+ atomicAnd(&sb.b, 123u);
+ atomicOr(&sb.b, 123u);
+ atomicXor(&sb.b, 123u);
+ atomicExchange(&sb.b, 123u);
+ atomicCompareExchangeWeak(&sb.b, 123u, 345u);
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ padding : vec4<f32>;
+ a : atomic<i32>;
+ b : atomic<u32>;
+};
+)";
+
+ auto* expect = R"(
+@internal(intrinsic_atomic_store_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32)
+
+@internal(intrinsic_atomic_load_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_1(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> i32
+
+@internal(intrinsic_atomic_add_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_2(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_sub_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_max_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_4(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_min_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_5(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_and_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_or_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_7(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_xor_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_8(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_exchange_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_9(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32) -> i32
+
+@internal(intrinsic_atomic_compare_exchange_weak_storage_i32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_10(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : i32, param_2 : i32) -> vec2<i32>
+
+@internal(intrinsic_atomic_store_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_11(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32)
+
+@internal(intrinsic_atomic_load_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> u32
+
+@internal(intrinsic_atomic_add_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_sub_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_14(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_max_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_15(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_min_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_16(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_and_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_17(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_or_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_18(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_xor_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_19(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_exchange_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_20(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32) -> u32
+
+@internal(intrinsic_atomic_compare_exchange_weak_storage_u32) @internal(disable_validation__function_has_no_body)
+fn tint_symbol_21(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, param_1 : u32, param_2 : u32) -> vec2<u32>
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ tint_symbol(sb, 16u, 123);
+ tint_symbol_1(sb, 16u);
+ tint_symbol_2(sb, 16u, 123);
+ tint_symbol_3(sb, 16u, 123);
+ tint_symbol_4(sb, 16u, 123);
+ tint_symbol_5(sb, 16u, 123);
+ tint_symbol_6(sb, 16u, 123);
+ tint_symbol_7(sb, 16u, 123);
+ tint_symbol_8(sb, 16u, 123);
+ tint_symbol_9(sb, 16u, 123);
+ tint_symbol_10(sb, 16u, 123, 345);
+ tint_symbol_11(sb, 20u, 123u);
+ tint_symbol_12(sb, 20u);
+ tint_symbol_13(sb, 20u, 123u);
+ tint_symbol_14(sb, 20u, 123u);
+ tint_symbol_15(sb, 20u, 123u);
+ tint_symbol_16(sb, 20u, 123u);
+ tint_symbol_17(sb, 20u, 123u);
+ tint_symbol_18(sb, 20u, 123u);
+ tint_symbol_19(sb, 20u, 123u);
+ tint_symbol_20(sb, 20u, 123u);
+ tint_symbol_21(sb, 20u, 123u, 345u);
+}
+
+@group(0) @binding(0) var<storage, read_write> sb : SB;
+
+struct SB {
+ padding : vec4<f32>;
+ a : atomic<i32>;
+ b : atomic<u32>;
+}
+)";
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(DecomposeMemoryAccessTest, WorkgroupBufferAtomics) {
auto* src = R"(
struct S {
@@ -1422,6 +2751,50 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(DecomposeMemoryAccessTest, WorkgroupBufferAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ atomicStore(&(w.a), 123);
+ atomicLoad(&(w.a));
+ atomicAdd(&(w.a), 123);
+ atomicSub(&(w.a), 123);
+ atomicMax(&(w.a), 123);
+ atomicMin(&(w.a), 123);
+ atomicAnd(&(w.a), 123);
+ atomicOr(&(w.a), 123);
+ atomicXor(&(w.a), 123);
+ atomicExchange(&(w.a), 123);
+ atomicCompareExchangeWeak(&(w.a), 123, 345);
+ atomicStore(&(w.b), 123u);
+ atomicLoad(&(w.b));
+ atomicAdd(&(w.b), 123u);
+ atomicSub(&(w.b), 123u);
+ atomicMax(&(w.b), 123u);
+ atomicMin(&(w.b), 123u);
+ atomicAnd(&(w.b), 123u);
+ atomicOr(&(w.b), 123u);
+ atomicXor(&(w.b), 123u);
+ atomicExchange(&(w.b), 123u);
+ atomicCompareExchangeWeak(&(w.b), 123u, 345u);
+}
+
+var<workgroup> w : S;
+
+struct S {
+ padding : vec4<f32>;
+ a : atomic<i32>;
+ b : atomic<u32>;
+}
+)";
+
+ auto* expect = src;
+
+ auto got = Run<DecomposeMemoryAccess>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/external_texture_transform_test.cc b/src/transform/external_texture_transform_test.cc
index 28baadf..0c41717 100644
--- a/src/transform/external_texture_transform_test.cc
+++ b/src/transform/external_texture_transform_test.cc
@@ -50,6 +50,34 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ExternalTextureTransformTest, SampleLevelSinglePlane_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureSampleLevel(t, s, (coord.xy / vec2<f32>(4.0, 4.0)));
+}
+
+@group(0) @binding(1) var t : texture_external;
+
+@group(0) @binding(0) var s : sampler;
+)";
+
+ auto* expect = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureSampleLevel(t, s, (coord.xy / vec2<f32>(4.0, 4.0)), 0.0);
+}
+
+@group(0) @binding(1) var t : texture_2d<f32>;
+
+@group(0) @binding(0) var s : sampler;
+)";
+
+ auto got = Run<ExternalTextureTransform>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ExternalTextureTransformTest, LoadSinglePlane) {
auto* src = R"(
@group(0) @binding(0) var t : texture_external;
@@ -74,6 +102,30 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ExternalTextureTransformTest, LoadSinglePlane_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureLoad(t, vec2<i32>(1, 1));
+}
+
+@group(0) @binding(0) var t : texture_external;
+)";
+
+ auto* expect = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureLoad(t, vec2<i32>(1, 1), 0);
+}
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+)";
+
+ auto got = Run<ExternalTextureTransform>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ExternalTextureTransformTest, DimensionsSinglePlane) {
auto* src = R"(
@group(0) @binding(0) var t : texture_external;
@@ -102,6 +154,34 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ExternalTextureTransformTest, DimensionsSinglePlane_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ var dim : vec2<i32>;
+ dim = textureDimensions(t);
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
+}
+
+@group(0) @binding(0) var t : texture_external;
+)";
+
+ auto* expect = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ var dim : vec2<i32>;
+ dim = textureDimensions(t);
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
+}
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+)";
+
+ auto got = Run<ExternalTextureTransform>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/first_index_offset_test.cc b/src/transform/first_index_offset_test.cc
index 05fefa8..61075a6 100644
--- a/src/transform/first_index_offset_test.cc
+++ b/src/transform/first_index_offset_test.cc
@@ -139,6 +139,52 @@
EXPECT_EQ(data->first_instance_offset, 0u);
}
+TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ test(vert_idx);
+ return vec4<f32>();
+}
+
+fn test(vert_idx : u32) -> u32 {
+ return vert_idx;
+}
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ first_vertex_index : u32;
+}
+
+@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(vertex)
+fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ test((vert_idx + tint_symbol_1.first_vertex_index));
+ return vec4<f32>();
+}
+
+fn test(vert_idx : u32) -> u32 {
+ return vert_idx;
+}
+)";
+
+ DataMap config;
+ config.Add<FirstIndexOffset::BindingPoint>(1, 2);
+ auto got = Run<FirstIndexOffset>(src, std::move(config));
+
+ EXPECT_EQ(expect, str(got));
+
+ auto* data = got.data.Get<FirstIndexOffset::Data>();
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(data->has_vertex_index, true);
+ EXPECT_EQ(data->has_instance_index, false);
+ EXPECT_EQ(data->first_vertex_offset, 0u);
+ EXPECT_EQ(data->first_instance_offset, 0u);
+}
+
TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) {
auto* src = R"(
fn test(inst_idx : u32) -> u32 {
@@ -185,6 +231,52 @@
EXPECT_EQ(data->first_instance_offset, 0u);
}
+TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ test(inst_idx);
+ return vec4<f32>();
+}
+
+fn test(inst_idx : u32) -> u32 {
+ return inst_idx;
+}
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ first_instance_index : u32;
+}
+
+@binding(1) @group(7) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(vertex)
+fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ test((inst_idx + tint_symbol_1.first_instance_index));
+ return vec4<f32>();
+}
+
+fn test(inst_idx : u32) -> u32 {
+ return inst_idx;
+}
+)";
+
+ DataMap config;
+ config.Add<FirstIndexOffset::BindingPoint>(1, 7);
+ auto got = Run<FirstIndexOffset>(src, std::move(config));
+
+ EXPECT_EQ(expect, str(got));
+
+ auto* data = got.data.Get<FirstIndexOffset::Data>();
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(data->has_vertex_index, false);
+ EXPECT_EQ(data->has_instance_index, true);
+ EXPECT_EQ(data->first_vertex_offset, 0u);
+ EXPECT_EQ(data->first_instance_offset, 0u);
+}
+
TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) {
auto* src = R"(
fn test(instance_idx : u32, vert_idx : u32) -> u32 {
@@ -244,6 +336,65 @@
EXPECT_EQ(data->first_instance_offset, 4u);
}
+TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
+ test(inputs.instance_idx, inputs.vert_idx);
+ return vec4<f32>();
+}
+
+struct Inputs {
+ @builtin(instance_index) instance_idx : u32;
+ @builtin(vertex_index) vert_idx : u32;
+};
+
+fn test(instance_idx : u32, vert_idx : u32) -> u32 {
+ return instance_idx + vert_idx;
+}
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ first_vertex_index : u32;
+ first_instance_index : u32;
+}
+
+@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(vertex)
+fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
+ test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index));
+ return vec4<f32>();
+}
+
+struct Inputs {
+ @builtin(instance_index)
+ instance_idx : u32;
+ @builtin(vertex_index)
+ vert_idx : u32;
+}
+
+fn test(instance_idx : u32, vert_idx : u32) -> u32 {
+ return (instance_idx + vert_idx);
+}
+)";
+
+ DataMap config;
+ config.Add<FirstIndexOffset::BindingPoint>(1, 2);
+ auto got = Run<FirstIndexOffset>(src, std::move(config));
+
+ EXPECT_EQ(expect, str(got));
+
+ auto* data = got.data.Get<FirstIndexOffset::Data>();
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(data->has_vertex_index, true);
+ EXPECT_EQ(data->has_instance_index, true);
+ EXPECT_EQ(data->first_vertex_offset, 0u);
+ EXPECT_EQ(data->first_instance_offset, 4u);
+}
+
TEST_F(FirstIndexOffsetTest, NestedCalls) {
auto* src = R"(
fn func1(vert_idx : u32) -> u32 {
@@ -298,6 +449,60 @@
EXPECT_EQ(data->first_instance_offset, 0u);
}
+TEST_F(FirstIndexOffsetTest, NestedCalls_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ func2(vert_idx);
+ return vec4<f32>();
+}
+
+fn func2(vert_idx : u32) -> u32 {
+ return func1(vert_idx);
+}
+
+fn func1(vert_idx : u32) -> u32 {
+ return vert_idx;
+}
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ first_vertex_index : u32;
+}
+
+@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(vertex)
+fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ func2((vert_idx + tint_symbol_1.first_vertex_index));
+ return vec4<f32>();
+}
+
+fn func2(vert_idx : u32) -> u32 {
+ return func1(vert_idx);
+}
+
+fn func1(vert_idx : u32) -> u32 {
+ return vert_idx;
+}
+)";
+
+ DataMap config;
+ config.Add<FirstIndexOffset::BindingPoint>(1, 2);
+ auto got = Run<FirstIndexOffset>(src, std::move(config));
+
+ EXPECT_EQ(expect, str(got));
+
+ auto* data = got.data.Get<FirstIndexOffset::Data>();
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(data->has_vertex_index, true);
+ EXPECT_EQ(data->has_instance_index, false);
+ EXPECT_EQ(data->first_vertex_offset, 0u);
+ EXPECT_EQ(data->first_instance_offset, 0u);
+}
+
TEST_F(FirstIndexOffsetTest, MultipleEntryPoints) {
auto* src = R"(
fn func(i : u32) -> u32 {
@@ -369,6 +574,77 @@
EXPECT_EQ(data->first_instance_offset, 4u);
}
+TEST_F(FirstIndexOffsetTest, MultipleEntryPoints_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ func(vert_idx);
+ return vec4<f32>();
+}
+
+@stage(vertex)
+fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ func(vert_idx + inst_idx);
+ return vec4<f32>();
+}
+
+@stage(vertex)
+fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ func(inst_idx);
+ return vec4<f32>();
+}
+
+fn func(i : u32) -> u32 {
+ return i;
+}
+)";
+
+ auto* expect = R"(
+struct tint_symbol {
+ first_vertex_index : u32;
+ first_instance_index : u32;
+}
+
+@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
+
+@stage(vertex)
+fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
+ func((vert_idx + tint_symbol_1.first_vertex_index));
+ return vec4<f32>();
+}
+
+@stage(vertex)
+fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)));
+ return vec4<f32>();
+}
+
+@stage(vertex)
+fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
+ func((inst_idx + tint_symbol_1.first_instance_index));
+ return vec4<f32>();
+}
+
+fn func(i : u32) -> u32 {
+ return i;
+}
+)";
+
+ DataMap config;
+ config.Add<FirstIndexOffset::BindingPoint>(1, 2);
+ auto got = Run<FirstIndexOffset>(src, std::move(config));
+
+ EXPECT_EQ(expect, str(got));
+
+ auto* data = got.data.Get<FirstIndexOffset::Data>();
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(data->has_vertex_index, true);
+ EXPECT_EQ(data->has_instance_index, true);
+ EXPECT_EQ(data->first_vertex_offset, 0u);
+ EXPECT_EQ(data->first_instance_offset, 4u);
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/fold_trivial_single_use_lets_test.cc b/src/transform/fold_trivial_single_use_lets_test.cc
index 42aae29..32109c6 100644
--- a/src/transform/fold_trivial_single_use_lets_test.cc
+++ b/src/transform/fold_trivial_single_use_lets_test.cc
@@ -112,6 +112,26 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(FoldTrivialSingleUseLetsTest, NoFold_NonTrivialLet_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ let x = 1;
+ let y = function_with_posssible_side_effect();
+ _ = (x + y);
+}
+
+fn function_with_posssible_side_effect() -> i32 {
+ return 1;
+}
+)";
+
+ auto* expect = src;
+
+ auto got = Run<FoldTrivialSingleUseLets>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(FoldTrivialSingleUseLetsTest, NoFold_UseInSubBlock) {
auto* src = R"(
fn f() {
diff --git a/src/transform/localize_struct_array_assignment_test.cc b/src/transform/localize_struct_array_assignment_test.cc
index 0147d8e..7d92ba6 100644
--- a/src/transform/localize_struct_array_assignment_test.cc
+++ b/src/transform/localize_struct_array_assignment_test.cc
@@ -90,6 +90,64 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(LocalizeStructArrayAssignmentTest, StructArray_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s1 : OuterS;
+ s1.a1[uniforms.i] = v;
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+struct OuterS {
+ a1 : array<InnerS, 8>;
+};
+
+struct InnerS {
+ v : i32;
+};
+
+@block struct Uniforms {
+ i : u32;
+};
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s1 : OuterS;
+ {
+ let tint_symbol = &(s1.a1);
+ var tint_symbol_1 = *(tint_symbol);
+ tint_symbol_1[uniforms.i] = v;
+ *(tint_symbol) = tint_symbol_1;
+ }
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+struct OuterS {
+ a1 : array<InnerS, 8>;
+}
+
+struct InnerS {
+ v : i32;
+}
+
+@block
+struct Uniforms {
+ i : u32;
+}
+)";
+
+ auto got =
+ Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray) {
auto* src = R"(
@block struct Uniforms {
@@ -156,6 +214,72 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s1 : OuterS;
+ s1.s2.a[uniforms.i] = v;
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+struct OuterS {
+ s2 : S1;
+};
+
+struct S1 {
+ a : array<InnerS, 8>;
+};
+
+struct InnerS {
+ v : i32;
+};
+
+@block struct Uniforms {
+ i : u32;
+};
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s1 : OuterS;
+ {
+ let tint_symbol = &(s1.s2.a);
+ var tint_symbol_1 = *(tint_symbol);
+ tint_symbol_1[uniforms.i] = v;
+ *(tint_symbol) = tint_symbol_1;
+ }
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+struct OuterS {
+ s2 : S1;
+}
+
+struct S1 {
+ a : array<InnerS, 8>;
+}
+
+struct InnerS {
+ v : i32;
+}
+
+@block
+struct Uniforms {
+ i : u32;
+}
+)";
+
+ auto got =
+ Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(LocalizeStructArrayAssignmentTest, StructArrayArray) {
auto* src = R"(
@block struct Uniforms {
@@ -437,6 +561,91 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(LocalizeStructArrayAssignmentTest,
+ IndexingWithSideEffectFunc_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s : OuterS;
+ s.a1[getNextIndex()].a2[uniforms.j] = v;
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+@block struct Uniforms {
+ i : u32;
+ j : u32;
+};
+
+var<private> nextIndex : u32;
+fn getNextIndex() -> u32 {
+ nextIndex = nextIndex + 1u;
+ return nextIndex;
+}
+
+struct OuterS {
+ a1 : array<S1, 8>;
+};
+
+struct S1 {
+ a2 : array<InnerS, 8>;
+};
+
+struct InnerS {
+ v : i32;
+};
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var v : InnerS;
+ var s : OuterS;
+ {
+ let tint_symbol = &(s.a1);
+ var tint_symbol_1 = *(tint_symbol);
+ let tint_symbol_2 = &(tint_symbol_1[getNextIndex()].a2);
+ var tint_symbol_3 = *(tint_symbol_2);
+ tint_symbol_3[uniforms.j] = v;
+ *(tint_symbol_2) = tint_symbol_3;
+ *(tint_symbol) = tint_symbol_1;
+ }
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+@block
+struct Uniforms {
+ i : u32;
+ j : u32;
+}
+
+var<private> nextIndex : u32;
+
+fn getNextIndex() -> u32 {
+ nextIndex = (nextIndex + 1u);
+ return nextIndex;
+}
+
+struct OuterS {
+ a1 : array<S1, 8>;
+}
+
+struct S1 {
+ a2 : array<InnerS, 8>;
+}
+
+struct InnerS {
+ v : i32;
+}
+)";
+
+ auto got =
+ Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg) {
auto* src = R"(
@block struct Uniforms {
@@ -500,6 +709,71 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var s1 : OuterS;
+ f(&s1);
+}
+
+fn f(p : ptr<function, OuterS>) {
+ var v : InnerS;
+ (*p).a1[uniforms.i] = v;
+}
+
+struct InnerS {
+ v : i32;
+};
+struct OuterS {
+ a1 : array<InnerS, 8>;
+};
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+@block struct Uniforms {
+ i : u32;
+};
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ var s1 : OuterS;
+ f(&(s1));
+}
+
+fn f(p : ptr<function, OuterS>) {
+ var v : InnerS;
+ {
+ let tint_symbol = &((*(p)).a1);
+ var tint_symbol_1 = *(tint_symbol);
+ tint_symbol_1[uniforms.i] = v;
+ *(tint_symbol) = tint_symbol_1;
+ }
+}
+
+struct InnerS {
+ v : i32;
+}
+
+struct OuterS {
+ a1 : array<InnerS, 8>;
+}
+
+@group(1) @binding(4) var<uniform> uniforms : Uniforms;
+
+@block
+struct Uniforms {
+ i : u32;
+}
+)";
+
+ auto got =
+ Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerVar) {
auto* src = R"(
@block
diff --git a/src/transform/module_scope_var_to_entry_point_param_test.cc b/src/transform/module_scope_var_to_entry_point_param_test.cc
index 52967a7..0a0d761 100644
--- a/src/transform/module_scope_var_to_entry_point_param_test.cc
+++ b/src/transform/module_scope_var_to_entry_point_param_test.cc
@@ -63,6 +63,31 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Basic_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ w = p;
+}
+
+var<workgroup> w : f32;
+var<private> p : f32;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol : f32;
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32;
+ tint_symbol = tint_symbol_1;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, FunctionCalls) {
auto* src = R"(
var<private> p : f32;
@@ -116,6 +141,59 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, FunctionCalls_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ foo(1.0);
+}
+
+fn foo(a : f32) {
+ let b : f32 = 2.0;
+ bar(a, b);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32) {
+ p = a;
+ w = b;
+}
+
+var<private> p : f32;
+var<workgroup> w : f32;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+ @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+ foo(1.0, &(tint_symbol), &(tint_symbol_1));
+}
+
+fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<workgroup, f32>) {
+ let b : f32 = 2.0;
+ bar(a, b, tint_symbol_2, tint_symbol_3);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr<workgroup, f32>) {
+ *(tint_symbol_4) = a;
+ *(tint_symbol_5) = b;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Constructors) {
auto* src = R"(
var<private> a : f32 = 1.0;
@@ -141,6 +219,31 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Constructors_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ let x : f32 = a + b;
+}
+
+var<private> b : f32 = f32();
+var<private> a : f32 = 1.0;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32 = 1.0;
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32 = f32();
+ let x : f32 = (tint_symbol + tint_symbol_1);
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers) {
auto* src = R"(
var<private> p : f32;
@@ -172,6 +275,37 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ let p_ptr : ptr<private, f32> = &p;
+ let w_ptr : ptr<workgroup, f32> = &w;
+ let x : f32 = *p_ptr + *w_ptr;
+ *p_ptr = x;
+}
+
+var<workgroup> w : f32;
+var<private> p : f32;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+ @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+ let p_ptr : ptr<private, f32> = &(tint_symbol);
+ let w_ptr : ptr<workgroup, f32> = &(tint_symbol_1);
+ let x : f32 = (*(p_ptr) + *(w_ptr));
+ *(p_ptr) = x;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref) {
auto* src = R"(
var<private> v : f32;
@@ -211,6 +345,45 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ foo();
+}
+
+fn foo() {
+ bar(&v);
+}
+
+fn bar(p : ptr<private, f32>) {
+ (*p) = 0.0;
+}
+
+var<private> v : f32;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+ foo(&(tint_symbol));
+}
+
+fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>) {
+ bar(tint_symbol_1);
+}
+
+fn bar(p : ptr<private, f32>) {
+ *(p) = 0.0;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_Basic) {
auto* src = R"(
struct S {
@@ -246,6 +419,40 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_Basic_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ _ = u;
+ _ = s;
+}
+
+@group(0) @binding(0) var<uniform> u : S;
+@group(0) @binding(1) var<storage> s : S;
+
+struct S {
+ a : f32;
+};
+
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, S>) {
+ _ = *(tint_symbol);
+ _ = *(tint_symbol_1);
+}
+
+struct S {
+ a : f32;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray) {
auto* src = R"(
@group(0) @binding(0)
@@ -273,6 +480,33 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ _ = buffer[0];
+}
+
+@group(0) @binding(0)
+var<storage> buffer : array<f32>;
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ arr : array<f32>;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+ _ = (*(tint_symbol)).arr[0];
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArrayInsideFunction) {
auto* src = R"(
@group(0) @binding(0)
@@ -308,6 +542,41 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest,
+ Buffer_RuntimeArrayInsideFunction_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ foo();
+}
+
+fn foo() {
+ _ = buffer[0];
+}
+
+@group(0) @binding(0) var<storage> buffer : array<f32>;
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ arr : array<f32>;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+ foo(&((*(tint_symbol)).arr));
+}
+
+fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<storage, array<f32>>) {
+ _ = (*(tint_symbol_2))[0];
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_Alias) {
auto* src = R"(
type myarray = array<f32>;
@@ -339,6 +608,37 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest,
+ Buffer_RuntimeArray_Alias_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ _ = buffer[0];
+}
+
+@group(0) @binding(0) var<storage> buffer : myarray;
+
+type myarray = array<f32>;
+)";
+
+ auto* expect = R"(
+struct tint_symbol_1 {
+ arr : array<f32>;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+ _ = (*(tint_symbol)).arr[0];
+}
+
+type myarray = array<f32>;
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_ArrayOfStruct) {
auto* src = R"(
struct S {
@@ -374,6 +674,40 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_ArrayOfStruct_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ _ = buffer[0];
+}
+
+@group(0) @binding(0) var<storage> buffer : array<S>;
+
+struct S {
+ f : f32;
+};
+)";
+
+ auto* expect = R"(
+struct S {
+ f : f32;
+}
+
+struct tint_symbol_1 {
+ arr : array<S>;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+ _ = (*(tint_symbol)).arr[0];
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_FunctionCalls) {
auto* src = R"(
struct S {
@@ -437,6 +771,69 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_FunctionCalls_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ foo(1.0);
+}
+
+fn foo(a : f32) {
+ let b : f32 = 2.0;
+ _ = u;
+ bar(a, b);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32) {
+ _ = u;
+ _ = s;
+}
+
+struct S {
+ a : f32;
+};
+
+@group(0) @binding(0)
+var<uniform> u : S;
+@group(0) @binding(1)
+var<storage> s : S;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, S>) {
+ foo(1.0, tint_symbol, tint_symbol_1);
+}
+
+fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
+ let b : f32 = 2.0;
+ _ = *(tint_symbol_2);
+ bar(a, b, tint_symbol_2, tint_symbol_3);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr<storage, S>) {
+ _ = *(tint_symbol_4);
+ _ = *(tint_symbol_5);
+}
+
+struct S {
+ a : f32;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, HandleTypes_Basic) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
@@ -515,6 +912,60 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ModuleScopeVarToEntryPointParamTest,
+ HandleTypes_FunctionCalls_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ foo(1.0);
+}
+
+fn foo(a : f32) {
+ let b : f32 = 2.0;
+ _ = t;
+ bar(a, b);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32) {
+ _ = t;
+ _ = s;
+}
+
+@group(0) @binding(0) var t : texture_2d<f32>;
+@group(0) @binding(1) var s : sampler;
+)";
+
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_1 : sampler) {
+ foo(1.0, tint_symbol, tint_symbol_1);
+}
+
+fn foo(a : f32, tint_symbol_2 : texture_2d<f32>, tint_symbol_3 : sampler) {
+ let b : f32 = 2.0;
+ _ = tint_symbol_2;
+ bar(a, b, tint_symbol_2, tint_symbol_3);
+ no_uses();
+}
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32, tint_symbol_4 : texture_2d<f32>, tint_symbol_5 : sampler) {
+ _ = tint_symbol_4;
+ _ = tint_symbol_5;
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, Matrix) {
auto* src = R"(
var<workgroup> m : mat2x2<f32>;
@@ -626,6 +1077,49 @@
EXPECT_EQ(expect, str(got));
}
+// Test that we do not duplicate a struct type used by multiple workgroup
+// variables that are promoted to threadgroup memory arguments.
+TEST_F(ModuleScopeVarToEntryPointParamTest,
+ DuplicateThreadgroupArgumentTypes_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main() {
+ let x = a;
+ let y = b;
+}
+
+var<workgroup> a : S;
+var<workgroup> b : S;
+
+struct S {
+ m : mat2x2<f32>;
+};
+)";
+
+ auto* expect = R"(
+struct S {
+ m : mat2x2<f32>;
+}
+
+struct tint_symbol_3 {
+ a : S;
+ b : S;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
+ let tint_symbol : ptr<workgroup, S> = &((*(tint_symbol_1)).a);
+ let tint_symbol_2 : ptr<workgroup, S> = &((*(tint_symbol_1)).b);
+ let x = *(tint_symbol);
+ let y = *(tint_symbol_2);
+}
+)";
+
+ auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ModuleScopeVarToEntryPointParamTest, UnusedVariables) {
auto* src = R"(
struct S {
diff --git a/src/transform/multiplanar_external_texture_test.cc b/src/transform/multiplanar_external_texture_test.cc
index a0a42b8..8a43308 100644
--- a/src/transform/multiplanar_external_texture_test.cc
+++ b/src/transform/multiplanar_external_texture_test.cc
@@ -136,6 +136,49 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the transform works with a textureDimensions call.
+TEST_F(MultiplanarExternalTextureTest, Dimensions_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ var dim : vec2<i32>;
+ dim = textureDimensions(ext_tex);
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
+}
+
+@group(0) @binding(0) var ext_tex : texture_external;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ var dim : vec2<i32>;
+ dim = textureDimensions(ext_tex);
+ return vec4<f32>(0.0, 0.0, 0.0, 0.0);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+)";
+
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Test that the transform works with a textureSampleLevel call.
TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleLevel) {
auto* src = R"(
@@ -192,6 +235,62 @@
EXPECT_EQ(expect, str(got));
}
+// Test that the transform works with a textureSampleLevel call.
+TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleLevel_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureSampleLevel(ext_tex, s, coord.xy);
+}
+
+@group(0) @binding(1) var ext_tex : texture_external;
+@group(0) @binding(0) var s : sampler;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
+}
+
+@group(0) @binding(1) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(0) var s : sampler;
+)";
+
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the transform works with a textureLoad call.
TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad) {
auto* src = R"(
@@ -245,6 +344,59 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the transform works with a textureLoad call.
+TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureLoad(ext_tex, vec2<i32>(1, 1));
+}
+
+@group(0) @binding(0) var ext_tex : texture_external;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
+
+fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureLoad(plane0, coord, 0);
+ }
+ let y = (textureLoad(plane0, coord, 0).r - 0.0625);
+ let uv = (textureLoad(plane1, coord, 0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+)";
+
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the transform works with both a textureSampleLevel and textureLoad
// call.
TEST_F(MultiplanarExternalTextureTest, TextureSampleAndTextureLoad) {
@@ -316,6 +468,77 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the transform works with both a textureSampleLevel and textureLoad
+// call.
+TEST_F(MultiplanarExternalTextureTest, TextureSampleAndTextureLoad_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return textureSampleLevel(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
+}
+
+@group(0) @binding(0) var s : sampler;
+@group(0) @binding(1) var ext_tex : texture_external;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureLoad(plane0, coord, 0);
+ }
+ let y = (textureLoad(plane0, coord, 0).r - 0.0625);
+ let uv = (textureLoad(plane1, coord, 0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+@stage(fragment)
+fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
+ return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
+}
+
+@group(0) @binding(0) var s : sampler;
+
+@group(0) @binding(1) var ext_tex : texture_2d<f32>;
+)";
+
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the transform works with many instances of texture_external.
TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleLevel) {
auto* src = R"(
@@ -464,6 +687,73 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the texture_external passed as a function parameter produces the
+// correct output.
+TEST_F(MultiplanarExternalTextureTest,
+ ExternalTexturePassedAsParam_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main() {
+ f(ext_tex, smp);
+}
+
+fn f(t : texture_external, s : sampler) {
+ textureSampleLevel(t, s, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) var ext_tex : texture_external;
+@group(0) @binding(1) var smp : sampler;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@stage(fragment)
+fn main() {
+ f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
+}
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+ textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(1) var smp : sampler;
+)";
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{
+ {{0, 0}, {{0, 2}, {0, 3}}},
+ });
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the texture_external passed as a parameter not in the first
// position produces the correct output.
TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsSecondParam) {
@@ -606,6 +896,84 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that multiple texture_external params passed to a function produces the
+// correct output.
+TEST_F(MultiplanarExternalTextureTest,
+ ExternalTexturePassedAsParamMultiple_OutOfOrder) {
+ auto* src = R"(
+@stage(fragment)
+fn main() {
+ f(ext_tex, smp, ext_tex2);
+}
+
+fn f(t : texture_external, s : sampler, t2 : texture_external) {
+ textureSampleLevel(t, s, vec2<f32>(1.0, 2.0));
+ textureSampleLevel(t2, s, vec2<f32>(1.0, 2.0));
+}
+
+@group(0) @binding(0) var ext_tex : texture_external;
+@group(0) @binding(1) var smp : sampler;
+@group(0) @binding(2) var ext_tex2 : texture_external;
+
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
+
+@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
+
+@stage(fragment)
+fn main() {
+ f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
+}
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
+ textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
+ textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(1) var smp : sampler;
+
+@group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
+)";
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{
+ {{0, 0}, {{0, 3}, {0, 4}}},
+ {{0, 2}, {{0, 5}, {0, 6}}},
+ });
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the texture_external passed to as a parameter to multiple
// functions produces the correct output.
TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamNested) {
@@ -680,6 +1048,81 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the texture_external passed to as a parameter to multiple
+// functions produces the correct output.
+TEST_F(MultiplanarExternalTextureTest,
+ ExternalTexturePassedAsParamNested_OutOfOrder) {
+ auto* src = R"(
+fn nested(t : texture_external, s : sampler) {
+ textureSampleLevel(t, s, vec2<f32>(1.0, 2.0));
+}
+
+fn f(t : texture_external, s : sampler) {
+ nested(t, s);
+}
+
+@group(0) @binding(0) var ext_tex : texture_external;
+@group(0) @binding(1) var smp : sampler;
+
+@stage(fragment)
+fn main() {
+ f(ext_tex, smp);
+}
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+ textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
+}
+
+fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
+ nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(1) var smp : sampler;
+
+@stage(fragment)
+fn main() {
+ f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
+}
+)";
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{
+ {{0, 0}, {{0, 2}, {0, 3}}},
+ });
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
// Tests that the transform works with a function using an external texture,
// even if there's no external texture declared at module scope.
TEST_F(MultiplanarExternalTextureTest,
@@ -780,6 +1223,75 @@
EXPECT_EQ(expect, str(got));
}
+// Tests that the the transform handles aliases to external textures
+TEST_F(MultiplanarExternalTextureTest, ExternalTextureAlias_OutOfOrder) {
+ auto* src = R"(
+[[stage(fragment)]]
+fn main() {
+ f(ext_tex, smp);
+}
+
+fn f(t : ET, s : sampler) {
+ textureSampleLevel(t, s, vec2<f32>(1.0, 2.0));
+}
+
+[[group(0), binding(0)]] var ext_tex : ET;
+[[group(0), binding(1)]] var smp : sampler;
+
+type ET = texture_external;
+)";
+
+ auto* expect = R"(
+struct ExternalTextureParams {
+ numPlanes : u32;
+ vr : f32;
+ ug : f32;
+ vg : f32;
+ ub : f32;
+}
+
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@stage(fragment)
+fn main() {
+ f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
+}
+
+fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
+ if ((params.numPlanes == 1u)) {
+ return textureSampleLevel(plane0, smp, coord, 0.0);
+ }
+ let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
+ let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
+ let u = uv.x;
+ let v = uv.y;
+ let r = ((1.164000034 * y) + (params.vr * v));
+ let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
+ let b = ((1.164000034 * y) + (params.ub * u));
+ return vec4<f32>(r, g, b, 1.0);
+}
+
+fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+ textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
+}
+
+@group(0) @binding(0) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(1) var smp : sampler;
+
+type ET = texture_external;
+)";
+ DataMap data;
+ data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+ MultiplanarExternalTexture::BindingsMap{
+ {{0, 0}, {{0, 2}, {0, 3}}},
+ });
+ auto got = Run<MultiplanarExternalTexture>(src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/num_workgroups_from_uniform_test.cc b/src/transform/num_workgroups_from_uniform_test.cc
index dedee54..8471b03 100644
--- a/src/transform/num_workgroups_from_uniform_test.cc
+++ b/src/transform/num_workgroups_from_uniform_test.cc
@@ -146,6 +146,52 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(NumWorkgroupsFromUniformTest, StructOnlyMember_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main(in : Builtins) {
+ let groups_x = in.num_wgs.x;
+ let groups_y = in.num_wgs.y;
+ let groups_z = in.num_wgs.z;
+}
+
+struct Builtins {
+ @builtin(num_workgroups) num_wgs : vec3<u32>;
+};
+)";
+
+ auto* expect = R"(
+struct tint_symbol_2 {
+ num_workgroups : vec3<u32>;
+}
+
+@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
+
+fn main_inner(in : Builtins) {
+ let groups_x = in.num_wgs.x;
+ let groups_y = in.num_wgs.y;
+ let groups_z = in.num_wgs.z;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main() {
+ main_inner(Builtins(tint_symbol_3.num_workgroups));
+}
+
+struct Builtins {
+ num_wgs : vec3<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ data.Add<NumWorkgroupsFromUniform::Config>(sem::BindingPoint{0, 30u});
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(
+ src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(NumWorkgroupsFromUniformTest, StructMultipleMembers) {
auto* src = R"(
struct Builtins {
@@ -203,6 +249,64 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(NumWorkgroupsFromUniformTest, StructMultipleMembers_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn main(in : Builtins) {
+ let groups_x = in.num_wgs.x;
+ let groups_y = in.num_wgs.y;
+ let groups_z = in.num_wgs.z;
+}
+
+struct Builtins {
+ @builtin(global_invocation_id) gid : vec3<u32>;
+ @builtin(num_workgroups) num_wgs : vec3<u32>;
+ @builtin(workgroup_id) wgid : vec3<u32>;
+};
+
+)";
+
+ auto* expect = R"(
+struct tint_symbol_2 {
+ num_workgroups : vec3<u32>;
+}
+
+@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
+
+struct tint_symbol_1 {
+ @builtin(global_invocation_id)
+ gid : vec3<u32>;
+ @builtin(workgroup_id)
+ wgid : vec3<u32>;
+}
+
+fn main_inner(in : Builtins) {
+ let groups_x = in.num_wgs.x;
+ let groups_y = in.num_wgs.y;
+ let groups_z = in.num_wgs.z;
+}
+
+@stage(compute) @workgroup_size(1)
+fn main(tint_symbol : tint_symbol_1) {
+ main_inner(Builtins(tint_symbol.gid, tint_symbol_3.num_workgroups, tint_symbol.wgid));
+}
+
+struct Builtins {
+ gid : vec3<u32>;
+ num_wgs : vec3<u32>;
+ wgid : vec3<u32>;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
+ data.Add<NumWorkgroupsFromUniform::Config>(sem::BindingPoint{0, 30u});
+ auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(
+ src, data);
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(NumWorkgroupsFromUniformTest, MultipleEntryPoints) {
auto* src = R"(
struct Builtins1 {
diff --git a/src/transform/pad_array_elements_test.cc b/src/transform/pad_array_elements_test.cc
index 5139edc..f3da053 100644
--- a/src/transform/pad_array_elements_test.cc
+++ b/src/transform/pad_array_elements_test.cc
@@ -216,6 +216,42 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PadArrayElementsTest, ArrayAlias_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var arr : Array;
+ arr = Array();
+ arr = Array(1, 2, 3, 4);
+ let vals : Array = Array(1, 2, 3, 4);
+ arr = vals;
+ let x = arr[3];
+}
+
+type Array = @stride(16) array<i32, 4>;
+)";
+ auto* expect = R"(
+struct tint_padded_array_element {
+ @size(16)
+ el : i32;
+}
+
+fn f() {
+ var arr : array<tint_padded_array_element, 4u>;
+ arr = array<tint_padded_array_element, 4u>();
+ arr = array<tint_padded_array_element, 4u>(tint_padded_array_element(1), tint_padded_array_element(2), tint_padded_array_element(3), tint_padded_array_element(4));
+ let vals : array<tint_padded_array_element, 4u> = array<tint_padded_array_element, 4u>(tint_padded_array_element(1), tint_padded_array_element(2), tint_padded_array_element(3), tint_padded_array_element(4));
+ arr = vals;
+ let x = arr[3].el;
+}
+
+type Array = array<tint_padded_array_element, 4u>;
+)";
+
+ auto got = Run<PadArrayElements>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(PadArrayElementsTest, ArraysInStruct) {
auto* src = R"(
struct S {
@@ -364,6 +400,65 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PadArrayElementsTest, AccessArraysOfArraysInStruct_OutOfOrder) {
+ auto* src = R"(
+fn f(s : S) -> i32 {
+ return s.a[2] + s.b[1][2] + s.c[3][1][2];
+}
+
+struct S {
+ a : @stride(512) array<i32, 4>;
+ b : @stride(512) array<@stride(32) array<i32, 4>, 4>;
+ c : @stride(512) array<@stride(64) array<@stride(8) array<i32, 4>, 4>, 4>;
+};
+)";
+ auto* expect = R"(
+struct tint_padded_array_element {
+ @size(512)
+ el : i32;
+}
+
+struct tint_padded_array_element_1 {
+ @size(32)
+ el : i32;
+}
+
+struct tint_padded_array_element_2 {
+ @size(512)
+ el : array<tint_padded_array_element_1, 4u>;
+}
+
+struct tint_padded_array_element_3 {
+ @size(8)
+ el : i32;
+}
+
+struct tint_padded_array_element_4 {
+ @size(64)
+ el : array<tint_padded_array_element_3, 4u>;
+}
+
+struct tint_padded_array_element_5 {
+ @size(512)
+ el : array<tint_padded_array_element_4, 4u>;
+}
+
+fn f(s : S) -> i32 {
+ return ((s.a[2].el + s.b[1].el[2].el) + s.c[3].el[1].el[2].el);
+}
+
+struct S {
+ a : array<tint_padded_array_element, 4u>;
+ b : array<tint_padded_array_element_2, 4u>;
+ c : array<tint_padded_array_element_5, 4u>;
+}
+)";
+
+ auto got = Run<PadArrayElements>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(PadArrayElementsTest, DeclarationOrder) {
auto* src = R"(
type T0 = i32;
diff --git a/src/transform/promote_initializers_to_const_var_test.cc b/src/transform/promote_initializers_to_const_var_test.cc
index c9210dd..109615b 100644
--- a/src/transform/promote_initializers_to_const_var_test.cc
+++ b/src/transform/promote_initializers_to_const_var_test.cc
@@ -91,6 +91,38 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PromoteInitializersToConstVarTest, BasicStruct_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var x = S(1, 2.0, vec3<f32>()).b;
+}
+
+struct S {
+ a : i32;
+ b : f32;
+ c : vec3<f32>;
+};
+)";
+
+ auto* expect = R"(
+fn f() {
+ let tint_symbol = S(1, 2.0, vec3<f32>());
+ var x = tint_symbol.b;
+}
+
+struct S {
+ a : i32;
+ b : f32;
+ c : vec3<f32>;
+}
+)";
+
+ DataMap data;
+ auto got = Run<PromoteInitializersToConstVar>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopInit) {
auto* src = R"(
fn f() {
@@ -155,6 +187,44 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PromoteInitializersToConstVarTest, StructInForLoopInit_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var insert_after = 1;
+ for(var x = S(1, 2.0, vec3<f32>()).b; ; ) {
+ break;
+ }
+}
+
+struct S {
+ a : i32;
+ b : f32;
+ c : vec3<f32>;
+};
+)";
+
+ auto* expect = R"(
+fn f() {
+ var insert_after = 1;
+ let tint_symbol = S(1, 2.0, vec3<f32>());
+ for(var x = tint_symbol.b; ; ) {
+ break;
+ }
+}
+
+struct S {
+ a : i32;
+ b : f32;
+ c : vec3<f32>;
+}
+)";
+
+ DataMap data;
+ auto got = Run<PromoteInitializersToConstVar>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopCond) {
auto* src = R"(
fn f() {
@@ -460,6 +530,46 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PromoteInitializersToConstVarTest, Mixed_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var x = S2(array<S1, 3u>(S1(1), S1(2), S1(3))).a[1].a;
+}
+
+struct S2 {
+ a : array<S1, 3u>;
+};
+
+struct S1 {
+ a : i32;
+};
+)";
+
+ auto* expect = R"(
+fn f() {
+ let tint_symbol = S1(1);
+ let tint_symbol_1 = S1(2);
+ let tint_symbol_2 = S1(3);
+ let tint_symbol_3 = array<S1, 3u>(tint_symbol, tint_symbol_1, tint_symbol_2);
+ let tint_symbol_4 = S2(tint_symbol_3);
+ var x = tint_symbol_4.a[1].a;
+}
+
+struct S2 {
+ a : array<S1, 3u>;
+}
+
+struct S1 {
+ a : i32;
+}
+)";
+
+ DataMap data;
+ auto got = Run<PromoteInitializersToConstVar>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl) {
auto* src = R"(
struct S {
@@ -486,6 +596,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+ var local_str = S(1, 2.0, 3);
+}
+
+let module_str : S = S(1, 2.0, 3);
+
+struct S {
+ a : i32;
+ b : f32;
+ c : i32;
+}
+
+let module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+)";
+
+ auto* expect = src;
+
+ DataMap data;
+ auto got = Run<PromoteInitializersToConstVar>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/remove_phonies_test.cc b/src/transform/remove_phonies_test.cc
index d073d35..a8fd5ea 100644
--- a/src/transform/remove_phonies_test.cc
+++ b/src/transform/remove_phonies_test.cc
@@ -130,6 +130,54 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RemovePhoniesTest, SingleSideEffects_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ _ = neg(1);
+ _ = add(2, 3);
+ _ = add(neg(4), neg(5));
+ _ = u32(neg(6));
+ _ = f32(add(7, 8));
+ _ = vec2<f32>(f32(neg(9)));
+ _ = vec3<i32>(1, neg(10), 3);
+ _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
+}
+
+fn add(a : i32, b : i32) -> i32 {
+ return (a + b);
+}
+
+fn neg(a : i32) -> i32 {
+ return -(a);
+}
+)";
+
+ auto* expect = R"(
+fn f() {
+ neg(1);
+ add(2, 3);
+ add(neg(4), neg(5));
+ neg(6);
+ add(7, 8);
+ neg(9);
+ neg(10);
+ add(11, 12);
+}
+
+fn add(a : i32, b : i32) -> i32 {
+ return (a + b);
+}
+
+fn neg(a : i32) -> i32 {
+ return -(a);
+}
+)";
+
+ auto got = Run<RemovePhonies>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RemovePhoniesTest, MultipleSideEffects) {
auto* src = R"(
fn neg(a : i32) -> i32 {
@@ -187,6 +235,63 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RemovePhoniesTest, MultipleSideEffects_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ _ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
+ _ = add(9, neg(10)) + neg(11);
+ _ = xor(12u, 13u) + xor(14u, 15u);
+ _ = neg(16) / neg(17) + add(18, 19);
+}
+
+fn neg(a : i32) -> i32 {
+ return -(a);
+}
+
+fn add(a : i32, b : i32) -> i32 {
+ return (a + b);
+}
+
+fn xor(a : u32, b : u32) -> u32 {
+ return (a ^ b);
+}
+)";
+
+ auto* expect = R"(
+fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
+}
+
+fn phony_sink_1(p0 : i32, p1 : i32) {
+}
+
+fn phony_sink_2(p0 : u32, p1 : u32) {
+}
+
+fn f() {
+ phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
+ phony_sink_1(add(9, neg(10)), neg(11));
+ phony_sink_2(xor(12u, 13u), xor(14u, 15u));
+ phony_sink(neg(16), neg(17), add(18, 19));
+}
+
+fn neg(a : i32) -> i32 {
+ return -(a);
+}
+
+fn add(a : i32, b : i32) -> i32 {
+ return (a + b);
+}
+
+fn xor(a : u32, b : u32) -> u32 {
+ return (a ^ b);
+}
+)";
+
+ auto got = Run<RemovePhonies>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RemovePhoniesTest, ForLoop) {
auto* src = R"(
struct S {
@@ -254,6 +359,73 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RemovePhoniesTest, ForLoop_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ for (_ = &s.arr; ;_ = &s.arr) {
+ break;
+ }
+ for (_ = x(); ;_ = y() + z()) {
+ break;
+ }
+}
+
+fn x() -> i32 {
+ return 0;
+}
+
+fn y() -> i32 {
+ return 0;
+}
+
+fn z() -> i32 {
+ return 0;
+}
+
+struct S {
+ arr : array<i32>;
+};
+
+@group(0) @binding(0) var<storage, read_write> s : S;
+)";
+
+ auto* expect = R"(
+fn phony_sink(p0 : i32, p1 : i32) {
+}
+
+fn f() {
+ for(; ; ) {
+ break;
+ }
+ for(x(); ; phony_sink(y(), z())) {
+ break;
+ }
+}
+
+fn x() -> i32 {
+ return 0;
+}
+
+fn y() -> i32 {
+ return 0;
+}
+
+fn z() -> i32 {
+ return 0;
+}
+
+struct S {
+ arr : array<i32>;
+}
+
+@group(0) @binding(0) var<storage, read_write> s : S;
+)";
+
+ auto got = Run<RemovePhonies>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/robustness_test.cc b/src/transform/robustness_test.cc
index 8af6cce..ac422c0 100644
--- a/src/transform/robustness_test.cc
+++ b/src/transform/robustness_test.cc
@@ -48,6 +48,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_Clamp_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ let b : f32 = a[c];
+}
+
+let c : u32 = 1u;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ let b : f32 = a[1u];
+}
+
+let c : u32 = 1u;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Array_Idx_Nested_Scalar) {
auto* src = R"(
var<private> a : array<f32, 3>;
@@ -78,6 +104,36 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_Nested_Scalar_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var c : f32 = a[ b[i] ];
+}
+
+var<private> i : u32;
+
+var<private> b : array<i32, 5>;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
+}
+
+var<private> i : u32;
+
+var<private> b : array<i32, 5>;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Array_Idx_Scalar) {
auto* src = R"(
var<private> a : array<f32, 3>;
@@ -100,6 +156,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_Scalar_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[1];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[1];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Array_Idx_Expr) {
auto* src = R"(
var<private> a : array<f32, 3>;
@@ -126,6 +204,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_Expr_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[c + 2 - 3];
+}
+
+var<private> c : i32;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
+}
+
+var<private> c : i32;
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Array_Idx_Negative) {
auto* src = R"(
var<private> a : array<f32, 3>;
@@ -148,6 +252,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_Negative_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[-1];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[0];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Array_Idx_OutOfBounds) {
auto* src = R"(
var<private> a : array<f32, 3>;
@@ -170,6 +296,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Array_Idx_OutOfBounds_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[3];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2];
+}
+
+var<private> a : array<f32, 3>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
// TODO(crbug.com/tint/1177) - Validation currently forbids arrays larger than
// 0xffffffff. If WGSL supports 64-bit indexing, re-enable this test.
TEST_F(RobustnessTest, DISABLED_LargeArrays_Idx) {
@@ -272,6 +420,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Idx_Scalar_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[1];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[1];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Idx_Expr) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -298,6 +468,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Idx_Expr_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[c + 2 - 3];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -320,6 +516,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a.xy[2];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a.xy[1];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -345,6 +563,33 @@
EXPECT_EQ(expect, str(got));
}
+
+TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a.xy[c];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a.xy[min(u32(c), 1u)];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -371,6 +616,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a.xy[c + 2 - 3];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
+}
+
+var<private> c : i32;
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Idx_Negative) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -393,6 +664,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Idx_Negative_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[-1];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[0];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Vector_Idx_OutOfBounds) {
auto* src = R"(
var<private> a : vec3<f32>;
@@ -415,6 +708,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Vector_Idx_OutOfBounds_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[3];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2];
+}
+
+var<private> a : vec3<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_Scalar) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -437,6 +752,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_Scalar_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[2][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_Expr_Column) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -463,6 +800,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_Expr_Column_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[c + 2 - 3][1];
+}
+
+var<private> c : i32;
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
+}
+
+var<private> c : i32;
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_Expr_Row) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -489,6 +852,32 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_Expr_Row_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[1][c + 2 - 3];
+}
+
+var<private> c : i32;
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
+}
+
+var<private> c : i32;
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_Negative_Column) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -511,6 +900,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_Negative_Column_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[-1][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[0][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_Negative_Row) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -533,6 +944,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_Negative_Row_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[2][-1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2][0];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -555,6 +988,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[5][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@@ -577,6 +1032,28 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var b : f32 = a[2][5];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto* expect = R"(
+fn f() {
+ var b : f32 = a[2][1];
+}
+
+var<private> a : mat3x2<f32>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
// TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest, DISABLED_Vector_Constant_Id_Clamps) {
// @override(1300) let idx : i32;
@@ -644,9 +1121,36 @@
EXPECT_EQ(expect, str(got));
}
-// TODO(dsinclair): Clamp atomics when available.
-TEST_F(RobustnessTest, DISABLED_Atomics_Clamp) {
- FAIL();
+TEST_F(RobustnessTest, RuntimeArray_Clamps_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var d : f32 = s.b[25];
+}
+
+@group(0) @binding(0) var<storage, read> s : S;
+
+struct S {
+ a : f32;
+ b : array<f32>;
+};
+)";
+
+ auto* expect = R"(
+fn f() {
+ var d : f32 = s.b[min(25u, (arrayLength(&(s.b)) - 1u))];
+}
+
+@group(0) @binding(0) var<storage, read> s : S;
+
+struct S {
+ a : f32;
+ b : array<f32>;
+}
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
}
// Clamp textureLoad() coord, array_index and level values
@@ -715,6 +1219,72 @@
EXPECT_EQ(expect, str(got));
}
+// Clamp textureLoad() coord, array_index and level values
+TEST_F(RobustnessTest, TextureLoad_Clamp_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var array_idx : i32;
+ var level_idx : i32;
+ var sample_idx : i32;
+
+ textureLoad(tex_1d, 1, level_idx);
+ textureLoad(tex_2d, vec2<i32>(1, 2), level_idx);
+ textureLoad(tex_2d_arr, vec2<i32>(1, 2), array_idx, level_idx);
+ textureLoad(tex_3d, vec3<i32>(1, 2, 3), level_idx);
+ textureLoad(tex_ms_2d, vec2<i32>(1, 2), sample_idx);
+ textureLoad(tex_depth_2d, vec2<i32>(1, 2), level_idx);
+ textureLoad(tex_depth_2d_arr, vec2<i32>(1, 2), array_idx, level_idx);
+ textureLoad(tex_external, vec2<i32>(1, 2));
+}
+
+@group(0) @binding(0) var tex_1d : texture_1d<f32>;
+@group(0) @binding(0) var tex_2d : texture_2d<f32>;
+@group(0) @binding(0) var tex_2d_arr : texture_2d_array<f32>;
+@group(0) @binding(0) var tex_3d : texture_3d<f32>;
+@group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d<f32>;
+@group(0) @binding(0) var tex_depth_2d : texture_depth_2d;
+@group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array;
+@group(0) @binding(0) var tex_external : texture_external;
+)";
+
+ auto* expect =
+ R"(
+fn f() {
+ var array_idx : i32;
+ var level_idx : i32;
+ var sample_idx : i32;
+ textureLoad(tex_1d, clamp(1, i32(), (textureDimensions(tex_1d, clamp(level_idx, 0, (textureNumLevels(tex_1d) - 1))) - i32(1))), clamp(level_idx, 0, (textureNumLevels(tex_1d) - 1)));
+ textureLoad(tex_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_2d, clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1))) - vec2<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1)));
+ textureLoad(tex_2d_arr, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1))) - vec2<i32>(1))), clamp(array_idx, 0, (textureNumLayers(tex_2d_arr) - 1)), clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1)));
+ textureLoad(tex_3d, clamp(vec3<i32>(1, 2, 3), vec3<i32>(), (textureDimensions(tex_3d, clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1))) - vec3<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1)));
+ textureLoad(tex_ms_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_ms_2d) - vec2<i32>(1))), sample_idx);
+ textureLoad(tex_depth_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_depth_2d, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1))) - vec2<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1)));
+ textureLoad(tex_depth_2d_arr, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_depth_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d_arr) - 1))) - vec2<i32>(1))), clamp(array_idx, 0, (textureNumLayers(tex_depth_2d_arr) - 1)), clamp(level_idx, 0, (textureNumLevels(tex_depth_2d_arr) - 1)));
+ textureLoad(tex_external, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_external) - vec2<i32>(1))));
+}
+
+@group(0) @binding(0) var tex_1d : texture_1d<f32>;
+
+@group(0) @binding(0) var tex_2d : texture_2d<f32>;
+
+@group(0) @binding(0) var tex_2d_arr : texture_2d_array<f32>;
+
+@group(0) @binding(0) var tex_3d : texture_3d<f32>;
+
+@group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d<f32>;
+
+@group(0) @binding(0) var tex_depth_2d : texture_depth_2d;
+
+@group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array;
+
+@group(0) @binding(0) var tex_external : texture_external;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
// Clamp textureStore() coord, array_index and level values
TEST_F(RobustnessTest, TextureStore_Clamp) {
auto* src = R"(
@@ -756,6 +1326,48 @@
EXPECT_EQ(expect, str(got));
}
+// Clamp textureStore() coord, array_index and level values
+TEST_F(RobustnessTest, TextureStore_Clamp_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ textureStore(tex1d, 10, vec4<i32>());
+ textureStore(tex2d, vec2<i32>(10, 20), vec4<i32>());
+ textureStore(tex2d_arr, vec2<i32>(10, 20), 50, vec4<i32>());
+ textureStore(tex3d, vec3<i32>(10, 20, 30), vec4<i32>());
+}
+
+@group(0) @binding(0) var tex1d : texture_storage_1d<rgba8sint, write>;
+
+@group(0) @binding(1) var tex2d : texture_storage_2d<rgba8sint, write>;
+
+@group(0) @binding(2) var tex2d_arr : texture_storage_2d_array<rgba8sint, write>;
+
+@group(0) @binding(3) var tex3d : texture_storage_3d<rgba8sint, write>;
+
+)";
+
+ auto* expect = R"(
+fn f() {
+ textureStore(tex1d, clamp(10, i32(), (textureDimensions(tex1d) - i32(1))), vec4<i32>());
+ textureStore(tex2d, clamp(vec2<i32>(10, 20), vec2<i32>(), (textureDimensions(tex2d) - vec2<i32>(1))), vec4<i32>());
+ textureStore(tex2d_arr, clamp(vec2<i32>(10, 20), vec2<i32>(), (textureDimensions(tex2d_arr) - vec2<i32>(1))), clamp(50, 0, (textureNumLayers(tex2d_arr) - 1)), vec4<i32>());
+ textureStore(tex3d, clamp(vec3<i32>(10, 20, 30), vec3<i32>(), (textureDimensions(tex3d) - vec3<i32>(1))), vec4<i32>());
+}
+
+@group(0) @binding(0) var tex1d : texture_storage_1d<rgba8sint, write>;
+
+@group(0) @binding(1) var tex2d : texture_storage_2d<rgba8sint, write>;
+
+@group(0) @binding(2) var tex2d_arr : texture_storage_2d_array<rgba8sint, write>;
+
+@group(0) @binding(3) var tex3d : texture_storage_3d<rgba8sint, write>;
+)";
+
+ auto got = Run<Robustness>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
// TODO(dsinclair): Test for scoped variables when shadowing is implemented
TEST_F(RobustnessTest, DISABLED_Shadowed_Variable) {
// var a : array<f32, 3>;
diff --git a/src/transform/unshadow_test.cc b/src/transform/unshadow_test.cc
index ddba826..f6383ef 100644
--- a/src/transform/unshadow_test.cc
+++ b/src/transform/unshadow_test.cc
@@ -84,6 +84,36 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, LocalShadowsAlias_OutOfOrder) {
+ auto* src = R"(
+fn X() {
+ var a = false;
+}
+
+fn Y() {
+ let a = true;
+}
+
+type a = i32;
+)";
+
+ auto* expect = R"(
+fn X() {
+ var a_1 = false;
+}
+
+fn Y() {
+ let a_2 = true;
+}
+
+type a = i32;
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(UnshadowTest, LocalShadowsStruct) {
auto* src = R"(
struct a {
@@ -118,13 +148,50 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, LocalShadowsStruct_OutOfOrder) {
+ auto* src = R"(
+fn X() {
+ var a = true;
+}
+
+fn Y() {
+ let a = false;
+}
+
+struct a {
+ m : i32;
+};
+
+)";
+
+ auto* expect = R"(
+fn X() {
+ var a_1 = true;
+}
+
+fn Y() {
+ let a_2 = false;
+}
+
+struct a {
+ m : i32;
+}
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(UnshadowTest, LocalShadowsFunction) {
auto* src = R"(
fn a() {
var a = true;
+ var b = false;
}
fn b() {
+ let a = true;
let b = false;
}
)";
@@ -132,11 +199,44 @@
auto* expect = R"(
fn a() {
var a_1 = true;
+ var b_1 = false;
}
fn b() {
+ let a_2 = true;
+ let b_2 = false;
+}
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, LocalShadowsFunction_OutOfOrder) {
+ auto* src = R"(
+fn b() {
+ let a = true;
+ let b = false;
+}
+
+fn a() {
+ var a = true;
+ var b = false;
+}
+
+)";
+
+ auto* expect = R"(
+fn b() {
+ let a_1 = true;
let b_1 = false;
}
+
+fn a() {
+ var a_2 = true;
+ var b_2 = false;
+}
)";
auto got = Run<Unshadow>(src);
@@ -174,6 +274,36 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, LocalShadowsGlobalVar_OutOfOrder) {
+ auto* src = R"(
+fn X() {
+ var a = (a == 123);
+}
+
+fn Y() {
+ let a = (a == 321);
+}
+
+var<private> a : i32;
+)";
+
+ auto* expect = R"(
+fn X() {
+ var a_1 = (a == 123);
+}
+
+fn Y() {
+ let a_2 = (a == 321);
+}
+
+var<private> a : i32;
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(UnshadowTest, LocalShadowsGlobalLet) {
auto* src = R"(
let a : i32 = 1;
@@ -204,6 +334,36 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, LocalShadowsGlobalLet_OutOfOrder) {
+ auto* src = R"(
+fn X() {
+ var a = (a == 123);
+}
+
+fn Y() {
+ let a = (a == 321);
+}
+
+let a : i32 = 1;
+)";
+
+ auto* expect = R"(
+fn X() {
+ var a_1 = (a == 123);
+}
+
+fn Y() {
+ let a_2 = (a == 321);
+}
+
+let a : i32 = 1;
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(UnshadowTest, LocalShadowsLocalVar) {
auto* src = R"(
fn X() {
@@ -360,6 +520,26 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, ParamShadowsGlobalLet_OutOfOrder) {
+ auto* src = R"(
+fn F(a : bool) {
+}
+
+let a : i32 = 1;
+)";
+
+ auto* expect = R"(
+fn F(a_1 : bool) {
+}
+
+let a : i32 = 1;
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(UnshadowTest, ParamShadowsAlias) {
auto* src = R"(
type a = i32;
@@ -392,6 +572,38 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(UnshadowTest, ParamShadowsAlias_OutOfOrder) {
+ auto* src = R"(
+fn F(a : a) {
+ {
+ var a = (a == 123);
+ }
+ {
+ let a = (a == 321);
+ }
+}
+
+type a = i32;
+)";
+
+ auto* expect = R"(
+fn F(a_1 : a) {
+ {
+ var a_2 = (a_1 == 123);
+ }
+ {
+ let a_3 = (a_1 == 321);
+ }
+}
+
+type a = i32;
+)";
+
+ auto got = Run<Unshadow>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc
index b2482af..8c551ec 100644
--- a/src/transform/vertex_pulling_test.cc
+++ b/src/transform/vertex_pulling_test.cc
@@ -426,6 +426,86 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(VertexPullingTest,
+ ExistingVertexIndexAndInstanceIndex_Struct_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn main(inputs : Inputs) -> @builtin(position) vec4<f32> {
+ return vec4<f32>(inputs.var_a, inputs.var_b, 0.0, 1.0);
+}
+
+struct Inputs {
+ @location(0) var_a : f32;
+ @location(1) var_b : f32;
+ @builtin(vertex_index) custom_vertex_index : u32;
+ @builtin(instance_index) custom_instance_index : u32;
+};
+)";
+
+ auto* expect = R"(
+struct TintVertexData {
+ tint_vertex_data : @stride(4) array<u32>;
+}
+
+@binding(0) @group(4) var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
+
+@binding(1) @group(4) var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
+
+struct tint_symbol {
+ @builtin(vertex_index)
+ custom_vertex_index : u32;
+ @builtin(instance_index)
+ custom_instance_index : u32;
+}
+
+@stage(vertex)
+fn main(tint_symbol_1 : tint_symbol) -> @builtin(position) vec4<f32> {
+ var inputs : Inputs;
+ inputs.custom_vertex_index = tint_symbol_1.custom_vertex_index;
+ inputs.custom_instance_index = tint_symbol_1.custom_instance_index;
+ {
+ let buffer_array_base_0 = inputs.custom_vertex_index;
+ inputs.var_a = bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[buffer_array_base_0]);
+ let buffer_array_base_1 = inputs.custom_instance_index;
+ inputs.var_b = bitcast<f32>(tint_pulling_vertex_buffer_1.tint_vertex_data[buffer_array_base_1]);
+ }
+ return vec4<f32>(inputs.var_a, inputs.var_b, 0.0, 1.0);
+}
+
+struct Inputs {
+ @location(0)
+ var_a : f32;
+ @location(1)
+ var_b : f32;
+ @builtin(vertex_index)
+ custom_vertex_index : u32;
+ @builtin(instance_index)
+ custom_instance_index : u32;
+}
+)";
+
+ VertexPulling::Config cfg;
+ cfg.vertex_state = {{
+ {
+ 4,
+ VertexStepMode::kVertex,
+ {{VertexFormat::kFloat32, 0, 0}},
+ },
+ {
+ 4,
+ VertexStepMode::kInstance,
+ {{VertexFormat::kFloat32, 0, 1}},
+ },
+ }};
+ cfg.entry_point_name = "main";
+
+ DataMap data;
+ data.Add<VertexPulling::Config>(cfg);
+ auto got = Run<VertexPulling>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex_SeparateStruct) {
auto* src = R"(
struct Inputs {
@@ -502,6 +582,83 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(VertexPullingTest,
+ ExistingVertexIndexAndInstanceIndex_SeparateStruct_OutOfOrder) {
+ auto* src = R"(
+@stage(vertex)
+fn main(inputs : Inputs, indices : Indices) -> @builtin(position) vec4<f32> {
+ return vec4<f32>(inputs.var_a, inputs.var_b, 0.0, 1.0);
+}
+
+struct Inputs {
+ @location(0) var_a : f32;
+ @location(1) var_b : f32;
+};
+
+struct Indices {
+ @builtin(vertex_index) custom_vertex_index : u32;
+ @builtin(instance_index) custom_instance_index : u32;
+};
+)";
+
+ auto* expect = R"(
+struct TintVertexData {
+ tint_vertex_data : @stride(4) array<u32>;
+}
+
+@binding(0) @group(4) var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
+
+@binding(1) @group(4) var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
+
+@stage(vertex)
+fn main(indices : Indices) -> @builtin(position) vec4<f32> {
+ var inputs : Inputs;
+ {
+ let buffer_array_base_0 = indices.custom_vertex_index;
+ inputs.var_a = bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[buffer_array_base_0]);
+ let buffer_array_base_1 = indices.custom_instance_index;
+ inputs.var_b = bitcast<f32>(tint_pulling_vertex_buffer_1.tint_vertex_data[buffer_array_base_1]);
+ }
+ return vec4<f32>(inputs.var_a, inputs.var_b, 0.0, 1.0);
+}
+
+struct Inputs {
+ @location(0)
+ var_a : f32;
+ @location(1)
+ var_b : f32;
+}
+
+struct Indices {
+ @builtin(vertex_index)
+ custom_vertex_index : u32;
+ @builtin(instance_index)
+ custom_instance_index : u32;
+}
+)";
+
+ VertexPulling::Config cfg;
+ cfg.vertex_state = {{
+ {
+ 4,
+ VertexStepMode::kVertex,
+ {{VertexFormat::kFloat32, 0, 0}},
+ },
+ {
+ 4,
+ VertexStepMode::kInstance,
+ {{VertexFormat::kFloat32, 0, 1}},
+ },
+ }};
+ cfg.entry_point_name = "main";
+
+ DataMap data;
+ data.Add<VertexPulling::Config>(cfg);
+ auto got = Run<VertexPulling>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(VertexPullingTest, TwoAttributesSameBuffer) {
auto* src = R"(
@stage(vertex)
diff --git a/src/transform/wrap_arrays_in_structs_test.cc b/src/transform/wrap_arrays_in_structs_test.cc
index 4f3b5d4..c9daaf8 100644
--- a/src/transform/wrap_arrays_in_structs_test.cc
+++ b/src/transform/wrap_arrays_in_structs_test.cc
@@ -172,6 +172,48 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(WrapArraysInStructsTest, ArrayAlias_OutOfOrder) {
+ auto* src = R"(
+fn f() {
+ var arr : Array;
+ arr = Array();
+ arr = Array(Inner(1, 2), Inner(3, 4));
+ let vals : Array = Array(Inner(1, 2), Inner(3, 4));
+ arr = vals;
+ let x = arr[3];
+}
+
+type Array = array<Inner, 2>;
+type Inner = array<i32, 2>;
+)";
+ auto* expect = R"(
+struct tint_array_wrapper_1 {
+ arr : array<i32, 2u>;
+}
+
+struct tint_array_wrapper {
+ arr : array<tint_array_wrapper_1, 2u>;
+}
+
+fn f() {
+ var arr : tint_array_wrapper;
+ arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>());
+ arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
+ let vals : tint_array_wrapper = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
+ arr = vals;
+ let x = arr.arr[3];
+}
+
+type Array = tint_array_wrapper;
+
+type Inner = tint_array_wrapper_1;
+)";
+
+ auto got = Run<WrapArraysInStructs>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(WrapArraysInStructsTest, ArraysInStruct) {
auto* src = R"(
struct S {
@@ -326,6 +368,57 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(WrapArraysInStructsTest, DeclarationOrder_OutOfOrder) {
+ auto* src = R"(
+fn f2() {
+ var v : array<i32, 3>;
+}
+
+type T3 = i32;
+
+fn f1(a : array<i32, 2>) {
+}
+
+type T2 = i32;
+
+type T1 = array<i32, 1>;
+
+type T0 = i32;
+)";
+ auto* expect = R"(
+struct tint_array_wrapper {
+ arr : array<i32, 3u>;
+}
+
+fn f2() {
+ var v : tint_array_wrapper;
+}
+
+type T3 = i32;
+
+struct tint_array_wrapper_1 {
+ arr : array<i32, 2u>;
+}
+
+fn f1(a : tint_array_wrapper_1) {
+}
+
+type T2 = i32;
+
+struct tint_array_wrapper_2 {
+ arr : array<i32, 1u>;
+}
+
+type T1 = tint_array_wrapper_2;
+
+type T0 = i32;
+)";
+
+ auto got = Run<WrapArraysInStructs>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/transform/zero_init_workgroup_memory_test.cc b/src/transform/zero_init_workgroup_memory_test.cc
index 6cceb20..54e1aa2 100644
--- a/src/transform/zero_init_workgroup_memory_test.cc
+++ b/src/transform/zero_init_workgroup_memory_test.cc
@@ -93,6 +93,29 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest, UnreferencedWorkgroupVars_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+}
+
+fn unreferenced() {
+ b = c;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : i32;
+
+var<workgroup> c : i32;
+)";
+ auto* expect = src;
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex) {
auto* src = R"(
var<workgroup> v : i32;
@@ -121,6 +144,34 @@
}
TEST_F(ZeroInitWorkgroupMemoryTest,
+ SingleWorkgroupVar_ExistingLocalIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ _ = v; // Initialization should be inserted above this statement
+}
+
+var<workgroup> v : i32;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ {
+ v = i32();
+ }
+ workgroupBarrier();
+ _ = v;
+}
+
+var<workgroup> v : i32;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ZeroInitWorkgroupMemoryTest,
SingleWorkgroupVar_ExistingLocalIndexInStruct) {
auto* src = R"(
var<workgroup> v : i32;
@@ -157,6 +208,43 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest,
+ SingleWorkgroupVar_ExistingLocalIndexInStruct_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f(params : Params) {
+ _ = v; // Initialization should be inserted above this statement
+}
+
+struct Params {
+ @builtin(local_invocation_index) local_idx : u32;
+};
+
+var<workgroup> v : i32;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(params : Params) {
+ {
+ v = i32();
+ }
+ workgroupBarrier();
+ _ = v;
+}
+
+struct Params {
+ @builtin(local_invocation_index)
+ local_idx : u32;
+}
+
+var<workgroup> v : i32;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex) {
auto* src = R"(
var<workgroup> v : i32;
@@ -185,6 +273,34 @@
}
TEST_F(ZeroInitWorkgroupMemoryTest,
+ SingleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+ _ = v; // Initialization should be inserted above this statement
+}
+
+var<workgroup> v : i32;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
+ {
+ v = i32();
+ }
+ workgroupBarrier();
+ _ = v;
+}
+
+var<workgroup> v : i32;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ZeroInitWorkgroupMemoryTest,
MultipleWorkgroupVar_ExistingLocalIndex_Size1) {
auto* src = R"(
struct S {
@@ -249,6 +365,70 @@
}
TEST_F(ZeroInitWorkgroupMemoryTest,
+ MultipleWorkgroupVar_ExistingLocalIndex_Size1_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ _ = a; // Initialization should be inserted above this statement
+ _ = b;
+ _ = c;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+};
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ {
+ a = i32();
+ b.x = i32();
+ }
+ for(var idx : u32 = local_idx; (idx < 8u); idx = (idx + 1u)) {
+ let i : u32 = idx;
+ b.y[i] = i32();
+ }
+ for(var idx_1 : u32 = local_idx; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
+ let i_1 : u32 = idx_1;
+ c[i_1].x = i32();
+ }
+ for(var idx_2 : u32 = local_idx; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
+ let i_2 : u32 = (idx_2 / 8u);
+ let i : u32 = (idx_2 % 8u);
+ c[i_2].y[i] = i32();
+ }
+ workgroupBarrier();
+ _ = a;
+ _ = b;
+ _ = c;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+}
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ZeroInitWorkgroupMemoryTest,
MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3) {
auto* src = R"(
struct S {
@@ -534,6 +714,70 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest,
+ MultipleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
+ _ = a; // Initialization should be inserted above this statement
+ _ = b;
+ _ = c;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+};
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index : u32) {
+ {
+ a = i32();
+ b.x = i32();
+ }
+ for(var idx : u32 = local_invocation_index; (idx < 8u); idx = (idx + 1u)) {
+ let i : u32 = idx;
+ b.y[i] = i32();
+ }
+ for(var idx_1 : u32 = local_invocation_index; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
+ let i_1 : u32 = idx_1;
+ c[i_1].x = i32();
+ }
+ for(var idx_2 : u32 = local_invocation_index; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
+ let i_2 : u32 = (idx_2 / 8u);
+ let i : u32 = (idx_2 % 8u);
+ c[i_2].y[i] = i32();
+ }
+ workgroupBarrier();
+ _ = a;
+ _ = b;
+ _ = c;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+}
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints) {
auto* src = R"(
struct S {
@@ -633,6 +877,106 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest,
+ MultipleWorkgroupVar_MultipleEntryPoints_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f1() {
+ _ = a; // Initialization should be inserted above this statement
+ _ = c;
+}
+
+@stage(compute) @workgroup_size(1, 2, 3)
+fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
+ _ = b; // Initialization should be inserted above this statement
+}
+
+@stage(compute) @workgroup_size(4, 5, 6)
+fn f3() {
+ _ = c; // Initialization should be inserted above this statement
+ _ = a;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+};
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f1(@builtin(local_invocation_index) local_invocation_index : u32) {
+ {
+ a = i32();
+ }
+ for(var idx : u32 = local_invocation_index; (idx < 32u); idx = (idx + 1u)) {
+ let i : u32 = idx;
+ c[i].x = i32();
+ }
+ for(var idx_1 : u32 = local_invocation_index; (idx_1 < 256u); idx_1 = (idx_1 + 1u)) {
+ let i_1 : u32 = (idx_1 / 8u);
+ let i_2 : u32 = (idx_1 % 8u);
+ c[i_1].y[i_2] = i32();
+ }
+ workgroupBarrier();
+ _ = a;
+ _ = c;
+}
+
+@stage(compute) @workgroup_size(1, 2, 3)
+fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index_1 : u32) {
+ if ((local_invocation_index_1 < 1u)) {
+ b.x = i32();
+ }
+ for(var idx_2 : u32 = local_invocation_index_1; (idx_2 < 8u); idx_2 = (idx_2 + 6u)) {
+ let i_3 : u32 = idx_2;
+ b.y[i_3] = i32();
+ }
+ workgroupBarrier();
+ _ = b;
+}
+
+@stage(compute) @workgroup_size(4, 5, 6)
+fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) {
+ if ((local_invocation_index_2 < 1u)) {
+ a = i32();
+ }
+ if ((local_invocation_index_2 < 32u)) {
+ let i_4 : u32 = local_invocation_index_2;
+ c[i_4].x = i32();
+ }
+ for(var idx_3 : u32 = local_invocation_index_2; (idx_3 < 256u); idx_3 = (idx_3 + 120u)) {
+ let i_5 : u32 = (idx_3 / 8u);
+ let i_6 : u32 = (idx_3 % 8u);
+ c[i_5].y[i_6] = i32();
+ }
+ workgroupBarrier();
+ _ = c;
+ _ = a;
+}
+
+var<workgroup> a : i32;
+
+var<workgroup> b : S;
+
+var<workgroup> c : array<S, 32>;
+
+struct S {
+ x : i32;
+ y : array<i32, 8>;
+}
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage) {
auto* src = R"(
var<workgroup> v : i32;
@@ -676,6 +1020,49 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ call_use_v(); // Initialization should be inserted above this statement
+}
+
+fn call_use_v() {
+ use_v();
+}
+
+fn use_v() {
+ _ = v;
+}
+
+var<workgroup> v : i32;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_idx : u32) {
+ {
+ v = i32();
+ }
+ workgroupBarrier();
+ call_use_v();
+}
+
+fn call_use_v() {
+ use_v();
+}
+
+fn use_v() {
+ _ = v;
+}
+
+var<workgroup> v : i32;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics) {
auto* src = R"(
var<workgroup> i : atomic<i32>;
@@ -709,6 +1096,39 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+ atomicLoad(&(i)); // Initialization should be inserted above this statement
+ atomicLoad(&(u));
+}
+
+var<workgroup> i : atomic<i32>;
+var<workgroup> u : atomic<u32>;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
+ {
+ atomicStore(&(i), i32());
+ atomicStore(&(u), u32());
+ }
+ workgroupBarrier();
+ atomicLoad(&(i));
+ atomicLoad(&(u));
+}
+
+var<workgroup> i : atomic<i32>;
+
+var<workgroup> u : atomic<u32>;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics) {
auto* src = R"(
struct S {
@@ -756,6 +1176,53 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+ _ = w.a; // Initialization should be inserted above this statement
+}
+
+var<workgroup> w : S;
+
+struct S {
+ a : i32;
+ i : atomic<i32>;
+ b : f32;
+ u : atomic<u32>;
+ c : u32;
+};
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
+ {
+ w.a = i32();
+ atomicStore(&(w.i), i32());
+ w.b = f32();
+ atomicStore(&(w.u), u32());
+ w.c = u32();
+ }
+ workgroupBarrier();
+ _ = w.a;
+}
+
+var<workgroup> w : S;
+
+struct S {
+ a : i32;
+ i : atomic<i32>;
+ b : f32;
+ u : atomic<u32>;
+ c : u32;
+}
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics) {
auto* src = R"(
var<workgroup> w : array<atomic<u32>, 4>;
@@ -784,6 +1251,34 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+ atomicLoad(&w[0]); // Initialization should be inserted above this statement
+}
+
+var<workgroup> w : array<atomic<u32>, 4>;
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
+ for(var idx : u32 = local_invocation_index; (idx < 4u); idx = (idx + 1u)) {
+ let i : u32 = idx;
+ atomicStore(&(w[i]), u32());
+ }
+ workgroupBarrier();
+ atomicLoad(&(w[0]));
+}
+
+var<workgroup> w : array<atomic<u32>, 4>;
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics) {
auto* src = R"(
struct S {
@@ -832,6 +1327,55 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(ZeroInitWorkgroupMemoryTest,
+ WorkgroupArrayOfStructOfAtomics_OutOfOrder) {
+ auto* src = R"(
+@stage(compute) @workgroup_size(1)
+fn f() {
+ _ = w[0].a; // Initialization should be inserted above this statement
+}
+
+var<workgroup> w : array<S, 4>;
+
+struct S {
+ a : i32;
+ i : atomic<i32>;
+ b : f32;
+ u : atomic<u32>;
+ c : u32;
+};
+)";
+ auto* expect = R"(
+@stage(compute) @workgroup_size(1)
+fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
+ for(var idx : u32 = local_invocation_index; (idx < 4u); idx = (idx + 1u)) {
+ let i_1 : u32 = idx;
+ w[i_1].a = i32();
+ atomicStore(&(w[i_1].i), i32());
+ w[i_1].b = f32();
+ atomicStore(&(w[i_1].u), u32());
+ w[i_1].c = u32();
+ }
+ workgroupBarrier();
+ _ = w[0].a;
+}
+
+var<workgroup> w : array<S, 4>;
+
+struct S {
+ a : i32;
+ i : atomic<i32>;
+ b : f32;
+ u : atomic<u32>;
+ c : u32;
+}
+)";
+
+ auto got = Run<ZeroInitWorkgroupMemory>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/writer/hlsl/generator_impl_sanitizer_test.cc b/src/writer/hlsl/generator_impl_sanitizer_test.cc
index d17bdca..41f5840 100644
--- a/src/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -181,7 +181,6 @@
auto* expect = R"(cbuffer cbuffer_tint_symbol_1 : register(b4, space3) {
uint4 tint_symbol_1[2];
};
-
ByteAddressBuffer b : register(t1, space2);
ByteAddressBuffer c : register(t2, space2);
diff --git a/test/out_of_order_decls/alias/alias.wgsl b/test/out_of_order_decls/alias/alias.wgsl
new file mode 100644
index 0000000..b1c9e66
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl
@@ -0,0 +1,7 @@
+type T1 = T2;
+type T2 = i32;
+
+@stage(fragment)
+fn f() {
+ var v : T1;
+}
diff --git a/test/out_of_order_decls/alias/alias.wgsl.expected.glsl b/test/out_of_order_decls/alias/alias.wgsl.expected.glsl
new file mode 100644
index 0000000..e1e01c7
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl.expected.glsl
@@ -0,0 +1,11 @@
+#version 310 es
+precision mediump float;
+
+void f() {
+ int v = 0;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/alias/alias.wgsl.expected.hlsl b/test/out_of_order_decls/alias/alias.wgsl.expected.hlsl
new file mode 100644
index 0000000..9047109
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl.expected.hlsl
@@ -0,0 +1,4 @@
+void f() {
+ int v = 0;
+ return;
+}
diff --git a/test/out_of_order_decls/alias/alias.wgsl.expected.msl b/test/out_of_order_decls/alias/alias.wgsl.expected.msl
new file mode 100644
index 0000000..3054a16
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+fragment void f() {
+ int v = 0;
+ return;
+}
+
diff --git a/test/out_of_order_decls/alias/alias.wgsl.expected.spvasm b/test/out_of_order_decls/alias/alias.wgsl.expected.spvasm
new file mode 100644
index 0000000..5afeefa
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl.expected.spvasm
@@ -0,0 +1,21 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 9
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %f "f"
+ OpName %v "v"
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %8 = OpConstantNull %int
+ %f = OpFunction %void None %1
+ %4 = OpLabel
+ %v = OpVariable %_ptr_Function_int Function %8
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/alias/alias.wgsl.expected.wgsl b/test/out_of_order_decls/alias/alias.wgsl.expected.wgsl
new file mode 100644
index 0000000..ca3ee03
--- /dev/null
+++ b/test/out_of_order_decls/alias/alias.wgsl.expected.wgsl
@@ -0,0 +1,8 @@
+type T1 = T2;
+
+type T2 = i32;
+
+@stage(fragment)
+fn f() {
+ var v : T1;
+}
diff --git a/test/out_of_order_decls/alias/struct.wgsl b/test/out_of_order_decls/alias/struct.wgsl
new file mode 100644
index 0000000..20f80a4
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl
@@ -0,0 +1,10 @@
+type T = S;
+
+struct S {
+ m : i32;
+}
+
+@stage(fragment)
+fn f() {
+ var v : T;
+}
diff --git a/test/out_of_order_decls/alias/struct.wgsl.expected.glsl b/test/out_of_order_decls/alias/struct.wgsl.expected.glsl
new file mode 100644
index 0000000..508e315
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl.expected.glsl
@@ -0,0 +1,15 @@
+#version 310 es
+precision mediump float;
+
+struct S {
+ int m;
+};
+
+void f() {
+ S v = S(0);
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/alias/struct.wgsl.expected.hlsl b/test/out_of_order_decls/alias/struct.wgsl.expected.hlsl
new file mode 100644
index 0000000..c113dac
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl.expected.hlsl
@@ -0,0 +1,8 @@
+struct S {
+ int m;
+};
+
+void f() {
+ S v = (S)0;
+ return;
+}
diff --git a/test/out_of_order_decls/alias/struct.wgsl.expected.msl b/test/out_of_order_decls/alias/struct.wgsl.expected.msl
new file mode 100644
index 0000000..c2a67d0
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+ int m;
+};
+
+fragment void f() {
+ S v = {};
+ return;
+}
+
diff --git a/test/out_of_order_decls/alias/struct.wgsl.expected.spvasm b/test/out_of_order_decls/alias/struct.wgsl.expected.spvasm
new file mode 100644
index 0000000..4194956
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl.expected.spvasm
@@ -0,0 +1,25 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 10
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %f "f"
+ OpName %S "S"
+ OpMemberName %S 0 "m"
+ OpName %v "v"
+ OpMemberDecorate %S 0 Offset 0
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %S = OpTypeStruct %int
+%_ptr_Function_S = OpTypePointer Function %S
+ %9 = OpConstantNull %S
+ %f = OpFunction %void None %1
+ %4 = OpLabel
+ %v = OpVariable %_ptr_Function_S Function %9
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/alias/struct.wgsl.expected.wgsl b/test/out_of_order_decls/alias/struct.wgsl.expected.wgsl
new file mode 100644
index 0000000..20f80a4
--- /dev/null
+++ b/test/out_of_order_decls/alias/struct.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+type T = S;
+
+struct S {
+ m : i32;
+}
+
+@stage(fragment)
+fn f() {
+ var v : T;
+}
diff --git a/test/out_of_order_decls/array/alias.wgsl b/test/out_of_order_decls/array/alias.wgsl
new file mode 100644
index 0000000..95cec5a
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl
@@ -0,0 +1,7 @@
+var<private> A : array<T, 4>;
+type T = i32;
+
+@stage(fragment)
+fn f() {
+ A[0] = 1;
+}
diff --git a/test/out_of_order_decls/array/alias.wgsl.expected.glsl b/test/out_of_order_decls/array/alias.wgsl.expected.glsl
new file mode 100644
index 0000000..91dd20a
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl.expected.glsl
@@ -0,0 +1,12 @@
+#version 310 es
+precision mediump float;
+
+int A[4] = int[4](0, 0, 0, 0);
+void f() {
+ A[0] = 1;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/array/alias.wgsl.expected.hlsl b/test/out_of_order_decls/array/alias.wgsl.expected.hlsl
new file mode 100644
index 0000000..c2ce6c2
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl.expected.hlsl
@@ -0,0 +1,6 @@
+static int A[4] = (int[4])0;
+
+void f() {
+ A[0] = 1;
+ return;
+}
diff --git a/test/out_of_order_decls/array/alias.wgsl.expected.msl b/test/out_of_order_decls/array/alias.wgsl.expected.msl
new file mode 100644
index 0000000..e345a6d
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+ int arr[4];
+};
+
+fragment void f() {
+ thread tint_array_wrapper tint_symbol = {};
+ tint_symbol.arr[0] = 1;
+ return;
+}
+
diff --git a/test/out_of_order_decls/array/alias.wgsl.expected.spvasm b/test/out_of_order_decls/array/alias.wgsl.expected.spvasm
new file mode 100644
index 0000000..0daa352
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl.expected.spvasm
@@ -0,0 +1,30 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 16
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %A "A"
+ OpName %f "f"
+ OpDecorate %_arr_int_uint_4 ArrayStride 4
+ %int = OpTypeInt 32 1
+ %uint = OpTypeInt 32 0
+ %uint_4 = OpConstant %uint 4
+%_arr_int_uint_4 = OpTypeArray %int %uint_4
+%_ptr_Private__arr_int_uint_4 = OpTypePointer Private %_arr_int_uint_4
+ %7 = OpConstantNull %_arr_int_uint_4
+ %A = OpVariable %_ptr_Private__arr_int_uint_4 Private %7
+ %void = OpTypeVoid
+ %8 = OpTypeFunction %void
+ %int_0 = OpConstant %int 0
+%_ptr_Private_int = OpTypePointer Private %int
+ %int_1 = OpConstant %int 1
+ %f = OpFunction %void None %8
+ %11 = OpLabel
+ %14 = OpAccessChain %_ptr_Private_int %A %int_0
+ OpStore %14 %int_1
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/array/alias.wgsl.expected.wgsl b/test/out_of_order_decls/array/alias.wgsl.expected.wgsl
new file mode 100644
index 0000000..1e581e0
--- /dev/null
+++ b/test/out_of_order_decls/array/alias.wgsl.expected.wgsl
@@ -0,0 +1,8 @@
+var<private> A : array<T, 4>;
+
+type T = i32;
+
+@stage(fragment)
+fn f() {
+ A[0] = 1;
+}
diff --git a/test/out_of_order_decls/array/struct.wgsl b/test/out_of_order_decls/array/struct.wgsl
new file mode 100644
index 0000000..ab88948
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl
@@ -0,0 +1,9 @@
+var<private> A : array<S, 4>;
+struct S {
+ m : i32;
+};
+
+@stage(fragment)
+fn f() {
+ A[0] = S(1);
+}
diff --git a/test/out_of_order_decls/array/struct.wgsl.expected.glsl b/test/out_of_order_decls/array/struct.wgsl.expected.glsl
new file mode 100644
index 0000000..2f4eab1
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl.expected.glsl
@@ -0,0 +1,17 @@
+#version 310 es
+precision mediump float;
+
+struct S {
+ int m;
+};
+
+S A[4] = S[4](S(0), S(0), S(0), S(0));
+void f() {
+ S tint_symbol = S(1);
+ A[0] = tint_symbol;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/array/struct.wgsl.expected.hlsl b/test/out_of_order_decls/array/struct.wgsl.expected.hlsl
new file mode 100644
index 0000000..a708176
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl.expected.hlsl
@@ -0,0 +1,11 @@
+struct S {
+ int m;
+};
+
+static S A[4] = (S[4])0;
+
+void f() {
+ const S tint_symbol = {1};
+ A[0] = tint_symbol;
+ return;
+}
diff --git a/test/out_of_order_decls/array/struct.wgsl.expected.msl b/test/out_of_order_decls/array/struct.wgsl.expected.msl
new file mode 100644
index 0000000..4e91e2e
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl.expected.msl
@@ -0,0 +1,18 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+ int m;
+};
+
+struct tint_array_wrapper {
+ S arr[4];
+};
+
+fragment void f() {
+ thread tint_array_wrapper tint_symbol_1 = {};
+ S const tint_symbol = {.m=1};
+ tint_symbol_1.arr[0] = tint_symbol;
+ return;
+}
+
diff --git a/test/out_of_order_decls/array/struct.wgsl.expected.spvasm b/test/out_of_order_decls/array/struct.wgsl.expected.spvasm
new file mode 100644
index 0000000..317a445
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl.expected.spvasm
@@ -0,0 +1,35 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 18
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %S "S"
+ OpMemberName %S 0 "m"
+ OpName %A "A"
+ OpName %f "f"
+ OpMemberDecorate %S 0 Offset 0
+ OpDecorate %_arr_S_uint_4 ArrayStride 4
+ %int = OpTypeInt 32 1
+ %S = OpTypeStruct %int
+ %uint = OpTypeInt 32 0
+ %uint_4 = OpConstant %uint 4
+%_arr_S_uint_4 = OpTypeArray %S %uint_4
+%_ptr_Private__arr_S_uint_4 = OpTypePointer Private %_arr_S_uint_4
+ %8 = OpConstantNull %_arr_S_uint_4
+ %A = OpVariable %_ptr_Private__arr_S_uint_4 Private %8
+ %void = OpTypeVoid
+ %9 = OpTypeFunction %void
+ %int_0 = OpConstant %int 0
+%_ptr_Private_S = OpTypePointer Private %S
+ %int_1 = OpConstant %int 1
+ %17 = OpConstantComposite %S %int_1
+ %f = OpFunction %void None %9
+ %12 = OpLabel
+ %15 = OpAccessChain %_ptr_Private_S %A %int_0
+ OpStore %15 %17
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/array/struct.wgsl.expected.wgsl b/test/out_of_order_decls/array/struct.wgsl.expected.wgsl
new file mode 100644
index 0000000..3e57163
--- /dev/null
+++ b/test/out_of_order_decls/array/struct.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+var<private> A : array<S, 4>;
+
+struct S {
+ m : i32;
+}
+
+@stage(fragment)
+fn f() {
+ A[0] = S(1);
+}
diff --git a/test/out_of_order_decls/func/func.wgsl b/test/out_of_order_decls/func/func.wgsl
new file mode 100644
index 0000000..1a0f212
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl
@@ -0,0 +1,7 @@
+@stage(fragment)
+fn f1() {
+ f2();
+}
+
+fn f2() {
+}
diff --git a/test/out_of_order_decls/func/func.wgsl.expected.glsl b/test/out_of_order_decls/func/func.wgsl.expected.glsl
new file mode 100644
index 0000000..4a87ea7
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl.expected.glsl
@@ -0,0 +1,14 @@
+#version 310 es
+precision mediump float;
+
+void f2() {
+}
+
+void f1() {
+ f2();
+}
+
+void main() {
+ f1();
+ return;
+}
diff --git a/test/out_of_order_decls/func/func.wgsl.expected.hlsl b/test/out_of_order_decls/func/func.wgsl.expected.hlsl
new file mode 100644
index 0000000..620afd7
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl.expected.hlsl
@@ -0,0 +1,7 @@
+void f2() {
+}
+
+void f1() {
+ f2();
+ return;
+}
diff --git a/test/out_of_order_decls/func/func.wgsl.expected.msl b/test/out_of_order_decls/func/func.wgsl.expected.msl
new file mode 100644
index 0000000..e04a45a
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl.expected.msl
@@ -0,0 +1,11 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void f2() {
+}
+
+fragment void f1() {
+ f2();
+ return;
+}
+
diff --git a/test/out_of_order_decls/func/func.wgsl.expected.spvasm b/test/out_of_order_decls/func/func.wgsl.expected.spvasm
new file mode 100644
index 0000000..19570a4
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl.expected.spvasm
@@ -0,0 +1,22 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 8
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f1 "f1"
+ OpExecutionMode %f1 OriginUpperLeft
+ OpName %f2 "f2"
+ OpName %f1 "f1"
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %f2 = OpFunction %void None %1
+ %4 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %f1 = OpFunction %void None %1
+ %6 = OpLabel
+ %7 = OpFunctionCall %void %f2
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/func/func.wgsl.expected.wgsl b/test/out_of_order_decls/func/func.wgsl.expected.wgsl
new file mode 100644
index 0000000..1a0f212
--- /dev/null
+++ b/test/out_of_order_decls/func/func.wgsl.expected.wgsl
@@ -0,0 +1,7 @@
+@stage(fragment)
+fn f1() {
+ f2();
+}
+
+fn f2() {
+}
diff --git a/test/out_of_order_decls/func/let.wgsl b/test/out_of_order_decls/func/let.wgsl
new file mode 100644
index 0000000..25a56c6
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ let b = a;
+}
+
+let a : i32 = 1;
diff --git a/test/out_of_order_decls/func/let.wgsl.expected.glsl b/test/out_of_order_decls/func/let.wgsl.expected.glsl
new file mode 100644
index 0000000..f308959
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl.expected.glsl
@@ -0,0 +1,12 @@
+#version 310 es
+precision mediump float;
+
+const int a = 1;
+void f() {
+ int b = a;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/func/let.wgsl.expected.hlsl b/test/out_of_order_decls/func/let.wgsl.expected.hlsl
new file mode 100644
index 0000000..c35c250
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl.expected.hlsl
@@ -0,0 +1,6 @@
+static const int a = 1;
+
+void f() {
+ const int b = a;
+ return;
+}
diff --git a/test/out_of_order_decls/func/let.wgsl.expected.msl b/test/out_of_order_decls/func/let.wgsl.expected.msl
new file mode 100644
index 0000000..2509d6c
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl.expected.msl
@@ -0,0 +1,10 @@
+#include <metal_stdlib>
+
+using namespace metal;
+constant int a = 1;
+
+fragment void f() {
+ int const b = a;
+ return;
+}
+
diff --git a/test/out_of_order_decls/func/let.wgsl.expected.spvasm b/test/out_of_order_decls/func/let.wgsl.expected.spvasm
new file mode 100644
index 0000000..452861f
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl.expected.spvasm
@@ -0,0 +1,19 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 7
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %a "a"
+ OpName %f "f"
+ %int = OpTypeInt 32 1
+ %a = OpConstant %int 1
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %f = OpFunction %void None %3
+ %6 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/func/let.wgsl.expected.wgsl b/test/out_of_order_decls/func/let.wgsl.expected.wgsl
new file mode 100644
index 0000000..25a56c6
--- /dev/null
+++ b/test/out_of_order_decls/func/let.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ let b = a;
+}
+
+let a : i32 = 1;
diff --git a/test/out_of_order_decls/func/type.wgsl b/test/out_of_order_decls/func/type.wgsl
new file mode 100644
index 0000000..4810fc1
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ var b : T;
+}
+
+type T = i32;
diff --git a/test/out_of_order_decls/func/type.wgsl.expected.glsl b/test/out_of_order_decls/func/type.wgsl.expected.glsl
new file mode 100644
index 0000000..573558e
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl.expected.glsl
@@ -0,0 +1,11 @@
+#version 310 es
+precision mediump float;
+
+void f() {
+ int b = 0;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/func/type.wgsl.expected.hlsl b/test/out_of_order_decls/func/type.wgsl.expected.hlsl
new file mode 100644
index 0000000..f4b327c
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl.expected.hlsl
@@ -0,0 +1,4 @@
+void f() {
+ int b = 0;
+ return;
+}
diff --git a/test/out_of_order_decls/func/type.wgsl.expected.msl b/test/out_of_order_decls/func/type.wgsl.expected.msl
new file mode 100644
index 0000000..438c74f
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+fragment void f() {
+ int b = 0;
+ return;
+}
+
diff --git a/test/out_of_order_decls/func/type.wgsl.expected.spvasm b/test/out_of_order_decls/func/type.wgsl.expected.spvasm
new file mode 100644
index 0000000..1de57b4
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl.expected.spvasm
@@ -0,0 +1,21 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 9
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %f "f"
+ OpName %b "b"
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %8 = OpConstantNull %int
+ %f = OpFunction %void None %1
+ %4 = OpLabel
+ %b = OpVariable %_ptr_Function_int Function %8
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/func/type.wgsl.expected.wgsl b/test/out_of_order_decls/func/type.wgsl.expected.wgsl
new file mode 100644
index 0000000..4810fc1
--- /dev/null
+++ b/test/out_of_order_decls/func/type.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ var b : T;
+}
+
+type T = i32;
diff --git a/test/out_of_order_decls/func/var.wgsl b/test/out_of_order_decls/func/var.wgsl
new file mode 100644
index 0000000..d77a968
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ let b = a;
+}
+
+var<private> a : i32 = 1;
diff --git a/test/out_of_order_decls/func/var.wgsl.expected.glsl b/test/out_of_order_decls/func/var.wgsl.expected.glsl
new file mode 100644
index 0000000..6cf55fb
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl.expected.glsl
@@ -0,0 +1,12 @@
+#version 310 es
+precision mediump float;
+
+int a = 1;
+void f() {
+ int b = a;
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/func/var.wgsl.expected.hlsl b/test/out_of_order_decls/func/var.wgsl.expected.hlsl
new file mode 100644
index 0000000..813886a
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl.expected.hlsl
@@ -0,0 +1,6 @@
+static int a = 1;
+
+void f() {
+ const int b = a;
+ return;
+}
diff --git a/test/out_of_order_decls/func/var.wgsl.expected.msl b/test/out_of_order_decls/func/var.wgsl.expected.msl
new file mode 100644
index 0000000..832e2eb
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+fragment void f() {
+ thread int tint_symbol = 1;
+ int const b = tint_symbol;
+ return;
+}
+
diff --git a/test/out_of_order_decls/func/var.wgsl.expected.spvasm b/test/out_of_order_decls/func/var.wgsl.expected.spvasm
new file mode 100644
index 0000000..16c9dc9
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl.expected.spvasm
@@ -0,0 +1,22 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 10
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %a "a"
+ OpName %f "f"
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+%_ptr_Private_int = OpTypePointer Private %int
+ %a = OpVariable %_ptr_Private_int Private %int_1
+ %void = OpTypeVoid
+ %5 = OpTypeFunction %void
+ %f = OpFunction %void None %5
+ %8 = OpLabel
+ %9 = OpLoad %int %a
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/func/var.wgsl.expected.wgsl b/test/out_of_order_decls/func/var.wgsl.expected.wgsl
new file mode 100644
index 0000000..d77a968
--- /dev/null
+++ b/test/out_of_order_decls/func/var.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+@stage(fragment)
+fn f() {
+ let b = a;
+}
+
+var<private> a : i32 = 1;
diff --git a/test/out_of_order_decls/struct/alias.wgsl b/test/out_of_order_decls/struct/alias.wgsl
new file mode 100644
index 0000000..e225a73
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl
@@ -0,0 +1,10 @@
+struct S {
+ m : T;
+}
+
+type T = i32;
+
+@stage(fragment)
+fn f() {
+ var v : S;
+}
diff --git a/test/out_of_order_decls/struct/alias.wgsl.expected.glsl b/test/out_of_order_decls/struct/alias.wgsl.expected.glsl
new file mode 100644
index 0000000..508e315
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl.expected.glsl
@@ -0,0 +1,15 @@
+#version 310 es
+precision mediump float;
+
+struct S {
+ int m;
+};
+
+void f() {
+ S v = S(0);
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/struct/alias.wgsl.expected.hlsl b/test/out_of_order_decls/struct/alias.wgsl.expected.hlsl
new file mode 100644
index 0000000..c113dac
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl.expected.hlsl
@@ -0,0 +1,8 @@
+struct S {
+ int m;
+};
+
+void f() {
+ S v = (S)0;
+ return;
+}
diff --git a/test/out_of_order_decls/struct/alias.wgsl.expected.msl b/test/out_of_order_decls/struct/alias.wgsl.expected.msl
new file mode 100644
index 0000000..c2a67d0
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+ int m;
+};
+
+fragment void f() {
+ S v = {};
+ return;
+}
+
diff --git a/test/out_of_order_decls/struct/alias.wgsl.expected.spvasm b/test/out_of_order_decls/struct/alias.wgsl.expected.spvasm
new file mode 100644
index 0000000..4194956
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl.expected.spvasm
@@ -0,0 +1,25 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 10
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %f "f"
+ OpName %S "S"
+ OpMemberName %S 0 "m"
+ OpName %v "v"
+ OpMemberDecorate %S 0 Offset 0
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %S = OpTypeStruct %int
+%_ptr_Function_S = OpTypePointer Function %S
+ %9 = OpConstantNull %S
+ %f = OpFunction %void None %1
+ %4 = OpLabel
+ %v = OpVariable %_ptr_Function_S Function %9
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/struct/alias.wgsl.expected.wgsl b/test/out_of_order_decls/struct/alias.wgsl.expected.wgsl
new file mode 100644
index 0000000..e225a73
--- /dev/null
+++ b/test/out_of_order_decls/struct/alias.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+struct S {
+ m : T;
+}
+
+type T = i32;
+
+@stage(fragment)
+fn f() {
+ var v : S;
+}
diff --git a/test/out_of_order_decls/struct/struct.wgsl b/test/out_of_order_decls/struct/struct.wgsl
new file mode 100644
index 0000000..d4e405f
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl
@@ -0,0 +1,12 @@
+struct S1 {
+ m : S2;
+}
+
+struct S2 {
+ m : i32;
+}
+
+@stage(fragment)
+fn f() {
+ var v : S1;
+}
diff --git a/test/out_of_order_decls/struct/struct.wgsl.expected.glsl b/test/out_of_order_decls/struct/struct.wgsl.expected.glsl
new file mode 100644
index 0000000..a95a80d
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl.expected.glsl
@@ -0,0 +1,19 @@
+#version 310 es
+precision mediump float;
+
+struct S2 {
+ int m;
+};
+
+struct S1 {
+ S2 m;
+};
+
+void f() {
+ S1 v = S1(S2(0));
+}
+
+void main() {
+ f();
+ return;
+}
diff --git a/test/out_of_order_decls/struct/struct.wgsl.expected.hlsl b/test/out_of_order_decls/struct/struct.wgsl.expected.hlsl
new file mode 100644
index 0000000..2f48847
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl.expected.hlsl
@@ -0,0 +1,11 @@
+struct S2 {
+ int m;
+};
+struct S1 {
+ S2 m;
+};
+
+void f() {
+ S1 v = (S1)0;
+ return;
+}
diff --git a/test/out_of_order_decls/struct/struct.wgsl.expected.msl b/test/out_of_order_decls/struct/struct.wgsl.expected.msl
new file mode 100644
index 0000000..6dcac2b
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl.expected.msl
@@ -0,0 +1,16 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S2 {
+ int m;
+};
+
+struct S1 {
+ S2 m;
+};
+
+fragment void f() {
+ S1 v = {};
+ return;
+}
+
diff --git a/test/out_of_order_decls/struct/struct.wgsl.expected.spvasm b/test/out_of_order_decls/struct/struct.wgsl.expected.spvasm
new file mode 100644
index 0000000..30b44c5
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl.expected.spvasm
@@ -0,0 +1,29 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 11
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %f "f"
+ OpExecutionMode %f OriginUpperLeft
+ OpName %f "f"
+ OpName %S1 "S1"
+ OpMemberName %S1 0 "m"
+ OpName %S2 "S2"
+ OpMemberName %S2 0 "m"
+ OpName %v "v"
+ OpMemberDecorate %S1 0 Offset 0
+ OpMemberDecorate %S2 0 Offset 0
+ %void = OpTypeVoid
+ %1 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %S2 = OpTypeStruct %int
+ %S1 = OpTypeStruct %S2
+%_ptr_Function_S1 = OpTypePointer Function %S1
+ %10 = OpConstantNull %S1
+ %f = OpFunction %void None %1
+ %4 = OpLabel
+ %v = OpVariable %_ptr_Function_S1 Function %10
+ OpReturn
+ OpFunctionEnd
diff --git a/test/out_of_order_decls/struct/struct.wgsl.expected.wgsl b/test/out_of_order_decls/struct/struct.wgsl.expected.wgsl
new file mode 100644
index 0000000..d4e405f
--- /dev/null
+++ b/test/out_of_order_decls/struct/struct.wgsl.expected.wgsl
@@ -0,0 +1,12 @@
+struct S1 {
+ m : S2;
+}
+
+struct S2 {
+ m : i32;
+}
+
+@stage(fragment)
+fn f() {
+ var v : S1;
+}