From 6688b0a3c78420d05f3d2256e5abace842401319 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Fri, 11 Feb 2022 12:59:08 +0000 Subject: [PATCH] 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 Kokoro: Kokoro --- src/resolver/call_test.cc | 12 + src/resolver/dependency_graph.cc | 42 +- src/resolver/dependency_graph.h | 5 +- src/resolver/dependency_graph_test.cc | 36 +- src/resolver/resolver.cc | 8 +- .../add_spirv_block_attribute_test.cc | 62 + src/transform/array_length_from_uniform.cc | 6 +- .../array_length_from_uniform_test.cc | 48 + src/transform/calculate_array_length.cc | 18 +- src/transform/calculate_array_length_test.cc | 130 +- .../canonicalize_entry_point_io_test.cc | 1641 +++++++++++++++++ src/transform/combine_samplers_test.cc | 288 +++ src/transform/decompose_memory_access_test.cc | 1373 ++++++++++++++ .../external_texture_transform_test.cc | 80 + src/transform/first_index_offset_test.cc | 276 +++ .../fold_trivial_single_use_lets_test.cc | 20 + .../localize_struct_array_assignment_test.cc | 274 +++ ...ule_scope_var_to_entry_point_param_test.cc | 494 +++++ .../multiplanar_external_texture_test.cc | 512 +++++ .../num_workgroups_from_uniform_test.cc | 104 ++ src/transform/pad_array_elements_test.cc | 95 + .../promote_initializers_to_const_var_test.cc | 136 ++ src/transform/remove_phonies_test.cc | 172 ++ src/transform/robustness_test.cc | 618 ++++++- src/transform/unshadow_test.cc | 212 +++ src/transform/vertex_pulling_test.cc | 157 ++ src/transform/wrap_arrays_in_structs_test.cc | 93 + .../zero_init_workgroup_memory_test.cc | 544 ++++++ .../hlsl/generator_impl_sanitizer_test.cc | 1 - test/out_of_order_decls/alias/alias.wgsl | 7 + .../alias/alias.wgsl.expected.glsl | 11 + .../alias/alias.wgsl.expected.hlsl | 4 + .../alias/alias.wgsl.expected.msl | 8 + .../alias/alias.wgsl.expected.spvasm | 21 + .../alias/alias.wgsl.expected.wgsl | 8 + test/out_of_order_decls/alias/struct.wgsl | 10 + .../alias/struct.wgsl.expected.glsl | 15 + .../alias/struct.wgsl.expected.hlsl | 8 + .../alias/struct.wgsl.expected.msl | 12 + .../alias/struct.wgsl.expected.spvasm | 25 + .../alias/struct.wgsl.expected.wgsl | 10 + test/out_of_order_decls/array/alias.wgsl | 7 + .../array/alias.wgsl.expected.glsl | 12 + .../array/alias.wgsl.expected.hlsl | 6 + .../array/alias.wgsl.expected.msl | 13 + .../array/alias.wgsl.expected.spvasm | 30 + .../array/alias.wgsl.expected.wgsl | 8 + test/out_of_order_decls/array/struct.wgsl | 9 + .../array/struct.wgsl.expected.glsl | 17 + .../array/struct.wgsl.expected.hlsl | 11 + .../array/struct.wgsl.expected.msl | 18 + .../array/struct.wgsl.expected.spvasm | 35 + .../array/struct.wgsl.expected.wgsl | 10 + test/out_of_order_decls/func/func.wgsl | 7 + .../func/func.wgsl.expected.glsl | 14 + .../func/func.wgsl.expected.hlsl | 7 + .../func/func.wgsl.expected.msl | 11 + .../func/func.wgsl.expected.spvasm | 22 + .../func/func.wgsl.expected.wgsl | 7 + test/out_of_order_decls/func/let.wgsl | 6 + .../func/let.wgsl.expected.glsl | 12 + .../func/let.wgsl.expected.hlsl | 6 + .../func/let.wgsl.expected.msl | 10 + .../func/let.wgsl.expected.spvasm | 19 + .../func/let.wgsl.expected.wgsl | 6 + test/out_of_order_decls/func/type.wgsl | 6 + .../func/type.wgsl.expected.glsl | 11 + .../func/type.wgsl.expected.hlsl | 4 + .../func/type.wgsl.expected.msl | 8 + .../func/type.wgsl.expected.spvasm | 21 + .../func/type.wgsl.expected.wgsl | 6 + test/out_of_order_decls/func/var.wgsl | 6 + .../func/var.wgsl.expected.glsl | 12 + .../func/var.wgsl.expected.hlsl | 6 + .../func/var.wgsl.expected.msl | 9 + .../func/var.wgsl.expected.spvasm | 22 + .../func/var.wgsl.expected.wgsl | 6 + test/out_of_order_decls/struct/alias.wgsl | 10 + .../struct/alias.wgsl.expected.glsl | 15 + .../struct/alias.wgsl.expected.hlsl | 8 + .../struct/alias.wgsl.expected.msl | 12 + .../struct/alias.wgsl.expected.spvasm | 25 + .../struct/alias.wgsl.expected.wgsl | 10 + test/out_of_order_decls/struct/struct.wgsl | 12 + .../struct/struct.wgsl.expected.glsl | 19 + .../struct/struct.wgsl.expected.hlsl | 11 + .../struct/struct.wgsl.expected.msl | 16 + .../struct/struct.wgsl.expected.spvasm | 29 + .../struct/struct.wgsl.expected.wgsl | 12 + 89 files changed, 8070 insertions(+), 125 deletions(-) create mode 100644 test/out_of_order_decls/alias/alias.wgsl create mode 100644 test/out_of_order_decls/alias/alias.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/alias/alias.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/alias/alias.wgsl.expected.msl create mode 100644 test/out_of_order_decls/alias/alias.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/alias/alias.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/alias/struct.wgsl create mode 100644 test/out_of_order_decls/alias/struct.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/alias/struct.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/alias/struct.wgsl.expected.msl create mode 100644 test/out_of_order_decls/alias/struct.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/alias/struct.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/array/alias.wgsl create mode 100644 test/out_of_order_decls/array/alias.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/array/alias.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/array/alias.wgsl.expected.msl create mode 100644 test/out_of_order_decls/array/alias.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/array/alias.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/array/struct.wgsl create mode 100644 test/out_of_order_decls/array/struct.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/array/struct.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/array/struct.wgsl.expected.msl create mode 100644 test/out_of_order_decls/array/struct.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/array/struct.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/func/func.wgsl create mode 100644 test/out_of_order_decls/func/func.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/func/func.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/func/func.wgsl.expected.msl create mode 100644 test/out_of_order_decls/func/func.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/func/func.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/func/let.wgsl create mode 100644 test/out_of_order_decls/func/let.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/func/let.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/func/let.wgsl.expected.msl create mode 100644 test/out_of_order_decls/func/let.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/func/let.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/func/type.wgsl create mode 100644 test/out_of_order_decls/func/type.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/func/type.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/func/type.wgsl.expected.msl create mode 100644 test/out_of_order_decls/func/type.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/func/type.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/func/var.wgsl create mode 100644 test/out_of_order_decls/func/var.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/func/var.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/func/var.wgsl.expected.msl create mode 100644 test/out_of_order_decls/func/var.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/func/var.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/struct/alias.wgsl create mode 100644 test/out_of_order_decls/struct/alias.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/struct/alias.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/struct/alias.wgsl.expected.msl create mode 100644 test/out_of_order_decls/struct/alias.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/struct/alias.wgsl.expected.wgsl create mode 100644 test/out_of_order_decls/struct/struct.wgsl create mode 100644 test/out_of_order_decls/struct/struct.wgsl.expected.glsl create mode 100644 test/out_of_order_decls/struct/struct.wgsl.expected.hlsl create mode 100644 test/out_of_order_decls/struct/struct.wgsl.expected.msl create mode 100644 test/out_of_order_decls/struct/struct.wgsl.expected.spvasm create mode 100644 test/out_of_order_decls/struct/struct.wgsl.expected.wgsl diff --git a/src/resolver/call_test.cc b/src/resolver/call_test.cc index 3448726ea7..9a3cc85523 100644 --- a/src/resolver/call_test.cc +++ b/src/resolver/call_test.cc @@ -101,6 +101,18 @@ TEST_F(ResolverCallTest, Valid) { 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 d520cd7361..7aa5c52080 100644 --- a/src/resolver/dependency_graph.cc +++ b/src/resolver/dependency_graph.cc @@ -474,7 +474,7 @@ struct DependencyAnalysis { /// 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 @@ struct DependencyAnalysis { // 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 @@ struct DependencyAnalysis { 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 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 @@ DependencyGraph::~DependencyGraph() = default; 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 6235ad74cb..294786da68 100644 --- a/src/resolver/dependency_graph.h +++ b/src/resolver/dependency_graph.h @@ -40,14 +40,11 @@ struct DependencyGraph { /// @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 ordered_globals; diff --git a/src/resolver/dependency_graph_test.cc b/src/resolver/dependency_graph_test.cc index 0b9080ccb5..2329d86f04 100644 --- a/src/resolver/dependency_graph_test.cc +++ b/src/resolver/dependency_graph_test.cc @@ -29,13 +29,10 @@ using ::testing::ElementsAre; template class ResolverDependencyGraphTestWithParam : public ResolverTestWithParam { 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 @@ void SymbolTestHelper::Build() { //////////////////////////////////////////////////////////////////////////////// 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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, FuncCall) { 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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeConstructed) { {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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByLocal) { {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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByParam) { 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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedAsReturnType) { 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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeByStructMember) { 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 @@ TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, VarUsed) { 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 2a77c96cbd..623af60b78 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -95,8 +95,7 @@ bool Resolver::Resolve() { } 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::Resolve() { 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()) { 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 21dbe19a0f..256e8b7338 100644 --- a/src/transform/add_spirv_block_attribute_test.cc +++ b/src/transform/add_spirv_block_attribute_test.cc @@ -548,6 +548,68 @@ fn main() { 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 u1 : MyInner; + +type MyInner = Inner; + +@group(0) @binding(0) +var 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 u1 : u1_block; + +type MyInner = Inner; + +@group(0) @binding(0) var u0 : MyOuter; + +type MyOuter = Outer; + +@internal(spirv_block) +struct Outer { + i : MyInner; +} + +struct Inner { + f : f32; +} +)"; + + auto got = Run(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 9270fd99fb..9735317f52 100644 --- a/src/transform/array_length_from_uniform.cc +++ b/src/transform/array_length_from_uniform.cc @@ -154,10 +154,8 @@ void ArrayLengthFromUniform::Run(CloneContext& ctx, buffer_size_ubo = ctx.dst->Global( ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct), ast::StorageClass::kUniform, - ast::AttributeList{ - ctx.dst->create(cfg->ubo_binding.group), - ctx.dst->create( - 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 a4f2fa9a32..1ae41ba4ca 100644 --- a/src/transform/array_length_from_uniform_test.cc +++ b/src/transform/array_length_from_uniform_test.cc @@ -536,6 +536,54 @@ fn main() { got.data.Get()->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 sb : SB; + +struct SB { + x : i32; + arr : array; +}; +)"; + + auto* expect = R"( +struct tint_symbol { + buffer_size : array, 1u>; +} + +@group(0) @binding(30) var 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 sb : SB; + +struct SB { + x : i32; + arr : array; +} +)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); + EXPECT_EQ(std::unordered_set({0}), + got.data.Get()->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 ac57650ed0..b745bd23b2 100644 --- a/src/transform/calculate_array_length.cc +++ b/src/transform/calculate_array_length.cc @@ -101,7 +101,7 @@ void CalculateArrayLength::Run(CloneContext& ctx, auto* type = CreateASTTypeFor(ctx, buffer_type); auto* disable_validation = ctx.dst->Disable( ast::DisabledValidation::kIgnoreConstructibleFunctionParameter); - auto* func = ctx.dst->create( + ctx.dst->AST().AddFunction(ctx.dst->create( name, ast::VariableList{ // Note: The buffer parameter requires the kStorage StorageClass @@ -118,20 +118,8 @@ void CalculateArrayLength::Run(CloneContext& ctx, ast::AttributeList{ ctx.dst->ASTNodes().Create(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()) { - insert_after = arr->ElemType(); - } - if (auto* str = insert_after->As()) { - 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 fcf70010d3..fa1f274e83 100644 --- a/src/transform/calculate_array_length_test.cc +++ b/src/transform/calculate_array_length_test.cc @@ -111,14 +111,14 @@ fn main() { )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) + struct SB { x : i32; arr : array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) - @group(0) @binding(0) var sb : SB; @stage(compute) @workgroup_size(1) @@ -149,13 +149,13 @@ fn main() { } )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array, result : ptr) + struct S { f : f32; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array, result : ptr) - @group(0) @binding(0) var arr : array; @stage(compute) @workgroup_size(1) @@ -186,13 +186,13 @@ fn main() { } )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array>, result : ptr) + struct S { f : f32; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array>, result : ptr) - @group(0) @binding(0) var arr : array>; @stage(compute) @workgroup_size(1) @@ -261,14 +261,14 @@ fn main() { )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) + struct SB { x : i32; arr : array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) - @group(0) @binding(0) var sb : SB; @stage(compute) @workgroup_size(1) @@ -334,15 +334,15 @@ fn main() { )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) + struct SB { x : i32; y : f32; arr : @stride(64) array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) - @group(0) @binding(0) var sb : SB; @stage(compute) @workgroup_size(1) @@ -381,14 +381,14 @@ fn main() { )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) + struct SB { x : i32; arr : array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) - @group(0) @binding(0) var sb : SB; @stage(compute) @workgroup_size(1) @@ -442,6 +442,12 @@ fn main() { )"; auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr) + +@internal(intrinsic_buffer_size) +fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr) + @internal(intrinsic_buffer_size) fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array, result : ptr) @@ -450,17 +456,11 @@ struct SB1 { arr1 : array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr) - struct SB2 { x : i32; arr2 : array>; } -@internal(intrinsic_buffer_size) -fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr) - @group(0) @binding(0) var sb1 : SB1; @group(0) @binding(1) var sb2 : SB2; @@ -512,14 +512,14 @@ fn main() { auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) + struct SB { x : i32; arr : array; } -@internal(intrinsic_buffer_size) -fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, result : ptr) - @group(0) @binding(0) var a : SB; @group(0) @binding(1) var b : SB; @@ -544,6 +544,82 @@ fn main() { 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 sb1 : SB1; + +struct SB1 { + x : i32; + arr1 : array; +}; + +@group(0) @binding(1) var sb2 : SB2; + +struct SB2 { + x : i32; + arr2 : array>; +}; + +@group(0) @binding(2) var sb3 : array; +)"; + + auto* expect = R"( +@internal(intrinsic_buffer_size) +fn tint_symbol(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB1, result : ptr) + +@internal(intrinsic_buffer_size) +fn tint_symbol_3(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB2, result : ptr) + +@internal(intrinsic_buffer_size) +fn tint_symbol_6(@internal(disable_validation__ignore_constructible_function_parameter) buffer : array, result : ptr) + +@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 sb1 : SB1; + +struct SB1 { + x : i32; + arr1 : array; +} + +@group(0) @binding(1) var sb2 : SB2; + +struct SB2 { + x : i32; + arr2 : array>; +} + +@group(0) @binding(2) var sb3 : array; +)"; + + auto got = Run(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 23030cbf1e..990a0c51ae 100644 --- a/src/transform/canonicalize_entry_point_io_test.cc +++ b/src/transform/canonicalize_entry_point_io_test.cc @@ -203,6 +203,42 @@ fn frag_main(tint_symbol : tint_symbol_1) { 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::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Spirv) { auto* src = R"( struct FragBuiltins { @@ -257,6 +293,60 @@ fn frag_main() { 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; +}; +struct FragLocations { + @location(1) loc1 : f32; + @location(2) @interpolate(flat) loc2 : vec4; +}; +)"; + + auto* expect = R"( +@location(0) @internal(disable_validation__ignore_storage_class) var loc0_1 : f32; + +@location(1) @internal(disable_validation__ignore_storage_class) var loc1_1 : f32; + +@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var loc2_1 : vec4; + +@builtin(position) @internal(disable_validation__ignore_storage_class) var coord_1 : vec4; + +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; +} + +struct FragLocations { + loc1 : f32; + loc2 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kSpirv); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_kMsl) { auto* src = R"( struct FragBuiltins { @@ -312,6 +402,61 @@ fn frag_main(@builtin(position) coord : vec4, tint_symbol : tint_symbol_1) 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; +}; +struct FragLocations { + @location(1) loc1 : f32; + @location(2) @interpolate(flat) loc2 : vec4; +}; +)"; + + auto* expect = R"( +struct tint_symbol_1 { + @location(0) + loc0 : f32; + @location(1) + loc1 : f32; + @location(2) @interpolate(flat) + loc2 : vec4; +} + +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, tint_symbol : tint_symbol_1) { + frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(coord)); +} + +struct FragBuiltins { + coord : vec4; +} + +struct FragLocations { + loc1 : f32; + loc2 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl) { auto* src = R"( struct FragBuiltins { @@ -369,6 +514,63 @@ fn frag_main(tint_symbol : tint_symbol_1) { 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; +}; +struct FragLocations { + @location(1) loc1 : f32; + @location(2) @interpolate(flat) loc2 : vec4; +}; +)"; + + auto* expect = R"( +struct tint_symbol_1 { + @location(0) + loc0 : f32; + @location(1) + loc1 : f32; + @location(2) @interpolate(flat) + loc2 : vec4; + @builtin(position) + coord : vec4; +} + +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; +} + +struct FragLocations { + loc1 : f32; + loc2 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Spirv) { auto* src = R"( @stage(fragment) @@ -525,6 +727,62 @@ fn frag_main() { 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(0.5, 0.5, 0.5, 1.0); + return output; +} + +struct FragOutput { + @location(0) color : vec4; + @builtin(frag_depth) depth : f32; + @builtin(sample_mask) mask : u32; +}; +)"; + + auto* expect = R"( +@location(0) @internal(disable_validation__ignore_storage_class) var color_1 : vec4; + +@builtin(frag_depth) @internal(disable_validation__ignore_storage_class) var depth_1 : f32; + +@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var mask_1 : array; + +fn frag_main_inner() -> FragOutput { + var output : FragOutput; + output.depth = 1.0; + output.mask = 7u; + output.color = vec4(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; + depth : f32; + mask : u32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kSpirv); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Msl) { auto* src = R"( struct FragOutput { @@ -586,6 +844,67 @@ fn frag_main() -> tint_symbol { 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(0.5, 0.5, 0.5, 1.0); + return output; +} + +struct FragOutput { + @location(0) color : vec4; + @builtin(frag_depth) depth : f32; + @builtin(sample_mask) mask : u32; +}; +)"; + + auto* expect = R"( +struct tint_symbol { + @location(0) + color : vec4; + @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(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; + depth : f32; + mask : u32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl) { auto* src = R"( struct FragOutput { @@ -647,6 +966,67 @@ fn frag_main() -> tint_symbol { 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(0.5, 0.5, 0.5, 1.0); + return output; +} + +struct FragOutput { + @location(0) color : vec4; + @builtin(frag_depth) depth : f32; + @builtin(sample_mask) mask : u32; +}; +)"; + + auto* expect = R"( +struct tint_symbol { + @location(0) + color : vec4; + @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(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; + depth : f32; + mask : u32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Spirv) { auto* src = R"( @@ -715,6 +1095,74 @@ fn frag_main2() { EXPECT_EQ(expect, str(got)); } +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 value_1 : f32; + +@location(1) @internal(disable_validation__ignore_storage_class) var mul_1 : f32; + +@location(0) @internal(disable_validation__ignore_storage_class) var value_2 : f32; + +@location(1) @internal(disable_validation__ignore_storage_class) var 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::ShaderStyle::kSpirv); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Msl) { auto* src = R"( @@ -789,6 +1237,80 @@ fn frag_main2(tint_symbol_2 : tint_symbol_3) { EXPECT_EQ(expect, str(got)); } +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::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Hlsl) { auto* src = R"( @@ -863,6 +1385,80 @@ fn frag_main2(tint_symbol_2 : tint_symbol_3) { 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::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) { auto* src = R"( struct FragmentInput { @@ -931,6 +1527,74 @@ fn frag_main1(tint_symbol : tint_symbol_1) { 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 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 global_inputs : FragmentInput; + +struct FragmentInput { + col1 : f32; + col2 : f32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) { auto* src = R"( type myf32 = f32; @@ -1018,6 +1682,93 @@ fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { 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::ShaderStyle::kMsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) { auto* src = R"( struct VertexOut { @@ -1110,6 +1861,98 @@ fn frag_main(tint_symbol_1 : tint_symbol_2) { 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; + @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; +} + +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; + loc1 : f32; + loc2 : f32; + loc3 : f32; +} + +struct FragmentIn { + loc1 : f32; + loc2 : f32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(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 @@ fn frag_main() { 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()); +} + +@stage(fragment) +fn frag_main(inputs : FragmentInterface) -> FragmentInterface { + return inputs; +} + +struct VertexIn { + @location(0) i : i32; + @location(1) u : u32; + @location(2) vi : vec4; + @location(3) vu : vec4; +}; + +struct VertexOut { + @location(0) @interpolate(flat) i : i32; + @location(1) @interpolate(flat) u : u32; + @location(2) @interpolate(flat) vi : vec4; + @location(3) @interpolate(flat) vu : vec4; + @builtin(position) pos : vec4; +}; + +struct FragmentInterface { + @location(0) @interpolate(flat) i : i32; + @location(1) @interpolate(flat) u : u32; + @location(2) @interpolate(flat) vi : vec4; + @location(3) @interpolate(flat) vu : vec4; +}; +)"; + + auto* expect = + R"( +@location(0) @internal(disable_validation__ignore_storage_class) var i_1 : i32; + +@location(1) @internal(disable_validation__ignore_storage_class) var u_1 : u32; + +@location(2) @internal(disable_validation__ignore_storage_class) var vi_1 : vec4; + +@location(3) @internal(disable_validation__ignore_storage_class) var vu_1 : vec4; + +@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var i_2 : i32; + +@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var u_2 : u32; + +@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vi_2 : vec4; + +@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vu_2 : vec4; + +@builtin(position) @internal(disable_validation__ignore_storage_class) var pos_1 : vec4; + +@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var i_3 : i32; + +@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var u_3 : u32; + +@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vi_3 : vec4; + +@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vu_3 : vec4; + +@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var i_4 : i32; + +@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var u_4 : u32; + +@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vi_4 : vec4; + +@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var vu_4 : vec4; + +fn vert_main_inner(in : VertexIn) -> VertexOut { + return VertexOut(in.i, in.u, in.vi, in.vu, vec4()); +} + +@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; + vu : vec4; +} + +struct VertexOut { + i : i32; + u : u32; + vi : vec4; + vu : vec4; + pos : vec4; +} + +struct FragmentInterface { + i : i32; + u : u32; + vi : vec4; + vu : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kSpirv); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) { auto* src = R"( struct VertexOut { @@ -1306,6 +2281,71 @@ fn main2() -> tint_symbol_1 { 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 { + return vec4(); +} + +struct VertexOut { + [[builtin(position), invariant]] pos : vec4; +}; +)"; + + auto* expect = R"( +struct tint_symbol { + @builtin(position) @invariant + pos : vec4; +} + +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; +} + +fn main2_inner() -> vec4 { + return vec4(); +} + +@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; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes) { auto* src = R"( struct FragmentInput { @@ -1374,6 +2414,74 @@ fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { 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; + @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; +} + +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; + @align(128) + loc0 : f32; +} + +struct FragmentOutput { + @size(16) + value : f32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) { auto* src = R"( struct VertexOutput { @@ -1479,6 +2587,111 @@ fn frag_main(tint_symbol_1 : tint_symbol_2) { 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; + @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; + @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; +} + +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; + @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; + d : u32; + a : f32; + c : i32; +} + +struct FragmentInputExtra { + d : u32; + pos : vec4; + a : f32; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) { auto* src = R"( @stage(fragment) @@ -1703,6 +2916,60 @@ fn frag_main() -> tint_symbol { EXPECT_EQ(expect, str(got)); } +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::ShaderStyle::kMsl, 0x03u); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_StructWithoutAuthoredMask) { auto* src = R"( @@ -1755,6 +3022,58 @@ fn frag_main() -> tint_symbol { 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::ShaderStyle::kMsl, 0x03u); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_MultipleShaders) { auto* src = R"( @stage(fragment) @@ -2009,6 +3328,48 @@ fn vert_main() { 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; +}; +)"; + + auto* expect = R"( +@builtin(position) @internal(disable_validation__ignore_storage_class) var pos_1 : vec4; + +@builtin(pointsize) @internal(disable_validation__ignore_storage_class) var 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; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_ReturnStruct_Msl) { auto* src = R"( struct VertOut { @@ -2055,6 +3416,53 @@ fn vert_main() -> tint_symbol { 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; +}; +)"; + + auto* expect = R"( +struct tint_symbol { + @builtin(position) + pos : vec4; + @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; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Spirv) { auto* src = R"( var vertex_point_size : f32; @@ -2133,6 +3541,85 @@ fn vert_main() { 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 vertex_point_size : f32; +var vertex_point_size_1 : f32; +var vertex_point_size_2 : f32; + +struct VertOut { + @location(0) vertex_point_size : f32; + @builtin(position) vertex_point_size_1 : vec4; +}; +)"; + + auto* expect = R"( +@location(0) @internal(disable_validation__ignore_storage_class) var collide_2 : f32; + +@location(1) @internal(disable_validation__ignore_storage_class) var collide_3 : f32; + +@location(0) @internal(disable_validation__ignore_storage_class) var vertex_point_size_3 : f32; + +@builtin(position) @internal(disable_validation__ignore_storage_class) var vertex_point_size_1_1 : vec4; + +@builtin(pointsize) @internal(disable_validation__ignore_storage_class) var 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 vertex_point_size : f32; + +var vertex_point_size_1 : f32; + +var vertex_point_size_2 : f32; + +struct VertOut { + vertex_point_size : f32; + vertex_point_size_1 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Msl) { auto* src = R"( struct VertIn1 { @@ -2209,6 +3696,83 @@ fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { 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; + @builtin(position) vertex_point_size_1 : vec4; +}; +)"; + + 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; + @builtin(position) + vertex_point_size_1 : vec4; + @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; + vertex_point_size_1 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Hlsl) { auto* src = R"( struct VertIn1 { @@ -2285,6 +3849,83 @@ fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { 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; + @builtin(position) vertex_point_size_1 : vec4; +}; +)"; + + 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; + @builtin(position) + vertex_point_size_1 : vec4; + @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; + vertex_point_size_1 : vec4; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl, 0xFFFFFFFF, true); + auto got = Run(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 f0c3a33476..481ef3dd75 100644 --- a/src/transform/combine_samplers_test.cc +++ b/src/transform/combine_samplers_test.cc @@ -65,6 +65,34 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, SimplePair_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return textureSample(t, s, vec2(1.0, 2.0)); +} + +@group(0) @binding(0) var t : texture_2d; + +@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; + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler; + +fn main() -> vec4 { + return textureSample(t_s, placeholder_sampler, vec2(1.0, 2.0)); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, SimplePairInAFunction) { auto* src = R"( @group(0) @binding(0) var t : texture_2d; @@ -101,6 +129,42 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, SimplePairInAFunction_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return sample(t, s, vec2(1.0, 2.0)); +} + +fn sample(t : texture_2d, s : sampler, coords : vec2) -> vec4 { + return textureSample(t, s, coords); +} + +@group(0) @binding(1) var s : sampler; + +@group(0) @binding(0) var t : texture_2d; +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d; + +fn main() -> vec4 { + return sample(t_s, vec2(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, coords : vec2) -> vec4 { + return textureSample(t_s_1, placeholder_sampler, coords); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, SimplePairRename) { auto* src = R"( @group(0) @binding(1) var t : texture_2d; @@ -212,6 +276,45 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, AliasedTypes_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return sample(t, s, vec2(1.0, 2.0)); +} + +fn sample(t : Tex2d, s : sampler, coords : vec2) -> vec4 { + return textureSample(t, s, coords); +} + +@group(0) @binding(0) var t : Tex2d; +@group(0) @binding(1) var s : sampler; + +type Tex2d = texture_2d; +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d; + +fn main() -> vec4 { + return sample(t_s, vec2(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, coords : vec2) -> vec4 { + return textureSample(t_s_1, placeholder_sampler, coords); +} + +type Tex2d = texture_2d; +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, SimplePairInTwoFunctions) { auto* src = R"( @group(0) @binding(0) var t : texture_2d; @@ -256,6 +359,49 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, SimplePairInTwoFunctions_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return f(t, s, vec2(1.0, 2.0)); +} + +fn f(t : texture_2d, s : sampler, coords : vec2) -> vec4 { + return g(t, s, coords); +} + +fn g(t : texture_2d, s : sampler, coords : vec2) -> vec4 { + return textureSample(t, s, coords); +} + +@group(0) @binding(1) var s : sampler; +@group(0) @binding(0) var t : texture_2d; +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var t_s : texture_2d; + +fn main() -> vec4 { + return f(t_s, vec2(1.0, 2.0)); +} + +fn f(t_s_1 : texture_2d, coords : vec2) -> vec4 { + 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, coords : vec2) -> vec4 { + return textureSample(t_s_2, placeholder_sampler, coords); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, TwoFunctionsGenerateSamePair) { auto* src = R"( @group(1) @binding(0) var tex : texture_2d; @@ -500,6 +646,44 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, GlobalTextureLocalSampler_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return f(samp1, samp2, vec2(1.0, 2.0)); +} + +fn f(s1 : sampler, s2 : sampler, coords : vec2) -> vec4 { + 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; +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp1 : texture_2d; + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp2 : texture_2d; + +fn main() -> vec4 { + return f(tex_samp1, tex_samp2, vec2(1.0, 2.0)); +} + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler; + +fn f(tex_s1 : texture_2d, tex_s2 : texture_2d, coords : vec2) -> vec4 { + return (textureSample(tex_s1, placeholder_sampler, coords) + textureSample(tex_s2, placeholder_sampler, coords)); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, LocalTextureGlobalSampler) { auto* src = R"( @group(0) @binding(0) var tex1 : texture_2d; @@ -540,6 +724,44 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, LocalTextureGlobalSampler_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return f(tex1, tex2, vec2(1.0, 2.0)); +} + +fn f(t1 : texture_2d, t2 : texture_2d, coords : vec2) -> vec4 { + return textureSample(t1, samp, coords) + textureSample(t2, samp, coords); +} + +@group(0) @binding(2) var samp : sampler; +@group(0) @binding(0) var tex1 : texture_2d; +@group(0) @binding(1) var tex2 : texture_2d; +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex1_samp : texture_2d; + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex2_samp : texture_2d; + +fn main() -> vec4 { + return f(tex1_samp, tex2_samp, vec2(1.0, 2.0)); +} + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler; + +fn f(t1_samp : texture_2d, t2_samp : texture_2d, coords : vec2) -> vec4 { + return (textureSample(t1_samp, placeholder_sampler, coords) + textureSample(t2_samp, placeholder_sampler, coords)); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, TextureLoadNoSampler) { auto* src = R"( @group(0) @binding(0) var tex : texture_2d; @@ -687,6 +909,41 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, TextureSampleCompareInAFunction_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return vec4(f(tex, samp, vec2(1.0, 2.0))); +} + +fn f(t : texture_depth_2d, s : sampler_comparison, coords : vec2) -> 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 { + return vec4(f(tex_samp, vec2(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 { + return textureSampleCompare(t_s, placeholder_comparison_sampler, coords, 5.0); +} +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(CombineSamplersTest, BindingPointCollision) { auto* src = R"( @group(1) @binding(0) var tex : texture_2d; @@ -719,6 +976,37 @@ fn main() -> vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(CombineSamplersTest, BindingPointCollision_OutOfOrder) { + auto* src = R"( +fn main() -> vec4 { + return textureSample(tex, samp, gcoords); +} + +@group(1) @binding(1) var samp : sampler; +@group(0) @binding(0) var gcoords : vec2; +@group(1) @binding(0) var tex : texture_2d; + +)"; + auto* expect = R"( +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var tex_samp : texture_2d; + +@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler; + +fn main() -> vec4 { + return textureSample(tex_samp, placeholder_sampler, gcoords); +} + +@internal(disable_validation__binding_point_collision) @group(0) @binding(0) var gcoords : vec2; +)"; + + DataMap data; + data.Add(CombineSamplers::BindingMap(), + sem::BindingPoint()); + auto got = Run(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 dffb3f2200..2f8ed59540 100644 --- a/src/transform/decompose_memory_access_test.cc +++ b/src/transform/decompose_memory_access_test.cc @@ -246,6 +246,202 @@ fn main() { 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 = sb.d; + var e : vec2 = sb.e; + var f : vec2 = sb.f; + var g : vec3 = sb.g; + var h : vec3 = sb.h; + var i : vec3 = sb.i; + var j : vec4 = sb.j; + var k : vec4 = sb.k; + var l : vec4 = sb.l; + var m : mat2x2 = sb.m; + var n : mat2x3 = sb.n; + var o : mat2x4 = sb.o; + var p : mat3x2 = sb.p; + var q : mat3x3 = sb.q; + var r : mat3x4 = sb.r; + var s : mat4x2 = sb.s; + var t : mat4x3 = sb.t; + var u : mat4x4 = sb.u; + var v : array, 2> = sb.v; +} + +@group(0) @binding(0) var sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x2 { + return mat2x2(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 { + return mat2x3(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 { + return mat2x4(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 { + return mat3x2(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 { + return mat3x3(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 { + return mat3x4(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 { + return mat4x2(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 { + return mat4x3(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 { + return mat4x4(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, 2u> { + var arr : array, 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 = tint_symbol_3(sb, 16u); + var e : vec2 = tint_symbol_4(sb, 24u); + var f : vec2 = tint_symbol_5(sb, 32u); + var g : vec3 = tint_symbol_6(sb, 48u); + var h : vec3 = tint_symbol_7(sb, 64u); + var i : vec3 = tint_symbol_8(sb, 80u); + var j : vec4 = tint_symbol_9(sb, 96u); + var k : vec4 = tint_symbol_10(sb, 112u); + var l : vec4 = tint_symbol_11(sb, 128u); + var m : mat2x2 = tint_symbol_12(sb, 144u); + var n : mat2x3 = tint_symbol_13(sb, 160u); + var o : mat2x4 = tint_symbol_14(sb, 192u); + var p : mat3x2 = tint_symbol_15(sb, 224u); + var q : mat3x3 = tint_symbol_16(sb, 256u); + var r : mat3x4 = tint_symbol_17(sb, 304u); + var s : mat4x2 = tint_symbol_18(sb, 352u); + var t : mat4x3 = tint_symbol_19(sb, 384u); + var u : mat4x4 = tint_symbol_20(sb, 448u); + var v : array, 2> = tint_symbol_21(sb, 512u); +} + +@group(0) @binding(0) var sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 2>; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad) { auto* src = R"( struct UB { @@ -442,6 +638,202 @@ fn main() { 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 = ub.d; + var e : vec2 = ub.e; + var f : vec2 = ub.f; + var g : vec3 = ub.g; + var h : vec3 = ub.h; + var i : vec3 = ub.i; + var j : vec4 = ub.j; + var k : vec4 = ub.k; + var l : vec4 = ub.l; + var m : mat2x2 = ub.m; + var n : mat2x3 = ub.n; + var o : mat2x4 = ub.o; + var p : mat3x2 = ub.p; + var q : mat3x3 = ub.q; + var r : mat3x4 = ub.r; + var s : mat4x2 = ub.s; + var t : mat4x3 = ub.t; + var u : mat4x4 = ub.u; + var v : array, 2> = ub.v; +} + +@group(0) @binding(0) var ub : UB; + +struct UB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : UB, offset : u32) -> mat2x2 { + return mat2x2(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 { + return mat2x3(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 { + return mat2x4(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 { + return mat3x2(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 { + return mat3x3(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 { + return mat3x4(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 { + return mat4x2(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 { + return mat4x3(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 { + return mat4x4(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, 2u> { + var arr : array, 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 = tint_symbol_3(ub, 16u); + var e : vec2 = tint_symbol_4(ub, 24u); + var f : vec2 = tint_symbol_5(ub, 32u); + var g : vec3 = tint_symbol_6(ub, 48u); + var h : vec3 = tint_symbol_7(ub, 64u); + var i : vec3 = tint_symbol_8(ub, 80u); + var j : vec4 = tint_symbol_9(ub, 96u); + var k : vec4 = tint_symbol_10(ub, 112u); + var l : vec4 = tint_symbol_11(ub, 128u); + var m : mat2x2 = tint_symbol_12(ub, 144u); + var n : mat2x3 = tint_symbol_13(ub, 160u); + var o : mat2x4 = tint_symbol_14(ub, 192u); + var p : mat3x2 = tint_symbol_15(ub, 224u); + var q : mat3x3 = tint_symbol_16(ub, 256u); + var r : mat3x4 = tint_symbol_17(ub, 304u); + var s : mat4x2 = tint_symbol_18(ub, 352u); + var t : mat4x3 = tint_symbol_19(ub, 384u); + var u : mat4x4 = tint_symbol_20(ub, 448u); + var v : array, 2> = tint_symbol_21(ub, 512u); +} + +@group(0) @binding(0) var ub : UB; + +struct UB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 2>; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, SB_BasicStore) { auto* src = R"( struct SB { @@ -655,6 +1047,219 @@ fn main() { 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(); + sb.e = vec2(); + sb.f = vec2(); + sb.g = vec3(); + sb.h = vec3(); + sb.i = vec3(); + sb.j = vec4(); + sb.k = vec4(); + sb.l = vec4(); + sb.m = mat2x2(); + sb.n = mat2x3(); + sb.o = mat2x4(); + sb.p = mat3x2(); + sb.q = mat3x3(); + sb.r = mat3x4(); + sb.s = mat4x2(); + sb.t = mat4x3(); + sb.u = mat4x4(); + sb.v = array, 2>(); +} + +@group(0) @binding(0) var sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +fn tint_symbol_12(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x2) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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, 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()); + tint_symbol_4(sb, 24u, vec2()); + tint_symbol_5(sb, 32u, vec2()); + tint_symbol_6(sb, 48u, vec3()); + tint_symbol_7(sb, 64u, vec3()); + tint_symbol_8(sb, 80u, vec3()); + tint_symbol_9(sb, 96u, vec4()); + tint_symbol_10(sb, 112u, vec4()); + tint_symbol_11(sb, 128u, vec4()); + tint_symbol_12(sb, 144u, mat2x2()); + tint_symbol_13(sb, 160u, mat2x3()); + tint_symbol_14(sb, 192u, mat2x4()); + tint_symbol_15(sb, 224u, mat3x2()); + tint_symbol_16(sb, 256u, mat3x3()); + tint_symbol_17(sb, 304u, mat3x4()); + tint_symbol_18(sb, 352u, mat4x2()); + tint_symbol_19(sb, 384u, mat4x3()); + tint_symbol_20(sb, 448u, mat4x4()); + tint_symbol_21(sb, 512u, array, 2>()); +} + +@group(0) @binding(0) var sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 2>; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, LoadStructure) { auto* src = R"( struct SB { @@ -813,6 +1418,164 @@ fn main() { 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 sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +@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 + +fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32) -> mat2x2 { + return mat2x2(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 { + return mat2x3(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 { + return mat2x4(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 { + return mat3x2(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 { + return mat3x3(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 { + return mat3x4(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 { + return mat4x2(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 { + return mat4x3(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 { + return mat4x4(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, 2u> { + var arr : array, 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 sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 2>; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, StoreStructure) { auto* src = R"( struct SB { @@ -1009,6 +1772,202 @@ fn main() { 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 sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +@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) + +fn tint_symbol_13(@internal(disable_validation__ignore_constructible_function_parameter) buffer : SB, offset : u32, value : mat2x2) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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, 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 sb : SB; + +struct SB { + a : i32; + b : u32; + c : f32; + d : vec2; + e : vec2; + f : vec2; + g : vec3; + h : vec3; + i : vec3; + j : vec4; + k : vec4; + l : vec4; + m : mat2x2; + n : mat2x3; + o : mat2x4; + p : mat3x2; + q : mat3x3; + r : mat3x4; + s : mat4x2; + t : mat4x3; + u : mat4x4; + v : array, 2>; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain) { auto* src = R"( struct S1 { @@ -1079,6 +2038,76 @@ fn main() { 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : @stride(256) array; +}; + +struct S2 { + a : i32; + b : @stride(32) array; + c : i32; +}; + +struct S1 { + a : i32; + b : vec3; + 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : @stride(256) array; +} + +struct S2 { + a : i32; + b : @stride(32) array; + c : i32; +} + +struct S1 { + a : i32; + b : vec3; + c : i32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChain) { auto* src = R"( struct S1 { @@ -1148,6 +2177,75 @@ fn main() { 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : @stride(256) array; +}; + +struct S2 { + a : i32; + b : @stride(32) array; + c : i32; +}; + +struct S1 { + a : i32; + b : vec3; + 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : @stride(256) array; +} + +struct S2 { + a : i32; + b : @stride(32) array; + c : i32; +} + +struct S1 { + a : i32; + b : vec3; + c : i32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChainWithAliases) { auto* src = R"( struct S1 { @@ -1233,6 +2331,92 @@ fn main() { 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : A2_Array; +}; + +type A2_Array = @stride(256) array; + +type A2 = S2; + +struct S2 { + a : i32; + b : A1_Array; + c : i32; +}; + +type A1 = S1; + +type A1_Array = @stride(32) array; + +struct S1 { + a : i32; + b : vec3; + 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 sb : SB; + +struct SB { + @size(128) + a : i32; + b : A2_Array; +} + +type A2_Array = @stride(256) array; + +type A2 = S2; + +struct S2 { + a : i32; + b : A1_Array; + c : i32; +} + +type A1 = S1; + +type A1_Array = @stride(32) array; + +struct S1 { + a : i32; + b : vec3; + c : i32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, StorageBufferAtomics) { auto* src = R"( struct SB { @@ -1378,6 +2562,151 @@ fn main() { 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 sb : SB; + +struct SB { + padding : vec4; + a : atomic; + b : atomic; +}; +)"; + + 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 + +@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 + +@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 sb : SB; + +struct SB { + padding : vec4; + a : atomic; + b : atomic; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(DecomposeMemoryAccessTest, WorkgroupBufferAtomics) { auto* src = R"( struct S { @@ -1422,6 +2751,50 @@ fn main() { 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 w : S; + +struct S { + padding : vec4; + a : atomic; + b : atomic; +} +)"; + + auto* expect = src; + + auto got = Run(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 28baadf243..0c41717a41 100644 --- a/src/transform/external_texture_transform_test.cc +++ b/src/transform/external_texture_transform_test.cc @@ -50,6 +50,34 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(ExternalTextureTransformTest, SampleLevelSinglePlane_OutOfOrder) { + auto* src = R"( +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return textureSampleLevel(t, s, (coord.xy / vec2(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) -> @location(0) vec4 { + return textureSampleLevel(t, s, (coord.xy / vec2(4.0, 4.0)), 0.0); +} + +@group(0) @binding(1) var t : texture_2d; + +@group(0) @binding(0) var s : sampler; +)"; + + auto got = Run(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 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(ExternalTextureTransformTest, LoadSinglePlane_OutOfOrder) { + auto* src = R"( +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return textureLoad(t, vec2(1, 1)); +} + +@group(0) @binding(0) var t : texture_external; +)"; + + auto* expect = R"( +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return textureLoad(t, vec2(1, 1), 0); +} + +@group(0) @binding(0) var t : texture_2d; +)"; + + auto got = Run(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 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(ExternalTextureTransformTest, DimensionsSinglePlane_OutOfOrder) { + auto* src = R"( +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + var dim : vec2; + dim = textureDimensions(t); + return vec4(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) -> @location(0) vec4 { + var dim : vec2; + dim = textureDimensions(t); + return vec4(0.0, 0.0, 0.0, 0.0); +} + +@group(0) @binding(0) var t : texture_2d; +)"; + + auto got = Run(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 05fefa84ae..61075a6081 100644 --- a/src/transform/first_index_offset_test.cc +++ b/src/transform/first_index_offset_test.cc @@ -139,6 +139,52 @@ fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4 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 { + test(vert_idx); + return vec4(); +} + +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + auto* expect = R"( +struct tint_symbol { + first_vertex_index : u32; +} + +@binding(1) @group(2) var tint_symbol_1 : tint_symbol; + +@stage(vertex) +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4 { + test((vert_idx + tint_symbol_1.first_vertex_index)); + return vec4(); +} + +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + DataMap config; + config.Add(1, 2); + auto got = Run(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); + + auto* data = got.data.Get(); + + 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 @@ fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4first_instance_offset, 0u); } +TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex_OutOfOrder) { + auto* src = R"( +@stage(vertex) +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + test(inst_idx); + return vec4(); +} + +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} +)"; + + auto* expect = R"( +struct tint_symbol { + first_instance_index : u32; +} + +@binding(1) @group(7) var tint_symbol_1 : tint_symbol; + +@stage(vertex) +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + test((inst_idx + tint_symbol_1.first_instance_index)); + return vec4(); +} + +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} +)"; + + DataMap config; + config.Add(1, 7); + auto got = Run(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); + + auto* data = got.data.Get(); + + 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 @@ fn entry(inputs : Inputs) -> @builtin(position) vec4 { EXPECT_EQ(data->first_instance_offset, 4u); } +TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex_OutOfOrder) { + auto* src = R"( +@stage(vertex) +fn entry(inputs : Inputs) -> @builtin(position) vec4 { + test(inputs.instance_idx, inputs.vert_idx); + return vec4(); +} + +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 tint_symbol_1 : tint_symbol; + +@stage(vertex) +fn entry(inputs : Inputs) -> @builtin(position) vec4 { + test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index)); + return vec4(); +} + +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(1, 2); + auto got = Run(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); + + auto* data = got.data.Get(); + + 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 @@ fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4 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 { + func2(vert_idx); + return vec4(); +} + +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 tint_symbol_1 : tint_symbol; + +@stage(vertex) +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4 { + func2((vert_idx + tint_symbol_1.first_vertex_index)); + return vec4(); +} + +fn func2(vert_idx : u32) -> u32 { + return func1(vert_idx); +} + +fn func1(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + DataMap config; + config.Add(1, 2); + auto got = Run(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); + + auto* data = got.data.Get(); + + 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 @@ fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4first_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 { + func(vert_idx); + return vec4(); +} + +@stage(vertex) +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + func(vert_idx + inst_idx); + return vec4(); +} + +@stage(vertex) +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + func(inst_idx); + return vec4(); +} + +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 tint_symbol_1 : tint_symbol; + +@stage(vertex) +fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4 { + func((vert_idx + tint_symbol_1.first_vertex_index)); + return vec4(); +} + +@stage(vertex) +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index))); + return vec4(); +} + +@stage(vertex) +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4 { + func((inst_idx + tint_symbol_1.first_instance_index)); + return vec4(); +} + +fn func(i : u32) -> u32 { + return i; +} +)"; + + DataMap config; + config.Add(1, 2); + auto got = Run(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); + + auto* data = got.data.Get(); + + 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 42aae29163..32109c6733 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 @@ fn f() { 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(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 0147d8ebda..7d92ba61a2 100644 --- a/src/transform/localize_struct_array_assignment_test.cc +++ b/src/transform/localize_struct_array_assignment_test.cc @@ -90,6 +90,64 @@ fn main() { 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 uniforms : Uniforms; + +struct OuterS { + a1 : array; +}; + +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 uniforms : Uniforms; + +struct OuterS { + a1 : array; +} + +struct InnerS { + v : i32; +} + +@block +struct Uniforms { + i : u32; +} +)"; + + auto got = + Run(src); + EXPECT_EQ(expect, str(got)); +} + TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray) { auto* src = R"( @block struct Uniforms { @@ -156,6 +214,72 @@ fn main() { 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 uniforms : Uniforms; + +struct OuterS { + s2 : S1; +}; + +struct S1 { + a : array; +}; + +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 uniforms : Uniforms; + +struct OuterS { + s2 : S1; +} + +struct S1 { + a : array; +} + +struct InnerS { + v : i32; +} + +@block +struct Uniforms { + i : u32; +} +)"; + + auto got = + Run(src); + EXPECT_EQ(expect, str(got)); +} + TEST_F(LocalizeStructArrayAssignmentTest, StructArrayArray) { auto* src = R"( @block struct Uniforms { @@ -437,6 +561,91 @@ fn main() { 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 uniforms : Uniforms; + +@block struct Uniforms { + i : u32; + j : u32; +}; + +var nextIndex : u32; +fn getNextIndex() -> u32 { + nextIndex = nextIndex + 1u; + return nextIndex; +} + +struct OuterS { + a1 : array; +}; + +struct S1 { + a2 : array; +}; + +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 uniforms : Uniforms; + +@block +struct Uniforms { + i : u32; + j : u32; +} + +var nextIndex : u32; + +fn getNextIndex() -> u32 { + nextIndex = (nextIndex + 1u); + return nextIndex; +} + +struct OuterS { + a1 : array; +} + +struct S1 { + a2 : array; +} + +struct InnerS { + v : i32; +} +)"; + + auto got = + Run(src); + EXPECT_EQ(expect, str(got)); +} + TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg) { auto* src = R"( @block struct Uniforms { @@ -500,6 +709,71 @@ fn main() { 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) { + var v : InnerS; + (*p).a1[uniforms.i] = v; +} + +struct InnerS { + v : i32; +}; +struct OuterS { + a1 : array; +}; + +@group(1) @binding(4) var 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) { + 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; +} + +@group(1) @binding(4) var uniforms : Uniforms; + +@block +struct Uniforms { + i : u32; +} +)"; + + auto got = + Run(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 52967a796d..0a0d761d69 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 @@ fn main() { EXPECT_EQ(expect, str(got)); } +TEST_F(ModuleScopeVarToEntryPointParamTest, Basic_OutOfOrder) { + auto* src = R"( +@stage(compute) @workgroup_size(1) +fn main() { + w = p; +} + +var w : f32; +var p : f32; +)"; + + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn main() { + @internal(disable_validation__ignore_storage_class) var tint_symbol : f32; + @internal(disable_validation__ignore_storage_class) var tint_symbol_1 : f32; + tint_symbol = tint_symbol_1; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, FunctionCalls) { auto* src = R"( var p : f32; @@ -116,6 +141,59 @@ fn main() { 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 p : f32; +var w : f32; +)"; + + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn main() { + @internal(disable_validation__ignore_storage_class) var tint_symbol : f32; + @internal(disable_validation__ignore_storage_class) var 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, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr) { + 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, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr) { + *(tint_symbol_4) = a; + *(tint_symbol_5) = b; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Constructors) { auto* src = R"( var a : f32 = 1.0; @@ -141,6 +219,31 @@ fn main() { 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 b : f32 = f32(); +var a : f32 = 1.0; +)"; + + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn main() { + @internal(disable_validation__ignore_storage_class) var tint_symbol : f32 = 1.0; + @internal(disable_validation__ignore_storage_class) var tint_symbol_1 : f32 = f32(); + let x : f32 = (tint_symbol + tint_symbol_1); +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers) { auto* src = R"( var p : f32; @@ -172,6 +275,37 @@ fn main() { EXPECT_EQ(expect, str(got)); } +TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers_OutOfOrder) { + auto* src = R"( +@stage(compute) @workgroup_size(1) +fn main() { + let p_ptr : ptr = &p; + let w_ptr : ptr = &w; + let x : f32 = *p_ptr + *w_ptr; + *p_ptr = x; +} + +var w : f32; +var p : f32; +)"; + + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn main() { + @internal(disable_validation__ignore_storage_class) var tint_symbol : f32; + @internal(disable_validation__ignore_storage_class) var tint_symbol_1 : f32; + let p_ptr : ptr = &(tint_symbol); + let w_ptr : ptr = &(tint_symbol_1); + let x : f32 = (*(p_ptr) + *(w_ptr)); + *(p_ptr) = x; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref) { auto* src = R"( var v : f32; @@ -211,6 +345,45 @@ fn main() { 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) { + (*p) = 0.0; +} + +var v : f32; +)"; + + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn main() { + @internal(disable_validation__ignore_storage_class) var 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) { + bar(tint_symbol_1); +} + +fn bar(p : ptr) { + *(p) = 0.0; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_Basic) { auto* src = R"( struct S { @@ -246,6 +419,40 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 u : S; +@group(0) @binding(1) var 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, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr) { + _ = *(tint_symbol); + _ = *(tint_symbol_1); +} + +struct S { + a : f32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray) { auto* src = R"( @group(0) @binding(0) @@ -273,6 +480,33 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 buffer : array; +)"; + + auto* expect = R"( +struct tint_symbol_1 { + arr : array; +} + +@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) { + _ = (*(tint_symbol)).arr[0]; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArrayInsideFunction) { auto* src = R"( @group(0) @binding(0) @@ -308,6 +542,41 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 buffer : array; +)"; + + auto* expect = R"( +struct tint_symbol_1 { + arr : array; +} + +@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) { + foo(&((*(tint_symbol)).arr)); +} + +fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr>) { + _ = (*(tint_symbol_2))[0]; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_Alias) { auto* src = R"( type myarray = array; @@ -339,6 +608,37 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 buffer : myarray; + +type myarray = array; +)"; + + auto* expect = R"( +struct tint_symbol_1 { + arr : array; +} + +@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) { + _ = (*(tint_symbol)).arr[0]; +} + +type myarray = array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_ArrayOfStruct) { auto* src = R"( struct S { @@ -374,6 +674,40 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 buffer : array; + +struct S { + f : f32; +}; +)"; + + auto* expect = R"( +struct S { + f : f32; +} + +struct tint_symbol_1 { + arr : array; +} + +@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) { + _ = (*(tint_symbol)).arr[0]; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_FunctionCalls) { auto* src = R"( struct S { @@ -437,6 +771,69 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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 u : S; +@group(0) @binding(1) +var 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, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr) { + 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, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr) { + 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, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr) { + _ = *(tint_symbol_4); + _ = *(tint_symbol_5); +} + +struct S { + a : f32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, HandleTypes_Basic) { auto* src = R"( @group(0) @binding(0) var t : texture_2d; @@ -515,6 +912,60 @@ fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_paramete 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; +@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, @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, 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, tint_symbol_5 : sampler) { + _ = tint_symbol_4; + _ = tint_symbol_5; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ModuleScopeVarToEntryPointParamTest, Matrix) { auto* src = R"( var m : mat2x2; @@ -626,6 +1077,49 @@ fn main(@internal(disable_validation__entry_point_parameter) tint_symbol_1 : ptr 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 a : S; +var b : S; + +struct S { + m : mat2x2; +}; +)"; + + auto* expect = R"( +struct S { + m : mat2x2; +} + +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) { + let tint_symbol : ptr = &((*(tint_symbol_1)).a); + let tint_symbol_2 : ptr = &((*(tint_symbol_1)).b); + let x = *(tint_symbol); + let y = *(tint_symbol_2); +} +)"; + + auto got = Run(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 a0a42b805f..8a43308e99 100644 --- a/src/transform/multiplanar_external_texture_test.cc +++ b/src/transform/multiplanar_external_texture_test.cc @@ -136,6 +136,49 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { 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) -> @location(0) vec4 { + var dim : vec2; + dim = textureDimensions(ext_tex); + return vec4(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; + +@group(0) @binding(2) var ext_tex_params : ExternalTextureParams; + +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + var dim : vec2; + dim = textureDimensions(ext_tex); + return vec4(0.0, 0.0, 0.0, 0.0); +} + +@group(0) @binding(0) var ext_tex : texture_2d; +)"; + + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}}); + auto got = Run(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 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { 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) -> @location(0) vec4 { + 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; + +@group(0) @binding(3) var ext_tex_params : ExternalTextureParams; + +fn textureSampleExternal(plane0 : texture_2d, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params); +} + +@group(0) @binding(1) var ext_tex : texture_2d; + +@group(0) @binding(0) var s : sampler; +)"; + + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}}); + auto got = Run(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 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { 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) -> @location(0) vec4 { + return textureLoad(ext_tex, vec2(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; + +@group(0) @binding(2) var ext_tex_params : ExternalTextureParams; + +fn textureLoadExternal(plane0 : texture_2d, plane1 : texture_2d, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return textureLoadExternal(ext_tex, ext_tex_plane_1, vec2(1, 1), ext_tex_params); +} + +@group(0) @binding(0) var ext_tex : texture_2d; +)"; + + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}}); + auto got = Run(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 @@ fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { 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) -> @location(0) vec4 { + return textureSampleLevel(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2(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; + +@group(0) @binding(3) var ext_tex_params : ExternalTextureParams; + +fn textureSampleExternal(plane0 : texture_2d, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +fn textureLoadExternal(plane0 : texture_2d, plane1 : texture_2d, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +@stage(fragment) +fn main(@builtin(position) coord : vec4) -> @location(0) vec4 { + return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2(1, 1), ext_tex_params)); +} + +@group(0) @binding(0) var s : sampler; + +@group(0) @binding(1) var ext_tex : texture_2d; +)"; + + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}}); + auto got = Run(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 @@ fn main() { 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(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; + +@group(0) @binding(3) var ext_tex_params : ExternalTextureParams; + +@stage(fragment) +fn main() { + f(ext_tex, ext_tex_plane_1, ext_tex_params, smp); +} + +fn textureSampleExternal(plane0 : texture_2d, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +fn f(t : texture_2d, ext_tex_plane_1_1 : texture_2d, ext_tex_params_1 : ExternalTextureParams, s : sampler) { + textureSampleExternal(t, ext_tex_plane_1_1, s, vec2(1.0, 2.0), ext_tex_params_1); +} + +@group(0) @binding(0) var ext_tex : texture_2d; + +@group(0) @binding(1) var smp : sampler; +)"; + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{ + {{0, 0}, {{0, 2}, {0, 3}}}, + }); + auto got = Run(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 @@ fn main() { 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(1.0, 2.0)); + textureSampleLevel(t2, s, vec2(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; + +@group(0) @binding(4) var ext_tex_params : ExternalTextureParams; + +@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d; + +@group(0) @binding(6) var 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, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +fn f(t : texture_2d, ext_tex_plane_1_2 : texture_2d, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d, ext_tex_plane_1_3 : texture_2d, ext_tex_params_3 : ExternalTextureParams) { + textureSampleExternal(t, ext_tex_plane_1_2, s, vec2(1.0, 2.0), ext_tex_params_2); + textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2(1.0, 2.0), ext_tex_params_3); +} + +@group(0) @binding(0) var ext_tex : texture_2d; + +@group(0) @binding(1) var smp : sampler; + +@group(0) @binding(2) var ext_tex2 : texture_2d; +)"; + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{ + {{0, 0}, {{0, 3}, {0, 4}}}, + {{0, 2}, {{0, 5}, {0, 6}}}, + }); + auto got = Run(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 @@ fn main() { 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(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; + +@group(0) @binding(3) var ext_tex_params : ExternalTextureParams; + +fn textureSampleExternal(plane0 : texture_2d, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +fn nested(t : texture_2d, ext_tex_plane_1_1 : texture_2d, ext_tex_params_1 : ExternalTextureParams, s : sampler) { + textureSampleExternal(t, ext_tex_plane_1_1, s, vec2(1.0, 2.0), ext_tex_params_1); +} + +fn f(t : texture_2d, ext_tex_plane_1_2 : texture_2d, 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; + +@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::BindingsMap{ + {{0, 0}, {{0, 2}, {0, 3}}}, + }); + auto got = Run(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 @@ fn main() { 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(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; + +@group(0) @binding(3) var ext_tex_params : ExternalTextureParams; + +@stage(fragment) +fn main() { + f(ext_tex, ext_tex_plane_1, ext_tex_params, smp); +} + +fn textureSampleExternal(plane0 : texture_2d, plane1 : texture_2d, smp : sampler, coord : vec2, params : ExternalTextureParams) -> vec4 { + 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(r, g, b, 1.0); +} + +fn f(t : texture_2d, ext_tex_plane_1_1 : texture_2d, ext_tex_params_1 : ExternalTextureParams, s : sampler) { + textureSampleExternal(t, ext_tex_plane_1_1, s, vec2(1.0, 2.0), ext_tex_params_1); +} + +@group(0) @binding(0) var ext_tex : texture_2d; + +@group(0) @binding(1) var smp : sampler; + +type ET = texture_external; +)"; + DataMap data; + data.Add( + MultiplanarExternalTexture::BindingsMap{ + {{0, 0}, {{0, 2}, {0, 3}}}, + }); + auto got = Run(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 dedee54abe..8471b03c4d 100644 --- a/src/transform/num_workgroups_from_uniform_test.cc +++ b/src/transform/num_workgroups_from_uniform_test.cc @@ -146,6 +146,52 @@ fn main() { 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; +}; +)"; + + auto* expect = R"( +struct tint_symbol_2 { + num_workgroups : vec3; +} + +@group(0) @binding(30) var 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; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + data.Add(sem::BindingPoint{0, 30u}); + auto got = Run( + src, data); + EXPECT_EQ(expect, str(got)); +} + TEST_F(NumWorkgroupsFromUniformTest, StructMultipleMembers) { auto* src = R"( struct Builtins { @@ -203,6 +249,64 @@ fn main(tint_symbol : tint_symbol_1) { 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; + @builtin(num_workgroups) num_wgs : vec3; + @builtin(workgroup_id) wgid : vec3; +}; + +)"; + + auto* expect = R"( +struct tint_symbol_2 { + num_workgroups : vec3; +} + +@group(0) @binding(30) var tint_symbol_3 : tint_symbol_2; + +struct tint_symbol_1 { + @builtin(global_invocation_id) + gid : vec3; + @builtin(workgroup_id) + wgid : vec3; +} + +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; + num_wgs : vec3; + wgid : vec3; +} +)"; + + DataMap data; + data.Add( + CanonicalizeEntryPointIO::ShaderStyle::kHlsl); + data.Add(sem::BindingPoint{0, 30u}); + auto got = Run( + 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 5139edc3d5..f3da053a5d 100644 --- a/src/transform/pad_array_elements_test.cc +++ b/src/transform/pad_array_elements_test.cc @@ -216,6 +216,42 @@ fn f() { 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; +)"; + auto* expect = R"( +struct tint_padded_array_element { + @size(16) + el : i32; +} + +fn f() { + var arr : array; + arr = array(); + arr = array(tint_padded_array_element(1), tint_padded_array_element(2), tint_padded_array_element(3), tint_padded_array_element(4)); + let vals : array = array(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; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(PadArrayElementsTest, ArraysInStruct) { auto* src = R"( struct S { @@ -364,6 +400,65 @@ fn f(s : S) -> i32 { 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; + b : @stride(512) array<@stride(32) array, 4>; + c : @stride(512) array<@stride(64) array<@stride(8) array, 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; +} + +struct tint_padded_array_element_3 { + @size(8) + el : i32; +} + +struct tint_padded_array_element_4 { + @size(64) + el : array; +} + +struct tint_padded_array_element_5 { + @size(512) + el : array; +} + +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; + b : array; + c : array; +} +)"; + + auto got = Run(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 c9210dda1a..109615b3ca 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 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(PromoteInitializersToConstVarTest, BasicStruct_OutOfOrder) { + auto* src = R"( +fn f() { + var x = S(1, 2.0, vec3()).b; +} + +struct S { + a : i32; + b : f32; + c : vec3; +}; +)"; + + auto* expect = R"( +fn f() { + let tint_symbol = S(1, 2.0, vec3()); + var x = tint_symbol.b; +} + +struct S { + a : i32; + b : f32; + c : vec3; +} +)"; + + DataMap data; + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopInit) { auto* src = R"( fn f() { @@ -155,6 +187,44 @@ fn f() { 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()).b; ; ) { + break; + } +} + +struct S { + a : i32; + b : f32; + c : vec3; +}; +)"; + + auto* expect = R"( +fn f() { + var insert_after = 1; + let tint_symbol = S(1, 2.0, vec3()); + for(var x = tint_symbol.b; ; ) { + break; + } +} + +struct S { + a : i32; + b : f32; + c : vec3; +} +)"; + + DataMap data; + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopCond) { auto* src = R"( fn f() { @@ -460,6 +530,46 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(PromoteInitializersToConstVarTest, Mixed_OutOfOrder) { + auto* src = R"( +fn f() { + var x = S2(array(S1(1), S1(2), S1(3))).a[1].a; +} + +struct S2 { + a : array; +}; + +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(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; +} + +struct S1 { + a : i32; +} +)"; + + DataMap data; + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl) { auto* src = R"( struct S { @@ -486,6 +596,32 @@ let module_str : S = S(1, 2.0, 3); EXPECT_EQ(expect, str(got)); } +TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl_OutOfOrder) { + auto* src = R"( +fn f() { + var local_arr = array(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 = array(0.0, 1.0, 2.0, 3.0); +)"; + + auto* expect = src; + + DataMap data; + auto got = Run(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 d073d358af..a8fd5ea1cd 100644 --- a/src/transform/remove_phonies_test.cc +++ b/src/transform/remove_phonies_test.cc @@ -130,6 +130,54 @@ fn f() { 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(neg(9))); + _ = vec3(1, neg(10), 3); + _ = mat2x2(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(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RemovePhoniesTest, MultipleSideEffects) { auto* src = R"( fn neg(a : i32) -> i32 { @@ -187,6 +235,63 @@ fn f() { 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(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RemovePhoniesTest, ForLoop) { auto* src = R"( struct S { @@ -254,6 +359,73 @@ fn f() { 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; +}; + +@group(0) @binding(0) var 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; +} + +@group(0) @binding(0) var s : S; +)"; + + auto got = Run(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 8af6cce79a..ac422c0206 100644 --- a/src/transform/robustness_test.cc +++ b/src/transform/robustness_test.cc @@ -48,6 +48,32 @@ fn f() { 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 a : array; +)"; + + auto* expect = R"( +fn f() { + let b : f32 = a[1u]; +} + +let c : u32 = 1u; + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Idx_Nested_Scalar) { auto* src = R"( var a : array; @@ -78,6 +104,36 @@ fn f() { 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 i : u32; + +var b : array; + +var a : array; +)"; + + auto* expect = R"( +fn f() { + var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)]; +} + +var i : u32; + +var b : array; + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Idx_Scalar) { auto* src = R"( var a : array; @@ -100,6 +156,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Array_Idx_Scalar_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[1]; +} + +var a : array; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[1]; +} + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Idx_Expr) { auto* src = R"( var a : array; @@ -126,6 +204,32 @@ fn f() { 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 c : i32; + +var a : array; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; +} + +var c : i32; + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Idx_Negative) { auto* src = R"( var a : array; @@ -148,6 +252,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Array_Idx_Negative_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[-1]; +} + +var a : array; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[0]; +} + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Idx_OutOfBounds) { auto* src = R"( var a : array; @@ -170,6 +296,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Array_Idx_OutOfBounds_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[3]; +} + +var a : array; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2]; +} + +var a : array; +)"; + + auto got = Run(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 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Vector_Idx_Scalar_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[1]; +} + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[1]; +} + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Idx_Expr) { auto* src = R"( var a : vec3; @@ -298,6 +468,32 @@ fn f() { 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 c : i32; + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; +} + +var c : i32; + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar) { auto* src = R"( var a : vec3; @@ -320,6 +516,28 @@ fn f() { 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 a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a.xy[1]; +} + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) { auto* src = R"( var a : vec3; @@ -345,6 +563,33 @@ fn f() { 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 c : i32; + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a.xy[min(u32(c), 1u)]; +} + +var c : i32; + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr) { auto* src = R"( var a : vec3; @@ -371,6 +616,32 @@ fn f() { 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 c : i32; + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)]; +} + +var c : i32; + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Idx_Negative) { auto* src = R"( var a : vec3; @@ -393,6 +664,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Vector_Idx_Negative_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[-1]; +} + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[0]; +} + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Vector_Idx_OutOfBounds) { auto* src = R"( var a : vec3; @@ -415,6 +708,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Vector_Idx_OutOfBounds_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[3]; +} + +var a : vec3; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2]; +} + +var a : vec3; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_Scalar) { auto* src = R"( var a : mat3x2; @@ -437,6 +752,28 @@ fn f() { EXPECT_EQ(expect, str(got)); } +TEST_F(RobustnessTest, Matrix_Idx_Scalar_OutOfOrder) { + auto* src = R"( +fn f() { + var b : f32 = a[2][1]; +} + +var a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2][1]; +} + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_Expr_Column) { auto* src = R"( var a : mat3x2; @@ -463,6 +800,32 @@ fn f() { 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 c : i32; + +var a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1]; +} + +var c : i32; + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_Expr_Row) { auto* src = R"( var a : mat3x2; @@ -489,6 +852,32 @@ fn f() { 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 c : i32; + +var a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)]; +} + +var c : i32; + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_Negative_Column) { auto* src = R"( var a : mat3x2; @@ -511,6 +900,28 @@ fn f() { 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 a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[0][1]; +} + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_Negative_Row) { auto* src = R"( var a : mat3x2; @@ -533,6 +944,28 @@ fn f() { 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 a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2][0]; +} + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column) { auto* src = R"( var a : mat3x2; @@ -555,6 +988,28 @@ fn f() { 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 a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2][1]; +} + +var a : mat3x2; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row) { auto* src = R"( var a : mat3x2; @@ -577,6 +1032,28 @@ fn f() { 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 a : mat3x2; +)"; + + auto* expect = R"( +fn f() { + var b : f32 = a[2][1]; +} + +var a : mat3x2; +)"; + + auto got = Run(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 @@ fn f() { 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 s : S; + +struct S { + a : f32; + b : array; +}; +)"; + + auto* expect = R"( +fn f() { + var d : f32 = s.b[min(25u, (arrayLength(&(s.b)) - 1u))]; +} + +@group(0) @binding(0) var s : S; + +struct S { + a : f32; + b : array; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); } // Clamp textureLoad() coord, array_index and level values @@ -715,6 +1219,72 @@ fn f() { 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(1, 2), level_idx); + textureLoad(tex_2d_arr, vec2(1, 2), array_idx, level_idx); + textureLoad(tex_3d, vec3(1, 2, 3), level_idx); + textureLoad(tex_ms_2d, vec2(1, 2), sample_idx); + textureLoad(tex_depth_2d, vec2(1, 2), level_idx); + textureLoad(tex_depth_2d_arr, vec2(1, 2), array_idx, level_idx); + textureLoad(tex_external, vec2(1, 2)); +} + +@group(0) @binding(0) var tex_1d : texture_1d; +@group(0) @binding(0) var tex_2d : texture_2d; +@group(0) @binding(0) var tex_2d_arr : texture_2d_array; +@group(0) @binding(0) var tex_3d : texture_3d; +@group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; +@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(1, 2), vec2(), (textureDimensions(tex_2d, clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1))) - vec2(1))), clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1))); + textureLoad(tex_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1))) - vec2(1))), clamp(array_idx, 0, (textureNumLayers(tex_2d_arr) - 1)), clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1))); + textureLoad(tex_3d, clamp(vec3(1, 2, 3), vec3(), (textureDimensions(tex_3d, clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1))) - vec3(1))), clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1))); + textureLoad(tex_ms_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_ms_2d) - vec2(1))), sample_idx); + textureLoad(tex_depth_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1))) - vec2(1))), clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1))); + textureLoad(tex_depth_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d_arr) - 1))) - vec2(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(1, 2), vec2(), (textureDimensions(tex_external) - vec2(1)))); +} + +@group(0) @binding(0) var tex_1d : texture_1d; + +@group(0) @binding(0) var tex_2d : texture_2d; + +@group(0) @binding(0) var tex_2d_arr : texture_2d_array; + +@group(0) @binding(0) var tex_3d : texture_3d; + +@group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; + +@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(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 @@ fn f() { 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()); + textureStore(tex2d, vec2(10, 20), vec4()); + textureStore(tex2d_arr, vec2(10, 20), 50, vec4()); + textureStore(tex3d, vec3(10, 20, 30), vec4()); +} + +@group(0) @binding(0) var tex1d : texture_storage_1d; + +@group(0) @binding(1) var tex2d : texture_storage_2d; + +@group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; + +@group(0) @binding(3) var tex3d : texture_storage_3d; + +)"; + + auto* expect = R"( +fn f() { + textureStore(tex1d, clamp(10, i32(), (textureDimensions(tex1d) - i32(1))), vec4()); + textureStore(tex2d, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d) - vec2(1))), vec4()); + textureStore(tex2d_arr, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d_arr) - vec2(1))), clamp(50, 0, (textureNumLayers(tex2d_arr) - 1)), vec4()); + textureStore(tex3d, clamp(vec3(10, 20, 30), vec3(), (textureDimensions(tex3d) - vec3(1))), vec4()); +} + +@group(0) @binding(0) var tex1d : texture_storage_1d; + +@group(0) @binding(1) var tex2d : texture_storage_2d; + +@group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; + +@group(0) @binding(3) var tex3d : texture_storage_3d; +)"; + + auto got = Run(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; diff --git a/src/transform/unshadow_test.cc b/src/transform/unshadow_test.cc index ddba826c53..f6383ef091 100644 --- a/src/transform/unshadow_test.cc +++ b/src/transform/unshadow_test.cc @@ -84,6 +84,36 @@ fn Y() { 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(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(UnshadowTest, LocalShadowsStruct) { auto* src = R"( struct a { @@ -118,13 +148,50 @@ fn Y() { 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(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 @@ fn b() { 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(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(src); @@ -174,6 +274,36 @@ fn Y() { 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 a : i32; +)"; + + auto* expect = R"( +fn X() { + var a_1 = (a == 123); +} + +fn Y() { + let a_2 = (a == 321); +} + +var a : i32; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(UnshadowTest, LocalShadowsGlobalLet) { auto* src = R"( let a : i32 = 1; @@ -204,6 +334,36 @@ fn Y() { 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(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(UnshadowTest, LocalShadowsLocalVar) { auto* src = R"( fn X() { @@ -360,6 +520,26 @@ fn F(a_1 : bool) { 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(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(UnshadowTest, ParamShadowsAlias) { auto* src = R"( type a = i32; @@ -392,6 +572,38 @@ fn F(a_1 : a) { 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(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 b2482af084..8c551ec6db 100644 --- a/src/transform/vertex_pulling_test.cc +++ b/src/transform/vertex_pulling_test.cc @@ -426,6 +426,86 @@ fn main(tint_symbol_1 : tint_symbol) -> @builtin(position) vec4 { EXPECT_EQ(expect, str(got)); } +TEST_F(VertexPullingTest, + ExistingVertexIndexAndInstanceIndex_Struct_OutOfOrder) { + auto* src = R"( +@stage(vertex) +fn main(inputs : Inputs) -> @builtin(position) vec4 { + return vec4(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; +} + +@binding(0) @group(4) var tint_pulling_vertex_buffer_0 : TintVertexData; + +@binding(1) @group(4) var 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 { + 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(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(tint_pulling_vertex_buffer_1.tint_vertex_data[buffer_array_base_1]); + } + return vec4(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(cfg); + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(VertexPullingTest, ExistingVertexIndexAndInstanceIndex_SeparateStruct) { auto* src = R"( struct Inputs { @@ -502,6 +582,83 @@ fn main(indices : Indices) -> @builtin(position) vec4 { 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 { + return vec4(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; +} + +@binding(0) @group(4) var tint_pulling_vertex_buffer_0 : TintVertexData; + +@binding(1) @group(4) var tint_pulling_vertex_buffer_1 : TintVertexData; + +@stage(vertex) +fn main(indices : Indices) -> @builtin(position) vec4 { + var inputs : Inputs; + { + let buffer_array_base_0 = indices.custom_vertex_index; + inputs.var_a = bitcast(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(tint_pulling_vertex_buffer_1.tint_vertex_data[buffer_array_base_1]); + } + return vec4(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(cfg); + auto got = Run(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 4f3b5d4e66..c9daaf8f55 100644 --- a/src/transform/wrap_arrays_in_structs_test.cc +++ b/src/transform/wrap_arrays_in_structs_test.cc @@ -172,6 +172,48 @@ fn f() { 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; +type Inner = array; +)"; + auto* expect = R"( +struct tint_array_wrapper_1 { + arr : array; +} + +struct tint_array_wrapper { + arr : array; +} + +fn f() { + var arr : tint_array_wrapper; + arr = tint_array_wrapper(array()); + arr = tint_array_wrapper(array(tint_array_wrapper_1(array(1, 2)), tint_array_wrapper_1(array(3, 4)))); + let vals : tint_array_wrapper = tint_array_wrapper(array(tint_array_wrapper_1(array(1, 2)), tint_array_wrapper_1(array(3, 4)))); + arr = vals; + let x = arr.arr[3]; +} + +type Array = tint_array_wrapper; + +type Inner = tint_array_wrapper_1; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(WrapArraysInStructsTest, ArraysInStruct) { auto* src = R"( struct S { @@ -326,6 +368,57 @@ fn f2() { EXPECT_EQ(expect, str(got)); } +TEST_F(WrapArraysInStructsTest, DeclarationOrder_OutOfOrder) { + auto* src = R"( +fn f2() { + var v : array; +} + +type T3 = i32; + +fn f1(a : array) { +} + +type T2 = i32; + +type T1 = array; + +type T0 = i32; +)"; + auto* expect = R"( +struct tint_array_wrapper { + arr : array; +} + +fn f2() { + var v : tint_array_wrapper; +} + +type T3 = i32; + +struct tint_array_wrapper_1 { + arr : array; +} + +fn f1(a : tint_array_wrapper_1) { +} + +type T2 = i32; + +struct tint_array_wrapper_2 { + arr : array; +} + +type T1 = tint_array_wrapper_2; + +type T0 = i32; +)"; + + auto got = Run(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 6cceb20012..54e1aa26ba 100644 --- a/src/transform/zero_init_workgroup_memory_test.cc +++ b/src/transform/zero_init_workgroup_memory_test.cc @@ -93,6 +93,29 @@ fn f() { 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 a : i32; + +var b : i32; + +var c : i32; +)"; + auto* expect = src; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex) { auto* src = R"( var v : i32; @@ -120,6 +143,34 @@ fn f(@builtin(local_invocation_index) local_idx : u32) { EXPECT_EQ(expect, str(got)); } +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 v : i32; +)"; + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn f(@builtin(local_invocation_index) local_idx : u32) { + { + v = i32(); + } + workgroupBarrier(); + _ = v; +} + +var v : i32; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndexInStruct) { auto* src = R"( @@ -157,6 +208,43 @@ fn f(params : Params) { 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 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 v : i32; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex) { auto* src = R"( var v : i32; @@ -184,6 +272,34 @@ fn f(@builtin(local_invocation_index) local_invocation_index : u32) { EXPECT_EQ(expect, str(got)); } +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 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 v : i32; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size1) { auto* src = R"( @@ -248,6 +364,70 @@ fn f(@builtin(local_invocation_index) local_idx : u32) { EXPECT_EQ(expect, str(got)); } +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 a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +}; +)"; + 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 a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3) { auto* src = R"( @@ -534,6 +714,70 @@ fn f(@builtin(local_invocation_id) local_invocation_id : vec3, @builtin(loc 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) { + _ = a; // Initialization should be inserted above this statement + _ = b; + _ = c; +} + +var a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +}; +)"; + auto* expect = R"( +@stage(compute) @workgroup_size(1) +fn f(@builtin(local_invocation_id) local_invocation_id : vec3, @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 a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints) { auto* src = R"( struct S { @@ -633,6 +877,106 @@ fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) { 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) { + _ = 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 a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +}; +)"; + 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, @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 a : i32; + +var b : S; + +var c : array; + +struct S { + x : i32; + y : array; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage) { auto* src = R"( var v : i32; @@ -676,6 +1020,49 @@ fn f(@builtin(local_invocation_index) local_idx : u32) { 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 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 v : i32; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics) { auto* src = R"( var i : atomic; @@ -709,6 +1096,39 @@ fn f(@builtin(local_invocation_index) local_invocation_index : u32) { 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 i : atomic; +var u : atomic; +)"; + 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 i : atomic; + +var u : atomic; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics) { auto* src = R"( struct S { @@ -756,6 +1176,53 @@ fn f(@builtin(local_invocation_index) local_invocation_index : u32) { 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 w : S; + +struct S { + a : i32; + i : atomic; + b : f32; + u : atomic; + 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 w : S; + +struct S { + a : i32; + i : atomic; + b : f32; + u : atomic; + c : u32; +} +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics) { auto* src = R"( var w : array, 4>; @@ -784,6 +1251,34 @@ fn f(@builtin(local_invocation_index) local_invocation_index : u32) { 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 w : array, 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 w : array, 4>; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics) { auto* src = R"( struct S { @@ -832,6 +1327,55 @@ fn f(@builtin(local_invocation_index) local_invocation_index : u32) { 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 w : array; + +struct S { + a : i32; + i : atomic; + b : f32; + u : atomic; + 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 w : array; + +struct S { + a : i32; + i : atomic; + b : f32; + u : atomic; + c : u32; +} +)"; + + auto got = Run(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 d17bdca008..41f584003a 100644 --- a/src/writer/hlsl/generator_impl_sanitizer_test.cc +++ b/src/writer/hlsl/generator_impl_sanitizer_test.cc @@ -181,7 +181,6 @@ TEST_F(HlslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) { 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 0000000000..b1c9e66246 --- /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 0000000000..e1e01c76c8 --- /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 0000000000..9047109e73 --- /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 0000000000..3054a1615c --- /dev/null +++ b/test/out_of_order_decls/alias/alias.wgsl.expected.msl @@ -0,0 +1,8 @@ +#include + +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 0000000000..5afeefafc3 --- /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 0000000000..ca3ee03d48 --- /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 0000000000..20f80a43ac --- /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 0000000000..508e315221 --- /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 0000000000..c113dacf08 --- /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 0000000000..c2a67d04a2 --- /dev/null +++ b/test/out_of_order_decls/alias/struct.wgsl.expected.msl @@ -0,0 +1,12 @@ +#include + +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 0000000000..419495673e --- /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 0000000000..20f80a43ac --- /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 0000000000..95cec5aee3 --- /dev/null +++ b/test/out_of_order_decls/array/alias.wgsl @@ -0,0 +1,7 @@ +var A : array; +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 0000000000..91dd20a0bf --- /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 0000000000..c2ce6c2494 --- /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 0000000000..e345a6d58f --- /dev/null +++ b/test/out_of_order_decls/array/alias.wgsl.expected.msl @@ -0,0 +1,13 @@ +#include + +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 0000000000..0daa352c19 --- /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 0000000000..1e581e047f --- /dev/null +++ b/test/out_of_order_decls/array/alias.wgsl.expected.wgsl @@ -0,0 +1,8 @@ +var A : array; + +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 0000000000..ab88948726 --- /dev/null +++ b/test/out_of_order_decls/array/struct.wgsl @@ -0,0 +1,9 @@ +var A : array; +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 0000000000..2f4eab140b --- /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 0000000000..a708176999 --- /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 0000000000..4e91e2e7f0 --- /dev/null +++ b/test/out_of_order_decls/array/struct.wgsl.expected.msl @@ -0,0 +1,18 @@ +#include + +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 0000000000..317a4459c1 --- /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 0000000000..3e571637bd --- /dev/null +++ b/test/out_of_order_decls/array/struct.wgsl.expected.wgsl @@ -0,0 +1,10 @@ +var A : array; + +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 0000000000..1a0f2129cd --- /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 0000000000..4a87ea7323 --- /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 0000000000..620afd7ef0 --- /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 0000000000..e04a45af83 --- /dev/null +++ b/test/out_of_order_decls/func/func.wgsl.expected.msl @@ -0,0 +1,11 @@ +#include + +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 0000000000..19570a47b2 --- /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 0000000000..1a0f2129cd --- /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 0000000000..25a56c629b --- /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 0000000000..f308959472 --- /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 0000000000..c35c25093b --- /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 0000000000..2509d6c788 --- /dev/null +++ b/test/out_of_order_decls/func/let.wgsl.expected.msl @@ -0,0 +1,10 @@ +#include + +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 0000000000..452861f2a1 --- /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 0000000000..25a56c629b --- /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 0000000000..4810fc1639 --- /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 0000000000..573558edcd --- /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 0000000000..f4b327c8b7 --- /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 0000000000..438c74fb6d --- /dev/null +++ b/test/out_of_order_decls/func/type.wgsl.expected.msl @@ -0,0 +1,8 @@ +#include + +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 0000000000..1de57b46ea --- /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 0000000000..4810fc1639 --- /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 0000000000..d77a968c7a --- /dev/null +++ b/test/out_of_order_decls/func/var.wgsl @@ -0,0 +1,6 @@ +@stage(fragment) +fn f() { + let b = a; +} + +var 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 0000000000..6cf55fba3d --- /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 0000000000..813886a581 --- /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 0000000000..832e2eb450 --- /dev/null +++ b/test/out_of_order_decls/func/var.wgsl.expected.msl @@ -0,0 +1,9 @@ +#include + +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 0000000000..16c9dc963c --- /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 0000000000..d77a968c7a --- /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 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 0000000000..e225a735dd --- /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 0000000000..508e315221 --- /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 0000000000..c113dacf08 --- /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 0000000000..c2a67d04a2 --- /dev/null +++ b/test/out_of_order_decls/struct/alias.wgsl.expected.msl @@ -0,0 +1,12 @@ +#include + +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 0000000000..419495673e --- /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 0000000000..e225a735dd --- /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 0000000000..d4e405f848 --- /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 0000000000..a95a80d87a --- /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 0000000000..2f48847da4 --- /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 0000000000..6dcac2b034 --- /dev/null +++ b/test/out_of_order_decls/struct/struct.wgsl.expected.msl @@ -0,0 +1,16 @@ +#include + +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 0000000000..30b44c54e9 --- /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 0000000000..d4e405f848 --- /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; +}