[tint][ir][val] Handle construct with undefined args or results
- Implements `Unused` value in core IR
- Uses `Unused` in MSL backend to represent struct values that are not
used in the ModuleScopeVars to avoid validator failure
Fixes: 356878395
Fixes: 356896466
Change-Id: I6d0580b8eebf1fed12e75a001a4273bef74e6f7c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/201095
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/BUILD.bazel b/src/tint/lang/core/ir/BUILD.bazel
index 575f7dd..99c054a 100644
--- a/src/tint/lang/core/ir/BUILD.bazel
+++ b/src/tint/lang/core/ir/BUILD.bazel
@@ -86,6 +86,7 @@
"terminator.cc",
"unary.cc",
"unreachable.cc",
+ "unused.cc",
"user_call.cc",
"validator.cc",
"value.cc",
@@ -141,6 +142,7 @@
"traverse.h",
"unary.h",
"unreachable.h",
+ "unused.h",
"user_call.h",
"validator.h",
"value.h",
diff --git a/src/tint/lang/core/ir/BUILD.cmake b/src/tint/lang/core/ir/BUILD.cmake
index 3e5bb13..7b9e40f 100644
--- a/src/tint/lang/core/ir/BUILD.cmake
+++ b/src/tint/lang/core/ir/BUILD.cmake
@@ -138,6 +138,8 @@
lang/core/ir/unary.h
lang/core/ir/unreachable.cc
lang/core/ir/unreachable.h
+ lang/core/ir/unused.cc
+ lang/core/ir/unused.h
lang/core/ir/user_call.cc
lang/core/ir/user_call.h
lang/core/ir/validator.cc
diff --git a/src/tint/lang/core/ir/BUILD.gn b/src/tint/lang/core/ir/BUILD.gn
index 87e0186..85e5573 100644
--- a/src/tint/lang/core/ir/BUILD.gn
+++ b/src/tint/lang/core/ir/BUILD.gn
@@ -140,6 +140,8 @@
"unary.h",
"unreachable.cc",
"unreachable.h",
+ "unused.cc",
+ "unused.h",
"user_call.cc",
"user_call.h",
"validator.cc",
diff --git a/src/tint/lang/core/ir/builder.cc b/src/tint/lang/core/ir/builder.cc
index a1fb84a..5cad9c1 100644
--- a/src/tint/lang/core/ir/builder.cc
+++ b/src/tint/lang/core/ir/builder.cc
@@ -139,6 +139,10 @@
return Append(ir.allocators.instructions.Create<ir::Unreachable>(ir.NextInstructionId()));
}
+ir::Unused* Builder::Unused() {
+ return ir.allocators.values.Create<ir::Unused>();
+}
+
const core::type::Type* Builder::VectorPtrElementType(const core::type::Type* type) {
auto* vec_ptr_ty = type->As<core::type::Pointer>();
TINT_ASSERT(vec_ptr_ty);
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 0787547..dd2d301 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -66,6 +66,7 @@
#include "src/tint/lang/core/ir/swizzle.h"
#include "src/tint/lang/core/ir/terminate_invocation.h"
#include "src/tint/lang/core/ir/unreachable.h"
+#include "src/tint/lang/core/ir/unused.h"
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/lang/core/ir/value.h" // IWYU pragma: export
#include "src/tint/lang/core/ir/var.h"
@@ -1654,6 +1655,10 @@
/// @returns the instruction
ir::Unreachable* Unreachable();
+ /// Creates an unused instruction
+ /// @returns the instruction
+ ir::Unused* Unused();
+
/// Creates a new runtime value
/// @param type the return type
/// @returns the value
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index ed4fb2f..f72a756 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -60,6 +60,7 @@
#include "src/tint/lang/core/ir/swizzle.h"
#include "src/tint/lang/core/ir/terminate_invocation.h"
#include "src/tint/lang/core/ir/unreachable.h"
+#include "src/tint/lang/core/ir/unused.h"
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/lang/core/ir/var.h"
#include "src/tint/lang/core/type/struct.h"
@@ -446,6 +447,7 @@
};
emit(constant->Value());
},
+ [&](const tint::core::ir::Unused*) { out_ << StyleLiteral("unused"); },
[&](Default) { out_ << NameOf(val); });
}
diff --git a/src/tint/lang/core/ir/unused.cc b/src/tint/lang/core/ir/unused.cc
new file mode 100644
index 0000000..167510e
--- /dev/null
+++ b/src/tint/lang/core/ir/unused.cc
@@ -0,0 +1,45 @@
+// Copyright 2024 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/core/ir/unused.h"
+
+#include "src/tint/lang/core/ir/clone_context.h"
+#include "src/tint/lang/core/ir/module.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::core::ir::Unused);
+
+namespace tint::core::ir {
+
+Unused::Unused() = default;
+
+Unused::~Unused() = default;
+
+Unused* Unused::Clone(CloneContext& ctx) {
+ return ctx.ir.allocators.values.Create<Unused>();
+}
+
+} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/unused.h b/src/tint/lang/core/ir/unused.h
new file mode 100644
index 0000000..8b33549
--- /dev/null
+++ b/src/tint/lang/core/ir/unused.h
@@ -0,0 +1,47 @@
+// Copyright 2024 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_CORE_IR_UNUSED_H_
+#define SRC_TINT_LANG_CORE_IR_UNUSED_H_
+
+#include "src/tint/lang/core/ir/value.h"
+
+namespace tint::core::ir {
+
+/// Unused value in the IR.
+class Unused : public Castable<Unused, Value> {
+ public:
+ Unused();
+ ~Unused() override;
+
+ /// @copydoc Value::Clone()
+ Unused* Clone(CloneContext& ctx) override;
+};
+
+} // namespace tint::core::ir
+
+#endif // SRC_TINT_LANG_CORE_IR_UNUSED_H_
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index d2d6157..aebbe9c 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -71,6 +71,7 @@
#include "src/tint/lang/core/ir/terminate_invocation.h"
#include "src/tint/lang/core/ir/unary.h"
#include "src/tint/lang/core/ir/unreachable.h"
+#include "src/tint/lang/core/ir/unused.h"
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/lang/core/ir/var.h"
#include "src/tint/lang/core/type/bool.h"
@@ -271,12 +272,12 @@
/// @returns true if the result is not null
bool CheckResult(const Instruction* inst, size_t idx);
- /// Checks the number of results for @p inst are exactly equal to @p count and that none of
- /// them are null. Also checks that the types for the results are not null
+ /// Checks the results (and their types) for @p inst are not null. If count is specified then
+ /// number of results is checked to be exact.
/// @param inst the instruction
/// @param count the number of results to check
/// @returns true if the results count is as expected and none are null
- bool CheckResults(const ir::Instruction* inst, size_t count);
+ bool CheckResults(const ir::Instruction* inst, std::optional<size_t> count);
/// Checks the given operand is not null and its type is not null
/// @param inst the instruction
@@ -295,12 +296,12 @@
size_t min_count,
std::optional<size_t> max_count);
- /// Checks the number of operands for @p inst are exactly equal to @p count and that none of
- /// them are null. Also checks that the types for the operands are not null
+ /// Checks the operands (and their types) for @p inst are not null. If count is specified then
+ /// number of operands is checked to be exact.
/// @param inst the instruction
/// @param count the number of operands to check
/// @returns true if the operands count is as expected and none are null
- bool CheckOperands(const ir::Instruction* inst, size_t count);
+ bool CheckOperands(const ir::Instruction* inst, std::optional<size_t> count);
/// Checks the number of results for @p inst are exactly equal to @p num_results and the number
/// of operands is correctly. Both results and operands are confirmed to be non-null.
@@ -325,6 +326,12 @@
size_t num_results,
size_t num_operands);
+ /// Checks that the results and operands (and their types) for @p inst are not null.
+ /// Note: Does not check the number of results and operands.
+ /// @param inst the instruction
+ /// @returns true if the results and operands are not null
+ bool CheckResultsAndOperands(const ir::Instruction* inst);
+
/// Checks the given operand is not null
/// @param inst the instruction
/// @param operand the operand
@@ -774,15 +781,17 @@
return true;
}
-bool Validator::CheckResults(const ir::Instruction* inst, size_t count) {
- if (TINT_UNLIKELY(inst->Results().Length() != count)) {
- AddError(inst) << "expected exactly " << count << " results, got "
- << inst->Results().Length();
- return false;
+bool Validator::CheckResults(const ir::Instruction* inst, std::optional<size_t> count = {}) {
+ if (count.has_value()) {
+ if (TINT_UNLIKELY(inst->Results().Length() != count.value())) {
+ AddError(inst) << "expected exactly " << count.value() << " results, got "
+ << inst->Results().Length();
+ return false;
+ }
}
bool passed = true;
- for (size_t i = 0; i < count; i++) {
+ for (size_t i = 0; i < inst->Results().Length(); i++) {
if (TINT_UNLIKELY(!CheckResult(inst, i))) {
passed = false;
}
@@ -797,6 +806,12 @@
return false;
}
+ // ir::Unused is a internal value used by some transforms to track unused entries, and is
+ // removed as part of generating an output shader.
+ if (TINT_UNLIKELY(operand->Is<ir::Unused>())) {
+ return true;
+ }
+
// ir::Function does not have a meaningful type, so does not override the default Type()
// behaviour.
if (TINT_UNLIKELY(!operand->Is<ir::Function>() && operand->Type() == nullptr)) {
@@ -836,15 +851,17 @@
return passed;
}
-bool Validator::CheckOperands(const ir::Instruction* inst, size_t count) {
- if (TINT_UNLIKELY(inst->Operands().Length() != count)) {
- AddError(inst) << "expected exactly " << count << " operands, got "
- << inst->Operands().Length();
- return false;
+bool Validator::CheckOperands(const ir::Instruction* inst, std::optional<size_t> count = {}) {
+ if (count.has_value()) {
+ if (TINT_UNLIKELY(inst->Operands().Length() != count.value())) {
+ AddError(inst) << "expected exactly " << count.value() << " operands, got "
+ << inst->Operands().Length();
+ return false;
+ }
}
bool passed = true;
- for (size_t i = 0; i < count; i++) {
+ for (size_t i = 0; i < inst->Operands().Length(); i++) {
if (TINT_UNLIKELY(!CheckOperand(inst, i))) {
passed = false;
}
@@ -871,6 +888,13 @@
return results_passed && operands_passed;
}
+bool Validator::CheckResultsAndOperands(const ir::Instruction* inst) {
+ // Intentionally avoiding short-circuiting here
+ bool results_passed = CheckResults(inst);
+ bool operands_passed = CheckOperands(inst);
+ return results_passed && operands_passed;
+}
+
// TODO(353498500): Remove this function once it is no longer used.
void Validator::CheckOperandNotNull(const Instruction* inst, const ir::Value* operand, size_t idx) {
if (operand == nullptr) {
@@ -1162,7 +1186,7 @@
AddError(inst, i) << "operand missing usage";
} else if (auto fn = op->As<Function>(); fn && !all_functions_.Contains(fn)) {
AddError(inst, i) << NameOf(op) << " is not part of the module";
- } else if (!op->Is<Constant>() && !scope_stack_.Contains(op)) {
+ } else if (!op->Is<ir::Unused>() && !op->Is<Constant>() && !scope_stack_.Contains(op)) {
AddError(inst, i) << NameOf(op) << " is not in scope";
AddDeclarationNote(op);
}
@@ -1336,6 +1360,10 @@
return;
}
+ if (!CheckResultsAndOperands(construct)) {
+ return;
+ }
+
if (auto* str = As<type::Struct>(construct->Result(0)->Type())) {
auto members = str->Members();
if (args.Length() != str->Members().Length()) {
@@ -1345,7 +1373,10 @@
return;
}
for (size_t i = 0; i < args.Length(); i++) {
- if (args[i] && args[i]->Type() != members[i]->Type()) {
+ if (args[i]->Is<ir::Unused>()) {
+ continue;
+ }
+ if (args[i]->Type() != members[i]->Type()) {
AddError(construct, Construct::kArgsOperandOffset + i)
<< "structure member " << i << " is of type "
<< style::Type(members[i]->Type()->FriendlyName())
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 5fb3314..43f6d90 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -933,6 +933,22 @@
ASSERT_EQ(res, Success) << res.Failure();
}
+TEST_F(IR_ValidatorTest, Construct_Struct_UnusedArgs) {
+ auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.i32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* f = b.Function("f", ty.void_());
+ b.Append(f->Block(), [&] {
+ b.Construct(str_ty, 1_i, b.Unused());
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success) << res.Failure();
+}
+
TEST_F(IR_ValidatorTest, Construct_Struct_NotEnoughArgs) {
auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
{mod.symbols.New("a"), ty.i32()},
@@ -1048,6 +1064,91 @@
)");
}
+TEST_F(IR_ValidatorTest, Construct_NullArg) {
+ auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.i32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* f = b.Function("f", ty.void_());
+ b.Append(f->Block(), [&] {
+ b.Construct(str_ty, 1_i, nullptr);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:8:33 error: construct: operand is undefined
+ %2:MyStruct = construct 1i, undef
+ ^^^^^
+
+:7:3 note: in block
+ $B1: {
+ ^^^
+
+note: # Disassembly
+MyStruct = struct @align(4) {
+ a:i32 @offset(0)
+ b:u32 @offset(4)
+}
+
+%f = func():void {
+ $B1: {
+ %2:MyStruct = construct 1i, undef
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Construct_NullResult) {
+ auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), ty.i32()},
+ {mod.symbols.New("b"), ty.u32()},
+ });
+
+ auto* f = b.Function("f", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* c = b.Construct(str_ty, 1_i, 2_u);
+ c->SetResults(Vector<ir::InstructionResult*, 1>{nullptr});
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:8:5 error: construct: result is undefined
+ undef = construct 1i, 2u
+ ^^^^^
+
+:7:3 note: in block
+ $B1: {
+ ^^^
+
+:8:5 error: construct: result is undefined
+ undef = construct 1i, 2u
+ ^^^^^
+
+:7:3 note: in block
+ $B1: {
+ ^^^
+
+note: # Disassembly
+MyStruct = struct @align(4) {
+ a:i32 @offset(0)
+ b:u32 @offset(4)
+}
+
+%f = func():void {
+ $B1: {
+ undef = construct 1i, 2u
+ ret
+ }
+}
+)");
+}
+
TEST_F(IR_ValidatorTest, Block_NoTerminator) {
b.Function("my_func", ty.void_());
@@ -5914,7 +6015,15 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:5:15 error: store: operand type is undefined
+ R"(:4:5 error: construct: result type is undefined
+ %3:undef = construct 42u
+ ^^^^^^^^
+
+:2:3 note: in block
+ $B1: {
+ ^^^
+
+:5:15 error: store: operand type is undefined
store %2, %3
^^
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index ad64f17..a36dd02 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -65,6 +65,7 @@
#include "src/tint/lang/core/ir/swizzle.h"
#include "src/tint/lang/core/ir/terminate_invocation.h"
#include "src/tint/lang/core/ir/unreachable.h"
+#include "src/tint/lang/core/ir/unused.h"
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/lang/core/ir/validator.h"
#include "src/tint/lang/core/ir/var.h"
@@ -1049,8 +1050,8 @@
size_t i = 0;
bool needs_comma = false;
for (auto* arg : c->Args()) {
- if (arg == nullptr) {
- // Skip `undef` values.
+ if (arg->Is<tint::core::ir::Unused>()) {
+ // Skip `unused` values.
i++;
continue;
}
@@ -1058,7 +1059,7 @@
out << ", ";
}
// Emit field designators for structures so that we can skip padding members and
- // arguments that are `undef` values.
+ // arguments that are `undef` or `unused` values.
auto name = struct_ty->Members()[i]->Name().Name();
out << "." << name << "=";
EmitAndTakeAddressIfNeeded(out, arg);
diff --git a/src/tint/lang/msl/writer/raise/module_scope_vars.cc b/src/tint/lang/msl/writer/raise/module_scope_vars.cc
index fea01b9..efbde47 100644
--- a/src/tint/lang/msl/writer/raise/module_scope_vars.cc
+++ b/src/tint/lang/msl/writer/raise/module_scope_vars.cc
@@ -183,8 +183,8 @@
Vector<core::ir::Value*, 8> construct_args;
for (auto var : module_vars) {
if (!referenced_vars.Contains(var)) {
- // The variable isn't used by this entry point, so set the member to undef.
- construct_args.Push(nullptr);
+ // The variable isn't used by this entry point, so set the member to unused.
+ construct_args.Push(b.Unused());
continue;
}
diff --git a/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc b/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
index ede0e36..51b0aa8 100644
--- a/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
+++ b/src/tint/lang/msl/writer/raise/module_scope_vars_test.cc
@@ -1352,7 +1352,7 @@
%main_a = @fragment func(%a:ptr<uniform, i32, read> [@binding_point(1, 2)], %b:ptr<storage, i32, read_write> [@binding_point(3, 4)]):void {
$B1: {
- %4:tint_module_vars_struct = construct %a, %b, undef
+ %4:tint_module_vars_struct = construct %a, %b, unused
%tint_module_vars:tint_module_vars_struct = let %4
%6:ptr<uniform, i32, read> = access %tint_module_vars, 0u
%7:i32 = load %6
@@ -1367,7 +1367,7 @@
%main_b = @fragment func(%a_1:ptr<uniform, i32, read> [@binding_point(1, 2)]):void { # %a_1: 'a'
$B2: {
%c:ptr<private, i32, read_write> = var
- %15:tint_module_vars_struct = construct %a_1, undef, %c
+ %15:tint_module_vars_struct = construct %a_1, unused, %c
%tint_module_vars_1:tint_module_vars_struct = let %15 # %tint_module_vars_1: 'tint_module_vars'
%17:ptr<uniform, i32, read> = access %tint_module_vars_1, 0u
%18:i32 = load %17
@@ -1465,7 +1465,7 @@
}
%main_a = @fragment func(%a:ptr<uniform, i32, read> [@binding_point(1, 2)], %b:ptr<storage, i32, read_write> [@binding_point(3, 4)]):void {
$B2: {
- %8:tint_module_vars_struct = construct %a, %b, undef
+ %8:tint_module_vars_struct = construct %a, %b, unused
%tint_module_vars_1:tint_module_vars_struct = let %8 # %tint_module_vars_1: 'tint_module_vars'
%10:ptr<storage, i32, read_write> = access %tint_module_vars_1, 1u
%11:i32 = load %10
@@ -1479,7 +1479,7 @@
%main_b = @fragment func(%a_1:ptr<uniform, i32, read> [@binding_point(1, 2)]):void { # %a_1: 'a'
$B3: {
%c:ptr<private, i32, read_write> = var
- %18:tint_module_vars_struct = construct %a_1, undef, %c
+ %18:tint_module_vars_struct = construct %a_1, unused, %c
%tint_module_vars_2:tint_module_vars_struct = let %18 # %tint_module_vars_2: 'tint_module_vars'
%20:ptr<private, i32, read_write> = access %tint_module_vars_2, 2u
%21:i32 = load %20