[spirv] Add ForkExplicitLayoutTypes transform
Some storage classes require arrays and structures to have explicit
layout decorations, while others require them to not have any such
decorations. This transform forks structures that are shared between
those two storage classes to satisfy this SPIR-V validation.
When a type is forked, the transform introduces helper functions to
convert between the original type and the forked type for loads and
stores to the variable that needs the explicit layout decorations.
Array types will be handled in a follow up.
Bug: 42252012
Change-Id: I147d777547be08096d52f5221647614026ee8649
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/229976
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/writer/raise/BUILD.bazel b/src/tint/lang/spirv/writer/raise/BUILD.bazel
index 6b0ee8c..026d60f 100644
--- a/src/tint/lang/spirv/writer/raise/BUILD.bazel
+++ b/src/tint/lang/spirv/writer/raise/BUILD.bazel
@@ -41,6 +41,7 @@
srcs = [
"builtin_polyfill.cc",
"expand_implicit_splats.cc",
+ "fork_explicit_layout_types.cc",
"handle_matrix_arithmetic.cc",
"merge_return.cc",
"pass_matrix_by_pointer.cc",
@@ -52,6 +53,7 @@
hdrs = [
"builtin_polyfill.h",
"expand_implicit_splats.h",
+ "fork_explicit_layout_types.h",
"handle_matrix_arithmetic.h",
"merge_return.h",
"pass_matrix_by_pointer.h",
@@ -105,6 +107,7 @@
srcs = [
"builtin_polyfill_test.cc",
"expand_implicit_splats_test.cc",
+ "fork_explicit_layout_types_test.cc",
"handle_matrix_arithmetic_test.cc",
"merge_return_test.cc",
"pass_matrix_by_pointer_test.cc",
diff --git a/src/tint/lang/spirv/writer/raise/BUILD.cmake b/src/tint/lang/spirv/writer/raise/BUILD.cmake
index 51a7d8a..412648d 100644
--- a/src/tint/lang/spirv/writer/raise/BUILD.cmake
+++ b/src/tint/lang/spirv/writer/raise/BUILD.cmake
@@ -45,6 +45,8 @@
lang/spirv/writer/raise/builtin_polyfill.h
lang/spirv/writer/raise/expand_implicit_splats.cc
lang/spirv/writer/raise/expand_implicit_splats.h
+ lang/spirv/writer/raise/fork_explicit_layout_types.cc
+ lang/spirv/writer/raise/fork_explicit_layout_types.h
lang/spirv/writer/raise/handle_matrix_arithmetic.cc
lang/spirv/writer/raise/handle_matrix_arithmetic.h
lang/spirv/writer/raise/merge_return.cc
@@ -113,6 +115,7 @@
tint_add_target(tint_lang_spirv_writer_raise_test test
lang/spirv/writer/raise/builtin_polyfill_test.cc
lang/spirv/writer/raise/expand_implicit_splats_test.cc
+ lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
lang/spirv/writer/raise/handle_matrix_arithmetic_test.cc
lang/spirv/writer/raise/merge_return_test.cc
lang/spirv/writer/raise/pass_matrix_by_pointer_test.cc
diff --git a/src/tint/lang/spirv/writer/raise/BUILD.gn b/src/tint/lang/spirv/writer/raise/BUILD.gn
index e042ee7..2f4ee58 100644
--- a/src/tint/lang/spirv/writer/raise/BUILD.gn
+++ b/src/tint/lang/spirv/writer/raise/BUILD.gn
@@ -49,6 +49,8 @@
"builtin_polyfill.h",
"expand_implicit_splats.cc",
"expand_implicit_splats.h",
+ "fork_explicit_layout_types.cc",
+ "fork_explicit_layout_types.h",
"handle_matrix_arithmetic.cc",
"handle_matrix_arithmetic.h",
"merge_return.cc",
@@ -106,6 +108,7 @@
sources = [
"builtin_polyfill_test.cc",
"expand_implicit_splats_test.cc",
+ "fork_explicit_layout_types_test.cc",
"handle_matrix_arithmetic_test.cc",
"merge_return_test.cc",
"pass_matrix_by_pointer_test.cc",
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc
new file mode 100644
index 0000000..faebf3c
--- /dev/null
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc
@@ -0,0 +1,317 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/builder.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/validator.h"
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+namespace tint::spirv::writer::raise {
+
+namespace {
+
+/// PIMPL state for the transform.
+struct State {
+ /// The IR module.
+ core::ir::Module& ir;
+
+ /// The IR builder.
+ core::ir::Builder b{ir};
+
+ /// The type manager.
+ core::type::Manager& ty{ir.Types()};
+
+ /// The symbol table.
+ SymbolTable& sym{ir.symbols};
+
+ /// The array and structure types that must be emitted without explicit layout decorations.
+ Hashset<const core::type::Type*, 16> must_emit_without_explicit_layout{};
+
+ /// A map from original array/struct type to the explicitly laid out version.
+ Hashmap<const core::type::Type*, const core::type::Type*, 16> explicit_type_map{};
+
+ /// Helper functions for converting to and from explicitly laid out types.
+ Hashmap<const core::type::Type*, core::ir::Function*, 8> conversion_helpers{};
+
+ /// Process the module.
+ void Process() {
+ // Record arrays and structures that must not have explicit layout decorations, as well as
+ // variables in address space that must have explicit layout decorations.
+ Vector<core::ir::Var*, 16> vars_requiring_explicit_layout;
+ for (auto* inst : ir.Instructions()) {
+ auto* var = inst->As<core::ir::Var>();
+ if (!var) {
+ continue;
+ }
+
+ auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
+ switch (ptr->AddressSpace()) {
+ case core::AddressSpace::kPushConstant:
+ case core::AddressSpace::kStorage:
+ case core::AddressSpace::kUniform:
+ vars_requiring_explicit_layout.Push(var);
+ break;
+
+ case core::AddressSpace::kFunction:
+ case core::AddressSpace::kPrivate:
+ // TODO(crbug.com/401585324): Only do this for SPIR-V 1.5 and later.
+ RecordTypesThatMustNotHaveExplicitLayout(ptr);
+ break;
+
+ case core::AddressSpace::kWorkgroup:
+ case core::AddressSpace::kHandle:
+ case core::AddressSpace::kPixelLocal:
+ case core::AddressSpace::kIn:
+ case core::AddressSpace::kOut:
+ RecordTypesThatMustNotHaveExplicitLayout(ptr);
+ break;
+
+ case core::AddressSpace::kUndefined:
+ break;
+ }
+ }
+
+ // If a variable that requires explicit layout decorations is using types that will be
+ // emitted without explicit layout decorations, we need to rewrite its store type and
+ // introduce element-wise copies when loading and storing those types.
+ for (auto* var : vars_requiring_explicit_layout) {
+ UpdatePointerType(var->Result(0));
+ }
+ }
+
+ /// Add all array and structure types nested in @p ptr to the set of types that must be emitted
+ /// without explicit layout decorations.
+ /// @param ptr the pointer type whose store type will be recorded
+ void RecordTypesThatMustNotHaveExplicitLayout(const core::type::Pointer* ptr) {
+ // Look for arrays and structures at any nesting depth of this type.
+ Vector<const core::type::Type*, 8> type_queue;
+ type_queue.Push(ptr->StoreType());
+ while (!type_queue.IsEmpty()) {
+ auto* next = type_queue.Pop();
+ if (auto* str = next->As<core::type::Struct>()) {
+ // Record this structure and then check its members if we haven't seen it before.
+ if (must_emit_without_explicit_layout.Add(str)) {
+ for (auto* member : str->Members()) {
+ type_queue.Push(member->Type());
+ }
+ }
+ } else if (auto* arr = next->As<core::type::Array>()) {
+ // Record this array and then check its element type if we haven't seen it before.
+ if (must_emit_without_explicit_layout.Add(arr)) {
+ type_queue.Push(arr->ElemType());
+ }
+ }
+ }
+ }
+
+ /// Recursively fork a type to produce an identical version that will have explicit layout
+ /// decorations, if the original is going to be emitted without explicit layout decorations.
+ /// @param type the original type to fork
+ /// @returns the forked type, or `nullptr` if forking was not necessary
+ const core::type::Type* GetForkedType(const core::type::Type* type) {
+ return explicit_type_map.GetOrAdd(type, [&]() -> const core::type::Type* {
+ if (auto* str = type->As<core::type::Struct>()) {
+ return ForkStructIfNeeded(str);
+ }
+
+ // TODO(crbug.com/42252012): Fork array types too.
+
+ // Any other type is safe to use unchanged, as they do not have layout decorations.
+ return nullptr;
+ });
+ }
+
+ /// Recursively fork a structure type to produce an identical version that will have explicit
+ /// layout decorations, if the original is going to be emitted without explicit layout
+ /// decorations.
+ /// @param original_struct the original structure type to fork
+ /// @returns the forked struct type, or `nullptr` if forking was not necessary
+ const core::type::Struct* ForkStructIfNeeded(const core::type::Struct* original_struct) {
+ // Fork each member type as necessary.
+ bool members_were_forked = false;
+ Vector<const core::type::StructMember*, 8> new_members;
+ for (auto* member : original_struct->Members()) {
+ auto* new_member_type = GetForkedType(member->Type());
+ if (!new_member_type) {
+ // The member type was not forked, so just use the original member type.
+ new_member_type = member->Type();
+ } else {
+ members_were_forked = true;
+ }
+ auto index = static_cast<uint32_t>(new_members.Length());
+ new_members.Push(ty.Get<core::type::StructMember>(
+ member->Name(), new_member_type, index, member->Offset(), member->Align(),
+ member->Size(), core::IOAttributes{}));
+ }
+
+ // If no members were forked and the struct itself is not shared with other address spaces,
+ // then the original struct can safely be reused.
+ if (!must_emit_without_explicit_layout.Contains(original_struct) && !members_were_forked) {
+ return nullptr;
+ }
+
+ // Create a new struct with the rewritten members.
+ auto name = sym.New(original_struct->Name().Name() + "_tint_explicit_layout");
+ auto* new_str = ty.Get<core::type::Struct>(name, //
+ std::move(new_members), //
+ original_struct->Align(), //
+ original_struct->Size(), //
+ original_struct->SizeNoPadding());
+ for (auto flag : original_struct->StructFlags()) {
+ new_str->SetStructFlag(flag);
+ }
+ return new_str;
+ }
+
+ /// Update the store type of an instruction result to use the forked version if needed.
+ /// Replace any uses of the instruction to take the new type into account.
+ /// @param result the instruction result to update
+ void UpdatePointerType(core::ir::InstructionResult* result) {
+ // Check if the store type needs to be forked.
+ auto* ptr = result->Type()->As<core::type::Pointer>();
+ auto* forked_type = GetForkedType(ptr->StoreType());
+ if (!forked_type) {
+ return;
+ }
+
+ // Update the store type to the forked type that will have an explicit layout.
+ auto* new_ptr = ty.ptr(ptr->AddressSpace(), forked_type, ptr->Access());
+ result->SetType(new_ptr);
+
+ // Update any uses of the instruction to take the new type into account.
+ // This may introduce manual copies to convert between the forked type and the original.
+ result->ForEachUseSorted([&](core::ir::Usage use) { //
+ ReplaceForkedPointerUse(use);
+ });
+ }
+
+ /// Replace a use of an instruction that produces a pointer to a type that has been forked.
+ /// @param use the use of the forked pointer
+ void ReplaceForkedPointerUse(core::ir::Usage use) {
+ tint::Switch(
+ use.instruction, //
+ [&](core::ir::Access* access) {
+ // If the access produces a pointer to a type that has been forked, we need to
+ // update the result type and then recurse into its uses.
+ UpdatePointerType(access->Result(0));
+ },
+ [&](core::ir::Let* let) {
+ // A let usage will propagate the pointer to a type that has been forked, so we need
+ // to update the result type and then recurse into its uses.
+ UpdatePointerType(let->Result(0));
+ },
+ [&](core::ir::Load* load) {
+ b.InsertAfter(load, [&] {
+ // Change the load instruction to produce the forked type, and then convert the
+ // result of the load to the original type.
+ auto* original_type = load->Result(0)->Type();
+ auto* forked_load = b.InstructionResult(load->From()->Type()->UnwrapPtr());
+ auto* converted = ConvertIfNeeded(original_type, forked_load);
+ load->Result(0)->ReplaceAllUsesWith(converted);
+ load->SetResults(Vector{forked_load});
+ });
+ },
+ [&](core::ir::Store* store) {
+ b.InsertBefore(store, [&] {
+ // Convert the `from` operand of the store instruction to the forked type.
+ auto* forked_type = store->To()->Type()->UnwrapPtr();
+ auto* converted = ConvertIfNeeded(forked_type, store->From());
+ store->SetOperand(core::ir::Store::kFromOperandOffset, converted);
+ });
+ },
+ TINT_ICE_ON_NO_MATCH);
+ }
+
+ /// Convert a value to/from the explicitly laid out type, if necessary.
+ /// @param src the value to convert
+ /// @param dst_type the type to convert it to
+ /// @returns the converted value
+ core::ir::Value* ConvertIfNeeded(const core::type::Type* dst_type, core::ir::Value* src) {
+ // If the source type is already the same as the destination type, there is nothing to do.
+ auto* src_type = src->Type();
+ if (src_type == dst_type) {
+ return src;
+ }
+
+ // TODO(crbug.com/401587662): Use OpCopyLogical with SPIR-V 1.4 and later.
+
+ // Create a helper function to do the conversion.
+ auto* helper = conversion_helpers.GetOrAdd(src_type, [&] {
+ auto* param = b.FunctionParam("tint_source", src_type);
+ auto* func = b.Function("tint_convert_explicit_layout", dst_type);
+ func->AppendParam(param);
+ b.Append(func->Block(), [&] {
+ if (auto* src_struct = src_type->As<core::type::Struct>()) {
+ auto* dst_struct = dst_type->As<core::type::Struct>();
+ b.Return(func, ConvertStruct(src_struct, dst_struct, param));
+ } else {
+ // TODO(crbug.com/42252012): Convert array types.
+ TINT_UNIMPLEMENTED();
+ }
+ });
+ return func;
+ });
+ return b.Call(helper, src)->Result(0);
+ }
+
+ /// Recursively convert a struct type to/from the explicitly laid out version.
+ core::ir::Value* ConvertStruct(const core::type::Struct* src_struct,
+ const core::type::Struct* dst_struct,
+ core::ir::Value* input) {
+ // Convert each member separately and the reconstruct the target struct.
+ Vector<core::ir::Value*, 4> construct_args;
+ for (uint32_t i = 0; i < dst_struct->Members().Length(); i++) {
+ auto* src_member = src_struct->Members()[i];
+ auto* dst_member = dst_struct->Members()[i];
+ auto* extracted = b.Access(src_member->Type(), input, u32(i))->Result(0);
+ auto* converted = ConvertIfNeeded(dst_member->Type(), extracted);
+ construct_args.Push(converted);
+ }
+ return b.Construct(dst_struct, std::move(construct_args))->Result(0);
+ }
+};
+
+} // namespace
+
+Result<SuccessType> ForkExplicitLayoutTypes(core::ir::Module& ir) {
+ auto result = ValidateAndDumpIfNeeded(ir, "spirv.ForkExplicitLayoutTypes");
+ if (result != Success) {
+ return result;
+ }
+
+ State{ir}.Process();
+
+ return Success;
+}
+
+} // namespace tint::spirv::writer::raise
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h
new file mode 100644
index 0000000..4db9557
--- /dev/null
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h
@@ -0,0 +1,49 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_SPIRV_WRITER_RAISE_FORK_EXPLICIT_LAYOUT_TYPES_H_
+#define SRC_TINT_LANG_SPIRV_WRITER_RAISE_FORK_EXPLICIT_LAYOUT_TYPES_H_
+
+#include "src/tint/utils/result/result.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Module;
+}
+
+namespace tint::spirv::writer::raise {
+
+/// ForkExplicitLayoutTypes is a transform that forks array and structures types that are shared
+/// between address spaces that require explicit layout in SPIR-V and those that cannot have them.
+///
+/// @param module the module to transform
+/// @returns success or failure
+Result<SuccessType> ForkExplicitLayoutTypes(core::ir::Module& module);
+
+} // namespace tint::spirv::writer::raise
+
+#endif // SRC_TINT_LANG_SPIRV_WRITER_RAISE_FORK_EXPLICIT_LAYOUT_TYPES_H_
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
new file mode 100644
index 0000000..29eaf99
--- /dev/null
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
@@ -0,0 +1,1588 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/transform/helper_test.h"
+#include "src/tint/lang/core/type/struct.h"
+
+namespace tint::spirv::writer::raise {
+namespace {
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+using SpirvWriter_ForkExplicitLayoutTypesTest = core::ir::transform::TransformTest;
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, NoModify_Struct_NotInHostShareable) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* wg_buffer = b.Var("wg_buffer", ty.ptr(workgroup, structure));
+ mod.root_block->Append(wg_buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Let("let", b.Load(wg_buffer));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %wg_buffer:ptr<workgroup, MyStruct, read_write> = var undef
+}
+
+%foo = func():void {
+ $B2: {
+ %3:MyStruct = load %wg_buffer
+ %let:MyStruct = let %3
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(src, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, NoModify_Struct_InHostShareable_NotShared) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ // Sharing with a function parameter shouldn't cause rewrite.
+ auto* foo = b.Function("foo", ty.void_());
+ auto* param = b.FunctionParam("param", structure);
+ foo->AppendParam(param);
+ b.Append(foo->Block(), [&] { b.Return(foo); });
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ // Sharing with a let shouldn't cause rewrite.
+ b.Let("let", b.Load(buffer));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func(%param:MyStruct):void {
+ $B2: {
+ ret
+ }
+}
+%foo_1 = func():void { # %foo_1: 'foo'
+ $B3: {
+ %5:MyStruct = load %buffer
+ %let:MyStruct = let %5
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(src, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, PushConstant_SharedWithPrivate) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ });
+
+ mod.root_block->Append(b.Var("buffer", ty.ptr(push_constant, structure)));
+ mod.root_block->Append(b.Var("local", ty.ptr(private_, structure)));
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<push_constant, MyStruct, read> = var undef
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<push_constant, MyStruct_tint_explicit_layout, read> = var undef
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Storage_SharedWithPrivate) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+ mod.root_block->Append(b.Var("local", ty.ptr(private_, structure)));
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Uniform_SharedWithPrivate) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(uniform, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+ mod.root_block->Append(b.Var("local", ty.ptr(private_, structure)));
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<uniform, MyStruct, read> = var undef @binding_point(0, 0)
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<uniform, MyStruct_tint_explicit_layout, read> = var undef @binding_point(0, 0)
+ %local:ptr<private, MyStruct, read_write> = var undef
+}
+
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Storage_SharedWithWorkgroup) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+ mod.root_block->Append(b.Var("local", ty.ptr(workgroup, structure)));
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+ %local:ptr<workgroup, MyStruct, read_write> = var undef
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+ %local:ptr<workgroup, MyStruct, read_write> = var undef
+}
+
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Storage_SharedWithFunction) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", b.Zero(structure));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var MyStruct(0u)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var MyStruct(0u)
+ ret
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, LoadFromStorage_Struct_Shared) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", b.Load(buffer));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:MyStruct = load %buffer
+ %local:ptr<function, MyStruct, read_write> = var %3
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:MyStruct_tint_explicit_layout = load %buffer
+ %4:MyStruct = call %tint_convert_explicit_layout, %3
+ %local:ptr<function, MyStruct, read_write> = var %4
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:MyStruct_tint_explicit_layout):MyStruct {
+ $B3: {
+ %8:u32 = access %tint_source, 0u
+ %9:u32 = access %tint_source, 1u
+ %10:MyStruct = construct %8, %9
+ ret %10
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, LoadFromStorage_NestedStruct_Shared) {
+ auto* inner = ty.Struct(mod.symbols.New("Inner"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* outer = ty.Struct(mod.symbols.New("Outer"), {
+ {mod.symbols.New("a"), inner},
+ {mod.symbols.New("b"), inner},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, outer));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", b.Load(buffer));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:Outer = load %buffer
+ %local:ptr<function, Outer, read_write> = var %3
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+Inner_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer_tint_explicit_layout = struct @align(4) {
+ a_1:Inner_tint_explicit_layout @offset(0)
+ b_1:Inner_tint_explicit_layout @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:Outer_tint_explicit_layout = load %buffer
+ %4:Outer = call %tint_convert_explicit_layout, %3
+ %local:ptr<function, Outer, read_write> = var %4
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:Outer_tint_explicit_layout):Outer {
+ $B3: {
+ %8:Inner_tint_explicit_layout = access %tint_source, 0u
+ %9:Inner = call %tint_convert_explicit_layout_1, %8
+ %11:Inner_tint_explicit_layout = access %tint_source, 1u
+ %12:Inner = call %tint_convert_explicit_layout_1, %11
+ %13:Outer = construct %9, %12
+ ret %13
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:Inner_tint_explicit_layout):Inner { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %15:u32 = access %tint_source_1, 0u
+ %16:u32 = access %tint_source_1, 1u
+ %17:Inner = construct %15, %16
+ ret %17
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, StoreToStorage_Struct_Shared) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ auto* local = b.Var("local", ty.ptr<function>(structure));
+ b.Store(buffer, b.Load(local));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %4:MyStruct = load %local
+ store %buffer, %4
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %4:MyStruct = load %local
+ %5:MyStruct_tint_explicit_layout = call %tint_convert_explicit_layout, %4
+ store %buffer, %5
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:MyStruct):MyStruct_tint_explicit_layout {
+ $B3: {
+ %8:u32 = access %tint_source, 0u
+ %9:u32 = access %tint_source, 1u
+ %10:MyStruct_tint_explicit_layout = construct %8, %9
+ ret %10
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, StoreToStorage_NestedStruct_Shared) {
+ auto* inner = ty.Struct(mod.symbols.New("Inner"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* outer = ty.Struct(mod.symbols.New("Outer"), {
+ {mod.symbols.New("a"), inner},
+ {mod.symbols.New("b"), inner},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, outer, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ auto* local = b.Var("local", ty.ptr<function>(outer));
+ b.Store(buffer, b.Load(local));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Outer, read_write> = var undef
+ %4:Outer = load %local
+ store %buffer, %4
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+Inner_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer_tint_explicit_layout = struct @align(4) {
+ a_1:Inner_tint_explicit_layout @offset(0)
+ b_1:Inner_tint_explicit_layout @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Outer, read_write> = var undef
+ %4:Outer = load %local
+ %5:Outer_tint_explicit_layout = call %tint_convert_explicit_layout, %4
+ store %buffer, %5
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:Outer):Outer_tint_explicit_layout {
+ $B3: {
+ %8:Inner = access %tint_source, 0u
+ %9:Inner_tint_explicit_layout = call %tint_convert_explicit_layout_1, %8
+ %11:Inner = access %tint_source, 1u
+ %12:Inner_tint_explicit_layout = call %tint_convert_explicit_layout_1, %11
+ %13:Outer_tint_explicit_layout = construct %9, %12
+ ret %13
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:Inner):Inner_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %15:u32 = access %tint_source_1, 0u
+ %16:u32 = access %tint_source_1, 1u
+ %17:Inner_tint_explicit_layout = construct %15, %16
+ ret %17
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, InnerStructShared_OuterStructNotShared) {
+ auto* inner = ty.Struct(mod.symbols.New("Inner"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* outer = ty.Struct(mod.symbols.New("Outer"), {
+ {mod.symbols.New("a"), inner},
+ {mod.symbols.New("b"), inner},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, outer, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ auto* local = b.Var("local", ty.ptr<function>(inner));
+ auto* load_inner = b.Load(local);
+ b.Store(buffer, b.Construct(outer, load_inner, load_inner));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Inner, read_write> = var undef
+ %4:Inner = load %local
+ %5:Outer = construct %4, %4
+ store %buffer, %5
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+Inner_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer_tint_explicit_layout = struct @align(4) {
+ a_1:Inner_tint_explicit_layout @offset(0)
+ b_1:Inner_tint_explicit_layout @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Inner, read_write> = var undef
+ %4:Inner = load %local
+ %5:Outer = construct %4, %4
+ %6:Outer_tint_explicit_layout = call %tint_convert_explicit_layout, %5
+ store %buffer, %6
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:Outer):Outer_tint_explicit_layout {
+ $B3: {
+ %9:Inner = access %tint_source, 0u
+ %10:Inner_tint_explicit_layout = call %tint_convert_explicit_layout_1, %9
+ %12:Inner = access %tint_source, 1u
+ %13:Inner_tint_explicit_layout = call %tint_convert_explicit_layout_1, %12
+ %14:Outer_tint_explicit_layout = construct %10, %13
+ ret %14
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:Inner):Inner_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %16:u32 = access %tint_source_1, 0u
+ %17:u32 = access %tint_source_1, 1u
+ %18:Inner_tint_explicit_layout = construct %16, %17
+ ret %18
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest,
+ OuterStructNotShared_OneInnerStructShared_OneInnerStructNotShared) {
+ auto* inner_shared =
+ ty.Struct(mod.symbols.New("InnerShared"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* inner_not_shared =
+ ty.Struct(mod.symbols.New("InnerNotShared"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* outer = ty.Struct(mod.symbols.New("Outer"), {
+ {mod.symbols.New("a"), inner_shared},
+ {mod.symbols.New("b"), inner_not_shared},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, outer, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", ty.ptr<function>(inner_shared));
+ b.Store(buffer, b.Construct(outer, b.Zero(inner_shared), b.Zero(inner_not_shared)));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+InnerShared = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+InnerNotShared = struct @align(4) {
+ a_1:u32 @offset(0)
+ b_1:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_2:InnerShared @offset(0)
+ b_2:InnerNotShared @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, InnerShared, read_write> = var undef
+ %4:Outer = construct InnerShared(0u), InnerNotShared(0u)
+ store %buffer, %4
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+InnerShared = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+InnerNotShared = struct @align(4) {
+ a_1:u32 @offset(0)
+ b_1:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_2:InnerShared @offset(0)
+ b_2:InnerNotShared @offset(8)
+}
+
+InnerShared_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer_tint_explicit_layout = struct @align(4) {
+ a_2:InnerShared_tint_explicit_layout @offset(0)
+ b_2:InnerNotShared @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, InnerShared, read_write> = var undef
+ %4:Outer = construct InnerShared(0u), InnerNotShared(0u)
+ %5:Outer_tint_explicit_layout = call %tint_convert_explicit_layout, %4
+ store %buffer, %5
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:Outer):Outer_tint_explicit_layout {
+ $B3: {
+ %8:InnerShared = access %tint_source, 0u
+ %9:InnerShared_tint_explicit_layout = call %tint_convert_explicit_layout_1, %8
+ %11:InnerNotShared = access %tint_source, 1u
+ %12:Outer_tint_explicit_layout = construct %9, %11
+ ret %12
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:InnerShared):InnerShared_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %14:u32 = access %tint_source_1, 0u
+ %15:u32 = access %tint_source_1, 1u
+ %16:InnerShared_tint_explicit_layout = construct %14, %15
+ ret %16
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedStruct_MultipleLoadsAndStores) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ auto* let = b.Let("let", b.Load(buffer));
+ b.Var("local", b.Load(buffer));
+ b.Store(buffer, b.Zero(structure));
+ b.Store(buffer, let);
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:MyStruct = load %buffer
+ %let:MyStruct = let %3
+ %5:MyStruct = load %buffer
+ %local:ptr<function, MyStruct, read_write> = var %5
+ store %buffer, MyStruct(0u)
+ store %buffer, %let
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:MyStruct_tint_explicit_layout = load %buffer
+ %4:MyStruct = call %tint_convert_explicit_layout, %3
+ %let:MyStruct = let %4
+ %7:MyStruct_tint_explicit_layout = load %buffer
+ %8:MyStruct = call %tint_convert_explicit_layout, %7
+ %local:ptr<function, MyStruct, read_write> = var %8
+ %10:MyStruct_tint_explicit_layout = call %tint_convert_explicit_layout_1, MyStruct(0u)
+ store %buffer, %10
+ %12:MyStruct_tint_explicit_layout = call %tint_convert_explicit_layout_1, %let
+ store %buffer, %12
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:MyStruct_tint_explicit_layout):MyStruct {
+ $B3: {
+ %14:u32 = access %tint_source, 0u
+ %15:u32 = access %tint_source, 1u
+ %16:MyStruct = construct %14, %15
+ ret %16
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:MyStruct):MyStruct_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %18:u32 = access %tint_source_1, 0u
+ %19:u32 = access %tint_source_1, 1u
+ %20:MyStruct_tint_explicit_layout = construct %18, %19
+ ret %20
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedStruct_UsesViaLet) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", ty.ptr(function, structure));
+ auto* let_ptr = b.Let("let_ptr", buffer);
+ auto* load = b.Load(let_ptr);
+ b.Store(let_ptr, load);
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %let_ptr:ptr<storage, MyStruct, read_write> = let %buffer
+ %5:MyStruct = load %let_ptr
+ store %let_ptr, %5
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %let_ptr:ptr<storage, MyStruct_tint_explicit_layout, read_write> = let %buffer
+ %5:MyStruct_tint_explicit_layout = load %let_ptr
+ %6:MyStruct = call %tint_convert_explicit_layout, %5
+ %8:MyStruct_tint_explicit_layout = call %tint_convert_explicit_layout_1, %6
+ store %let_ptr, %8
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:MyStruct_tint_explicit_layout):MyStruct {
+ $B3: {
+ %11:u32 = access %tint_source, 0u
+ %12:u32 = access %tint_source, 1u
+ %13:MyStruct = construct %11, %12
+ ret %13
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:MyStruct):MyStruct_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %15:u32 = access %tint_source_1, 0u
+ %16:u32 = access %tint_source_1, 1u
+ %17:MyStruct_tint_explicit_layout = construct %15, %16
+ ret %17
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedStruct_AccessScalarMember) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, structure, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", ty.ptr<function>(structure));
+ auto* load = b.Load(b.Access(ty.ptr<storage, u32, read_write>(), buffer, 0_u));
+ b.Store(b.Access(ty.ptr<storage, u32, read_write>(), buffer, 1_u), load);
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %4:ptr<storage, u32, read_write> = access %buffer, 0u
+ %5:u32 = load %4
+ %6:ptr<storage, u32, read_write> = access %buffer, 1u
+ store %6, %5
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var undef
+ %4:ptr<storage, u32, read_write> = access %buffer, 0u
+ %5:u32 = load %4
+ %6:ptr<storage, u32, read_write> = access %buffer, 1u
+ store %6, %5
+ ret
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedStruct_AccessStructMember) {
+ auto* inner = ty.Struct(mod.symbols.New("Inner"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* outer = ty.Struct(mod.symbols.New("Outer"), {
+ {mod.symbols.New("a"), inner},
+ {mod.symbols.New("b"), inner},
+ });
+
+ auto* buffer = b.Var("buffer", ty.ptr(storage, outer, read_write));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", ty.ptr<function>(outer));
+ auto* load = b.Load(b.Access(ty.ptr(storage, inner, read_write), buffer, 0_u));
+ b.Store(b.Access(ty.ptr(storage, inner, read_write), buffer, 1_u), load);
+ b.Return(func);
+ });
+
+ auto* src = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Outer, read_write> = var undef
+ %4:ptr<storage, Inner, read_write> = access %buffer, 0u
+ %5:Inner = load %4
+ %6:ptr<storage, Inner, read_write> = access %buffer, 1u
+ store %6, %5
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Inner = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer = struct @align(4) {
+ a_1:Inner @offset(0)
+ b_1:Inner @offset(8)
+}
+
+Inner_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+Outer_tint_explicit_layout = struct @align(4) {
+ a_1:Inner_tint_explicit_layout @offset(0)
+ b_1:Inner_tint_explicit_layout @offset(8)
+}
+
+$B1: { # root
+ %buffer:ptr<storage, Outer_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, Outer, read_write> = var undef
+ %4:ptr<storage, Inner_tint_explicit_layout, read_write> = access %buffer, 0u
+ %5:Inner_tint_explicit_layout = load %4
+ %6:Inner = call %tint_convert_explicit_layout, %5
+ %8:ptr<storage, Inner_tint_explicit_layout, read_write> = access %buffer, 1u
+ %9:Inner_tint_explicit_layout = call %tint_convert_explicit_layout_1, %6
+ store %8, %9
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:Inner_tint_explicit_layout):Inner {
+ $B3: {
+ %12:u32 = access %tint_source, 0u
+ %13:u32 = access %tint_source, 1u
+ %14:Inner = construct %12, %13
+ ret %14
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:Inner):Inner_tint_explicit_layout { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %16:u32 = access %tint_source_1, 0u
+ %17:u32 = access %tint_source_1, 1u
+ %18:Inner_tint_explicit_layout = construct %16, %17
+ ret %18
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedStruct_MultipleVars) {
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* buffer_0 = b.Var("buffer_0", ty.ptr(storage, structure));
+ auto* buffer_1 = b.Var("buffer_1", ty.ptr(storage, structure));
+ auto* buffer_2 = b.Var("buffer_2", ty.ptr(storage, structure));
+ buffer_0->SetBindingPoint(0, 0);
+ buffer_1->SetBindingPoint(0, 1);
+ buffer_2->SetBindingPoint(0, 2);
+ mod.root_block->Append(buffer_0);
+ mod.root_block->Append(buffer_1);
+ mod.root_block->Append(buffer_2);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", b.Zero(structure));
+ b.Let("let_0", b.Load(buffer_0));
+ b.Let("let_1", b.Load(buffer_1));
+ b.Let("let_2", b.Load(buffer_2));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer_0:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 0)
+ %buffer_1:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 1)
+ %buffer_2:ptr<storage, MyStruct, read_write> = var undef @binding_point(0, 2)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var MyStruct(0u)
+ %6:MyStruct = load %buffer_0
+ %let_0:MyStruct = let %6
+ %8:MyStruct = load %buffer_1
+ %let_1:MyStruct = let %8
+ %10:MyStruct = load %buffer_2
+ %let_2:MyStruct = let %10
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+MyStruct_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+$B1: { # root
+ %buffer_0:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+ %buffer_1:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 1)
+ %buffer_2:ptr<storage, MyStruct_tint_explicit_layout, read_write> = var undef @binding_point(0, 2)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, MyStruct, read_write> = var MyStruct(0u)
+ %6:MyStruct_tint_explicit_layout = load %buffer_0
+ %7:MyStruct = call %tint_convert_explicit_layout, %6
+ %let_0:MyStruct = let %7
+ %10:MyStruct_tint_explicit_layout = load %buffer_1
+ %11:MyStruct = call %tint_convert_explicit_layout, %10
+ %let_1:MyStruct = let %11
+ %13:MyStruct_tint_explicit_layout = load %buffer_2
+ %14:MyStruct = call %tint_convert_explicit_layout, %13
+ %let_2:MyStruct = let %14
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:MyStruct_tint_explicit_layout):MyStruct {
+ $B3: {
+ %17:u32 = access %tint_source, 0u
+ %18:u32 = access %tint_source, 1u
+ %19:MyStruct = construct %17, %18
+ ret %19
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, MultipleSharedStructs) {
+ auto* s0 = ty.Struct(mod.symbols.New("S_0"), {
+ {mod.symbols.New("a"), ty.u32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+ auto* s1 = ty.Struct(mod.symbols.New("S_1"), {
+ {mod.symbols.New("c"), ty.f32()},
+ {mod.symbols.New("d"), ty.f32()},
+ });
+ auto* s2 = ty.Struct(mod.symbols.New("S_2"), {
+ {mod.symbols.New("e"), ty.i32()},
+ {mod.symbols.New("f"), ty.i32()},
+ });
+
+ auto* buffer_0 = b.Var("buffer_0", ty.ptr(storage, s0));
+ auto* buffer_1 = b.Var("buffer_1", ty.ptr(storage, s1));
+ auto* buffer_2 = b.Var("buffer_2", ty.ptr(storage, s2));
+ buffer_0->SetBindingPoint(0, 0);
+ buffer_1->SetBindingPoint(0, 1);
+ buffer_2->SetBindingPoint(0, 2);
+ mod.root_block->Append(buffer_0);
+ mod.root_block->Append(buffer_1);
+ mod.root_block->Append(buffer_2);
+
+ auto* func = b.Function("foo", ty.void_());
+ b.Append(func->Block(), [&] {
+ b.Var("local", b.Zero(s0));
+ b.Var("local", b.Zero(s1));
+ b.Var("local", b.Zero(s2));
+ b.Let("let_0", b.Load(buffer_0));
+ b.Let("let_1", b.Load(buffer_1));
+ b.Let("let_2", b.Load(buffer_2));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+S_0 = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+S_1 = struct @align(4) {
+ c:f32 @offset(0)
+ d:f32 @offset(4)
+}
+
+S_2 = struct @align(4) {
+ e:i32 @offset(0)
+ f:i32 @offset(4)
+}
+
+$B1: { # root
+ %buffer_0:ptr<storage, S_0, read_write> = var undef @binding_point(0, 0)
+ %buffer_1:ptr<storage, S_1, read_write> = var undef @binding_point(0, 1)
+ %buffer_2:ptr<storage, S_2, read_write> = var undef @binding_point(0, 2)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, S_0, read_write> = var S_0(0u)
+ %local_1:ptr<function, S_1, read_write> = var S_1(0.0f) # %local_1: 'local'
+ %local_2:ptr<function, S_2, read_write> = var S_2(0i) # %local_2: 'local'
+ %8:S_0 = load %buffer_0
+ %let_0:S_0 = let %8
+ %10:S_1 = load %buffer_1
+ %let_1:S_1 = let %10
+ %12:S_2 = load %buffer_2
+ %let_2:S_2 = let %12
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+S_0 = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+S_1 = struct @align(4) {
+ c:f32 @offset(0)
+ d:f32 @offset(4)
+}
+
+S_2 = struct @align(4) {
+ e:i32 @offset(0)
+ f:i32 @offset(4)
+}
+
+S_0_tint_explicit_layout = struct @align(4) {
+ a:u32 @offset(0)
+ b:u32 @offset(4)
+}
+
+S_1_tint_explicit_layout = struct @align(4) {
+ c:f32 @offset(0)
+ d:f32 @offset(4)
+}
+
+S_2_tint_explicit_layout = struct @align(4) {
+ e:i32 @offset(0)
+ f:i32 @offset(4)
+}
+
+$B1: { # root
+ %buffer_0:ptr<storage, S_0_tint_explicit_layout, read_write> = var undef @binding_point(0, 0)
+ %buffer_1:ptr<storage, S_1_tint_explicit_layout, read_write> = var undef @binding_point(0, 1)
+ %buffer_2:ptr<storage, S_2_tint_explicit_layout, read_write> = var undef @binding_point(0, 2)
+}
+
+%foo = func():void {
+ $B2: {
+ %local:ptr<function, S_0, read_write> = var S_0(0u)
+ %local_1:ptr<function, S_1, read_write> = var S_1(0.0f) # %local_1: 'local'
+ %local_2:ptr<function, S_2, read_write> = var S_2(0i) # %local_2: 'local'
+ %8:S_0_tint_explicit_layout = load %buffer_0
+ %9:S_0 = call %tint_convert_explicit_layout, %8
+ %let_0:S_0 = let %9
+ %12:S_1_tint_explicit_layout = load %buffer_1
+ %13:S_1 = call %tint_convert_explicit_layout_1, %12
+ %let_1:S_1 = let %13
+ %16:S_2_tint_explicit_layout = load %buffer_2
+ %17:S_2 = call %tint_convert_explicit_layout_2, %16
+ %let_2:S_2 = let %17
+ ret
+ }
+}
+%tint_convert_explicit_layout = func(%tint_source:S_0_tint_explicit_layout):S_0 {
+ $B3: {
+ %21:u32 = access %tint_source, 0u
+ %22:u32 = access %tint_source, 1u
+ %23:S_0 = construct %21, %22
+ ret %23
+ }
+}
+%tint_convert_explicit_layout_1 = func(%tint_source_1:S_1_tint_explicit_layout):S_1 { # %tint_convert_explicit_layout_1: 'tint_convert_explicit_layout', %tint_source_1: 'tint_source'
+ $B4: {
+ %25:f32 = access %tint_source_1, 0u
+ %26:f32 = access %tint_source_1, 1u
+ %27:S_1 = construct %25, %26
+ ret %27
+ }
+}
+%tint_convert_explicit_layout_2 = func(%tint_source_2:S_2_tint_explicit_layout):S_2 { # %tint_convert_explicit_layout_2: 'tint_convert_explicit_layout', %tint_source_2: 'tint_source'
+ $B5: {
+ %29:i32 = access %tint_source_2, 0u
+ %30:i32 = access %tint_source_2, 1u
+ %31:S_2 = construct %29, %30
+ ret %31
+ }
+}
+)";
+
+ Run(ForkExplicitLayoutTypes);
+
+ EXPECT_EQ(expect, str());
+}
+
+} // namespace
+} // namespace tint::spirv::writer::raise
diff --git a/src/tint/lang/spirv/writer/raise/raise.cc b/src/tint/lang/spirv/writer/raise/raise.cc
index 0ab8bd0..8a02bf2 100644
--- a/src/tint/lang/spirv/writer/raise/raise.cc
+++ b/src/tint/lang/spirv/writer/raise/raise.cc
@@ -52,6 +52,7 @@
#include "src/tint/lang/spirv/writer/common/option_helpers.h"
#include "src/tint/lang/spirv/writer/raise/builtin_polyfill.h"
#include "src/tint/lang/spirv/writer/raise/expand_implicit_splats.h"
+#include "src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h"
#include "src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h"
#include "src/tint/lang/spirv/writer/raise/merge_return.h"
#include "src/tint/lang/spirv/writer/raise/pass_matrix_by_pointer.h"
@@ -176,6 +177,11 @@
raise::ShaderIOConfig{push_constant_layout.Get(), options.emit_vertex_point_size,
!options.use_storage_input_output_16, options.depth_range_offsets});
RUN_TRANSFORM(core::ir::transform::Std140, module);
+
+ // ForkExplicitLayoutTypes must come after Std140, since it rewrites host-shareable array types
+ // to use the explicitly laid array type defined by the SPIR-V dialect.
+ RUN_TRANSFORM(raise::ForkExplicitLayoutTypes, module);
+
RUN_TRANSFORM(raise::VarForDynamicIndex, module);
return Success;
diff --git a/test/tint/bug/tint/366037039.wgsl.expected.spvasm b/test/tint/bug/tint/366037039.wgsl.expected.spvasm
index 98594dc..95b027d 100644
--- a/test/tint/bug/tint/366037039.wgsl.expected.spvasm
+++ b/test/tint/bug/tint/366037039.wgsl.expected.spvasm
@@ -1,20 +1,24 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 1
-; Bound: 66
+; Bound: 78
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
OpExecutionMode %unused_entry_point LocalSize 1 1 1
+ OpMemberName %S_tint_explicit_layout 0 "a"
+ OpMemberName %S_tint_explicit_layout 1 "b"
+ OpMemberName %S_tint_explicit_layout 2 "c"
+ OpName %S_tint_explicit_layout "S_tint_explicit_layout"
+ OpMemberName %ubuffer_block_tint_explicit_layout 0 "inner"
+ OpName %ubuffer_block_tint_explicit_layout "ubuffer_block_tint_explicit_layout"
+ OpMemberName %sbuffer_block_tint_explicit_layout 0 "inner"
+ OpName %sbuffer_block_tint_explicit_layout "sbuffer_block_tint_explicit_layout"
OpMemberName %S 0 "a"
OpMemberName %S 1 "b"
OpMemberName %S 2 "c"
OpName %S "S"
- OpMemberName %ubuffer_block 0 "inner"
- OpName %ubuffer_block "ubuffer_block"
- OpMemberName %sbuffer_block 0 "inner"
- OpName %sbuffer_block "sbuffer_block"
OpName %wbuffer "wbuffer"
OpName %foo "foo"
OpName %u "u"
@@ -25,17 +29,19 @@
OpName %tint_store_and_preserve_padding_0 "tint_store_and_preserve_padding"
OpName %value_param_0 "value_param"
OpName %unused_entry_point "unused_entry_point"
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
+ OpName %tint_convert_explicit_layout "tint_convert_explicit_layout"
+ OpName %tint_source "tint_source"
+ OpMemberDecorate %S_tint_explicit_layout 0 Offset 0
+ OpMemberDecorate %S_tint_explicit_layout 1 Offset 12
OpDecorate %_arr_v3uint_uint_4 ArrayStride 16
- OpMemberDecorate %S 2 Offset 16
- OpMemberDecorate %ubuffer_block 0 Offset 0
- OpDecorate %ubuffer_block Block
+ OpMemberDecorate %S_tint_explicit_layout 2 Offset 16
+ OpMemberDecorate %ubuffer_block_tint_explicit_layout 0 Offset 0
+ OpDecorate %ubuffer_block_tint_explicit_layout Block
OpDecorate %1 DescriptorSet 0
OpDecorate %1 Binding 0
OpDecorate %1 NonWritable
- OpMemberDecorate %sbuffer_block 0 Offset 0
- OpDecorate %sbuffer_block Block
+ OpMemberDecorate %sbuffer_block_tint_explicit_layout 0 Offset 0
+ OpDecorate %sbuffer_block_tint_explicit_layout Block
OpDecorate %9 DescriptorSet 0
OpDecorate %9 Binding 1
OpDecorate %9 Coherent
@@ -43,86 +49,100 @@
%v3uint = OpTypeVector %uint 3
%uint_4 = OpConstant %uint 4
%_arr_v3uint_uint_4 = OpTypeArray %v3uint %uint_4
+%S_tint_explicit_layout = OpTypeStruct %v3uint %uint %_arr_v3uint_uint_4
+%ubuffer_block_tint_explicit_layout = OpTypeStruct %S_tint_explicit_layout
+%_ptr_Uniform_ubuffer_block_tint_explicit_layout = OpTypePointer Uniform %ubuffer_block_tint_explicit_layout
+ %1 = OpVariable %_ptr_Uniform_ubuffer_block_tint_explicit_layout Uniform
+%sbuffer_block_tint_explicit_layout = OpTypeStruct %S_tint_explicit_layout
+%_ptr_StorageBuffer_sbuffer_block_tint_explicit_layout = OpTypePointer StorageBuffer %sbuffer_block_tint_explicit_layout
+ %9 = OpVariable %_ptr_StorageBuffer_sbuffer_block_tint_explicit_layout StorageBuffer
%S = OpTypeStruct %v3uint %uint %_arr_v3uint_uint_4
-%ubuffer_block = OpTypeStruct %S
-%_ptr_Uniform_ubuffer_block = OpTypePointer Uniform %ubuffer_block
- %1 = OpVariable %_ptr_Uniform_ubuffer_block Uniform
-%sbuffer_block = OpTypeStruct %S
-%_ptr_StorageBuffer_sbuffer_block = OpTypePointer StorageBuffer %sbuffer_block
- %9 = OpVariable %_ptr_StorageBuffer_sbuffer_block StorageBuffer
%_ptr_Workgroup_S = OpTypePointer Workgroup %S
%wbuffer = OpVariable %_ptr_Workgroup_S Workgroup
%void = OpTypeVoid
- %16 = OpTypeFunction %void
-%_ptr_Uniform_S = OpTypePointer Uniform %S
+ %17 = OpTypeFunction %void
+%_ptr_Uniform_S_tint_explicit_layout = OpTypePointer Uniform %S_tint_explicit_layout
%uint_0 = OpConstant %uint 0
-%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
- %29 = OpConstantNull %S
- %31 = OpTypeFunction %void %S
+%_ptr_StorageBuffer_S_tint_explicit_layout = OpTypePointer StorageBuffer %S_tint_explicit_layout
+ %34 = OpConstantNull %S
+ %36 = OpTypeFunction %void %S
%_ptr_StorageBuffer_v3uint = OpTypePointer StorageBuffer %v3uint
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%uint_1 = OpConstant %uint 1
- %44 = OpTypeFunction %void %_arr_v3uint_uint_4
+ %49 = OpTypeFunction %void %_arr_v3uint_uint_4
%_ptr_Function__arr_v3uint_uint_4 = OpTypePointer Function %_arr_v3uint_uint_4
%bool = OpTypeBool
%uint_2 = OpConstant %uint 2
%_ptr_Function_v3uint = OpTypePointer Function %v3uint
- %foo = OpFunction %void None %16
- %17 = OpLabel
- %18 = OpAccessChain %_ptr_Uniform_S %1 %uint_0
- %u = OpLoad %S %18 None
- %22 = OpAccessChain %_ptr_StorageBuffer_S %9 %uint_0
- %s = OpLoad %S %22 None
- %25 = OpAccessChain %_ptr_StorageBuffer_S %9 %uint_0
- %w = OpLoad %S %25 None
- %27 = OpFunctionCall %void %tint_store_and_preserve_padding %29
- OpStore %wbuffer %29 None
+ %72 = OpTypeFunction %S %S_tint_explicit_layout
+ %foo = OpFunction %void None %17
+ %18 = OpLabel
+ %19 = OpAccessChain %_ptr_Uniform_S_tint_explicit_layout %1 %uint_0
+ %22 = OpLoad %S_tint_explicit_layout %19 None
+ %u = OpFunctionCall %S %tint_convert_explicit_layout %22
+ %25 = OpAccessChain %_ptr_StorageBuffer_S_tint_explicit_layout %9 %uint_0
+ %27 = OpLoad %S_tint_explicit_layout %25 None
+ %s = OpFunctionCall %S %tint_convert_explicit_layout %27
+ %29 = OpAccessChain %_ptr_StorageBuffer_S_tint_explicit_layout %9 %uint_0
+ %30 = OpLoad %S_tint_explicit_layout %29 None
+ %w = OpFunctionCall %S %tint_convert_explicit_layout %30
+ %32 = OpFunctionCall %void %tint_store_and_preserve_padding %34
+ OpStore %wbuffer %34 None
OpReturn
OpFunctionEnd
-%tint_store_and_preserve_padding = OpFunction %void None %31
+%tint_store_and_preserve_padding = OpFunction %void None %36
%value_param = OpFunctionParameter %S
- %32 = OpLabel
- %33 = OpAccessChain %_ptr_StorageBuffer_v3uint %9 %uint_0 %uint_0
- %35 = OpCompositeExtract %v3uint %value_param 0
- OpStore %33 %35 None
- %36 = OpAccessChain %_ptr_StorageBuffer_uint %9 %uint_0 %uint_1
- %39 = OpCompositeExtract %uint %value_param 1
- OpStore %36 %39 None
- %40 = OpCompositeExtract %_arr_v3uint_uint_4 %value_param 2
- %41 = OpFunctionCall %void %tint_store_and_preserve_padding_0 %40
+ %37 = OpLabel
+ %38 = OpAccessChain %_ptr_StorageBuffer_v3uint %9 %uint_0 %uint_0
+ %40 = OpCompositeExtract %v3uint %value_param 0
+ OpStore %38 %40 None
+ %41 = OpAccessChain %_ptr_StorageBuffer_uint %9 %uint_0 %uint_1
+ %44 = OpCompositeExtract %uint %value_param 1
+ OpStore %41 %44 None
+ %45 = OpCompositeExtract %_arr_v3uint_uint_4 %value_param 2
+ %46 = OpFunctionCall %void %tint_store_and_preserve_padding_0 %45
OpReturn
OpFunctionEnd
-%tint_store_and_preserve_padding_0 = OpFunction %void None %44
+%tint_store_and_preserve_padding_0 = OpFunction %void None %49
%value_param_0 = OpFunctionParameter %_arr_v3uint_uint_4
- %45 = OpLabel
- %46 = OpVariable %_ptr_Function__arr_v3uint_uint_4 Function
- OpStore %46 %value_param_0
- OpBranch %48
- %48 = OpLabel
- OpBranch %51
- %51 = OpLabel
- %53 = OpPhi %uint %uint_0 %48 %54 %50
- OpLoopMerge %52 %50 None
- OpBranch %49
- %49 = OpLabel
- %55 = OpUGreaterThanEqual %bool %53 %uint_4
- OpSelectionMerge %57 None
- OpBranchConditional %55 %58 %57
- %58 = OpLabel
- OpBranch %52
- %57 = OpLabel
- %59 = OpAccessChain %_ptr_StorageBuffer_v3uint %9 %uint_0 %uint_2 %53
- %61 = OpAccessChain %_ptr_Function_v3uint %46 %53
- %63 = OpLoad %v3uint %61 None
- OpStore %59 %63 None
- OpBranch %50
%50 = OpLabel
- %54 = OpIAdd %uint %53 %uint_1
- OpBranch %51
- %52 = OpLabel
+ %51 = OpVariable %_ptr_Function__arr_v3uint_uint_4 Function
+ OpStore %51 %value_param_0
+ OpBranch %53
+ %53 = OpLabel
+ OpBranch %56
+ %56 = OpLabel
+ %58 = OpPhi %uint %uint_0 %53 %59 %55
+ OpLoopMerge %57 %55 None
+ OpBranch %54
+ %54 = OpLabel
+ %60 = OpUGreaterThanEqual %bool %58 %uint_4
+ OpSelectionMerge %62 None
+ OpBranchConditional %60 %63 %62
+ %63 = OpLabel
+ OpBranch %57
+ %62 = OpLabel
+ %64 = OpAccessChain %_ptr_StorageBuffer_v3uint %9 %uint_0 %uint_2 %58
+ %66 = OpAccessChain %_ptr_Function_v3uint %51 %58
+ %68 = OpLoad %v3uint %66 None
+ OpStore %64 %68 None
+ OpBranch %55
+ %55 = OpLabel
+ %59 = OpIAdd %uint %58 %uint_1
+ OpBranch %56
+ %57 = OpLabel
OpReturn
OpFunctionEnd
-%unused_entry_point = OpFunction %void None %16
- %65 = OpLabel
+%unused_entry_point = OpFunction %void None %17
+ %70 = OpLabel
OpReturn
OpFunctionEnd
+%tint_convert_explicit_layout = OpFunction %S None %72
+%tint_source = OpFunctionParameter %S_tint_explicit_layout
+ %73 = OpLabel
+ %74 = OpCompositeExtract %v3uint %tint_source 0
+ %75 = OpCompositeExtract %uint %tint_source 1
+ %76 = OpCompositeExtract %_arr_v3uint_uint_4 %tint_source 2
+ %77 = OpCompositeConstruct %S %74 %75 %76
+ OpReturnValue %77
+ OpFunctionEnd
diff --git a/test/tint/bug/tint/366314931.wgsl.expected.spvasm b/test/tint/bug/tint/366314931.wgsl.expected.spvasm
index a358856..67e9d5e 100644
--- a/test/tint/bug/tint/366314931.wgsl.expected.spvasm
+++ b/test/tint/bug/tint/366314931.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 1
-; Bound: 41
+; Bound: 42
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
@@ -11,17 +11,20 @@
OpMemberName %S 1 "u"
OpName %S "S"
OpName %wgvar "wgvar"
- OpMemberName %output_block 0 "inner"
- OpName %output_block "output_block"
+ OpMemberName %S_tint_explicit_layout 0 "v"
+ OpMemberName %S_tint_explicit_layout 1 "u"
+ OpName %S_tint_explicit_layout "S_tint_explicit_layout"
+ OpMemberName %output_block_tint_explicit_layout 0 "inner"
+ OpName %output_block_tint_explicit_layout "output_block_tint_explicit_layout"
OpName %main_local_invocation_index_Input "main_local_invocation_index_Input"
OpName %main_inner "main_inner"
OpName %tint_local_index "tint_local_index"
OpName %x "x"
OpName %main "main"
- OpMemberDecorate %S 0 Offset 0
- OpMemberDecorate %S 1 Offset 12
- OpMemberDecorate %output_block 0 Offset 0
- OpDecorate %output_block Block
+ OpMemberDecorate %S_tint_explicit_layout 0 Offset 0
+ OpMemberDecorate %S_tint_explicit_layout 1 Offset 12
+ OpMemberDecorate %output_block_tint_explicit_layout 0 Offset 0
+ OpDecorate %output_block_tint_explicit_layout Block
OpDecorate %6 DescriptorSet 0
OpDecorate %6 Binding 0
OpDecorate %6 Coherent
@@ -31,46 +34,47 @@
%S = OpTypeStruct %v3uint %uint
%_ptr_Workgroup_S = OpTypePointer Workgroup %S
%wgvar = OpVariable %_ptr_Workgroup_S Workgroup
-%output_block = OpTypeStruct %S
-%_ptr_StorageBuffer_output_block = OpTypePointer StorageBuffer %output_block
- %6 = OpVariable %_ptr_StorageBuffer_output_block StorageBuffer
+%S_tint_explicit_layout = OpTypeStruct %v3uint %uint
+%output_block_tint_explicit_layout = OpTypeStruct %S_tint_explicit_layout
+%_ptr_StorageBuffer_output_block_tint_explicit_layout = OpTypePointer StorageBuffer %output_block_tint_explicit_layout
+ %6 = OpVariable %_ptr_StorageBuffer_output_block_tint_explicit_layout StorageBuffer
%_ptr_Input_uint = OpTypePointer Input %uint
%main_local_invocation_index_Input = OpVariable %_ptr_Input_uint Input
%void = OpTypeVoid
- %14 = OpTypeFunction %void %uint
+ %15 = OpTypeFunction %void %uint
%uint_1 = OpConstant %uint 1
%bool = OpTypeBool
%_ptr_Workgroup_v3uint = OpTypePointer Workgroup %v3uint
%uint_0 = OpConstant %uint 0
- %24 = OpConstantNull %v3uint
+ %25 = OpConstantNull %v3uint
%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
%uint_2 = OpConstant %uint 2
%uint_264 = OpConstant %uint 264
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
- %37 = OpTypeFunction %void
- %main_inner = OpFunction %void None %14
+ %38 = OpTypeFunction %void
+ %main_inner = OpFunction %void None %15
%tint_local_index = OpFunctionParameter %uint
- %15 = OpLabel
- %16 = OpULessThan %bool %tint_local_index %uint_1
- OpSelectionMerge %19 None
- OpBranchConditional %16 %20 %19
+ %16 = OpLabel
+ %17 = OpULessThan %bool %tint_local_index %uint_1
+ OpSelectionMerge %20 None
+ OpBranchConditional %17 %21 %20
+ %21 = OpLabel
+ %22 = OpAccessChain %_ptr_Workgroup_v3uint %wgvar %uint_0
+ OpStore %22 %25 None
+ %26 = OpAccessChain %_ptr_Workgroup_uint %wgvar %uint_1
+ OpAtomicStore %26 %uint_2 %uint_0 %uint_0
+ OpBranch %20
%20 = OpLabel
- %21 = OpAccessChain %_ptr_Workgroup_v3uint %wgvar %uint_0
- OpStore %21 %24 None
- %25 = OpAccessChain %_ptr_Workgroup_uint %wgvar %uint_1
- OpAtomicStore %25 %uint_2 %uint_0 %uint_0
- OpBranch %19
- %19 = OpLabel
OpControlBarrier %uint_2 %uint_2 %uint_264
- %31 = OpAccessChain %_ptr_Workgroup_uint %wgvar %uint_1
- %x = OpAtomicLoad %uint %31 %uint_2 %uint_0
- %33 = OpAccessChain %_ptr_StorageBuffer_uint %6 %uint_0 %uint_1
- OpAtomicStore %33 %uint_1 %uint_0 %x
+ %32 = OpAccessChain %_ptr_Workgroup_uint %wgvar %uint_1
+ %x = OpAtomicLoad %uint %32 %uint_2 %uint_0
+ %34 = OpAccessChain %_ptr_StorageBuffer_uint %6 %uint_0 %uint_1
+ OpAtomicStore %34 %uint_1 %uint_0 %x
OpReturn
OpFunctionEnd
- %main = OpFunction %void None %37
- %38 = OpLabel
- %39 = OpLoad %uint %main_local_invocation_index_Input None
- %40 = OpFunctionCall %void %main_inner %39
+ %main = OpFunction %void None %38
+ %39 = OpLabel
+ %40 = OpLoad %uint %main_local_invocation_index_Input None
+ %41 = OpFunctionCall %void %main_inner %40
OpReturn
OpFunctionEnd
diff --git a/test/tint/bug/tint/922.wgsl.expected.spvasm b/test/tint/bug/tint/922.wgsl.expected.spvasm
index a733272..6f9ae2e 100644
--- a/test/tint/bug/tint/922.wgsl.expected.spvasm
+++ b/test/tint/bug/tint/922.wgsl.expected.spvasm
@@ -1,21 +1,21 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 1
-; Bound: 247
+; Bound: 258
; Schema: 0
OpCapability Shader
- %159 = OpExtInstImport "GLSL.std.450"
+ %160 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %main_loc0_Input %main_loc1_Input %main_loc2_Input %main_loc3_Input %main_loc4_Input %main_loc0_Output %main_loc1_Output %main_position_Output %main___point_size_Output
- OpMemberName %Mat4x4_ 0 "mx"
- OpMemberName %Mat4x4_ 1 "my"
- OpMemberName %Mat4x4_ 2 "mz"
- OpMemberName %Mat4x4_ 3 "mw"
- OpName %Mat4x4_ "Mat4x4_"
- OpMemberName %ub_SceneParams 0 "u_Projection"
- OpName %ub_SceneParams "ub_SceneParams"
- OpMemberName %global_block 0 "inner"
- OpName %global_block "global_block"
+ OpMemberName %Mat4x4__tint_explicit_layout 0 "mx"
+ OpMemberName %Mat4x4__tint_explicit_layout 1 "my"
+ OpMemberName %Mat4x4__tint_explicit_layout 2 "mz"
+ OpMemberName %Mat4x4__tint_explicit_layout 3 "mw"
+ OpName %Mat4x4__tint_explicit_layout "Mat4x4__tint_explicit_layout"
+ OpMemberName %ub_SceneParams_tint_explicit_layout 0 "u_Projection"
+ OpName %ub_SceneParams_tint_explicit_layout "ub_SceneParams_tint_explicit_layout"
+ OpMemberName %global_block_tint_explicit_layout 0 "inner"
+ OpName %global_block_tint_explicit_layout "global_block_tint_explicit_layout"
OpMemberName %Mat4x2_ 0 "mx"
OpMemberName %Mat4x2_ 1 "my"
OpName %Mat4x2_ "Mat4x2_"
@@ -50,6 +50,11 @@
OpName %main_position_Output "main_position_Output"
OpName %main___point_size_Output "main___point_size_Output"
OpName %Mul "Mul"
+ OpMemberName %Mat4x4_ 0 "mx"
+ OpMemberName %Mat4x4_ 1 "my"
+ OpMemberName %Mat4x4_ 2 "mz"
+ OpMemberName %Mat4x4_ 3 "mw"
+ OpName %Mat4x4_ "Mat4x4_"
OpName %m8 "m8"
OpName %v "v"
OpName %m9 "m9"
@@ -136,13 +141,15 @@
OpName %tint_f32_to_i32 "tint_f32_to_i32"
OpName %value "value"
OpName %main "main"
- OpMemberDecorate %Mat4x4_ 0 Offset 0
- OpMemberDecorate %Mat4x4_ 1 Offset 16
- OpMemberDecorate %Mat4x4_ 2 Offset 32
- OpMemberDecorate %Mat4x4_ 3 Offset 48
- OpMemberDecorate %ub_SceneParams 0 Offset 0
- OpMemberDecorate %global_block 0 Offset 0
- OpDecorate %global_block Block
+ OpName %tint_convert_explicit_layout "tint_convert_explicit_layout"
+ OpName %tint_source "tint_source"
+ OpMemberDecorate %Mat4x4__tint_explicit_layout 0 Offset 0
+ OpMemberDecorate %Mat4x4__tint_explicit_layout 1 Offset 16
+ OpMemberDecorate %Mat4x4__tint_explicit_layout 2 Offset 32
+ OpMemberDecorate %Mat4x4__tint_explicit_layout 3 Offset 48
+ OpMemberDecorate %ub_SceneParams_tint_explicit_layout 0 Offset 0
+ OpMemberDecorate %global_block_tint_explicit_layout 0 Offset 0
+ OpDecorate %global_block_tint_explicit_layout Block
OpDecorate %1 DescriptorSet 0
OpDecorate %1 Binding 0
OpDecorate %1 NonWritable
@@ -177,11 +184,11 @@
OpDecorate %main___point_size_Output BuiltIn PointSize
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
- %Mat4x4_ = OpTypeStruct %v4float %v4float %v4float %v4float
-%ub_SceneParams = OpTypeStruct %Mat4x4_
-%global_block = OpTypeStruct %ub_SceneParams
-%_ptr_Uniform_global_block = OpTypePointer Uniform %global_block
- %1 = OpVariable %_ptr_Uniform_global_block Uniform
+%Mat4x4__tint_explicit_layout = OpTypeStruct %v4float %v4float %v4float %v4float
+%ub_SceneParams_tint_explicit_layout = OpTypeStruct %Mat4x4__tint_explicit_layout
+%global_block_tint_explicit_layout = OpTypeStruct %ub_SceneParams_tint_explicit_layout
+%_ptr_Uniform_global_block_tint_explicit_layout = OpTypePointer Uniform %global_block_tint_explicit_layout
+ %1 = OpVariable %_ptr_Uniform_global_block_tint_explicit_layout Uniform
%Mat4x2_ = OpTypeStruct %v4float %v4float
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
@@ -231,46 +238,48 @@
%main_position_Output = OpVariable %_ptr_Output_v4float Output
%_ptr_Output_float = OpTypePointer Output %float
%main___point_size_Output = OpVariable %_ptr_Output_float Output
- %60 = OpTypeFunction %v4float %Mat4x4_ %v4float
+ %Mat4x4_ = OpTypeStruct %v4float %v4float %v4float %v4float
+ %61 = OpTypeFunction %v4float %Mat4x4_ %v4float
%_ptr_Function_Mat4x4_ = OpTypePointer Function %Mat4x4_
- %64 = OpConstantNull %Mat4x4_
+ %65 = OpConstantNull %Mat4x4_
%_ptr_Function_v4float = OpTypePointer Function %v4float
- %87 = OpTypeFunction %v2float %Mat4x2_ %v4float
+ %88 = OpTypeFunction %v2float %Mat4x2_ %v4float
%_ptr_Function_Mat4x2_ = OpTypePointer Function %Mat4x2_
- %91 = OpConstantNull %Mat4x2_
- %104 = OpTypeFunction %Mat4x4_ %float
+ %92 = OpConstantNull %Mat4x2_
+ %105 = OpTypeFunction %Mat4x4_ %float
%_ptr_Function_float = OpTypePointer Function %float
%uint_0 = OpConstant %uint 0
%float_0 = OpConstant %float 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
- %128 = OpTypeFunction %Mat4x4_ %Mat4x3_
+ %129 = OpTypeFunction %Mat4x4_ %Mat4x3_
%_ptr_Function_Mat4x3_ = OpTypePointer Function %Mat4x3_
- %132 = OpConstantNull %Mat4x3_
+ %133 = OpConstantNull %Mat4x3_
%float_1 = OpConstant %float 1
%void = OpTypeVoid
- %148 = OpTypeFunction %void
+ %149 = OpTypeFunction %void
%_ptr_Function_v2float = OpTypePointer Function %v2float
%int = OpTypeInt 32 1
%uint_31 = OpConstant %uint 31
%_ptr_Uniform_Mat4x3_ = OpTypePointer Uniform %Mat4x3_
-%_ptr_Uniform_Mat4x4_ = OpTypePointer Uniform %Mat4x4_
+%_ptr_Uniform_Mat4x4__tint_explicit_layout = OpTypePointer Uniform %Mat4x4__tint_explicit_layout
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%float_2 = OpConstant %float 2
%bool = OpTypeBool
%_ptr_Uniform_Mat4x2_ = OpTypePointer Uniform %Mat4x2_
%VertexOutput = OpTypeStruct %v4float %v2float %v4float
- %217 = OpTypeFunction %VertexOutput %v3float %v2float %v4float %v3float %float
- %225 = OpTypeFunction %int %float
+ %220 = OpTypeFunction %VertexOutput %v3float %v2float %v4float %v3float %float
+ %228 = OpTypeFunction %int %float
%float_n2_14748365e_09 = OpConstant %float -2.14748365e+09
%int_n2147483648 = OpConstant %int -2147483648
%float_2_14748352e_09 = OpConstant %float 2.14748352e+09
%int_2147483647 = OpConstant %int 2147483647
- %Mul = OpFunction %v4float None %60
+ %251 = OpTypeFunction %Mat4x4_ %Mat4x4__tint_explicit_layout
+ %Mul = OpFunction %v4float None %61
%m8 = OpFunctionParameter %Mat4x4_
%v = OpFunctionParameter %v4float
- %61 = OpLabel
- %m9 = OpVariable %_ptr_Function_Mat4x4_ Function %64
+ %62 = OpLabel
+ %m9 = OpVariable %_ptr_Function_Mat4x4_ Function %65
%v1 = OpVariable %_ptr_Function_v4float Function %33
OpStore %m9 %m8 None
OpStore %v1 %v None
@@ -282,22 +291,22 @@
%x_e14 = OpLoad %v4float %v1 None
%x_e16 = OpLoad %Mat4x4_ %m9 None
%x_e18 = OpLoad %v4float %v1 None
- %75 = OpCompositeExtract %v4float %x_e4 0
- %76 = OpDot %float %75 %x_e6
- %77 = OpCompositeExtract %v4float %x_e8 1
- %78 = OpDot %float %77 %x_e10
- %79 = OpCompositeExtract %v4float %x_e12 2
- %80 = OpDot %float %79 %x_e14
- %81 = OpCompositeExtract %v4float %x_e16 3
- %82 = OpDot %float %81 %x_e18
- %83 = OpCompositeConstruct %v4float %76 %78 %80 %82
- OpReturnValue %83
+ %76 = OpCompositeExtract %v4float %x_e4 0
+ %77 = OpDot %float %76 %x_e6
+ %78 = OpCompositeExtract %v4float %x_e8 1
+ %79 = OpDot %float %78 %x_e10
+ %80 = OpCompositeExtract %v4float %x_e12 2
+ %81 = OpDot %float %80 %x_e14
+ %82 = OpCompositeExtract %v4float %x_e16 3
+ %83 = OpDot %float %82 %x_e18
+ %84 = OpCompositeConstruct %v4float %77 %79 %81 %83
+ OpReturnValue %84
OpFunctionEnd
- %Mul2 = OpFunction %v2float None %87
+ %Mul2 = OpFunction %v2float None %88
%m12 = OpFunctionParameter %Mat4x2_
%v4 = OpFunctionParameter %v4float
- %88 = OpLabel
- %m13 = OpVariable %_ptr_Function_Mat4x2_ Function %91
+ %89 = OpLabel
+ %m13 = OpVariable %_ptr_Function_Mat4x2_ Function %92
%v5 = OpVariable %_ptr_Function_v4float Function %33
OpStore %m13 %m12 None
OpStore %v5 %v4 None
@@ -305,71 +314,71 @@
%x_e6_0 = OpLoad %v4float %v5 None
%x_e8_0 = OpLoad %Mat4x2_ %m13 None
%x_e10_0 = OpLoad %v4float %v5 None
- %97 = OpCompositeExtract %v4float %x_e4_0 0
- %98 = OpDot %float %97 %x_e6_0
- %99 = OpCompositeExtract %v4float %x_e8_0 1
- %100 = OpDot %float %99 %x_e10_0
- %101 = OpCompositeConstruct %v2float %98 %100
- OpReturnValue %101
+ %98 = OpCompositeExtract %v4float %x_e4_0 0
+ %99 = OpDot %float %98 %x_e6_0
+ %100 = OpCompositeExtract %v4float %x_e8_0 1
+ %101 = OpDot %float %100 %x_e10_0
+ %102 = OpCompositeConstruct %v2float %99 %101
+ OpReturnValue %102
OpFunctionEnd
- %x_Mat4x4_ = OpFunction %Mat4x4_ None %104
+ %x_Mat4x4_ = OpFunction %Mat4x4_ None %105
%n = OpFunctionParameter %float
- %105 = OpLabel
+ %106 = OpLabel
%n1 = OpVariable %_ptr_Function_float Function %37
- %o = OpVariable %_ptr_Function_Mat4x4_ Function %64
+ %o = OpVariable %_ptr_Function_Mat4x4_ Function %65
OpStore %n1 %n None
%x_e4_1 = OpLoad %float %n1 None
- %110 = OpAccessChain %_ptr_Function_v4float %o %uint_0
- %112 = OpCompositeConstruct %v4float %x_e4_1 %float_0 %float_0 %float_0
- OpStore %110 %112 None
+ %111 = OpAccessChain %_ptr_Function_v4float %o %uint_0
+ %113 = OpCompositeConstruct %v4float %x_e4_1 %float_0 %float_0 %float_0
+ OpStore %111 %113 None
%x_e11 = OpLoad %float %n1 None
- %115 = OpAccessChain %_ptr_Function_v4float %o %uint_1
- %116 = OpCompositeConstruct %v4float %float_0 %x_e11 %float_0 %float_0
- OpStore %115 %116 None
+ %116 = OpAccessChain %_ptr_Function_v4float %o %uint_1
+ %117 = OpCompositeConstruct %v4float %float_0 %x_e11 %float_0 %float_0
+ OpStore %116 %117 None
%x_e18_0 = OpLoad %float %n1 None
- %118 = OpAccessChain %_ptr_Function_v4float %o %uint_2
- %120 = OpCompositeConstruct %v4float %float_0 %float_0 %x_e18_0 %float_0
- OpStore %118 %120 None
+ %119 = OpAccessChain %_ptr_Function_v4float %o %uint_2
+ %121 = OpCompositeConstruct %v4float %float_0 %float_0 %x_e18_0 %float_0
+ OpStore %119 %121 None
%x_e25 = OpLoad %float %n1 None
- %122 = OpAccessChain %_ptr_Function_v4float %o %uint_3
- %124 = OpCompositeConstruct %v4float %float_0 %float_0 %float_0 %x_e25
- OpStore %122 %124 None
+ %123 = OpAccessChain %_ptr_Function_v4float %o %uint_3
+ %125 = OpCompositeConstruct %v4float %float_0 %float_0 %float_0 %x_e25
+ OpStore %123 %125 None
%x_e27 = OpLoad %Mat4x4_ %o None
OpReturnValue %x_e27
OpFunctionEnd
- %x_Mat4x4_1 = OpFunction %Mat4x4_ None %128
+ %x_Mat4x4_1 = OpFunction %Mat4x4_ None %129
%m16 = OpFunctionParameter %Mat4x3_
- %129 = OpLabel
- %m17 = OpVariable %_ptr_Function_Mat4x3_ Function %132
- %o1 = OpVariable %_ptr_Function_Mat4x4_ Function %64
+ %130 = OpLabel
+ %m17 = OpVariable %_ptr_Function_Mat4x3_ Function %133
+ %o1 = OpVariable %_ptr_Function_Mat4x4_ Function %65
OpStore %m17 %m16 None
%x_e4_2 = OpFunctionCall %Mat4x4_ %x_Mat4x4_ %float_1
OpStore %o1 %x_e4_2 None
%x_e7 = OpLoad %Mat4x3_ %m17 None
- %137 = OpAccessChain %_ptr_Function_v4float %o1 %uint_0
- %138 = OpCompositeExtract %v4float %x_e7 0
- OpStore %137 %138 None
+ %138 = OpAccessChain %_ptr_Function_v4float %o1 %uint_0
+ %139 = OpCompositeExtract %v4float %x_e7 0
+ OpStore %138 %139 None
%x_e10_1 = OpLoad %Mat4x3_ %m17 None
- %140 = OpAccessChain %_ptr_Function_v4float %o1 %uint_1
- %141 = OpCompositeExtract %v4float %x_e10_1 1
- OpStore %140 %141 None
+ %141 = OpAccessChain %_ptr_Function_v4float %o1 %uint_1
+ %142 = OpCompositeExtract %v4float %x_e10_1 1
+ OpStore %141 %142 None
%x_e13 = OpLoad %Mat4x3_ %m17 None
- %143 = OpAccessChain %_ptr_Function_v4float %o1 %uint_2
- %144 = OpCompositeExtract %v4float %x_e13 2
- OpStore %143 %144 None
+ %144 = OpAccessChain %_ptr_Function_v4float %o1 %uint_2
+ %145 = OpCompositeExtract %v4float %x_e13 2
+ OpStore %144 %145 None
%x_e15 = OpLoad %Mat4x4_ %o1 None
OpReturnValue %x_e15
OpFunctionEnd
- %main1 = OpFunction %void None %148
- %149 = OpLabel
- %t_PosMtx = OpVariable %_ptr_Function_Mat4x3_ Function %132
+ %main1 = OpFunction %void None %149
+ %150 = OpLabel
+ %t_PosMtx = OpVariable %_ptr_Function_Mat4x3_ Function %133
%t_TexSpaceCoord = OpVariable %_ptr_Function_v2float Function %30
%x_e15_0 = OpLoad %float %a_PosMtxIdx1 None
- %154 = OpFunctionCall %int %tint_f32_to_i32 %x_e15_0
- %157 = OpBitcast %uint %154
- %158 = OpExtInst %uint %159 UMin %157 %uint_31
- %161 = OpAccessChain %_ptr_Uniform_Mat4x3_ %16 %uint_0 %uint_0 %158
- %x_e18_1 = OpLoad %Mat4x3_ %161 None
+ %155 = OpFunctionCall %int %tint_f32_to_i32 %x_e15_0
+ %158 = OpBitcast %uint %155
+ %159 = OpExtInst %uint %160 UMin %158 %uint_31
+ %162 = OpAccessChain %_ptr_Uniform_Mat4x3_ %16 %uint_0 %uint_0 %159
+ %x_e18_1 = OpLoad %Mat4x3_ %162 None
OpStore %t_PosMtx %x_e18_1 None
%x_e23 = OpLoad %Mat4x3_ %t_PosMtx None
%x_e24 = OpFunctionCall %Mat4x4_ %x_Mat4x4_1 %x_e23
@@ -377,94 +386,105 @@
%x_e29 = OpLoad %Mat4x3_ %t_PosMtx None
%x_e30 = OpFunctionCall %Mat4x4_ %x_Mat4x4_1 %x_e29
%x_e31 = OpLoad %v3float %a_Position1 None
- %170 = OpCompositeConstruct %v4float %x_e31 %float_1
- %x_e34 = OpFunctionCall %v4float %Mul %x_e30 %170
- %172 = OpAccessChain %_ptr_Uniform_Mat4x4_ %1 %uint_0 %uint_0
- %x_e35 = OpLoad %Mat4x4_ %172 None
+ %171 = OpCompositeConstruct %v4float %x_e31 %float_1
+ %x_e34 = OpFunctionCall %v4float %Mul %x_e30 %171
+ %173 = OpAccessChain %_ptr_Uniform_Mat4x4__tint_explicit_layout %1 %uint_0 %uint_0
+ %175 = OpLoad %Mat4x4__tint_explicit_layout %173 None
+ %x_e35 = OpFunctionCall %Mat4x4_ %tint_convert_explicit_layout %175
%x_e37 = OpLoad %Mat4x3_ %t_PosMtx None
%x_e38 = OpFunctionCall %Mat4x4_ %x_Mat4x4_1 %x_e37
%x_e39 = OpLoad %v3float %a_Position1 None
%x_e43 = OpLoad %Mat4x3_ %t_PosMtx None
%x_e44 = OpFunctionCall %Mat4x4_ %x_Mat4x4_1 %x_e43
%x_e45 = OpLoad %v3float %a_Position1 None
- %181 = OpCompositeConstruct %v4float %x_e45 %float_1
- %x_e48 = OpFunctionCall %v4float %Mul %x_e44 %181
+ %184 = OpCompositeConstruct %v4float %x_e45 %float_1
+ %x_e48 = OpFunctionCall %v4float %Mul %x_e44 %184
%x_e49 = OpFunctionCall %v4float %Mul %x_e35 %x_e48
OpStore %gl_Position %x_e49 None
%x_e50 = OpLoad %v4float %a_Color1 None
OpStore %v_Color %x_e50 None
- %185 = OpAccessChain %_ptr_Uniform_v4float %8 %uint_0 %uint_1
- %x_e52 = OpLoad %v4float %185 None
- %188 = OpCompositeExtract %float %x_e52 0
- %189 = OpFOrdEqual %bool %188 %float_2
- OpSelectionMerge %192 None
- OpBranchConditional %189 %193 %194
- %193 = OpLabel
+ %188 = OpAccessChain %_ptr_Uniform_v4float %8 %uint_0 %uint_1
+ %x_e52 = OpLoad %v4float %188 None
+ %191 = OpCompositeExtract %float %x_e52 0
+ %192 = OpFOrdEqual %bool %191 %float_2
+ OpSelectionMerge %195 None
+ OpBranchConditional %192 %196 %197
+ %196 = OpLabel
%x_e59 = OpLoad %v3float %a_Normal1 None
- %196 = OpAccessChain %_ptr_Uniform_Mat4x2_ %8 %uint_0 %uint_0 %uint_0
- %x_e64 = OpLoad %Mat4x2_ %196 None
+ %199 = OpAccessChain %_ptr_Uniform_Mat4x2_ %8 %uint_0 %uint_0 %uint_0
+ %x_e64 = OpLoad %Mat4x2_ %199 None
%x_e65 = OpLoad %v3float %a_Normal1 None
- %200 = OpCompositeConstruct %v4float %x_e65 %float_1
- %x_e68 = OpFunctionCall %v2float %Mul2 %x_e64 %200
- %202 = OpVectorShuffle %v2float %x_e68 %x_e68 0 1
- OpStore %v_TexCoord %202 None
- OpBranch %192
- %194 = OpLabel
+ %203 = OpCompositeConstruct %v4float %x_e65 %float_1
+ %x_e68 = OpFunctionCall %v2float %Mul2 %x_e64 %203
+ %205 = OpVectorShuffle %v2float %x_e68 %x_e68 0 1
+ OpStore %v_TexCoord %205 None
+ OpBranch %195
+ %197 = OpLabel
%x_e73 = OpLoad %v2float %a_UV1 None
- %204 = OpAccessChain %_ptr_Uniform_Mat4x2_ %8 %uint_0 %uint_0 %uint_0
- %x_e79 = OpLoad %Mat4x2_ %204 None
+ %207 = OpAccessChain %_ptr_Uniform_Mat4x2_ %8 %uint_0 %uint_0 %uint_0
+ %x_e79 = OpLoad %Mat4x2_ %207 None
%x_e80 = OpLoad %v2float %a_UV1 None
- %207 = OpCompositeConstruct %v4float %x_e80 %float_1 %float_1
- %x_e84 = OpFunctionCall %v2float %Mul2 %x_e79 %207
- %209 = OpVectorShuffle %v2float %x_e84 %x_e84 0 1
- OpStore %v_TexCoord %209 None
- OpBranch %192
- %192 = OpLabel
+ %210 = OpCompositeConstruct %v4float %x_e80 %float_1 %float_1
+ %x_e84 = OpFunctionCall %v2float %Mul2 %x_e79 %210
+ %212 = OpVectorShuffle %v2float %x_e84 %x_e84 0 1
+ OpStore %v_TexCoord %212 None
+ OpBranch %195
+ %195 = OpLabel
OpReturn
OpFunctionEnd
- %main_inner = OpFunction %VertexOutput None %217
+ %main_inner = OpFunction %VertexOutput None %220
%a_Position = OpFunctionParameter %v3float
%a_UV = OpFunctionParameter %v2float
%a_Color = OpFunctionParameter %v4float
%a_Normal = OpFunctionParameter %v3float
%a_PosMtxIdx = OpFunctionParameter %float
- %218 = OpLabel
+ %221 = OpLabel
OpStore %a_Position1 %a_Position None
OpStore %a_UV1 %a_UV None
OpStore %a_Color1 %a_Color None
OpStore %a_Normal1 %a_Normal None
OpStore %a_PosMtxIdx1 %a_PosMtxIdx None
- %219 = OpFunctionCall %void %main1
+ %222 = OpFunctionCall %void %main1
%x_e11_0 = OpLoad %v4float %v_Color None
%x_e13_0 = OpLoad %v2float %v_TexCoord None
%x_e15_1 = OpLoad %v4float %gl_Position None
- %223 = OpCompositeConstruct %VertexOutput %x_e11_0 %x_e13_0 %x_e15_1
- OpReturnValue %223
+ %226 = OpCompositeConstruct %VertexOutput %x_e11_0 %x_e13_0 %x_e15_1
+ OpReturnValue %226
OpFunctionEnd
-%tint_f32_to_i32 = OpFunction %int None %225
+%tint_f32_to_i32 = OpFunction %int None %228
%value = OpFunctionParameter %float
- %226 = OpLabel
- %227 = OpConvertFToS %int %value
- %228 = OpFOrdGreaterThanEqual %bool %value %float_n2_14748365e_09
- %230 = OpSelect %int %228 %227 %int_n2147483648
- %232 = OpFOrdLessThanEqual %bool %value %float_2_14748352e_09
- %234 = OpSelect %int %232 %230 %int_2147483647
- OpReturnValue %234
+ %229 = OpLabel
+ %230 = OpConvertFToS %int %value
+ %231 = OpFOrdGreaterThanEqual %bool %value %float_n2_14748365e_09
+ %233 = OpSelect %int %231 %230 %int_n2147483648
+ %235 = OpFOrdLessThanEqual %bool %value %float_2_14748352e_09
+ %237 = OpSelect %int %235 %233 %int_2147483647
+ OpReturnValue %237
OpFunctionEnd
- %main = OpFunction %void None %148
- %237 = OpLabel
- %238 = OpLoad %v3float %main_loc0_Input None
- %239 = OpLoad %v2float %main_loc1_Input None
- %240 = OpLoad %v4float %main_loc2_Input None
- %241 = OpLoad %v3float %main_loc3_Input None
- %242 = OpLoad %float %main_loc4_Input None
- %243 = OpFunctionCall %VertexOutput %main_inner %238 %239 %240 %241 %242
- %244 = OpCompositeExtract %v4float %243 0
- OpStore %main_loc0_Output %244 None
- %245 = OpCompositeExtract %v2float %243 1
- OpStore %main_loc1_Output %245 None
- %246 = OpCompositeExtract %v4float %243 2
- OpStore %main_position_Output %246 None
+ %main = OpFunction %void None %149
+ %240 = OpLabel
+ %241 = OpLoad %v3float %main_loc0_Input None
+ %242 = OpLoad %v2float %main_loc1_Input None
+ %243 = OpLoad %v4float %main_loc2_Input None
+ %244 = OpLoad %v3float %main_loc3_Input None
+ %245 = OpLoad %float %main_loc4_Input None
+ %246 = OpFunctionCall %VertexOutput %main_inner %241 %242 %243 %244 %245
+ %247 = OpCompositeExtract %v4float %246 0
+ OpStore %main_loc0_Output %247 None
+ %248 = OpCompositeExtract %v2float %246 1
+ OpStore %main_loc1_Output %248 None
+ %249 = OpCompositeExtract %v4float %246 2
+ OpStore %main_position_Output %249 None
OpStore %main___point_size_Output %float_1 None
OpReturn
OpFunctionEnd
+%tint_convert_explicit_layout = OpFunction %Mat4x4_ None %251
+%tint_source = OpFunctionParameter %Mat4x4__tint_explicit_layout
+ %252 = OpLabel
+ %253 = OpCompositeExtract %v4float %tint_source 0
+ %254 = OpCompositeExtract %v4float %tint_source 1
+ %255 = OpCompositeExtract %v4float %tint_source 2
+ %256 = OpCompositeExtract %v4float %tint_source 3
+ %257 = OpCompositeConstruct %Mat4x4_ %253 %254 %255 %256
+ OpReturnValue %257
+ OpFunctionEnd