Import Tint changes from Dawn
Changes:
- 07c73adc95a1177315563225ad57517af4376633 [tint] Remove unused includes from MSL validate.h by James Price <jrprice@google.com>
- 700892daf642d290bee5885644389af86d1d3ff0 Add WGSL writer helper to create a WGSL program. by dan sinclair <dsinclair@chromium.org>
- b94b9ca68ecc762078f1540e98e917f395ba1747 [tint][fuzz][ir] Include IR disassembly when SPIR-V val f... by Ben Clayton <bclayton@google.com>
- fc36dcfaa23772a4771dfdeb4df8d7b5d378a6a8 [tint][ir] Fix UAF in validator by Ben Clayton <bclayton@google.com>
- 0a60d528a4a693ba225af966043f01ae1fdfb997 [tint][ir] Stylize more validator diagnostics by Ben Clayton <bclayton@google.com>
- a655053bb2a77aeb4a7ebecd5f697c2102c12d6a [tint] Use EXPECT_DEATH_IF_SUPPORTED() by Ben Clayton <bclayton@google.com>
- 590110ebb1dae02b4fc98ed5ca08f9c3aeabb3f5 [tint][ir] Fix Std140 transform for arrays of matrices by Ben Clayton <bclayton@google.com>
- f35bd1bae613c97b4d93c19dd23a7009cd10e5fb [spirv-reader] Add transform to handle shader IO by James Price <jrprice@google.com>
- a5ded402136cc3e734bcf5692c3534f7aa2e5afd [ir] Add ReferencedModuleVars helper by James Price <jrprice@google.com>
- 0bb6d4d0b5c854e83cf3846092eb3079afd72194 [tint][ast] Include WGSL dump in fuzzer ICE message by Ben Clayton <bclayton@google.com>
- c25a748790c61d35b185fb2996875efaeb530662 [ir] Add Instruction::DetachResult() helper by James Price <jrprice@google.com>
GitOrigin-RevId: 07c73adc95a1177315563225ad57517af4376633
Change-Id: Ifa9a927222958aed0240d2b9a99024a3c8572750
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/187800
Kokoro: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/common/helper.cc b/src/tint/cmd/common/helper.cc
index 49fcf79..9f83e9a 100644
--- a/src/tint/cmd/common/helper.cc
+++ b/src/tint/cmd/common/helper.cc
@@ -124,23 +124,19 @@
exit(1);
}
- // Convert the IR module to a WGSL string.
+ // Convert the IR module to a Program.
tint::wgsl::writer::ProgramOptions writer_options;
writer_options.allow_non_uniform_derivatives =
opts.spirv_reader_options.allow_non_uniform_derivatives;
writer_options.allowed_features = opts.spirv_reader_options.allowed_features;
- auto wgsl_result = tint::wgsl::writer::WgslFromIR(result.Get(), writer_options);
- if (wgsl_result != Success) {
- std::cerr << "Failed to convert IR to WGSL:\n\n"
- << wgsl_result.Failure().reason << "\n";
+ auto prog_result = tint::wgsl::writer::ProgramFromIR(result.Get(), writer_options);
+ if (prog_result != Success) {
+ std::cerr << "Failed to convert IR to Program:\n\n"
+ << prog_result.Failure().reason << "\n";
exit(1);
}
- // Parse the WGSL string to produce a WGSL AST.
- tint::wgsl::reader::Options reader_options;
- reader_options.allowed_features = tint::wgsl::AllowedFeatures::Everything();
- auto file = std::make_unique<tint::Source::File>(opts.filename, wgsl_result->wgsl);
- return tint::wgsl::reader::Parse(file.get(), reader_options);
+ return prog_result.Move();
#else
std::cerr << "Tint not built with the WGSL writer enabled" << std::endl;
exit(1);
diff --git a/src/tint/cmd/remote_compile/BUILD.bazel b/src/tint/cmd/remote_compile/BUILD.bazel
index 823c8da..501d62a 100644
--- a/src/tint/cmd/remote_compile/BUILD.bazel
+++ b/src/tint/cmd/remote_compile/BUILD.bazel
@@ -42,11 +42,8 @@
"main.cc",
],
deps = [
- "//src/tint/lang/wgsl/ast",
"//src/tint/utils/macros",
"//src/tint/utils/socket",
- "//src/tint/utils/text",
- "//src/tint/utils/traits",
] + select({
":tint_build_msl_writer": [
diff --git a/src/tint/cmd/remote_compile/BUILD.cmake b/src/tint/cmd/remote_compile/BUILD.cmake
index a54b9fb..6841744 100644
--- a/src/tint/cmd/remote_compile/BUILD.cmake
+++ b/src/tint/cmd/remote_compile/BUILD.cmake
@@ -43,11 +43,8 @@
)
tint_target_add_dependencies(tint_cmd_remote_compile_cmd cmd
- tint_lang_wgsl_ast
tint_utils_macros
tint_utils_socket
- tint_utils_text
- tint_utils_traits
)
tint_target_add_external_dependencies(tint_cmd_remote_compile_cmd cmd
diff --git a/src/tint/cmd/remote_compile/BUILD.gn b/src/tint/cmd/remote_compile/BUILD.gn
index d2b7baf..6653c2a 100644
--- a/src/tint/cmd/remote_compile/BUILD.gn
+++ b/src/tint/cmd/remote_compile/BUILD.gn
@@ -43,11 +43,8 @@
sources = [ "main.cc" ]
deps = [
"${tint_src_dir}:thread",
- "${tint_src_dir}/lang/wgsl/ast",
"${tint_src_dir}/utils/macros",
"${tint_src_dir}/utils/socket",
- "${tint_src_dir}/utils/text",
- "${tint_src_dir}/utils/traits",
]
if (tint_build_msl_writer) {
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index c99263c..b6d308b 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -48,6 +48,7 @@
"//src/tint/cmd/common:test",
"//src/tint/lang/core/constant:test",
"//src/tint/lang/core/intrinsic:test",
+ "//src/tint/lang/core/ir/transform/common:test",
"//src/tint/lang/core/ir/transform:test",
"//src/tint/lang/core/ir:test",
"//src/tint/lang/core/type:test",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index 0cb839e..94c5d90 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -49,6 +49,7 @@
tint_cmd_common_test
tint_lang_core_constant_test
tint_lang_core_intrinsic_test
+ tint_lang_core_ir_transform_common_test
tint_lang_core_ir_transform_test
tint_lang_core_ir_test
tint_lang_core_type_test
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 54897e3..b8647d4 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -57,6 +57,7 @@
"${tint_src_dir}/lang/core/intrinsic:unittests",
"${tint_src_dir}/lang/core/ir:unittests",
"${tint_src_dir}/lang/core/ir/transform:unittests",
+ "${tint_src_dir}/lang/core/ir/transform/common:unittests",
"${tint_src_dir}/lang/core/type:unittests",
"${tint_src_dir}/lang/hlsl/writer/common:unittests",
"${tint_src_dir}/lang/msl/ir:unittests",
diff --git a/src/tint/lang/core/ir/access_test.cc b/src/tint/lang/core/ir/access_test.cc
index aee7288..072a953 100644
--- a/src/tint/lang/core/ir/access_test.cc
+++ b/src/tint/lang/core/ir/access_test.cc
@@ -62,7 +62,7 @@
}
TEST_F(IR_AccessTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/bitcast_test.cc b/src/tint/lang/core/ir/bitcast_test.cc
index c585375..debb722 100644
--- a/src/tint/lang/core/ir/bitcast_test.cc
+++ b/src/tint/lang/core/ir/bitcast_test.cc
@@ -72,7 +72,7 @@
}
TEST_F(IR_BitcastTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/block_param_test.cc b/src/tint/lang/core/ir/block_param_test.cc
index 96a8e02..d5f14e2 100644
--- a/src/tint/lang/core/ir/block_param_test.cc
+++ b/src/tint/lang/core/ir/block_param_test.cc
@@ -37,7 +37,7 @@
using IR_BlockParamTest = IRTestHelper;
TEST_F(IR_BlockParamTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/break_if_test.cc b/src/tint/lang/core/ir/break_if_test.cc
index 0e14900..d472c47 100644
--- a/src/tint/lang/core/ir/break_if_test.cc
+++ b/src/tint/lang/core/ir/break_if_test.cc
@@ -60,7 +60,7 @@
}
TEST_F(IR_BreakIfTest, Fail_NullLoop) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 99e94d1..84fc0ed 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -960,6 +960,19 @@
/// @returns the instruction
ir::Discard* Discard();
+ /// Creates a user function call instruction with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param func the function to call
+ /// @param args the call arguments
+ /// @returns the instruction
+ template <typename... ARGS>
+ ir::UserCall* CallWithResult(ir::InstructionResult* result,
+ ir::Function* func,
+ ARGS&&... args) {
+ return Append(ir.allocators.instructions.Create<ir::UserCall>(
+ result, func, Values(std::forward<ARGS>(args)...)));
+ }
+
/// Creates a user function call instruction
/// @param func the function to call
/// @param args the call arguments
@@ -976,8 +989,7 @@
/// @returns the instruction
template <typename... ARGS>
ir::UserCall* Call(const core::type::Type* type, ir::Function* func, ARGS&&... args) {
- return Append(ir.allocators.instructions.Create<ir::UserCall>(
- InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
+ return CallWithResult(InstructionResult(type), func, Values(std::forward<ARGS>(args)...));
}
/// Creates a user function call instruction
@@ -988,8 +1000,20 @@
template <typename TYPE, typename... ARGS>
ir::UserCall* Call(ir::Function* func, ARGS&&... args) {
auto* type = ir.Types().Get<TYPE>();
- return Append(ir.allocators.instructions.Create<ir::UserCall>(
- InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
+ return CallWithResult(InstructionResult(type), func, Values(std::forward<ARGS>(args)...));
+ }
+
+ /// Creates a core builtin call instruction with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param func the builtin function to call
+ /// @param args the call arguments
+ /// @returns the instruction
+ template <typename... ARGS>
+ ir::CoreBuiltinCall* CallWithResult(core::ir::InstructionResult* result,
+ core::BuiltinFn func,
+ ARGS&&... args) {
+ return Append(ir.allocators.instructions.Create<ir::CoreBuiltinCall>(
+ result, func, Values(std::forward<ARGS>(args)...)));
}
/// Creates a core builtin call instruction
@@ -999,8 +1023,7 @@
/// @returns the instruction
template <typename... ARGS>
ir::CoreBuiltinCall* Call(const core::type::Type* type, core::BuiltinFn func, ARGS&&... args) {
- return Append(ir.allocators.instructions.Create<ir::CoreBuiltinCall>(
- InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
+ return CallWithResult(InstructionResult(type), func, Values(std::forward<ARGS>(args)...));
}
/// Creates a core builtin call instruction
@@ -1011,11 +1034,22 @@
template <typename TYPE, typename... ARGS>
ir::CoreBuiltinCall* Call(core::BuiltinFn func, ARGS&&... args) {
auto* type = ir.Types().Get<TYPE>();
- return Append(ir.allocators.instructions.Create<ir::CoreBuiltinCall>(
- InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
+ return CallWithResult(InstructionResult(type), func, Values(std::forward<ARGS>(args)...));
}
- /// Creates a core builtin call instruction
+ /// Creates a builtin call instruction with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param func the builtin function to call
+ /// @param args the call arguments
+ /// @returns the instruction
+ template <typename KLASS, typename FUNC, typename... ARGS>
+ tint::traits::EnableIf<tint::traits::IsTypeOrDerived<KLASS, ir::BuiltinCall>, KLASS*>
+ CallWithResult(ir::InstructionResult* result, FUNC func, ARGS&&... args) {
+ return Append(ir.allocators.instructions.Create<KLASS>(
+ result, func, Values(std::forward<ARGS>(args)...)));
+ }
+
+ /// Creates a builtin call instruction
/// @param type the return type of the call
/// @param func the builtin function to call
/// @param args the call arguments
@@ -1023,8 +1057,8 @@
template <typename KLASS, typename FUNC, typename... ARGS>
tint::traits::EnableIf<tint::traits::IsTypeOrDerived<KLASS, ir::BuiltinCall>, KLASS*>
Call(const core::type::Type* type, FUNC func, ARGS&&... args) {
- return Append(ir.allocators.instructions.Create<KLASS>(
- InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
+ return CallWithResult<KLASS>(InstructionResult(type), func,
+ Values(std::forward<ARGS>(args)...));
}
/// Creates a value conversion instruction to the template type T
@@ -1046,6 +1080,16 @@
InstructionResult(to), Value(std::forward<VAL>(val))));
}
+ /// Creates a value constructor instruction with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param args the arguments to the constructor
+ /// @returns the instruction
+ template <typename... ARGS>
+ ir::Construct* ConstructWithResult(ir::InstructionResult* result, ARGS&&... args) {
+ return Append(ir.allocators.instructions.Create<ir::Construct>(
+ result, Values(std::forward<ARGS>(args)...)));
+ }
+
/// Creates a value constructor instruction to the template type T
/// @param args the arguments to the constructor
/// @returns the instruction
@@ -1061,8 +1105,17 @@
/// @returns the instruction
template <typename... ARGS>
ir::Construct* Construct(const core::type::Type* type, ARGS&&... args) {
- return Append(ir.allocators.instructions.Create<ir::Construct>(
- InstructionResult(type), Values(std::forward<ARGS>(args)...)));
+ return ConstructWithResult(InstructionResult(type), Values(std::forward<ARGS>(args)...));
+ }
+
+ /// Creates a load instruction with an existing result
+ /// @param result the instruction result to use
+ /// @param from the expression being loaded from
+ /// @returns the instruction
+ template <typename VAL>
+ ir::Load* LoadWithResult(ir::InstructionResult* result, VAL&& from) {
+ auto* value = Value(std::forward<VAL>(from));
+ return Append(ir.allocators.instructions.Create<ir::Load>(result, value));
}
/// Creates a load instruction
@@ -1071,8 +1124,7 @@
template <typename VAL>
ir::Load* Load(VAL&& from) {
auto* value = Value(std::forward<VAL>(from));
- return Append(ir.allocators.instructions.Create<ir::Load>(
- InstructionResult(value->Type()->UnwrapPtrOrRef()), value));
+ return LoadWithResult(InstructionResult(value->Type()->UnwrapPtrOrRef()), value);
}
/// Creates a store instruction
@@ -1102,6 +1154,22 @@
value_val));
}
+ /// Creates a load vector element instruction with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param from the vector pointer expression being loaded from
+ /// @param index the new vector element index
+ /// @returns the instruction
+ template <typename FROM, typename INDEX>
+ ir::LoadVectorElement* LoadVectorElementWithResult(ir::InstructionResult* result,
+ FROM&& from,
+ INDEX&& index) {
+ CheckForNonDeterministicEvaluation<FROM, INDEX>();
+ auto* from_val = Value(std::forward<FROM>(from));
+ auto* index_val = Value(std::forward<INDEX>(index));
+ return Append(
+ ir.allocators.instructions.Create<ir::LoadVectorElement>(result, from_val, index_val));
+ }
+
/// Creates a load vector element instruction
/// @param from the vector pointer expression being loaded from
/// @param index the new vector element index
@@ -1112,8 +1180,7 @@
auto* from_val = Value(std::forward<FROM>(from));
auto* index_val = Value(std::forward<INDEX>(index));
auto* res = InstructionResult(VectorPtrElementType(from_val->Type()));
- return Append(
- ir.allocators.instructions.Create<ir::LoadVectorElement>(res, from_val, index_val));
+ return LoadVectorElementWithResult(res, from_val, index_val);
}
/// Creates a new `var` declaration
@@ -1351,6 +1418,19 @@
return FunctionParam(type);
}
+ /// Creates a new `Access` with an existing instruction result
+ /// @param result the instruction result to use
+ /// @param object the object being accessed
+ /// @param indices the access indices
+ /// @returns the instruction
+ template <typename OBJ, typename... ARGS>
+ ir::Access* AccessWithResult(ir::InstructionResult* result, OBJ&& object, ARGS&&... indices) {
+ CheckForNonDeterministicEvaluation<OBJ, ARGS...>();
+ auto* obj_val = Value(std::forward<OBJ>(object));
+ return Append(ir.allocators.instructions.Create<ir::Access>(
+ result, obj_val, Values(std::forward<ARGS>(indices)...)));
+ }
+
/// Creates a new `Access`
/// @param type the return type
/// @param object the object being accessed
@@ -1358,10 +1438,8 @@
/// @returns the instruction
template <typename OBJ, typename... ARGS>
ir::Access* Access(const core::type::Type* type, OBJ&& object, ARGS&&... indices) {
- CheckForNonDeterministicEvaluation<OBJ, ARGS...>();
- auto* obj_val = Value(std::forward<OBJ>(object));
- return Append(ir.allocators.instructions.Create<ir::Access>(
- InstructionResult(type), obj_val, Values(std::forward<ARGS>(indices)...)));
+ return AccessWithResult(InstructionResult(type), std::forward<OBJ>(object),
+ Values(std::forward<ARGS>(indices)...));
}
/// Creates a new `Access`
diff --git a/src/tint/lang/core/ir/constant_test.cc b/src/tint/lang/core/ir/constant_test.cc
index c8b6ef1..e960936 100644
--- a/src/tint/lang/core/ir/constant_test.cc
+++ b/src/tint/lang/core/ir/constant_test.cc
@@ -112,11 +112,11 @@
}
TEST_F(IR_ConstantTest, Fail_NullValue) {
- EXPECT_DEATH({ Constant c(nullptr); }, "");
+ EXPECT_DEATH_IF_SUPPORTED({ Constant c(nullptr); }, "");
}
TEST_F(IR_ConstantTest, Fail_Builder_NullValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/construct_test.cc b/src/tint/lang/core/ir/construct_test.cc
index 6e4b005..d8cef8e 100644
--- a/src/tint/lang/core/ir/construct_test.cc
+++ b/src/tint/lang/core/ir/construct_test.cc
@@ -56,7 +56,7 @@
}
TEST_F(IR_ConstructTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/continue_test.cc b/src/tint/lang/core/ir/continue_test.cc
index 79875cf..d4d9c6b 100644
--- a/src/tint/lang/core/ir/continue_test.cc
+++ b/src/tint/lang/core/ir/continue_test.cc
@@ -58,7 +58,7 @@
}
TEST_F(IR_ContinueTest, Fail_NullLoop) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/convert_test.cc b/src/tint/lang/core/ir/convert_test.cc
index 73c3076..321809c 100644
--- a/src/tint/lang/core/ir/convert_test.cc
+++ b/src/tint/lang/core/ir/convert_test.cc
@@ -35,7 +35,7 @@
using IR_ConvertTest = IRTestHelper;
TEST_F(IR_ConvertTest, Fail_NullToType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/core_binary_test.cc b/src/tint/lang/core/ir/core_binary_test.cc
index 6fde891..6ce32d7 100644
--- a/src/tint/lang/core/ir/core_binary_test.cc
+++ b/src/tint/lang/core/ir/core_binary_test.cc
@@ -40,7 +40,7 @@
using IR_BinaryTest = IRTestHelper;
TEST_F(IR_BinaryTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/core_builtin_call_test.cc b/src/tint/lang/core/ir/core_builtin_call_test.cc
index 7897862..bfc897e 100644
--- a/src/tint/lang/core/ir/core_builtin_call_test.cc
+++ b/src/tint/lang/core/ir/core_builtin_call_test.cc
@@ -55,7 +55,7 @@
}
TEST_F(IR_CoreBuiltinCallTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -65,7 +65,7 @@
}
TEST_F(IR_CoreBuiltinCallTest, Fail_NoneFunction) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/core_unary_test.cc b/src/tint/lang/core/ir/core_unary_test.cc
index 88de1c1..26054a0 100644
--- a/src/tint/lang/core/ir/core_unary_test.cc
+++ b/src/tint/lang/core/ir/core_unary_test.cc
@@ -80,7 +80,7 @@
}
TEST_F(IR_UnaryTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/disassembly.cc b/src/tint/lang/core/ir/disassembly.cc
index a07033f..df0cc4e 100644
--- a/src/tint/lang/core/ir/disassembly.cc
+++ b/src/tint/lang/core/ir/disassembly.cc
@@ -30,6 +30,7 @@
#include <string_view>
#include "src//tint/lang/core/ir/unary.h"
+#include "src/tint/lang/core/binary_op.h"
#include "src/tint/lang/core/constant/composite.h"
#include "src/tint/lang/core/constant/scalar.h"
#include "src/tint/lang/core/constant/splat.h"
@@ -796,64 +797,7 @@
void Disassembly::EmitBinary(const Binary* b) {
SourceMarker sm(this);
EmitValueWithType(b);
- out_ << " = ";
- switch (b->Op()) {
- case BinaryOp::kAdd:
- out_ << StyleInstruction("add");
- break;
- case BinaryOp::kSubtract:
- out_ << StyleInstruction("sub");
- break;
- case BinaryOp::kMultiply:
- out_ << StyleInstruction("mul");
- break;
- case BinaryOp::kDivide:
- out_ << StyleInstruction("div");
- break;
- case BinaryOp::kModulo:
- out_ << StyleInstruction("mod");
- break;
- case BinaryOp::kAnd:
- out_ << StyleInstruction("and");
- break;
- case BinaryOp::kOr:
- out_ << StyleInstruction("or");
- break;
- case BinaryOp::kXor:
- out_ << StyleInstruction("xor");
- break;
- case BinaryOp::kEqual:
- out_ << StyleInstruction("eq");
- break;
- case BinaryOp::kNotEqual:
- out_ << StyleInstruction("neq");
- break;
- case BinaryOp::kLessThan:
- out_ << StyleInstruction("lt");
- break;
- case BinaryOp::kGreaterThan:
- out_ << StyleInstruction("gt");
- break;
- case BinaryOp::kLessThanEqual:
- out_ << StyleInstruction("lte");
- break;
- case BinaryOp::kGreaterThanEqual:
- out_ << StyleInstruction("gte");
- break;
- case BinaryOp::kShiftLeft:
- out_ << StyleInstruction("shl");
- break;
- case BinaryOp::kShiftRight:
- out_ << StyleInstruction("shr");
- break;
- case BinaryOp::kLogicalAnd:
- out_ << StyleInstruction("logical-and");
- break;
- case BinaryOp::kLogicalOr:
- out_ << StyleInstruction("logical-or");
- break;
- }
- out_ << " ";
+ out_ << " = " << NameOf(b->Op()) << " ";
EmitOperandList(b);
sm.Store(b);
@@ -862,25 +806,7 @@
void Disassembly::EmitUnary(const Unary* u) {
SourceMarker sm(this);
EmitValueWithType(u);
- out_ << " = ";
- switch (u->Op()) {
- case UnaryOp::kComplement:
- out_ << StyleInstruction("complement");
- break;
- case UnaryOp::kNegation:
- out_ << StyleInstruction("negation");
- break;
- case UnaryOp::kAddressOf:
- out_ << StyleInstruction("ref-to-ptr");
- break;
- case UnaryOp::kIndirection:
- out_ << StyleInstruction("ptr-to-ref");
- break;
- case UnaryOp::kNot:
- out_ << StyleInstruction("not");
- break;
- }
- out_ << " ";
+ out_ << " = " << NameOf(u->Op()) << " ";
EmitOperandList(u);
sm.Store(u);
@@ -984,4 +910,62 @@
return StyledText{} << StyleInstruction(name);
}
+StyledText Disassembly::NameOf(BinaryOp op) {
+ switch (op) {
+ case BinaryOp::kAdd:
+ return StyledText{} << StyleInstruction("add");
+ case BinaryOp::kSubtract:
+ return StyledText{} << StyleInstruction("sub");
+ case BinaryOp::kMultiply:
+ return StyledText{} << StyleInstruction("mul");
+ case BinaryOp::kDivide:
+ return StyledText{} << StyleInstruction("div");
+ case BinaryOp::kModulo:
+ return StyledText{} << StyleInstruction("mod");
+ case BinaryOp::kAnd:
+ return StyledText{} << StyleInstruction("and");
+ case BinaryOp::kOr:
+ return StyledText{} << StyleInstruction("or");
+ case BinaryOp::kXor:
+ return StyledText{} << StyleInstruction("xor");
+ case BinaryOp::kEqual:
+ return StyledText{} << StyleInstruction("eq");
+ case BinaryOp::kNotEqual:
+ return StyledText{} << StyleInstruction("neq");
+ case BinaryOp::kLessThan:
+ return StyledText{} << StyleInstruction("lt");
+ case BinaryOp::kGreaterThan:
+ return StyledText{} << StyleInstruction("gt");
+ case BinaryOp::kLessThanEqual:
+ return StyledText{} << StyleInstruction("lte");
+ case BinaryOp::kGreaterThanEqual:
+ return StyledText{} << StyleInstruction("gte");
+ case BinaryOp::kShiftLeft:
+ return StyledText{} << StyleInstruction("shl");
+ case BinaryOp::kShiftRight:
+ return StyledText{} << StyleInstruction("shr");
+ case BinaryOp::kLogicalAnd:
+ return StyledText{} << StyleInstruction("logical-and");
+ case BinaryOp::kLogicalOr:
+ return StyledText{} << StyleInstruction("logical-or");
+ }
+ TINT_UNREACHABLE() << op;
+}
+
+StyledText Disassembly::NameOf(UnaryOp op) {
+ switch (op) {
+ case UnaryOp::kComplement:
+ return StyledText{} << StyleInstruction("complement");
+ case UnaryOp::kNegation:
+ return StyledText{} << StyleInstruction("negation");
+ case UnaryOp::kAddressOf:
+ return StyledText{} << StyleInstruction("ref-to-ptr");
+ case UnaryOp::kIndirection:
+ return StyledText{} << StyleInstruction("ptr-to-ref");
+ case UnaryOp::kNot:
+ return StyledText{} << StyleInstruction("not");
+ }
+ TINT_UNREACHABLE() << op;
+}
+
} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/disassembly.h b/src/tint/lang/core/ir/disassembly.h
index 677b386..70c48b4 100644
--- a/src/tint/lang/core/ir/disassembly.h
+++ b/src/tint/lang/core/ir/disassembly.h
@@ -32,6 +32,7 @@
#include <string>
#include <string_view>
+#include "src/tint/lang/core/binary_op.h"
#include "src/tint/lang/core/ir/binary.h"
#include "src/tint/lang/core/ir/block.h"
#include "src/tint/lang/core/ir/block_param.h"
@@ -108,6 +109,12 @@
/// @returns the disassembled name for the Switch @p inst
StyledText NameOf(const Switch* inst);
+ /// @returns the disassembled name for the BinaryOp @p op
+ StyledText NameOf(BinaryOp op);
+
+ /// @returns the disassembled name for the UnaryOp @p op
+ StyledText NameOf(UnaryOp op);
+
/// @param inst the instruction to retrieve
/// @returns the source for the instruction
Source InstructionSource(const Instruction* inst) const {
diff --git a/src/tint/lang/core/ir/function_param_test.cc b/src/tint/lang/core/ir/function_param_test.cc
index 49bc373..80b150f 100644
--- a/src/tint/lang/core/ir/function_param_test.cc
+++ b/src/tint/lang/core/ir/function_param_test.cc
@@ -37,7 +37,7 @@
using IR_FunctionParamTest = IRTestHelper;
TEST_F(IR_FunctionParamTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -47,7 +47,7 @@
}
TEST_F(IR_FunctionParamTest, Fail_SetDuplicateBuiltin) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/function_test.cc b/src/tint/lang/core/ir/function_test.cc
index 19443f6..9f0161a 100644
--- a/src/tint/lang/core/ir/function_test.cc
+++ b/src/tint/lang/core/ir/function_test.cc
@@ -37,7 +37,7 @@
using IR_FunctionTest = IRTestHelper;
TEST_F(IR_FunctionTest, Fail_NullReturnType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -47,7 +47,7 @@
}
TEST_F(IR_FunctionTest, Fail_DoubleReturnBuiltin) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -59,7 +59,7 @@
}
TEST_F(IR_FunctionTest, Fail_NullParam) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -70,7 +70,7 @@
}
TEST_F(IR_FunctionTest, Fail_NullBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/if_test.cc b/src/tint/lang/core/ir/if_test.cc
index cb02e52..236a47d 100644
--- a/src/tint/lang/core/ir/if_test.cc
+++ b/src/tint/lang/core/ir/if_test.cc
@@ -55,7 +55,7 @@
}
TEST_F(IR_IfTest, Fail_NullTrueBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -65,7 +65,7 @@
}
TEST_F(IR_IfTest, Fail_NullFalseBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/instruction.cc b/src/tint/lang/core/ir/instruction.cc
index 8d5218c..3f99d68 100644
--- a/src/tint/lang/core/ir/instruction.cc
+++ b/src/tint/lang/core/ir/instruction.cc
@@ -73,4 +73,11 @@
Block()->Remove(this);
}
+InstructionResult* Instruction::DetachResult() {
+ TINT_ASSERT(Results().Length() == 1u);
+ auto* result = Results()[0];
+ SetResults({});
+ return result;
+}
+
} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/instruction.h b/src/tint/lang/core/ir/instruction.h
index f8c72d5..cff4ab0 100644
--- a/src/tint/lang/core/ir/instruction.h
+++ b/src/tint/lang/core/ir/instruction.h
@@ -115,6 +115,10 @@
/// Removes this instruction from the owning block
void Remove();
+ /// Detach an instruction result from this instruction.
+ /// @returns the instruction result that was detached
+ InstructionResult* DetachResult();
+
/// @param idx the index of the operand
/// @returns the operand with index @p idx, or `nullptr` if there are no operands or the index
/// is out of bounds.
diff --git a/src/tint/lang/core/ir/instruction_result_test.cc b/src/tint/lang/core/ir/instruction_result_test.cc
index 665f17c..01aff8d 100644
--- a/src/tint/lang/core/ir/instruction_result_test.cc
+++ b/src/tint/lang/core/ir/instruction_result_test.cc
@@ -37,7 +37,7 @@
using IR_InstructionResultTest = IRTestHelper;
TEST_F(IR_InstructionResultTest, Destroy_HasInstruction) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/instruction_test.cc b/src/tint/lang/core/ir/instruction_test.cc
index fa95ba5..3813711 100644
--- a/src/tint/lang/core/ir/instruction_test.cc
+++ b/src/tint/lang/core/ir/instruction_test.cc
@@ -30,6 +30,8 @@
#include "src/tint/lang/core/ir/ir_helper_test.h"
#include "src/tint/lang/core/ir/module.h"
+using namespace tint::core::number_suffixes; // NOLINT
+
namespace tint::core::ir {
namespace {
@@ -46,7 +48,7 @@
}
TEST_F(IR_InstructionTest, Fail_InsertBeforeNullptr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -58,7 +60,7 @@
}
TEST_F(IR_InstructionTest, Fail_InsertBeforeNotInserted) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -81,7 +83,7 @@
}
TEST_F(IR_InstructionTest, Fail_InsertAfterNullptr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -93,7 +95,7 @@
}
TEST_F(IR_InstructionTest, Fail_InsertAfterNotInserted) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -117,7 +119,7 @@
}
TEST_F(IR_InstructionTest, Fail_ReplaceWithNullptr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -131,7 +133,7 @@
}
TEST_F(IR_InstructionTest, Fail_ReplaceWithNotInserted) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -155,7 +157,7 @@
}
TEST_F(IR_InstructionTest, Fail_RemoveNotInserted) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -166,5 +168,16 @@
"");
}
+TEST_F(IR_InstructionTest, DetachResult) {
+ auto* inst = b.Let("foo", 42_u);
+ auto* result = inst->Result(0);
+ EXPECT_EQ(result->Instruction(), inst);
+
+ auto* detached = inst->DetachResult();
+ EXPECT_EQ(detached, result);
+ EXPECT_EQ(detached->Instruction(), nullptr);
+ EXPECT_EQ(inst->Results().Length(), 0u);
+}
+
} // namespace
} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/let_test.cc b/src/tint/lang/core/ir/let_test.cc
index 218bd1e..3d998d6 100644
--- a/src/tint/lang/core/ir/let_test.cc
+++ b/src/tint/lang/core/ir/let_test.cc
@@ -41,7 +41,7 @@
using IR_LetTest = IRTestHelper;
TEST_F(IR_LetTest, Fail_NullValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/loop_test.cc b/src/tint/lang/core/ir/loop_test.cc
index 0f522a0..6a7e214 100644
--- a/src/tint/lang/core/ir/loop_test.cc
+++ b/src/tint/lang/core/ir/loop_test.cc
@@ -47,7 +47,7 @@
}
TEST_F(IR_LoopTest, Fail_NullInitializerBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -57,7 +57,7 @@
}
TEST_F(IR_LoopTest, Fail_NullBodyBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -67,7 +67,7 @@
}
TEST_F(IR_LoopTest, Fail_NullContinuingBlock) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/multi_in_block_test.cc b/src/tint/lang/core/ir/multi_in_block_test.cc
index 5d4d459..583f544 100644
--- a/src/tint/lang/core/ir/multi_in_block_test.cc
+++ b/src/tint/lang/core/ir/multi_in_block_test.cc
@@ -36,7 +36,7 @@
using IR_MultiInBlockTest = IRTestHelper;
TEST_F(IR_MultiInBlockTest, Fail_NullInboundBranch) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/next_iteration_test.cc b/src/tint/lang/core/ir/next_iteration_test.cc
index e1554cb..89b4d40 100644
--- a/src/tint/lang/core/ir/next_iteration_test.cc
+++ b/src/tint/lang/core/ir/next_iteration_test.cc
@@ -35,7 +35,7 @@
using IR_NextIterationTest = IRTestHelper;
TEST_F(IR_NextIterationTest, Fail_NullLoop) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/swizzle_test.cc b/src/tint/lang/core/ir/swizzle_test.cc
index ba911f7..6d26ab0 100644
--- a/src/tint/lang/core/ir/swizzle_test.cc
+++ b/src/tint/lang/core/ir/swizzle_test.cc
@@ -54,7 +54,7 @@
}
TEST_F(IR_SwizzleTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -65,7 +65,7 @@
}
TEST_F(IR_SwizzleTest, Fail_EmptyIndices) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -76,7 +76,7 @@
}
TEST_F(IR_SwizzleTest, Fail_TooManyIndices) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
@@ -87,7 +87,7 @@
}
TEST_F(IR_SwizzleTest, Fail_IndexOutOfRange) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/transform/BUILD.bazel b/src/tint/lang/core/ir/transform/BUILD.bazel
index 467e451..3ea71c4 100644
--- a/src/tint/lang/core/ir/transform/BUILD.bazel
+++ b/src/tint/lang/core/ir/transform/BUILD.bazel
@@ -85,6 +85,7 @@
"//src/tint/lang/core/constant",
"//src/tint/lang/core/intrinsic",
"//src/tint/lang/core/ir",
+ "//src/tint/lang/core/ir/transform/common",
"//src/tint/lang/core/type",
"//src/tint/utils/containers",
"//src/tint/utils/diagnostic",
diff --git a/src/tint/lang/core/ir/transform/BUILD.cmake b/src/tint/lang/core/ir/transform/BUILD.cmake
index 1ae8efb..f3a8b32 100644
--- a/src/tint/lang/core/ir/transform/BUILD.cmake
+++ b/src/tint/lang/core/ir/transform/BUILD.cmake
@@ -34,6 +34,8 @@
# Do not modify this file directly
################################################################################
+include(lang/core/ir/transform/common/BUILD.cmake)
+
################################################################################
# Target: tint_lang_core_ir_transform
# Kind: lib
@@ -84,6 +86,7 @@
tint_lang_core_constant
tint_lang_core_intrinsic
tint_lang_core_ir
+ tint_lang_core_ir_transform_common
tint_lang_core_type
tint_utils_containers
tint_utils_diagnostic
diff --git a/src/tint/lang/core/ir/transform/BUILD.gn b/src/tint/lang/core/ir/transform/BUILD.gn
index 95de9fc..db194b7 100644
--- a/src/tint/lang/core/ir/transform/BUILD.gn
+++ b/src/tint/lang/core/ir/transform/BUILD.gn
@@ -88,6 +88,7 @@
"${tint_src_dir}/lang/core/constant",
"${tint_src_dir}/lang/core/intrinsic",
"${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/ir/transform/common",
"${tint_src_dir}/lang/core/type",
"${tint_src_dir}/utils/containers",
"${tint_src_dir}/utils/diagnostic",
diff --git a/src/tint/lang/core/ir/transform/binary_polyfill.cc b/src/tint/lang/core/ir/transform/binary_polyfill.cc
index 7d4a962..a5aec3c 100644
--- a/src/tint/lang/core/ir/transform/binary_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/binary_polyfill.cc
@@ -91,29 +91,18 @@
// Polyfill the binary instructions that we found.
for (auto* binary : worklist) {
- ir::Value* replacement = nullptr;
switch (binary->Op()) {
case BinaryOp::kDivide:
case BinaryOp::kModulo:
- replacement = IntDivMod(binary);
+ IntDivMod(binary);
break;
case BinaryOp::kShiftLeft:
case BinaryOp::kShiftRight:
- replacement = MaskShiftAmount(binary);
+ MaskShiftAmount(binary);
break;
default:
break;
}
- TINT_ASSERT(replacement);
-
- if (replacement != binary->Result(0)) {
- // Replace the old binary instruction result with the new value.
- if (auto name = ir.NameOf(binary->Result(0))) {
- ir.SetName(replacement, name);
- }
- binary->Result(0)->ReplaceAllUsesWith(replacement);
- binary->Destroy();
- }
}
}
@@ -145,8 +134,7 @@
/// Replace an integer divide or modulo with a call to helper function that prevents
/// divide-by-zero and signed integer overflow.
/// @param binary the binary instruction
- /// @returns the replacement value
- ir::Value* IntDivMod(ir::CoreBinary* binary) {
+ void IntDivMod(ir::CoreBinary* binary) {
auto* result_ty = binary->Result(0)->Type();
bool is_div = binary->Op() == BinaryOp::kDivide;
bool is_signed = result_ty->is_signed_integer_scalar_or_vector();
@@ -217,26 +205,23 @@
};
// Call the helper function, splatting the arguments to match the target vector width.
- Value* result = nullptr;
b.InsertBefore(binary, [&] {
auto* lhs = maybe_splat(binary->LHS());
auto* rhs = maybe_splat(binary->RHS());
- result = b.Call(result_ty, helper, lhs, rhs)->Result(0);
+ b.CallWithResult(binary->DetachResult(), helper, lhs, rhs);
});
- return result;
+ binary->Destroy();
}
/// Mask the RHS of a shift instruction to ensure it is modulo the bitwidth of the LHS.
/// @param binary the binary instruction
- /// @returns the replacement value
- ir::Value* MaskShiftAmount(ir::CoreBinary* binary) {
+ void MaskShiftAmount(ir::CoreBinary* binary) {
auto* lhs = binary->LHS();
auto* rhs = binary->RHS();
auto* mask = b.Constant(u32(lhs->Type()->DeepestElement()->Size() * 8 - 1));
auto* masked = b.And(rhs->Type(), rhs, MatchWidth(mask, rhs->Type()));
masked->InsertBefore(binary);
binary->SetOperand(ir::CoreBinary::kRhsOperandOffset, masked->Result(0));
- return binary->Result(0);
}
};
diff --git a/src/tint/lang/core/ir/transform/builtin_polyfill.cc b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
index 0eb45f8..ef68d5b 100644
--- a/src/tint/lang/core/ir/transform/builtin_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/builtin_polyfill.cc
@@ -147,72 +147,61 @@
// Polyfill the builtin call instructions that we found.
for (auto* builtin : worklist) {
- ir::Value* replacement = nullptr;
switch (builtin->Func()) {
case core::BuiltinFn::kClamp:
- replacement = ClampInt(builtin);
+ ClampInt(builtin);
break;
case core::BuiltinFn::kCountLeadingZeros:
- replacement = CountLeadingZeros(builtin);
+ CountLeadingZeros(builtin);
break;
case core::BuiltinFn::kCountTrailingZeros:
- replacement = CountTrailingZeros(builtin);
+ CountTrailingZeros(builtin);
break;
case core::BuiltinFn::kExtractBits:
- replacement = ExtractBits(builtin);
+ ExtractBits(builtin);
break;
case core::BuiltinFn::kFirstLeadingBit:
- replacement = FirstLeadingBit(builtin);
+ FirstLeadingBit(builtin);
break;
case core::BuiltinFn::kFirstTrailingBit:
- replacement = FirstTrailingBit(builtin);
+ FirstTrailingBit(builtin);
break;
case core::BuiltinFn::kInsertBits:
- replacement = InsertBits(builtin);
+ InsertBits(builtin);
break;
case core::BuiltinFn::kSaturate:
- replacement = Saturate(builtin);
+ Saturate(builtin);
break;
case core::BuiltinFn::kTextureSampleBaseClampToEdge:
- replacement = TextureSampleBaseClampToEdge_2d_f32(builtin);
+ TextureSampleBaseClampToEdge_2d_f32(builtin);
break;
case core::BuiltinFn::kDot4I8Packed:
- replacement = Dot4I8Packed(builtin);
+ Dot4I8Packed(builtin);
break;
case core::BuiltinFn::kDot4U8Packed:
- replacement = Dot4U8Packed(builtin);
+ Dot4U8Packed(builtin);
break;
case core::BuiltinFn::kPack4XI8:
- replacement = Pack4xI8(builtin);
+ Pack4xI8(builtin);
break;
case core::BuiltinFn::kPack4XU8:
- replacement = Pack4xU8(builtin);
+ Pack4xU8(builtin);
break;
case core::BuiltinFn::kPack4XI8Clamp:
- replacement = Pack4xI8Clamp(builtin);
+ Pack4xI8Clamp(builtin);
break;
case core::BuiltinFn::kPack4XU8Clamp:
- replacement = Pack4xU8Clamp(builtin);
+ Pack4xU8Clamp(builtin);
break;
case core::BuiltinFn::kUnpack4XI8:
- replacement = Unpack4xI8(builtin);
+ Unpack4xI8(builtin);
break;
case core::BuiltinFn::kUnpack4XU8:
- replacement = Unpack4xU8(builtin);
+ Unpack4xU8(builtin);
break;
default:
break;
}
- TINT_ASSERT(replacement);
-
- if (replacement != builtin->Result(0)) {
- // Replace the old builtin call result with the new value.
- if (auto name = ir.NameOf(builtin->Result(0))) {
- ir.SetName(replacement, name);
- }
- builtin->Result(0)->ReplaceAllUsesWith(replacement);
- builtin->Destroy();
- }
}
}
@@ -243,26 +232,22 @@
/// Polyfill a `clamp()` builtin call for integers.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* ClampInt(ir::CoreBuiltinCall* call) {
+ void ClampInt(ir::CoreBuiltinCall* call) {
auto* type = call->Result(0)->Type();
auto* e = call->Args()[0];
auto* low = call->Args()[1];
auto* high = call->Args()[2];
- Value* result = nullptr;
b.InsertBefore(call, [&] {
auto* max = b.Call(type, core::BuiltinFn::kMax, e, low);
- auto* min = b.Call(type, core::BuiltinFn::kMin, max, high);
- result = min->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kMin, max, high);
});
- return result;
+ call->Destroy();
}
/// Polyfill a `countLeadingZeros()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* CountLeadingZeros(ir::CoreBuiltinCall* call) {
+ void CountLeadingZeros(ir::CoreBuiltinCall* call) {
auto* input = call->Args()[0];
auto* result_ty = input->Type();
auto* uint_ty = MatchWidth(ty.u32(), result_ty);
@@ -271,7 +256,6 @@
// Make an u32 constant with the same component count as result_ty.
auto V = [&](uint32_t u) { return MatchWidth(b.Constant(u32(u)), result_ty); };
- Value* result = nullptr;
b.InsertBefore(call, [&] {
// %x = %input;
// if (%x is signed) {
@@ -309,23 +293,23 @@
b.LessThanEqual(bool_ty, x, V(0x7fffffff)));
auto* b0 =
b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1), b.Equal(bool_ty, x, V(0)));
- result = b.Add(uint_ty,
- b.Or(uint_ty, b16,
- b.Or(uint_ty, b8,
- b.Or(uint_ty, b4, b.Or(uint_ty, b2, b.Or(uint_ty, b1, b0))))),
- b0)
- ->Result(0);
+ Instruction* result = b.Add(
+ uint_ty,
+ b.Or(
+ uint_ty, b16,
+ b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b.Or(uint_ty, b1, b0))))),
+ b0);
if (result_ty->is_signed_integer_scalar_or_vector()) {
- result = b.Bitcast(result_ty, result)->Result(0);
+ result = b.Bitcast(result_ty, result);
}
+ result->SetResults(Vector{call->DetachResult()});
});
- return result;
+ call->Destroy();
}
/// Polyfill a `countTrailingZeros()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* CountTrailingZeros(ir::CoreBuiltinCall* call) {
+ void CountTrailingZeros(ir::CoreBuiltinCall* call) {
auto* input = call->Args()[0];
auto* result_ty = input->Type();
auto* uint_ty = MatchWidth(ty.u32(), result_ty);
@@ -334,7 +318,6 @@
// Make an u32 constant with the same component count as result_ty.
auto V = [&](uint32_t u) { return MatchWidth(b.Constant(u32(u)), result_ty); };
- Value* result = nullptr;
b.InsertBefore(call, [&] {
// %x = %input;
// if (%x is signed) {
@@ -372,22 +355,21 @@
b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000001)), V(0)));
auto* b0 =
b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1), b.Equal(bool_ty, x, V(0)));
- result = b.Add(uint_ty,
- b.Or(uint_ty, b16,
- b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1)))),
- b0)
- ->Result(0);
+ Instruction* result = b.Add(
+ uint_ty,
+ b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1)))),
+ b0);
if (result_ty->is_signed_integer_scalar_or_vector()) {
- result = b.Bitcast(result_ty, result)->Result(0);
+ result = b.Bitcast(result_ty, result);
}
+ result->SetResults(Vector{call->DetachResult()});
});
- return result;
+ call->Destroy();
}
/// Polyfill an `extractBits()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* ExtractBits(ir::CoreBuiltinCall* call) {
+ void ExtractBits(ir::CoreBuiltinCall* call) {
auto* offset = call->Args()[1];
auto* count = call->Args()[2];
@@ -406,7 +388,7 @@
call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 1, o->Result(0));
call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, c->Result(0));
});
- return call->Result(0);
+ break;
}
default:
TINT_UNIMPLEMENTED() << "extractBits polyfill level";
@@ -415,8 +397,7 @@
/// Polyfill a `firstLeadingBit()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* FirstLeadingBit(ir::CoreBuiltinCall* call) {
+ void FirstLeadingBit(ir::CoreBuiltinCall* call) {
auto* input = call->Args()[0];
auto* result_ty = input->Type();
auto* uint_ty = MatchWidth(ty.u32(), result_ty);
@@ -425,7 +406,6 @@
// Make an u32 constant with the same component count as result_ty.
auto V = [&](uint32_t u) { return MatchWidth(b.Constant(u32(u)), result_ty); };
- Value* result = nullptr;
b.InsertBefore(call, [&] {
// %x = %input;
// if (%x is signed) {
@@ -465,22 +445,21 @@
x = b.ShiftRight(uint_ty, x, b2)->Result(0);
auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(1), V(0),
b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000002)), V(0)));
- result = b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))))
- ->Result(0);
+ Instruction* result =
+ b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))));
result = b.Call(uint_ty, core::BuiltinFn::kSelect, result, V(0xffffffff),
- b.Equal(bool_ty, x, V(0)))
- ->Result(0);
+ b.Equal(bool_ty, x, V(0)));
if (result_ty->is_signed_integer_scalar_or_vector()) {
- result = b.Bitcast(result_ty, result)->Result(0);
+ result = b.Bitcast(result_ty, result);
}
+ result->SetResults(Vector{call->DetachResult()});
});
- return result;
+ call->Destroy();
}
/// Polyfill a `firstTrailingBit()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* FirstTrailingBit(ir::CoreBuiltinCall* call) {
+ void FirstTrailingBit(ir::CoreBuiltinCall* call) {
auto* input = call->Args()[0];
auto* result_ty = input->Type();
auto* uint_ty = MatchWidth(ty.u32(), result_ty);
@@ -489,7 +468,6 @@
// Make an u32 constant with the same component count as result_ty.
auto V = [&](uint32_t u) { return MatchWidth(b.Constant(u32(u)), result_ty); };
- Value* result = nullptr;
b.InsertBefore(call, [&] {
// %x = %input;
// if (%x is signed) {
@@ -525,22 +503,21 @@
x = b.ShiftRight(uint_ty, x, b2)->Result(0);
auto* b1 = b.Call(uint_ty, core::BuiltinFn::kSelect, V(0), V(1),
b.Equal(bool_ty, b.And(uint_ty, x, V(0x00000001)), V(0)));
- result = b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))))
- ->Result(0);
+ Instruction* result =
+ b.Or(uint_ty, b16, b.Or(uint_ty, b8, b.Or(uint_ty, b4, b.Or(uint_ty, b2, b1))));
result = b.Call(uint_ty, core::BuiltinFn::kSelect, result, V(0xffffffff),
- b.Equal(bool_ty, x, V(0)))
- ->Result(0);
+ b.Equal(bool_ty, x, V(0)));
if (result_ty->is_signed_integer_scalar_or_vector()) {
- result = b.Bitcast(result_ty, result)->Result(0);
+ result = b.Bitcast(result_ty, result);
}
+ result->SetResults(Vector{call->DetachResult()});
});
- return result;
+ call->Destroy();
}
/// Polyfill an `insertBits()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* InsertBits(ir::CoreBuiltinCall* call) {
+ void InsertBits(ir::CoreBuiltinCall* call) {
auto* offset = call->Args()[2];
auto* count = call->Args()[3];
@@ -559,7 +536,7 @@
call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 2, o->Result(0));
call->SetOperand(ir::CoreBuiltinCall::kArgsOperandOffset + 3, c->Result(0));
});
- return call->Result(0);
+ break;
}
default:
TINT_UNIMPLEMENTED() << "insertBits polyfill level";
@@ -568,8 +545,7 @@
/// Polyfill a `saturate()` builtin call.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Saturate(ir::CoreBuiltinCall* call) {
+ void Saturate(ir::CoreBuiltinCall* call) {
// Replace `saturate(x)` with `clamp(x, 0., 1.)`.
auto* type = call->Result(0)->Type();
ir::Constant* zero = nullptr;
@@ -581,21 +557,20 @@
zero = MatchWidth(b.Constant(0_h), type);
one = MatchWidth(b.Constant(1_h), type);
}
- auto* clamp = b.Call(type, core::BuiltinFn::kClamp, Vector{call->Args()[0], zero, one});
+ auto* clamp = b.CallWithResult(call->DetachResult(), core::BuiltinFn::kClamp,
+ Vector{call->Args()[0], zero, one});
clamp->InsertBefore(call);
- return clamp->Result(0);
+ call->Destroy();
}
/// Polyfill a `textureSampleBaseClampToEdge()` builtin call for 2D F32 textures.
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* TextureSampleBaseClampToEdge_2d_f32(ir::CoreBuiltinCall* call) {
+ void TextureSampleBaseClampToEdge_2d_f32(ir::CoreBuiltinCall* call) {
// Replace `textureSampleBaseClampToEdge(%texture, %sample, %coords)` with:
// %dims = vec2f(textureDimensions(%texture));
// %half_texel = vec2f(0.5) / dims;
// %clamped = clamp(%coord, %half_texel, 1.0 - %half_texel);
// %result = textureSampleLevel(%texture, %sampler, %clamped, 0);
- ir::Value* result = nullptr;
auto* texture = call->Args()[0];
auto* sampler = call->Args()[1];
auto* coords = call->Args()[2];
@@ -607,17 +582,15 @@
auto* one_minus_half_texel = b.Subtract(vec2f, b.Splat(vec2f, 1_f, 2), half_texel);
auto* clamped =
b.Call(vec2f, core::BuiltinFn::kClamp, coords, half_texel, one_minus_half_texel);
- result = b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureSampleLevel, texture, sampler,
- clamped, 0_f)
- ->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kTextureSampleLevel, texture,
+ sampler, clamped, 0_f);
});
- return result;
+ call->Destroy();
}
/// Polyfill a `dot4I8Packed()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Dot4I8Packed(ir::CoreBuiltinCall* call) {
+ void Dot4I8Packed(ir::CoreBuiltinCall* call) {
// Replace `dot4I8Packed(%x,%y)` with:
// %unpacked_x = unpack4xI8(%x);
// %unpacked_y = unpack4xI8(%y);
@@ -626,17 +599,15 @@
auto* y = call->Args()[1];
auto* unpacked_x = Unpack4xI8OnValue(call, x);
auto* unpacked_y = Unpack4xI8OnValue(call, y);
- ir::Value* result = nullptr;
b.InsertBefore(call, [&] {
- result = b.Call(ty.i32(), core::BuiltinFn::kDot, unpacked_x, unpacked_y)->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, unpacked_x, unpacked_y);
});
- return result;
+ call->Destroy();
}
/// Polyfill a `dot4U8Packed()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Dot4U8Packed(ir::CoreBuiltinCall* call) {
+ void Dot4U8Packed(ir::CoreBuiltinCall* call) {
// Replace `dot4U8Packed(%x,%y)` with:
// %unpacked_x = unpack4xU8(%x);
// %unpacked_y = unpack4xU8(%y);
@@ -645,23 +616,20 @@
auto* y = call->Args()[1];
auto* unpacked_x = Unpack4xU8OnValue(call, x);
auto* unpacked_y = Unpack4xU8OnValue(call, y);
- ir::Value* result = nullptr;
b.InsertBefore(call, [&] {
- result = b.Call(ty.u32(), core::BuiltinFn::kDot, unpacked_x, unpacked_y)->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, unpacked_x, unpacked_y);
});
- return result;
+ call->Destroy();
}
/// Polyfill a `pack4xI8()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Pack4xI8(ir::CoreBuiltinCall* call) {
+ void Pack4xI8(ir::CoreBuiltinCall* call) {
// Replace `pack4xI8(%x)` with:
// %n = vec4u(0, 8, 16, 24);
// %x_u32 = bitcast<vec4u>(%x)
// %x_u8 = (%x_u32 & vec4u(0xff)) << n;
// %result = dot(%x_u8, vec4u(1));
- ir::Value* result = nullptr;
auto* x = call->Args()[0];
b.InsertBefore(call, [&] {
auto* vec4u = ty.vec4<u32>();
@@ -671,22 +639,19 @@
auto* x_u32 = b.Bitcast(vec4u, x);
auto* x_u8 = b.ShiftLeft(
vec4u, b.And(vec4u, x_u32, b.Construct(vec4u, b.Constant(u32(0xff)))), n);
- result = b.Call(ty.u32(), core::BuiltinFn::kDot, x_u8,
- b.Construct(vec4u, (b.Constant(u32(1)))))
- ->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, x_u8,
+ b.Construct(vec4u, (b.Constant(u32(1)))));
});
- return result;
+ call->Destroy();
}
/// Polyfill a `pack4xU8()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Pack4xU8(ir::CoreBuiltinCall* call) {
+ void Pack4xU8(ir::CoreBuiltinCall* call) {
// Replace `pack4xU8(%x)` with:
// %n = vec4u(0, 8, 16, 24);
// %x_i8 = (%x & vec4u(0xff)) << %n;
// %result = dot(%x_i8, vec4u(1));
- ir::Value* result = nullptr;
auto* x = call->Args()[0];
b.InsertBefore(call, [&] {
auto* vec4u = ty.vec4<u32>();
@@ -695,17 +660,15 @@
b.Constant(u32(16)), b.Constant(u32(24)));
auto* x_u8 =
b.ShiftLeft(vec4u, b.And(vec4u, x, b.Construct(vec4u, b.Constant(u32(0xff)))), n);
- result = b.Call(ty.u32(), core::BuiltinFn::kDot, x_u8,
- b.Construct(vec4u, (b.Constant(u32(1)))))
- ->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, x_u8,
+ b.Construct(vec4u, (b.Constant(u32(1)))));
});
- return result;
+ call->Destroy();
}
/// Polyfill a `pack4xI8Clamp()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Pack4xI8Clamp(ir::CoreBuiltinCall* call) {
+ void Pack4xI8Clamp(ir::CoreBuiltinCall* call) {
// Replace `pack4xI8Clamp(%x)` with:
// %n = vec4u(0, 8, 16, 24);
// %min_i8_vec4 = vec4i(-128);
@@ -714,7 +677,6 @@
// %x_u32 = bitcast<vec4u>(%x_clamp);
// %x_u8 = (%x_u32 & vec4u(0xff)) << n;
// %result = dot(%x_u8, vec4u(1));
- ir::Value* result = nullptr;
auto* x = call->Args()[0];
b.InsertBefore(call, [&] {
auto* vec4i = ty.vec4<i32>();
@@ -728,17 +690,15 @@
auto* x_u32 = b.Bitcast(vec4u, x_clamp);
auto* x_u8 = b.ShiftLeft(
vec4u, b.And(vec4u, x_u32, b.Construct(vec4u, b.Constant(u32(0xff)))), n);
- result = b.Call(ty.u32(), core::BuiltinFn::kDot, x_u8,
- b.Construct(vec4u, (b.Constant(u32(1)))))
- ->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, x_u8,
+ b.Construct(vec4u, (b.Constant(u32(1)))));
});
- return result;
+ call->Destroy();
}
/// Polyfill a `pack4xU8Clamp()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Pack4xU8Clamp(ir::CoreBuiltinCall* call) {
+ void Pack4xU8Clamp(ir::CoreBuiltinCall* call) {
// Replace `pack4xU8Clamp(%x)` with:
// %n = vec4u(0, 8, 16, 24);
// %min_u8_vec4 = vec4u(0);
@@ -746,7 +706,6 @@
// %x_clamp = clamp(%x, vec4u(0), vec4u(255));
// %x_u8 = %x_clamp << n;
// %result = dot(%x_u8, vec4u(1));
- ir::Value* result = nullptr;
auto* x = call->Args()[0];
b.InsertBefore(call, [&] {
auto* vec4u = ty.vec4<u32>();
@@ -757,23 +716,22 @@
auto* max_u8_vec4 = b.Construct(vec4u, b.Constant(u32(255)));
auto* x_clamp = b.Call(vec4u, core::BuiltinFn::kClamp, x, min_u8_vec4, max_u8_vec4);
auto* x_u8 = b.ShiftLeft(vec4u, x_clamp, n);
- result = b.Call(ty.u32(), core::BuiltinFn::kDot, x_u8,
- b.Construct(vec4u, (b.Constant(u32(1)))))
- ->Result(0);
+ b.CallWithResult(call->DetachResult(), core::BuiltinFn::kDot, x_u8,
+ b.Construct(vec4u, (b.Constant(u32(1)))));
});
- return result;
+ call->Destroy();
}
/// Emit code for `unpack4xI8` on u32 value `x`, before the given call.
/// @param call the instruction that should follow the emitted code
/// @param x the u32 value to be unpacked
- ir::Value* Unpack4xI8OnValue(ir::CoreBuiltinCall* call, ir::Value* x) {
+ ir::Instruction* Unpack4xI8OnValue(ir::CoreBuiltinCall* call, ir::Value* x) {
// Replace `unpack4xI8(%x)` with:
// %n = vec4u(24, 16, 8, 0);
// %x_splat = vec4u(%x); // splat the scalar to a vector
// %x_vec4i = bitcast<vec4i>(%x_splat << n);
// %result = %x_vec4i >> vec4u(24);
- ir::Value* result = nullptr;
+ ir::Instruction* result = nullptr;
b.InsertBefore(call, [&] {
auto* vec4i = ty.vec4<i32>();
auto* vec4u = ty.vec4<u32>();
@@ -782,29 +740,29 @@
b.Constant(u32(8)), b.Constant(u32(0)));
auto* x_splat = b.Construct(vec4u, x);
auto* x_vec4i = b.Bitcast(vec4i, b.ShiftLeft(vec4u, x_splat, n));
- result =
- b.ShiftRight(vec4i, x_vec4i, b.Construct(vec4u, b.Constant(u32(24))))->Result(0);
+ result = b.ShiftRight(vec4i, x_vec4i, b.Construct(vec4u, b.Constant(u32(24))));
});
return result;
}
/// Polyfill a `unpack4xI8()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Unpack4xI8(ir::CoreBuiltinCall* call) {
- return Unpack4xI8OnValue(call, call->Args()[0]);
+ void Unpack4xI8(ir::CoreBuiltinCall* call) {
+ auto* result = Unpack4xI8OnValue(call, call->Args()[0]);
+ result->SetResults(Vector{call->DetachResult()});
+ call->Destroy();
}
/// Emit code for `unpack4xU8` on u32 value `x`, before the given call.
/// @param call the instruction that should follow the emitted code
/// @param x the u32 value to be unpacked
- ir::Value* Unpack4xU8OnValue(ir::CoreBuiltinCall* call, ir::Value* x) {
+ Instruction* Unpack4xU8OnValue(ir::CoreBuiltinCall* call, ir::Value* x) {
// Replace `unpack4xU8(%x)` with:
// %n = vec4u(0, 8, 16, 24);
// %x_splat = vec4u(%x); // splat the scalar to a vector
// %x_vec4u = %x_splat >> n;
// %result = %x_vec4u & vec4u(0xff);
- ir::Value* result = nullptr;
+ ir::Instruction* result = nullptr;
b.InsertBefore(call, [&] {
auto* vec4u = ty.vec4<u32>();
@@ -812,16 +770,17 @@
b.Constant(u32(16)), b.Constant(u32(24)));
auto* x_splat = b.Construct(vec4u, x);
auto* x_vec4u = b.ShiftRight(vec4u, x_splat, n);
- result = b.And(vec4u, x_vec4u, b.Construct(vec4u, b.Constant(u32(0xff))))->Result(0);
+ result = b.And(vec4u, x_vec4u, b.Construct(vec4u, b.Constant(u32(0xff))));
});
return result;
}
/// Polyfill a `unpack4xU8()` builtin call
/// @param call the builtin call instruction
- /// @returns the replacement value
- ir::Value* Unpack4xU8(ir::CoreBuiltinCall* call) {
- return Unpack4xU8OnValue(call, call->Args()[0]);
+ void Unpack4xU8(ir::CoreBuiltinCall* call) {
+ auto* result = Unpack4xU8OnValue(call, call->Args()[0]);
+ result->SetResults(Vector{call->DetachResult()});
+ call->Destroy();
}
};
diff --git a/src/tint/lang/core/ir/transform/common/BUILD.bazel b/src/tint/lang/core/ir/transform/common/BUILD.bazel
new file mode 100644
index 0000000..f05642f
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/BUILD.bazel
@@ -0,0 +1,103 @@
+# 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.bazel.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+load("//src/tint:flags.bzl", "COPTS")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+cc_library(
+ name = "common",
+ srcs = [
+ "referenced_module_vars.cc",
+ ],
+ hdrs = [
+ "referenced_module_vars.h",
+ ],
+ deps = [
+ "//src/tint/api/common",
+ "//src/tint/lang/core",
+ "//src/tint/lang/core/constant",
+ "//src/tint/lang/core/ir",
+ "//src/tint/lang/core/type",
+ "//src/tint/utils/containers",
+ "//src/tint/utils/diagnostic",
+ "//src/tint/utils/ice",
+ "//src/tint/utils/id",
+ "//src/tint/utils/macros",
+ "//src/tint/utils/math",
+ "//src/tint/utils/memory",
+ "//src/tint/utils/reflection",
+ "//src/tint/utils/result",
+ "//src/tint/utils/rtti",
+ "//src/tint/utils/symbol",
+ "//src/tint/utils/text",
+ "//src/tint/utils/traits",
+ ],
+ copts = COPTS,
+ visibility = ["//visibility:public"],
+)
+cc_library(
+ name = "test",
+ alwayslink = True,
+ srcs = [
+ "referenced_module_vars_test.cc",
+ ],
+ deps = [
+ "//src/tint/api/common",
+ "//src/tint/lang/core",
+ "//src/tint/lang/core/constant",
+ "//src/tint/lang/core/intrinsic",
+ "//src/tint/lang/core/ir",
+ "//src/tint/lang/core/ir/transform/common",
+ "//src/tint/lang/core/ir:test",
+ "//src/tint/lang/core/type",
+ "//src/tint/utils/containers",
+ "//src/tint/utils/diagnostic",
+ "//src/tint/utils/ice",
+ "//src/tint/utils/id",
+ "//src/tint/utils/macros",
+ "//src/tint/utils/math",
+ "//src/tint/utils/memory",
+ "//src/tint/utils/reflection",
+ "//src/tint/utils/result",
+ "//src/tint/utils/rtti",
+ "//src/tint/utils/symbol",
+ "//src/tint/utils/text",
+ "//src/tint/utils/traits",
+ "@gtest",
+ ],
+ copts = COPTS,
+ visibility = ["//visibility:public"],
+)
+
diff --git a/src/tint/lang/core/ir/transform/common/BUILD.cmake b/src/tint/lang/core/ir/transform/common/BUILD.cmake
new file mode 100644
index 0000000..7004d86
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/BUILD.cmake
@@ -0,0 +1,101 @@
+# 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.cmake.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+################################################################################
+# Target: tint_lang_core_ir_transform_common
+# Kind: lib
+################################################################################
+tint_add_target(tint_lang_core_ir_transform_common lib
+ lang/core/ir/transform/common/referenced_module_vars.cc
+ lang/core/ir/transform/common/referenced_module_vars.h
+)
+
+tint_target_add_dependencies(tint_lang_core_ir_transform_common lib
+ tint_api_common
+ tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_ir
+ tint_lang_core_type
+ tint_utils_containers
+ tint_utils_diagnostic
+ tint_utils_ice
+ tint_utils_id
+ tint_utils_macros
+ tint_utils_math
+ tint_utils_memory
+ tint_utils_reflection
+ tint_utils_result
+ tint_utils_rtti
+ tint_utils_symbol
+ tint_utils_text
+ tint_utils_traits
+)
+
+################################################################################
+# Target: tint_lang_core_ir_transform_common_test
+# Kind: test
+################################################################################
+tint_add_target(tint_lang_core_ir_transform_common_test test
+ lang/core/ir/transform/common/referenced_module_vars_test.cc
+)
+
+tint_target_add_dependencies(tint_lang_core_ir_transform_common_test test
+ tint_api_common
+ tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_intrinsic
+ tint_lang_core_ir
+ tint_lang_core_ir_transform_common
+ tint_lang_core_ir_test
+ tint_lang_core_type
+ tint_utils_containers
+ tint_utils_diagnostic
+ tint_utils_ice
+ tint_utils_id
+ tint_utils_macros
+ tint_utils_math
+ tint_utils_memory
+ tint_utils_reflection
+ tint_utils_result
+ tint_utils_rtti
+ tint_utils_symbol
+ tint_utils_text
+ tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_lang_core_ir_transform_common_test test
+ "gtest"
+)
diff --git a/src/tint/lang/core/ir/transform/common/BUILD.gn b/src/tint/lang/core/ir/transform/common/BUILD.gn
new file mode 100644
index 0000000..d050caf
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/BUILD.gn
@@ -0,0 +1,99 @@
+# 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.gn.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+import("../../../../../../../scripts/tint_overrides_with_defaults.gni")
+
+import("${tint_src_dir}/tint.gni")
+
+if (tint_build_unittests || tint_build_benchmarks) {
+ import("//testing/test.gni")
+}
+
+libtint_source_set("common") {
+ sources = [
+ "referenced_module_vars.cc",
+ "referenced_module_vars.h",
+ ]
+ deps = [
+ "${tint_src_dir}/api/common",
+ "${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/type",
+ "${tint_src_dir}/utils/containers",
+ "${tint_src_dir}/utils/diagnostic",
+ "${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
+ "${tint_src_dir}/utils/macros",
+ "${tint_src_dir}/utils/math",
+ "${tint_src_dir}/utils/memory",
+ "${tint_src_dir}/utils/reflection",
+ "${tint_src_dir}/utils/result",
+ "${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
+ "${tint_src_dir}/utils/text",
+ "${tint_src_dir}/utils/traits",
+ ]
+}
+if (tint_build_unittests) {
+ tint_unittests_source_set("unittests") {
+ sources = [ "referenced_module_vars_test.cc" ]
+ deps = [
+ "${tint_src_dir}:gmock_and_gtest",
+ "${tint_src_dir}/api/common",
+ "${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/intrinsic",
+ "${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/ir:unittests",
+ "${tint_src_dir}/lang/core/ir/transform/common",
+ "${tint_src_dir}/lang/core/type",
+ "${tint_src_dir}/utils/containers",
+ "${tint_src_dir}/utils/diagnostic",
+ "${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
+ "${tint_src_dir}/utils/macros",
+ "${tint_src_dir}/utils/math",
+ "${tint_src_dir}/utils/memory",
+ "${tint_src_dir}/utils/reflection",
+ "${tint_src_dir}/utils/result",
+ "${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
+ "${tint_src_dir}/utils/text",
+ "${tint_src_dir}/utils/traits",
+ ]
+ }
+}
diff --git a/src/tint/lang/core/ir/transform/common/referenced_module_vars.cc b/src/tint/lang/core/ir/transform/common/referenced_module_vars.cc
new file mode 100644
index 0000000..b97b9dd
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/referenced_module_vars.cc
@@ -0,0 +1,75 @@
+// 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/transform/common/referenced_module_vars.h"
+
+#include "src/tint/lang/core/ir/control_instruction.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/user_call.h"
+#include "src/tint/lang/core/ir/var.h"
+#include "src/tint/utils/rtti/switch.h"
+
+namespace tint::core::ir {
+
+const ReferencedModuleVars::VarSet& ReferencedModuleVars::TransitiveReferences(Function* func) {
+ return transitive_references_.GetOrAdd(func, [&] {
+ VarSet vars;
+ GetTransitiveReferences(func->Block(), vars);
+ return vars;
+ });
+}
+
+/// Get the set of variables transitively referenced by @p block.
+/// @param block the block
+/// @param vars the set of transitively referenced variables to populate
+void ReferencedModuleVars::GetTransitiveReferences(Block* block, VarSet& vars) {
+ // Add directly referenced vars.
+ if (auto itr = block_to_direct_vars_.Get(block)) {
+ for (auto& var : *itr) {
+ vars.Add(var);
+ }
+ }
+
+ // Loop over instructions in the block to find indirectly referenced vars.
+ for (auto* inst : *block) {
+ tint::Switch(
+ inst,
+ [&](UserCall* call) {
+ // Get variables referenced by a function called from this block.
+ const auto& callee_vars = TransitiveReferences(call->Target());
+ for (auto* var : callee_vars) {
+ vars.Add(var);
+ }
+ },
+ [&](ControlInstruction* ctrl) {
+ // Recurse into control instructions and gather their referenced vars.
+ ctrl->ForeachBlock([&](Block* blk) { GetTransitiveReferences(blk, vars); });
+ });
+ }
+}
+
+} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/transform/common/referenced_module_vars.h b/src/tint/lang/core/ir/transform/common/referenced_module_vars.h
new file mode 100644
index 0000000..a48b0a7
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/referenced_module_vars.h
@@ -0,0 +1,105 @@
+// 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_TRANSFORM_COMMON_REFERENCED_MODULE_VARS_H_
+#define SRC_TINT_LANG_CORE_IR_TRANSFORM_COMMON_REFERENCED_MODULE_VARS_H_
+
+#include <functional>
+
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/var.h"
+#include "src/tint/utils/containers/hashmap.h"
+#include "src/tint/utils/containers/unique_vector.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Block;
+class Function;
+} // namespace tint::core::ir
+
+namespace tint::core::ir {
+
+/// ReferencedModuleVars is a helper to determine the set of module-scope variables that are
+/// transitively referenced by functions in a module.
+/// References are determined lazily and cached for future requests.
+///
+/// Note: changes to the module can invalidate the cached data. This is intended to be created by
+/// a transform that need this information, and discarded when that transform completes. Tracking
+/// this information inside the IR module would add overhead any time an instruction is added or
+/// removed from the module. Since only a few transforms need this information, we expect it to be
+/// more efficient to generate it as and when needed instead.
+class ReferencedModuleVars {
+ public:
+ /// The signature of a predicate used to filter variables.
+ /// A predicate function should return `true` when the variable should be added to the set.
+ using Predicate = std::function<bool(const Var*)>;
+
+ /// A set of a variables referenced by a function (in declaration order).
+ using VarSet = UniqueVector<Var*, 16>;
+
+ /// Constructor.
+ /// @param ir the module
+ /// @param pred an optional predicate function for filtering variables
+ /// Note: @p pred is not stored by the class, so can be a lambda that captures by reference.
+ explicit ReferencedModuleVars(Module& ir, Predicate&& pred = {}) : ir_(ir) {
+ // Loop over module-scope variables, recording the blocks that they are referenced from.
+ for (auto inst : *ir_.root_block) {
+ if (auto* var = inst->As<Var>()) {
+ if (!pred || pred(var)) {
+ var->Result(0)->ForEachUse([&](const Usage& use) {
+ block_to_direct_vars_.GetOrAddZero(use.instruction->Block()).Add(var);
+ });
+ }
+ }
+ }
+ }
+
+ /// Get the set of transitively referenced module-scope variables for a function, filtered by
+ /// the predicate function if provided.
+ /// @param func the function
+ /// @returns the set of (possibly filtered) transitively reference module-scope variables
+ const VarSet& TransitiveReferences(Function* func);
+
+ private:
+ /// The module.
+ Module& ir_;
+
+ /// A map from blocks to their directly referenced variables.
+ Hashmap<Block*, VarSet, 64> block_to_direct_vars_{};
+
+ /// A map from functions to their transitively referenced variables.
+ Hashmap<Function*, VarSet, 8> transitive_references_;
+
+ /// Get the set of transitively referenced module-scope variables for a block.
+ /// @param block the block
+ /// @param vars the set of transitively reference module-scope variables to populate
+ void GetTransitiveReferences(Block* block, VarSet& vars);
+};
+
+} // namespace tint::core::ir
+
+#endif // SRC_TINT_LANG_CORE_IR_TRANSFORM_COMMON_REFERENCED_MODULE_VARS_H_
diff --git a/src/tint/lang/core/ir/transform/common/referenced_module_vars_test.cc b/src/tint/lang/core/ir/transform/common/referenced_module_vars_test.cc
new file mode 100644
index 0000000..3a5a032
--- /dev/null
+++ b/src/tint/lang/core/ir/transform/common/referenced_module_vars_test.cc
@@ -0,0 +1,422 @@
+// 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/transform/common/referenced_module_vars.h"
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "src/tint/lang/core/ir/disassembly.h"
+#include "src/tint/lang/core/ir/ir_helper_test.h"
+
+namespace tint::core::ir {
+namespace {
+
+using ::testing::ElementsAre;
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+class IR_ReferencedModuleVarsTest : public IRTestHelper {
+ protected:
+ /// @returns the module as a disassembled string
+ std::string Disassemble() const { return "\n" + ir::Disassemble(mod).Plain(); }
+};
+
+TEST_F(IR_ReferencedModuleVarsTest, EmptyRootBlock) {
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+%foo = func():void {
+ $B1: {
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ auto& foo_vars = vars.TransitiveReferences(foo);
+ EXPECT_TRUE(foo_vars.IsEmpty());
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, DirectUse) {
+ // Referenced.
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ auto* var_b = mod.root_block->Append(b.Var<workgroup, u32>("b"));
+ // Not referenced.
+ mod.root_block->Append(b.Var<workgroup, u32>("c"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Load(var_a);
+ b.Load(var_b);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<workgroup, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %5:u32 = load %a
+ %6:u32 = load %b
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a, var_b));
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, DirectUse_DeclarationOrder) {
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ auto* var_b = mod.root_block->Append(b.Var<workgroup, u32>("b"));
+ auto* var_c = mod.root_block->Append(b.Var<workgroup, u32>("c"));
+ auto* var_d = mod.root_block->Append(b.Var<workgroup, u32>("d"));
+ auto* var_e = mod.root_block->Append(b.Var<workgroup, u32>("e"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Load(var_b);
+ b.Load(var_e);
+ b.Load(var_d);
+ b.Load(var_c);
+ b.Load(var_a);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<workgroup, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+ %d:ptr<workgroup, u32, read_write> = var
+ %e:ptr<workgroup, u32, read_write> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %7:u32 = load %b
+ %8:u32 = load %e
+ %9:u32 = load %d
+ %10:u32 = load %c
+ %11:u32 = load %a
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a, var_b, var_c, var_d, var_e));
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, DirectUse_MultipleFunctions) {
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ auto* var_b = mod.root_block->Append(b.Var<workgroup, u32>("b"));
+ auto* var_c = mod.root_block->Append(b.Var<workgroup, u32>("c"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Load(var_a);
+ b.Load(var_b);
+ b.Return(foo);
+ });
+
+ auto* bar = b.Function("bar", ty.void_());
+ b.Append(bar->Block(), [&] { //
+ b.Load(var_a);
+ b.Load(var_c);
+ b.Return(bar);
+ });
+
+ auto* zoo = b.Function("zoo", ty.void_());
+ b.Append(zoo->Block(), [&] { //
+ b.Return(zoo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<workgroup, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %5:u32 = load %a
+ %6:u32 = load %b
+ ret
+ }
+}
+%bar = func():void {
+ $B3: {
+ %8:u32 = load %a
+ %9:u32 = load %c
+ ret
+ }
+}
+%zoo = func():void {
+ $B4: {
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a, var_b));
+ EXPECT_THAT(vars.TransitiveReferences(bar), ElementsAre(var_a, var_c));
+ EXPECT_TRUE(vars.TransitiveReferences(zoo).IsEmpty());
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, DirectUse_NestedInControlFlow) {
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ auto* var_b = mod.root_block->Append(b.Var<workgroup, u32>("b"));
+ auto* var_c = mod.root_block->Append(b.Var<workgroup, u32>("c"));
+ auto* var_d = mod.root_block->Append(b.Var<workgroup, u32>("c"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ auto* ifelse = b.If(true);
+ b.Append(ifelse->True(), [&] {
+ b.Load(var_a);
+ b.ExitIf(ifelse);
+ });
+ b.Append(ifelse->False(), [&] {
+ auto* loop = b.Loop();
+ b.Append(loop->Initializer(), [&] {
+ b.Load(var_b);
+ b.NextIteration(loop);
+ });
+ b.Append(loop->Body(), [&] {
+ b.Load(var_c);
+ b.Continue(loop);
+ });
+ b.Append(loop->Continuing(), [&] {
+ b.Load(var_d);
+ b.NextIteration(loop);
+ });
+ b.ExitIf(ifelse);
+ });
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<workgroup, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+ %c_1:ptr<workgroup, u32, read_write> = var # %c_1: 'c'
+}
+
+%foo = func():void {
+ $B2: {
+ if true [t: $B3, f: $B4] { # if_1
+ $B3: { # true
+ %6:u32 = load %a
+ exit_if # if_1
+ }
+ $B4: { # false
+ loop [i: $B5, b: $B6, c: $B7] { # loop_1
+ $B5: { # initializer
+ %7:u32 = load %b
+ next_iteration # -> $B6
+ }
+ $B6: { # body
+ %8:u32 = load %c
+ continue # -> $B7
+ }
+ $B7: { # continuing
+ %9:u32 = load %c_1
+ next_iteration # -> $B6
+ }
+ }
+ exit_if # if_1
+ }
+ }
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a, var_b, var_c, var_d));
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, IndirectUse) {
+ // Directly used by foo.
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ // Directly used by bar, called by zoo and foo.
+ auto* var_b = mod.root_block->Append(b.Var<workgroup, u32>("b"));
+ // Not used.
+ mod.root_block->Append(b.Var<workgroup, u32>("c"));
+
+ auto* bar = b.Function("bar", ty.void_());
+ b.Append(bar->Block(), [&] { //
+ b.Load(var_b);
+ b.Return(bar);
+ });
+
+ auto* zoo = b.Function("zoo", ty.void_());
+ b.Append(zoo->Block(), [&] { //
+ b.Call(bar);
+ b.Return(zoo);
+ });
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Load(var_a);
+ b.Call(zoo);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<workgroup, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+}
+
+%bar = func():void {
+ $B2: {
+ %5:u32 = load %b
+ ret
+ }
+}
+%zoo = func():void {
+ $B3: {
+ %7:void = call %bar
+ ret
+ }
+}
+%foo = func():void {
+ $B4: {
+ %9:u32 = load %a
+ %10:void = call %zoo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(bar), ElementsAre(var_b));
+ EXPECT_THAT(vars.TransitiveReferences(zoo), ElementsAre(var_b));
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a, var_b));
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, NoFunctionVars) {
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ auto* var_b = b.Var<function, u32>("b");
+ b.Load(var_a);
+ b.Load(var_b);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %b:ptr<function, u32, read_write> = var
+ %4:u32 = load %a
+ %5:u32 = load %b
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod);
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_a));
+}
+
+TEST_F(IR_ReferencedModuleVarsTest, Predicate) {
+ auto* var_a = mod.root_block->Append(b.Var<workgroup, u32>("a"));
+ auto* var_b = mod.root_block->Append(b.Var<private_, u32>("b"));
+ auto* var_c = mod.root_block->Append(b.Var<workgroup, u32>("c"));
+ auto* var_d = mod.root_block->Append(b.Var<private_, u32>("d"));
+ auto* var_e = mod.root_block->Append(b.Var<workgroup, u32>("e"));
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] { //
+ b.Load(var_a);
+ b.Load(var_b);
+ b.Load(var_c);
+ b.Load(var_d);
+ b.Load(var_e);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<workgroup, u32, read_write> = var
+ %b:ptr<private, u32, read_write> = var
+ %c:ptr<workgroup, u32, read_write> = var
+ %d:ptr<private, u32, read_write> = var
+ %e:ptr<workgroup, u32, read_write> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %7:u32 = load %a
+ %8:u32 = load %b
+ %9:u32 = load %c
+ %10:u32 = load %d
+ %11:u32 = load %e
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, Disassemble());
+
+ ReferencedModuleVars vars(mod, [](const Var* var) {
+ auto* view = var->Result(0)->Type()->As<type::MemoryView>();
+ return view->AddressSpace() == AddressSpace::kPrivate;
+ });
+ EXPECT_THAT(vars.TransitiveReferences(foo), ElementsAre(var_b, var_d));
+}
+
+} // namespace
+} // namespace tint::core::ir
diff --git a/src/tint/lang/core/ir/transform/conversion_polyfill.cc b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
index c5f7bbd..76d1920 100644
--- a/src/tint/lang/core/ir/transform/conversion_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/conversion_polyfill.cc
@@ -82,13 +82,7 @@
// Polyfill the conversion instructions that we found.
for (auto* convert : ftoi_worklist) {
- auto* replacement = ftoi(convert);
-
- // Replace the old conversion instruction result with the new value.
- if (auto name = ir.NameOf(convert->Result(0))) {
- ir.SetName(replacement, name);
- }
- convert->Result(0)->ReplaceAllUsesWith(replacement);
+ ftoi(convert);
convert->Destroy();
}
}
@@ -96,8 +90,7 @@
/// Replace a conversion instruction with a call to helper function that manually clamps the
/// result to within the limit of the destination type.
/// @param convert the conversion instruction
- /// @returns the replacement value
- ir::Value* ftoi(ir::Convert* convert) {
+ void ftoi(ir::Convert* convert) {
auto* res_ty = convert->Result(0)->Type();
auto* src_ty = convert->Args()[0]->Type();
auto* src_el_ty = src_ty->DeepestElement();
@@ -190,9 +183,8 @@
});
// Call the helper function, splatting the arguments to match the target vector width.
- auto* call = b.Call(res_ty, helper, convert->Args()[0]);
+ auto* call = b.CallWithResult(convert->DetachResult(), helper, convert->Args()[0]);
call->InsertBefore(convert);
- return call->Result(0);
}
/// Return a type with element type @p type that has the same number of vector components as
diff --git a/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc b/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
index d63e3ba..aa0d1dc 100644
--- a/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
+++ b/src/tint/lang/core/ir/transform/multiplanar_external_texture.cc
@@ -240,19 +240,18 @@
}
// Call the `TextureLoadExternal()` helper function.
- auto* helper = b.Call(ty.vec4<f32>(), TextureLoadExternal(), plane_0,
- plane_1, params, coords);
+ auto* helper = b.CallWithResult(call->DetachResult(), TextureLoadExternal(),
+ plane_0, plane_1, params, coords);
helper->InsertBefore(call);
- call->Result(0)->ReplaceAllUsesWith(helper->Result(0));
call->Destroy();
} else if (call->Func() == core::BuiltinFn::kTextureSampleBaseClampToEdge) {
// Call the `TextureSampleExternal()` helper function.
auto* sampler = call->Args()[1];
auto* coords = call->Args()[2];
- auto* helper = b.Call(ty.vec4<f32>(), TextureSampleExternal(), plane_0,
- plane_1, params, sampler, coords);
+ auto* helper =
+ b.CallWithResult(call->DetachResult(), TextureSampleExternal(), plane_0,
+ plane_1, params, sampler, coords);
helper->InsertBefore(call);
- call->Result(0)->ReplaceAllUsesWith(helper->Result(0));
call->Destroy();
} else {
TINT_ICE() << "unhandled texture_external builtin call: " << call->Func();
diff --git a/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc b/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
index 6597fb8..f032ba8 100644
--- a/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/lang/core/ir/transform/multiplanar_external_texture_test.cc
@@ -352,8 +352,8 @@
%6:texture_2d<f32> = load %texture_plane0
%7:texture_2d<f32> = load %texture_plane1
%8:tint_ExternalTextureParams = load %texture_params
- %9:vec4<f32> = call %tint_TextureLoadExternal, %6, %7, %8, %coords
- ret %9
+ %result:vec4<f32> = call %tint_TextureLoadExternal, %6, %7, %8, %coords
+ ret %result
}
}
%tint_TextureLoadExternal = func(%plane_0:texture_2d<f32>, %plane_1:texture_2d<f32>, %params:tint_ExternalTextureParams, %coords_1:vec2<u32>):vec4<f32> { # %coords_1: 'coords'
@@ -514,8 +514,8 @@
%7:texture_2d<f32> = load %texture_plane1
%8:tint_ExternalTextureParams = load %texture_params
%9:vec2<u32> = convert %coords
- %10:vec4<f32> = call %tint_TextureLoadExternal, %6, %7, %8, %9
- ret %10
+ %result:vec4<f32> = call %tint_TextureLoadExternal, %6, %7, %8, %9
+ ret %result
}
}
%tint_TextureLoadExternal = func(%plane_0:texture_2d<f32>, %plane_1:texture_2d<f32>, %params:tint_ExternalTextureParams, %coords_1:vec2<u32>):vec4<f32> { # %coords_1: 'coords'
@@ -677,8 +677,8 @@
%7:texture_2d<f32> = load %texture_plane0
%8:texture_2d<f32> = load %texture_plane1
%9:tint_ExternalTextureParams = load %texture_params
- %10:vec4<f32> = call %tint_TextureSampleExternal, %7, %8, %9, %sampler, %coords
- ret %10
+ %result:vec4<f32> = call %tint_TextureSampleExternal, %7, %8, %9, %sampler, %coords
+ ret %result
}
}
%tint_TextureSampleExternal = func(%plane_0:texture_2d<f32>, %plane_1:texture_2d<f32>, %params:tint_ExternalTextureParams, %sampler_1:sampler, %coords_1:vec2<f32>):vec4<f32> { # %sampler_1: 'sampler', %coords_1: 'coords'
@@ -856,8 +856,8 @@
%foo = func(%texture_plane0_1:texture_2d<f32>, %texture_plane1_1:texture_2d<f32>, %texture_params_1:tint_ExternalTextureParams, %sampler:sampler, %coords:vec2<f32>):vec4<f32> { # %texture_plane0_1: 'texture_plane0', %texture_plane1_1: 'texture_plane1', %texture_params_1: 'texture_params'
$B2: {
- %10:vec4<f32> = call %tint_TextureSampleExternal, %texture_plane0_1, %texture_plane1_1, %texture_params_1, %sampler, %coords
- ret %10
+ %result:vec4<f32> = call %tint_TextureSampleExternal, %texture_plane0_1, %texture_plane1_1, %texture_params_1, %sampler, %coords
+ ret %result
}
}
%bar = func(%sampler_1:sampler, %coords_1:vec2<f32>):vec4<f32> { # %sampler_1: 'sampler', %coords_1: 'coords'
@@ -865,8 +865,8 @@
%15:texture_2d<f32> = load %texture_plane0
%16:texture_2d<f32> = load %texture_plane1
%17:tint_ExternalTextureParams = load %texture_params
- %result:vec4<f32> = call %foo, %15, %16, %17, %sampler_1, %coords_1
- ret %result
+ %result_1:vec4<f32> = call %foo, %15, %16, %17, %sampler_1, %coords_1 # %result_1: 'result'
+ ret %result_1
}
}
%tint_TextureSampleExternal = func(%plane_0:texture_2d<f32>, %plane_1:texture_2d<f32>, %params:tint_ExternalTextureParams, %sampler_2:sampler, %coords_2:vec2<f32>):vec4<f32> { # %sampler_2: 'sampler', %coords_2: 'coords'
@@ -1062,8 +1062,8 @@
%foo = func(%texture_plane0_1:texture_2d<f32>, %texture_plane1_1:texture_2d<f32>, %texture_params_1:tint_ExternalTextureParams, %sampler:sampler, %coords:vec2<f32>):vec4<f32> { # %texture_plane0_1: 'texture_plane0', %texture_plane1_1: 'texture_plane1', %texture_params_1: 'texture_params'
$B2: {
- %10:vec4<f32> = call %tint_TextureSampleExternal, %texture_plane0_1, %texture_plane1_1, %texture_params_1, %sampler, %coords
- ret %10
+ %result:vec4<f32> = call %tint_TextureSampleExternal, %texture_plane0_1, %texture_plane1_1, %texture_params_1, %sampler, %coords
+ ret %result
}
}
%bar = func(%sampler_1:sampler, %coords_1:vec2<f32>):vec4<f32> { # %sampler_1: 'sampler', %coords_1: 'coords'
diff --git a/src/tint/lang/core/ir/transform/std140.cc b/src/tint/lang/core/ir/transform/std140.cc
index f2caaf5..a61b8bc 100644
--- a/src/tint/lang/core/ir/transform/std140.cc
+++ b/src/tint/lang/core/ir/transform/std140.cc
@@ -27,14 +27,23 @@
#include "src/tint/lang/core/ir/transform/std140.h"
+#include <cstdint>
#include <utility>
+#include "src/tint/lang/core/address_space.h"
#include "src/tint/lang/core/ir/builder.h"
+#include "src/tint/lang/core/ir/function_param.h"
#include "src/tint/lang/core/ir/module.h"
#include "src/tint/lang/core/ir/validator.h"
#include "src/tint/lang/core/type/array.h"
#include "src/tint/lang/core/type/matrix.h"
+#include "src/tint/lang/core/type/memory_view.h"
+#include "src/tint/lang/core/type/pointer.h"
#include "src/tint/lang/core/type/struct.h"
+#include "src/tint/lang/core/type/type.h"
+#include "src/tint/utils/containers/hashmap.h"
+#include "src/tint/utils/containers/vector.h"
+#include "src/tint/utils/text/string_stream.h"
using namespace tint::core::fluent_types; // NOLINT
using namespace tint::core::number_suffixes; // NOLINT
@@ -73,43 +82,51 @@
}
// Find uniform buffers that contain matrices that need to be decomposed.
- Vector<Var*, 8> buffer_variables;
+ Vector<std::pair<Var*, const core::type::Type*>, 8> buffer_variables;
for (auto inst : *ir.root_block) {
- auto* var = inst->As<Var>();
- if (!var || !var->Alive()) {
- continue;
- }
- auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
- if (!ptr || ptr->AddressSpace() != core::AddressSpace::kUniform) {
- continue;
- }
- if (RewriteType(ptr->StoreType()) != ptr->StoreType()) {
- buffer_variables.Push(var);
+ if (auto* var = inst->As<Var>()) {
+ auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
+ if (!ptr || ptr->AddressSpace() != core::AddressSpace::kUniform) {
+ continue;
+ }
+ auto* store_type = RewriteType(ptr->StoreType());
+ if (store_type != ptr->StoreType()) {
+ buffer_variables.Push(std::make_pair(var, store_type));
+ }
}
}
// Now process the buffer variables, replacing them with new variables that have decomposed
// matrices and updating all usages of the variables.
- for (auto* var : buffer_variables) {
+ for (auto var_and_ty : buffer_variables) {
// Create a new variable with the modified store type.
- const auto& bp = var->BindingPoint();
- auto* store_type = var->Result(0)->Type()->As<core::type::Pointer>()->StoreType();
- auto* new_var = b.Var(ty.ptr(uniform, RewriteType(store_type)));
+ auto* old_var = var_and_ty.first;
+ auto* new_var = b.Var(ty.ptr(uniform, var_and_ty.second));
+ const auto& bp = old_var->BindingPoint();
new_var->SetBindingPoint(bp->group, bp->binding);
- if (auto name = ir.NameOf(var)) {
+ if (auto name = ir.NameOf(old_var)) {
ir.SetName(new_var->Result(0), name);
}
- // Replace every instruction that uses the original variable.
- var->Result(0)->ForEachUse(
+ // Transform instructions that accessed the variable to use the decomposed var.
+ old_var->Result(0)->ForEachUse(
[&](Usage use) { Replace(use.instruction, new_var->Result(0)); });
// Replace the original variable with the new variable.
- var->ReplaceWith(new_var);
- var->Destroy();
+ old_var->ReplaceWith(new_var);
+ old_var->Destroy();
}
}
+ /// @param type the type to check
+ /// @returns the matrix if @p type is a matrix that needs to be decomposed
+ static const core::type::Matrix* NeedsDecomposing(const core::type::Type* type) {
+ if (auto* mat = type->As<core::type::Matrix>(); mat && NeedsDecomposing(mat)) {
+ return mat;
+ }
+ return nullptr;
+ }
+
/// @param mat the matrix type to check
/// @returns true if @p mat needs to be decomposed
static bool NeedsDecomposing(const core::type::Matrix* mat) {
@@ -125,10 +142,10 @@
/// @param type the type to rewrite
/// @returns the new type
const core::type::Type* RewriteType(const core::type::Type* type) {
- return rewritten_types.GetOrAdd(type, [&]() -> const core::type::Type* {
+ return rewritten_types.GetOrAdd(type, [&] {
return tint::Switch(
type,
- [&](const core::type::Array* arr) -> const core::type::Type* {
+ [&](const core::type::Array* arr) {
// Create a new array with element type potentially rewritten.
return ty.array(RewriteType(arr->ElemType()), arr->ConstantCount().value());
},
@@ -137,8 +154,7 @@
uint32_t member_index = 0;
Vector<const core::type::StructMember*, 4> new_members;
for (auto* member : str->Members()) {
- auto* mat = member->Type()->As<core::type::Matrix>();
- if (mat && NeedsDecomposing(mat)) {
+ if (auto* mat = NeedsDecomposing(member->Type())) {
// Decompose these matrices into a separate member for each column.
member_index_map.Add(member, member_index);
auto* col = mat->ColumnType();
@@ -182,6 +198,32 @@
}
return new_str;
},
+ [&](const core::type::Matrix* mat) -> const core::type::Type* {
+ if (!NeedsDecomposing(mat)) {
+ return mat;
+ }
+ StringStream name;
+ name << "mat" << mat->columns() << "x" << mat->rows() << "_"
+ << mat->ColumnType()->type()->FriendlyName() << "_std140";
+ Vector<core::type::StructMember*, 4> members;
+ // Decompose these matrices into a separate member for each column.
+ auto* col = mat->ColumnType();
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < mat->columns(); i++) {
+ StringStream ss;
+ ss << "col" << std::to_string(i);
+ members.Push(ty.Get<core::type::StructMember>(
+ sym.New(ss.str()), col, i, offset, col->Align(), col->Size(),
+ core::type::StructMemberAttributes{}));
+ offset += col->Align();
+ }
+
+ // Create a new struct with the rewritten members.
+ return ty.Get<core::type::Struct>(
+ sym.New(name.str()), std::move(members), col->Align(),
+ col->Align() * mat->columns(),
+ (col->Align() * (mat->columns() - 1)) + col->Size());
+ },
[&](Default) {
// This type cannot contain a matrix, so no changes needed.
return type;
@@ -189,19 +231,26 @@
});
}
- /// Load a decomposed matrix from a structure.
+ /// Reconstructs a column-decomposed matrix.
/// @param mat the matrix type
/// @param root the root value being accessed into
- /// @param indices the access indices that get to the first column of the decomposed matrix
+ /// @param indices the access indices that index the first column of the matrix.
/// @returns the loaded matrix
- Value* LoadMatrix(const core::type::Matrix* mat, Value* root, Vector<Value*, 4> indices) {
- // Load each column vector from the struct and reconstruct the original matrix type.
+ Value* RebuildMatrix(const core::type::Matrix* mat, Value* root, VectorRef<Value*> indices) {
+ // Recombine each column vector from the struct and reconstruct the original matrix type.
+ bool is_ptr = root->Type()->Is<core::type::Pointer>();
+ Vector<Value*, 4> column_indices(std::move(indices));
Vector<Value*, 4> args;
auto first_column = indices.Back()->As<Constant>()->Value()->ValueAs<uint32_t>();
for (uint32_t i = 0; i < mat->columns(); i++) {
- indices.Back() = b.Constant(u32(first_column + i));
- auto* access = b.Access(ty.ptr(uniform, mat->ColumnType()), root, indices);
- args.Push(b.Load(access->Result(0))->Result(0));
+ column_indices.Back() = b.Constant(u32(first_column + i));
+ if (is_ptr) {
+ auto* access = b.Access(ty.ptr(uniform, mat->ColumnType()), root, column_indices);
+ args.Push(b.Load(access)->Result(0));
+ } else {
+ auto* access = b.Access(mat->ColumnType(), root, column_indices);
+ args.Push(access->Result(0));
+ }
}
return b.Construct(mat, std::move(args))->Result(0);
}
@@ -228,16 +277,10 @@
uint32_t index = 0;
Vector<Value*, 4> args;
for (auto* member : str->Members()) {
- if (auto* mat = member->Type()->As<core::type::Matrix>();
- mat && NeedsDecomposing(mat)) {
- // Extract each decomposed column and reconstruct the matrix.
- Vector<Value*, 4> columns;
- for (uint32_t i = 0; i < mat->columns(); i++) {
- auto* extract = b.Access(mat->ColumnType(), input, u32(index));
- columns.Push(extract->Result(0));
- index++;
- }
- args.Push(b.Construct(mat, std::move(columns))->Result(0));
+ if (auto* mat = NeedsDecomposing(member->Type())) {
+ args.Push(
+ RebuildMatrix(mat, input, Vector{b.Constant(u32(index))}));
+ index += mat->columns();
} else {
// Extract and convert the member.
auto* type = input_str->Element(index);
@@ -268,6 +311,12 @@
});
return b.Load(new_arr)->Result(0);
},
+ [&](const core::type::Matrix* mat) -> Value* {
+ if (!NeedsDecomposing(mat)) {
+ return source;
+ }
+ return RebuildMatrix(mat, source, Vector{b.Constant(u32(0))});
+ },
[&](Default) { return source; });
}
@@ -279,28 +328,75 @@
tint::Switch(
inst, //
[&](Access* access) {
+ auto* object_ty = access->Object()->Type()->As<core::type::MemoryView>();
+ if (!object_ty || object_ty->AddressSpace() != core::AddressSpace::kUniform) {
+ // Access to non-uniform memory views does not require transformation.
+ return;
+ }
+
+ if (!replacement->Type()->Is<core::type::MemoryView>()) {
+ // The replacement is a value, in which case the decomposed matrix has
+ // already been reconstructed. In this situation the access only needs its
+ // return type updating, and downstream instructions need updating.
+ access->SetOperand(Access::kObjectOperandOffset, replacement);
+ auto* result = access->Result(0);
+ result->SetType(result->Type()->UnwrapPtrOrRef());
+ result->ForEachUse([&](Usage use) { Replace(use.instruction, result); });
+ return;
+ }
+
// Modify the access indices to take decomposed matrices into account.
- auto* current_type = access->Object()->Type()->UnwrapPtr();
+ auto* current_type = object_ty->StoreType();
Vector<Value*, 4> indices;
- for (auto idx : access->Indices()) {
- if (auto* str = current_type->As<core::type::Struct>()) {
+
+ if (NeedsDecomposing(current_type)) {
+ // Decomposed matrices are indexed using their first column vector
+ indices.Push(b.Constant(0_u));
+ }
+
+ for (size_t i = 0, n = access->Indices().Length(); i < n; i++) {
+ auto* idx = access->Indices()[i];
+
+ if (auto* mat = NeedsDecomposing(current_type)) {
+ // Access chain passes through decomposed matrix.
+ if (auto* const_idx = idx->As<Constant>()) {
+ // Column vector index is a constant.
+ // Instead of loading the whole matrix, fold the access of the
+ // matrix and the constant column index into an single access of
+ // column vector member.
+ auto* base_idx = indices.Back()->As<Constant>();
+ indices.Back() =
+ b.Constant(u32(base_idx->Value()->ValueAs<uint32_t>() +
+ const_idx->Value()->ValueAs<uint32_t>()));
+ current_type = mat->ColumnType();
+ i++; // We've already consumed the column access
+ } else {
+ // Column vector index is dynamic.
+ // Reconstruct the whole matrix and index that.
+ replacement = RebuildMatrix(mat, replacement, std::move(indices));
+ indices.Clear();
+ indices.Push(idx);
+ current_type = mat->ColumnType();
+ }
+ } else if (auto* str = current_type->As<core::type::Struct>()) {
+ // Remap member index
uint32_t old_index = idx->As<Constant>()->Value()->ValueAs<uint32_t>();
uint32_t new_index = *member_index_map.Get(str->Members()[old_index]);
- indices.Push(b.Constant(u32(new_index)));
current_type = str->Element(old_index);
+ indices.Push(b.Constant(u32(new_index)));
} else {
indices.Push(idx);
current_type = current_type->Elements().type;
+ if (NeedsDecomposing(current_type)) {
+ // Decomposed matrices are indexed using their first column vector
+ indices.Push(b.Constant(0_u));
+ }
}
+ }
- // If we've hit a matrix that was decomposed, load the whole matrix.
- // Any additional accesses will extract columns instead of producing
- // pointers.
- if (auto* mat = current_type->As<core::type::Matrix>();
- mat && NeedsDecomposing(mat)) {
- replacement = LoadMatrix(mat, replacement, std::move(indices));
- indices.Clear();
- }
+ if (auto* mat = NeedsDecomposing(current_type)) {
+ replacement = RebuildMatrix(mat, replacement, std::move(indices));
+ indices.Clear();
}
if (!indices.IsEmpty()) {
@@ -333,9 +429,7 @@
if (!replacement->Type()->Is<core::type::Pointer>()) {
// We have loaded a decomposed matrix and reconstructed it, so this is now
// extracting from a value type.
- auto* access =
- b.Access(load->Result(0)->Type(), replacement, load->Index());
- load->Result(0)->ReplaceAllUsesWith(access->Result(0));
+ b.AccessWithResult(load->DetachResult(), replacement, load->Index());
load->Destroy();
} else {
// There was no decomposed matrix on the path to this instruction so just
diff --git a/src/tint/lang/core/ir/transform/std140.h b/src/tint/lang/core/ir/transform/std140.h
index f931546..89852db 100644
--- a/src/tint/lang/core/ir/transform/std140.h
+++ b/src/tint/lang/core/ir/transform/std140.h
@@ -41,6 +41,8 @@
/// Std140 is a transform that rewrites matrix types in the uniform address space to conform to
/// GLSL's std140 layout rules.
+/// @note requires the DirectVariableAccess transform to have been run first to remove uniform
+/// pointer parameters.
/// @param module the module to transform
/// @returns success or failure
Result<SuccessType> Std140(Module& module);
diff --git a/src/tint/lang/core/ir/transform/std140_fuzz.cc b/src/tint/lang/core/ir/transform/std140_fuzz.cc
index 86e5730..091def8 100644
--- a/src/tint/lang/core/ir/transform/std140_fuzz.cc
+++ b/src/tint/lang/core/ir/transform/std140_fuzz.cc
@@ -28,12 +28,31 @@
#include "src/tint/lang/core/ir/transform/std140.h"
#include "src/tint/cmd/fuzz/ir/fuzz.h"
+#include "src/tint/lang/core/address_space.h"
+#include "src/tint/lang/core/ir/module.h"
#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/core/type/pointer.h"
namespace tint::core::ir::transform {
namespace {
+bool CanRun(Module& module) {
+ for (auto& fn : module.functions) {
+ for (auto* param : fn->Params()) {
+ if (auto* ptr = param->Type()->As<core::type::Pointer>();
+ ptr && ptr->AddressSpace() == core::AddressSpace::kUniform) {
+ return false; // Requires the DirectVariableAccess transform
+ }
+ }
+ }
+ return true;
+}
+
void Std140Fuzzer(Module& module) {
+ if (!CanRun(module)) {
+ return;
+ }
+
if (auto res = Std140(module); res != Success) {
return;
}
diff --git a/src/tint/lang/core/ir/transform/std140_test.cc b/src/tint/lang/core/ir/transform/std140_test.cc
index dd55108..28a67fa 100644
--- a/src/tint/lang/core/ir/transform/std140_test.cc
+++ b/src/tint/lang/core/ir/transform/std140_test.cc
@@ -29,6 +29,8 @@
#include <utility>
+#include "src/tint/lang/core/fluent_types.h"
+#include "src/tint/lang/core/ir/load_vector_element.h"
#include "src/tint/lang/core/ir/transform/helper_test.h"
#include "src/tint/lang/core/type/array.h"
#include "src/tint/lang/core/type/matrix.h"
@@ -148,8 +150,7 @@
EXPECT_EQ(expect, str());
}
-// Test that we do not decompose a mat2x2 that is used an array element type.
-TEST_F(IR_Std140Test, NoModify_Mat2x2_InsideArray) {
+TEST_F(IR_Std140Test, Load_Mat2x2f_InArray) {
auto* mat = ty.mat2x2<f32>();
auto* structure =
ty.Struct(mod.symbols.New("MyStruct"), {
@@ -186,7 +187,35 @@
)";
EXPECT_EQ(src, str());
- auto* expect = src;
+ auto* expect = R"(
+MyStruct = struct @align(8), @block {
+ arr:array<mat2x2<f32>, 4> @offset(0)
+}
+
+mat2x2_f32_std140 = struct @align(8) {
+ col0:vec2<f32> @offset(0)
+ col1:vec2<f32> @offset(8)
+}
+
+MyStruct_std140 = struct @align(8), @block {
+ arr:array<mat2x2_f32_std140, 4> @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<uniform, MyStruct_std140, read> = var @binding_point(0, 0)
+}
+
+%foo = func():mat2x2<f32> {
+ $B2: {
+ %3:ptr<uniform, vec2<f32>, read> = access %buffer, 0u, 2u, 0u
+ %4:vec2<f32> = load %3
+ %5:ptr<uniform, vec2<f32>, read> = access %buffer, 0u, 2u, 1u
+ %6:vec2<f32> = load %5
+ %7:mat2x2<f32> = construct %4, %6
+ ret %7
+ }
+}
+)";
Run(Std140);
@@ -264,7 +293,7 @@
EXPECT_EQ(expect, str());
}
-TEST_F(IR_Std140Test, Mat3x2_LoadColumn) {
+TEST_F(IR_Std140Test, Mat3x2_LoadConstantColumn) {
auto* mat = ty.mat3x2<f32>();
auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
{mod.symbols.New("a"), mat},
@@ -318,15 +347,83 @@
%foo = func():vec2<f32> {
$B2: {
- %3:ptr<uniform, vec2<f32>, read> = access %buffer, 0u
+ %3:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
%4:vec2<f32> = load %3
- %5:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
- %6:vec2<f32> = load %5
- %7:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
- %8:vec2<f32> = load %7
- %9:mat3x2<f32> = construct %4, %6, %8
- %10:vec2<f32> = access %9, 1u
- ret %10
+ ret %4
+ }
+}
+)";
+
+ Run(Std140);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_Std140Test, Mat3x2_LoadDynamicColumn) {
+ auto* mat = ty.mat3x2<f32>();
+ auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("a"), mat},
+ });
+ structure->SetStructFlag(core::type::kBlock);
+
+ auto* buffer = b.Var("buffer", ty.ptr(uniform, structure));
+ buffer->SetBindingPoint(0, 0);
+ mod.root_block->Append(buffer);
+
+ auto* func = b.Function("foo", mat->ColumnType());
+ auto* column = b.FunctionParam<i32>("column");
+ func->AppendParam(column);
+ b.Append(func->Block(), [&] {
+ auto* access = b.Access(ty.ptr(uniform, mat->ColumnType()), buffer, 0_u, column);
+ auto* load = b.Load(access);
+ b.Return(func, load);
+ });
+
+ auto* src = R"(
+MyStruct = struct @align(8), @block {
+ a:mat3x2<f32> @offset(0)
+}
+
+$B1: { # root
+ %buffer:ptr<uniform, MyStruct, read> = var @binding_point(0, 0)
+}
+
+%foo = func(%column:i32):vec2<f32> {
+ $B2: {
+ %4:ptr<uniform, vec2<f32>, read> = access %buffer, 0u, %column
+ %5:vec2<f32> = load %4
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(8), @block {
+ a:mat3x2<f32> @offset(0)
+}
+
+MyStruct_std140 = struct @align(8), @block {
+ a_col0:vec2<f32> @offset(0)
+ a_col1:vec2<f32> @offset(8)
+ a_col2:vec2<f32> @offset(16)
+}
+
+$B1: { # root
+ %buffer:ptr<uniform, MyStruct_std140, read> = var @binding_point(0, 0)
+}
+
+%foo = func(%column:i32):vec2<f32> {
+ $B2: {
+ %4:ptr<uniform, vec2<f32>, read> = access %buffer, 0u
+ %5:vec2<f32> = load %4
+ %6:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
+ %7:vec2<f32> = load %6
+ %8:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
+ %9:vec2<f32> = load %8
+ %10:mat3x2<f32> = construct %5, %7, %9
+ %11:vec2<f32> = access %10, %column
+ ret %11
}
}
)";
@@ -390,16 +487,9 @@
%foo = func():f32 {
$B2: {
- %3:ptr<uniform, vec2<f32>, read> = access %buffer, 0u
- %4:vec2<f32> = load %3
- %5:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
- %6:vec2<f32> = load %5
- %7:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
- %8:vec2<f32> = load %7
- %9:mat3x2<f32> = construct %4, %6, %8
- %10:vec2<f32> = access %9, 1u
- %11:f32 = access %10, 1u
- ret %11
+ %3:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
+ %4:f32 = load_vector_element %3, 1u
+ ret %4
}
}
)";
@@ -1709,48 +1799,35 @@
}
%load_vec_b = func():f32 {
$B7: {
- %29:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
+ %29:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
%30:vec2<f32> = load %29
- %31:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
- %32:vec2<f32> = load %31
- %33:ptr<uniform, vec2<f32>, read> = access %buffer, 3u
- %34:vec2<f32> = load %33
- %35:mat3x2<f32> = construct %30, %32, %34
- %36:vec2<f32> = access %35, 1u
- %37:f32 = access %36, 1u
- ret %37
+ %31:f32 = access %30, 1u
+ ret %31
}
}
%lve_a = func():f32 {
$B8: {
- %39:ptr<uniform, vec4<f32>, read> = access %buffer, 0u, 1u
- %40:f32 = load_vector_element %39, 1u
- ret %40
+ %33:ptr<uniform, vec4<f32>, read> = access %buffer, 0u, 1u
+ %34:f32 = load_vector_element %33, 1u
+ ret %34
}
}
%lve_b = func():f32 {
$B9: {
- %42:ptr<uniform, vec2<f32>, read> = access %buffer, 1u
- %43:vec2<f32> = load %42
- %44:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
- %45:vec2<f32> = load %44
- %46:ptr<uniform, vec2<f32>, read> = access %buffer, 3u
- %47:vec2<f32> = load %46
- %48:mat3x2<f32> = construct %43, %45, %47
- %49:vec2<f32> = access %48, 1u
- %50:f32 = access %49, 1u
- ret %50
+ %36:ptr<uniform, vec2<f32>, read> = access %buffer, 2u
+ %37:f32 = load_vector_element %36, 1u
+ ret %37
}
}
%convert_MyStruct = func(%input:MyStruct_std140):MyStruct {
$B10: {
- %52:mat4x4<f32> = access %input, 0u
- %53:vec2<f32> = access %input, 1u
- %54:vec2<f32> = access %input, 2u
- %55:vec2<f32> = access %input, 3u
- %56:mat3x2<f32> = construct %53, %54, %55
- %57:MyStruct = construct %52, %56
- ret %57
+ %39:mat4x4<f32> = access %input, 0u
+ %40:vec2<f32> = access %input, 1u
+ %41:vec2<f32> = access %input, 2u
+ %42:vec2<f32> = access %input, 3u
+ %43:mat3x2<f32> = construct %40, %41, %42
+ %44:MyStruct = construct %39, %43
+ ret %44
}
}
)";
@@ -1857,48 +1934,295 @@
%14:vec4<f16> = load %13
%15:mat4x4<f16> = construct %8, %10, %12, %14
%mat:mat4x4<f16> = let %15
- %17:ptr<uniform, vec3<f16>, read> = access %buffer, 4u
+ %17:ptr<uniform, vec3<f16>, read> = access %buffer, 5u
%18:vec3<f16> = load %17
- %19:ptr<uniform, vec3<f16>, read> = access %buffer, 5u
- %20:vec3<f16> = load %19
- %21:ptr<uniform, vec3<f16>, read> = access %buffer, 6u
- %22:vec3<f16> = load %21
- %23:ptr<uniform, vec3<f16>, read> = access %buffer, 7u
- %24:vec3<f16> = load %23
- %25:mat4x3<f16> = construct %18, %20, %22, %24
- %26:vec3<f16> = access %25, 1u
- %col:vec3<f16> = let %26
- %28:ptr<uniform, vec4<f16>, read> = access %buffer, 2u
- %29:vec4<f16> = load %28
- %30:ptr<uniform, vec4<f16>, read> = access %buffer, 3u
- %31:vec4<f16> = load %30
- %32:mat2x4<f16> = construct %29, %31
- %33:vec4<f16> = access %32, 0u
- %34:f16 = access %33, 3u
- %el:f16 = let %34
+ %col:vec3<f16> = let %18
+ %20:ptr<uniform, vec4<f16>, read> = access %buffer, 2u
+ %21:f16 = load_vector_element %20, 3u
+ %el:f16 = let %21
ret
}
}
%convert_MyStruct = func(%input:MyStruct_std140):MyStruct {
$B3: {
- %37:vec2<f16> = access %input, 0u
- %38:vec2<f16> = access %input, 1u
- %39:mat2x2<f16> = construct %37, %38
- %40:vec4<f16> = access %input, 2u
- %41:vec4<f16> = access %input, 3u
- %42:mat2x4<f16> = construct %40, %41
- %43:vec3<f16> = access %input, 4u
- %44:vec3<f16> = access %input, 5u
- %45:vec3<f16> = access %input, 6u
- %46:vec3<f16> = access %input, 7u
- %47:mat4x3<f16> = construct %43, %44, %45, %46
- %48:vec4<f16> = access %input, 8u
- %49:vec4<f16> = access %input, 9u
- %50:vec4<f16> = access %input, 10u
- %51:vec4<f16> = access %input, 11u
- %52:mat4x4<f16> = construct %48, %49, %50, %51
- %53:MyStruct = construct %39, %42, %47, %52
- ret %53
+ %24:vec2<f16> = access %input, 0u
+ %25:vec2<f16> = access %input, 1u
+ %26:mat2x2<f16> = construct %24, %25
+ %27:vec4<f16> = access %input, 2u
+ %28:vec4<f16> = access %input, 3u
+ %29:mat2x4<f16> = construct %27, %28
+ %30:vec3<f16> = access %input, 4u
+ %31:vec3<f16> = access %input, 5u
+ %32:vec3<f16> = access %input, 6u
+ %33:vec3<f16> = access %input, 7u
+ %34:mat4x3<f16> = construct %30, %31, %32, %33
+ %35:vec4<f16> = access %input, 8u
+ %36:vec4<f16> = access %input, 9u
+ %37:vec4<f16> = access %input, 10u
+ %38:vec4<f16> = access %input, 11u
+ %39:mat4x4<f16> = construct %35, %36, %37, %38
+ %40:MyStruct = construct %26, %29, %34, %39
+ ret %40
+ }
+}
+)";
+
+ Run(Std140);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_Std140Test, Mat3x3f_And_ArrayMat4x3f) { // crbug.com/338727551
+ auto* s =
+ ty.Struct(mod.symbols.New("S"), {
+ {mod.symbols.New("a"), ty.mat3x3<f32>()},
+ {mod.symbols.New("b"), ty.array<mat4x3<f32>, 3>()},
+ });
+ s->SetStructFlag(core::type::kBlock);
+
+ auto* u = b.Var("u", ty.ptr(uniform, s));
+ u->SetBindingPoint(0, 0);
+ mod.root_block->Append(u);
+
+ auto* f = b.Function("F", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* p = b.Access<ptr<uniform, vec3<f32>, read>>(u, 1_u, 0_u, 0_u);
+ auto* x = b.LoadVectorElement(p, 0_u);
+ b.Return(f, x);
+ });
+
+ auto* src = R"(
+S = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:array<mat4x3<f32>, 3> @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec3<f32>, read> = access %u, 1u, 0u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+S = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:array<mat4x3<f32>, 3> @offset(48)
+}
+
+mat4x3_f32_std140 = struct @align(16) {
+ col0:vec3<f32> @offset(0)
+ col1:vec3<f32> @offset(16)
+ col2:vec3<f32> @offset(32)
+ col3:vec3<f32> @offset(48)
+}
+
+S_std140 = struct @align(16), @block {
+ a_col0:vec3<f32> @offset(0)
+ a_col1:vec3<f32> @offset(16)
+ a_col2:vec3<f32> @offset(32)
+ b:array<mat4x3_f32_std140, 3> @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S_std140, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec3<f32>, read> = access %u, 3u, 0u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
+ }
+}
+)";
+
+ Run(Std140);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_Std140Test, Mat3x3f_And_ArrayStructMat4x3f) {
+ auto* s1 =
+ ty.Struct(mod.symbols.New("S1"), {
+ {mod.symbols.New("c"), ty.mat3x3<f32>()},
+ {mod.symbols.New("d"), ty.array<mat4x3<f32>, 3>()},
+ });
+ auto* s2 = ty.Struct(mod.symbols.New("S2"), {
+ {mod.symbols.New("a"), ty.mat3x3<f32>()},
+ {mod.symbols.New("b"), s1},
+ });
+ s2->SetStructFlag(core::type::kBlock);
+
+ auto* u = b.Var("u", ty.ptr(uniform, s2));
+ u->SetBindingPoint(0, 0);
+ mod.root_block->Append(u);
+
+ auto* f = b.Function("F", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* p = b.Access<ptr<uniform, vec3<f32>, read>>(u, 1_u, 1_u, 0_u, 0_u);
+ auto* x = b.LoadVectorElement(p, 0_u);
+ b.Return(f, x);
+ });
+
+ auto* src = R"(
+S1 = struct @align(16) {
+ c:mat3x3<f32> @offset(0)
+ d:array<mat4x3<f32>, 3> @offset(48)
+}
+
+S2 = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:S1 @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S2, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec3<f32>, read> = access %u, 1u, 1u, 0u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+S1 = struct @align(16) {
+ c:mat3x3<f32> @offset(0)
+ d:array<mat4x3<f32>, 3> @offset(48)
+}
+
+S2 = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:S1 @offset(48)
+}
+
+mat4x3_f32_std140 = struct @align(16) {
+ col0:vec3<f32> @offset(0)
+ col1:vec3<f32> @offset(16)
+ col2:vec3<f32> @offset(32)
+ col3:vec3<f32> @offset(48)
+}
+
+S1_std140 = struct @align(16) {
+ c_col0:vec3<f32> @offset(0)
+ c_col1:vec3<f32> @offset(16)
+ c_col2:vec3<f32> @offset(32)
+ d:array<mat4x3_f32_std140, 3> @offset(48)
+}
+
+S2_std140 = struct @align(16), @block {
+ a_col0:vec3<f32> @offset(0)
+ a_col1:vec3<f32> @offset(16)
+ a_col2:vec3<f32> @offset(32)
+ b:S1_std140 @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S2_std140, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec3<f32>, read> = access %u, 3u, 3u, 0u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
+ }
+}
+)";
+
+ Run(Std140);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_Std140Test, Mat3x3f_And_ArrayStructMat2x2f) {
+ auto* s1 = ty.Struct(mod.symbols.New("S1"), {
+ {mod.symbols.New("c"), ty.mat2x2<f32>()},
+ });
+ auto* s2 = ty.Struct(mod.symbols.New("S2"), {
+ {mod.symbols.New("a"), ty.mat3x3<f32>()},
+ {mod.symbols.New("b"), s1},
+ });
+ s2->SetStructFlag(core::type::kBlock);
+
+ auto* u = b.Var("u", ty.ptr(uniform, s2));
+ u->SetBindingPoint(0, 0);
+ mod.root_block->Append(u);
+
+ auto* f = b.Function("F", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* p = b.Access<ptr<uniform, vec2<f32>, read>>(u, 1_u, 0_u, 0_u);
+ auto* x = b.LoadVectorElement(p, 0_u);
+ b.Return(f, x);
+ });
+
+ auto* src = R"(
+S1 = struct @align(8) {
+ c:mat2x2<f32> @offset(0)
+}
+
+S2 = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:S1 @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S2, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec2<f32>, read> = access %u, 1u, 0u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+S1 = struct @align(8) {
+ c:mat2x2<f32> @offset(0)
+}
+
+S2 = struct @align(16), @block {
+ a:mat3x3<f32> @offset(0)
+ b:S1 @offset(48)
+}
+
+S1_std140 = struct @align(8) {
+ c_col0:vec2<f32> @offset(0)
+ c_col1:vec2<f32> @offset(8)
+}
+
+S2_std140 = struct @align(16), @block {
+ a_col0:vec3<f32> @offset(0)
+ a_col1:vec3<f32> @offset(16)
+ a_col2:vec3<f32> @offset(32)
+ b:S1_std140 @offset(48)
+}
+
+$B1: { # root
+ %u:ptr<uniform, S2_std140, read> = var @binding_point(0, 0)
+}
+
+%F = func():f32 {
+ $B2: {
+ %3:ptr<uniform, vec2<f32>, read> = access %u, 3u, 0u
+ %4:f32 = load_vector_element %3, 0u
+ ret %4
}
}
)";
diff --git a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
index 37bdb03..99d6be9 100644
--- a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
+++ b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc
@@ -86,8 +86,7 @@
}
// Construct the matrix from the column vectors and replace the original instruction.
- auto* replacement = b.Construct(mat, std::move(columns))->Result(0);
- construct->Result(0)->ReplaceAllUsesWith(replacement);
+ b.ConstructWithResult(construct->DetachResult(), std::move(columns));
construct->Destroy();
}
};
diff --git a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
index 26cbf76..8969c9f 100644
--- a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.cc
@@ -32,6 +32,7 @@
#include "src/tint/lang/core/ir/builder.h"
#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/transform/common/referenced_module_vars.h"
#include "src/tint/lang/core/ir/validator.h"
#include "src/tint/utils/containers/reverse.h"
@@ -61,17 +62,12 @@
/// The type manager.
core::type::Manager& ty{ir.Types()};
- /// VarSet is a hash set of workgroup variables.
- using VarSet = Hashset<Var*, 8>;
-
- /// A map from variable to an ID used for sorting.
- Hashmap<Var*, uint32_t, 8> var_to_id{};
-
- /// A map from blocks to their directly referenced workgroup variables.
- Hashmap<Block*, VarSet, 64> block_to_direct_vars{};
-
- /// A map from functions to their transitively referenced workgroup variables.
- Hashmap<Function*, VarSet, 8> function_to_transitive_vars{};
+ /// The mapping from functions to their transitively referenced workgroup variables.
+ ReferencedModuleVars referenced_module_vars_{
+ ir, [](const Var* var) {
+ auto* view = var->Result(0)->Type()->As<type::MemoryView>();
+ return view && view->AddressSpace() == AddressSpace::kWorkgroup;
+ }};
/// ArrayIndex represents a required array index for an access instruction.
struct ArrayIndex {
@@ -104,22 +100,6 @@
if (ir.root_block->IsEmpty()) {
return;
}
-
- // Loop over module-scope variables, looking for workgroup variables.
- uint32_t next_id = 0;
- for (auto inst : *ir.root_block) {
- if (auto* var = inst->As<Var>()) {
- auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
- if (ptr && ptr->AddressSpace() == core::AddressSpace::kWorkgroup) {
- // Record the usage of the variable for each block that references it.
- var->Result(0)->ForEachUse([&](const Usage& use) {
- block_to_direct_vars.GetOrAddZero(use.instruction->Block()).Add(var);
- });
- var_to_id.Add(var, next_id++);
- }
- }
- }
-
// Process each entry point function.
for (auto& func : ir.functions) {
if (func->Stage() == Function::PipelineStage::kCompute) {
@@ -132,20 +112,14 @@
/// @param func the entry point function
void ProcessEntryPoint(Function* func) {
// Get list of transitively referenced workgroup variables.
- auto vars = GetReferencedVars(func);
+ const auto& vars = referenced_module_vars_.TransitiveReferences(func);
if (vars.IsEmpty()) {
return;
}
- // Sort the variables to get deterministic output in tests.
- auto sorted_vars = vars.Vector();
- sorted_vars.Sort([&](Var* first, Var* second) {
- return *var_to_id.Get(first) < *var_to_id.Get(second);
- });
-
// Build list of store descriptors for all workgroup variables.
StoreMap stores;
- for (auto* var : sorted_vars) {
+ for (auto* var : vars) {
PrepareStores(var, var->Result(0)->Type()->UnwrapPtr(), 1, {}, stores);
}
@@ -188,46 +162,6 @@
});
}
- /// Get the set of workgroup variables transitively referenced by @p func.
- /// @param func the function
- /// @returns the set of transitively referenced workgroup variables
- VarSet GetReferencedVars(Function* func) {
- return function_to_transitive_vars.GetOrAdd(func, [&] {
- VarSet vars;
- GetReferencedVars(func->Block(), vars);
- return vars;
- });
- }
-
- /// Get the set of workgroup variables transitively referenced by @p block.
- /// @param block the block
- /// @param vars the set of transitively referenced workgroup variables to populate
- void GetReferencedVars(Block* block, VarSet& vars) {
- // Add directly referenced vars.
- if (auto itr = block_to_direct_vars.Get(block)) {
- for (auto& var : *itr) {
- vars.Add(var);
- }
- }
-
- // Loop over instructions in the block.
- for (auto* inst : *block) {
- tint::Switch(
- inst,
- [&](UserCall* call) {
- // Get variables referenced by a function called from this block.
- auto callee_vars = GetReferencedVars(call->Target());
- for (auto& var : callee_vars) {
- vars.Add(var);
- }
- },
- [&](ControlInstruction* ctrl) {
- // Recurse into control instructions and gather their referenced vars.
- ctrl->ForeachBlock([&](Block* blk) { GetReferencedVars(blk, vars); });
- });
- }
- }
-
/// Recursively generate store descriptors for a workgroup variable.
/// Determines the combined array iteration count of each inner element.
/// @param var the workgroup variable
diff --git a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory_test.cc b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory_test.cc
index bf1a82b..1843665 100644
--- a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory_test.cc
+++ b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory_test.cc
@@ -102,6 +102,48 @@
EXPECT_EQ(expect, str());
}
+TEST_F(IR_ZeroInitWorkgroupMemoryTest, NonWorkgroupVar) {
+ auto* var = b.Var("pvar", ty.ptr(private_, ty.bool_()));
+ mod.root_block->Append(var);
+
+ auto* func = MakeEntryPoint("main", 1, 1, 1);
+ b.Append(func->Block(), [&] { //
+ b.Load(var);
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %pvar:ptr<private, bool, read_write> = var
+}
+
+%main = @compute @workgroup_size(1, 1, 1) func():void {
+ $B2: {
+ %3:bool = load %pvar
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %pvar:ptr<private, bool, read_write> = var
+}
+
+%main = @compute @workgroup_size(1, 1, 1) func():void {
+ $B2: {
+ %3:bool = load %pvar
+ ret
+ }
+}
+)";
+
+ Run(ZeroInitWorkgroupMemory);
+
+ EXPECT_EQ(expect, str());
+}
+
TEST_F(IR_ZeroInitWorkgroupMemoryTest, ScalarBool) {
auto* var = MakeVar("wgvar", ty.bool_());
diff --git a/src/tint/lang/core/ir/user_call_test.cc b/src/tint/lang/core/ir/user_call_test.cc
index 768fea3..252375d 100644
--- a/src/tint/lang/core/ir/user_call_test.cc
+++ b/src/tint/lang/core/ir/user_call_test.cc
@@ -58,7 +58,7 @@
}
TEST_F(IR_UserCallTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index e945cc6..3e0e704 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -80,7 +80,6 @@
#include "src/tint/utils/containers/transform.h"
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/macros/defer.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
#include "src/tint/utils/rtti/switch.h"
#include "src/tint/utils/text/styled_text.h"
#include "src/tint/utils/text/text_style.h"
@@ -448,6 +447,7 @@
}
diag::Diagnostic& Validator::AddError(const Instruction* inst) {
+ diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
auto src = Disassembly().InstructionSource(inst);
auto& diag = AddError(src) << inst->FriendlyName() << ": ";
@@ -458,6 +458,7 @@
}
diag::Diagnostic& Validator::AddError(const Instruction* inst, size_t idx) {
+ diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
auto src =
Disassembly().OperandSource(Disassembly::IndexedValue{inst, static_cast<uint32_t>(idx)});
auto& diag = AddError(src) << inst->FriendlyName() << ": ";
@@ -469,6 +470,7 @@
}
diag::Diagnostic& Validator::AddResultError(const Instruction* inst, size_t idx) {
+ diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
auto src =
Disassembly().ResultSource(Disassembly::IndexedValue{inst, static_cast<uint32_t>(idx)});
auto& diag = AddError(src) << inst->FriendlyName() << ": ";
@@ -789,7 +791,10 @@
void Validator::CheckVar(const Var* var) {
if (var->Result(0) && var->Initializer()) {
if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtrOrRef()) {
- AddError(var) << "initializer has incorrect type";
+ AddError(var) << "initializer type "
+ << style::Type(var->Initializer()->Type()->FriendlyName())
+ << " does not match store type "
+ << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
}
}
}
@@ -799,7 +804,9 @@
if (let->Result(0) && let->Value()) {
if (let->Result(0)->Type() != let->Value()->Type()) {
- AddError(let) << "result type does not match value type";
+ AddError(let) << "result type " << style::Type(let->Result(0)->Type()->FriendlyName())
+ << " does not match value type "
+ << style::Type(let->Value()->Type()->FriendlyName());
}
}
}
@@ -859,8 +866,9 @@
for (size_t i = 0; i < args.Length(); i++) {
if (args[i]->Type() != params[i]->Type()) {
AddError(call, UserCall::kArgsOperandOffset + i)
- << "function parameter " << i << " is of type " << params[i]->Type()->FriendlyName()
- << ", but argument is of type " << args[i]->Type()->FriendlyName();
+ << "function parameter " << i << " is of type "
+ << style::Type(params[i]->Type()->FriendlyName()) << ", but argument is of type "
+ << style::Type(args[i]->Type()->FriendlyName());
}
}
}
@@ -880,13 +888,15 @@
auto desc_of = [&](Kind kind, const core::type::Type* type) {
switch (kind) {
case kPtr:
- return StyledText{} << "ptr<" << obj_view->AddressSpace() << ", "
- << type->FriendlyName() << ", " << obj_view->Access() << ">";
+ return StyledText{}
+ << style::Type("ptr<", obj_view->AddressSpace(), ", ", type->FriendlyName(),
+ ", ", obj_view->Access(), ">");
case kRef:
- return StyledText{} << "ref<" << obj_view->AddressSpace() << ", "
- << type->FriendlyName() << ", " << obj_view->Access() << ">";
+ return StyledText{}
+ << style::Type("ref<", obj_view->AddressSpace(), ", ", type->FriendlyName(),
+ ", ", obj_view->Access(), ">");
default:
- return StyledText{} << type->FriendlyName();
+ return StyledText{} << style::Type(type->FriendlyName());
}
};
@@ -957,7 +967,7 @@
if (TINT_UNLIKELY(!ok)) {
AddError(a) << "result of access chain is type " << desc_of(in_kind, ty)
- << " but instruction type is " << want->FriendlyName();
+ << " but instruction type is " << style::Type(want->FriendlyName());
}
}
@@ -966,11 +976,7 @@
if (b->LHS() && b->RHS()) {
auto symbols = SymbolTable::Wrap(mod_.symbols);
auto type_mgr = type::Manager::Wrap(mod_.Types());
- intrinsic::Context context{
- b->TableData(),
- type_mgr,
- symbols,
- };
+ intrinsic::Context context{b->TableData(), type_mgr, symbols};
auto overload =
core::intrinsic::LookupBinary(context, b->Op(), b->LHS()->Type(), b->RHS()->Type(),
@@ -982,11 +988,10 @@
if (auto* result = b->Result(0)) {
if (overload->return_type != result->Type()) {
- StringStream err;
- err << "binary instruction result type (" << result->Type()->FriendlyName()
- << ") does not match overload result type ("
- << overload->return_type->FriendlyName() << ")";
- AddError(b) << err.str();
+ AddError(b) << "result value type " << style::Type(result->Type()->FriendlyName())
+ << " does not match "
+ << style::Instruction(Disassembly().NameOf(b->Op())) << " result type "
+ << style::Type(overload->return_type->FriendlyName());
}
}
}
@@ -997,11 +1002,7 @@
if (u->Val()) {
auto symbols = SymbolTable::Wrap(mod_.symbols);
auto type_mgr = type::Manager::Wrap(mod_.Types());
- intrinsic::Context context{
- u->TableData(),
- type_mgr,
- symbols,
- };
+ intrinsic::Context context{u->TableData(), type_mgr, symbols};
auto overload = core::intrinsic::LookupUnary(context, u->Op(), u->Val()->Type(),
core::EvaluationStage::kRuntime);
@@ -1012,11 +1013,10 @@
if (auto* result = u->Result(0)) {
if (overload->return_type != result->Type()) {
- StringStream err;
- err << "unary instruction result type (" << result->Type()->FriendlyName()
- << ") does not match overload result type ("
- << overload->return_type->FriendlyName() << ")";
- AddError(u) << err.str();
+ AddError(u) << "result value type " << style::Type(result->Type()->FriendlyName())
+ << " does not match "
+ << style::Instruction(Disassembly().NameOf(u->Op())) << " result type "
+ << style::Type(overload->return_type->FriendlyName());
}
}
}
@@ -1026,7 +1026,8 @@
CheckOperandNotNull(if_, if_->Condition(), If::kConditionOperandOffset);
if (if_->Condition() && !if_->Condition()->Type()->Is<core::type::Bool>()) {
- AddError(if_, If::kConditionOperandOffset) << "condition must be a `bool` type";
+ AddError(if_, If::kConditionOperandOffset)
+ << "condition type must be " << style::Type("bool");
}
tasks_.Push([this] { control_stack_.Pop(); });
@@ -1114,9 +1115,9 @@
for (size_t i = 0; i < results.Length(); ++i) {
if (results[i] && args[i] && results[i]->Type() != args[i]->Type()) {
- AddError(e, i) << "argument type (" << results[i]->Type()->FriendlyName()
- << ") does not match control instruction type ("
- << args[i]->Type()->FriendlyName() << ")";
+ AddError(e, i) << "argument type " << style::Type(results[i]->Type()->FriendlyName())
+ << " does not match control instruction type "
+ << style::Type(args[i]->Type()->FriendlyName());
AddNote(e->ControlInstruction()) << "control instruction";
}
}
@@ -1150,7 +1151,10 @@
if (!ret->Value()) {
AddError(ret) << "expected return value";
} else if (ret->Value()->Type() != func->ReturnType()) {
- AddError(ret) << "return value type does not match function return type";
+ AddError(ret) << "return value type "
+ << style::Type(ret->Value()->Type()->FriendlyName())
+ << " does not match function return type "
+ << style::Type(func->ReturnType()->FriendlyName());
}
}
}
@@ -1214,7 +1218,10 @@
return;
}
if (l->Result(0)->Type() != mv->StoreType()) {
- AddError(l, Load::kFromOperandOffset) << "result type does not match source store type";
+ AddError(l, Load::kFromOperandOffset)
+ << "result type " << style::Type(l->Result(0)->Type()->FriendlyName())
+ << " does not match source store type "
+ << style::Type(mv->StoreType()->FriendlyName());
}
}
}
@@ -1230,8 +1237,12 @@
<< "store target operand is not a memory view";
return;
}
- if (from->Type() != mv->StoreType()) {
- AddError(s, Store::kFromOperandOffset) << "value type does not match store type";
+ auto* value_type = from->Type();
+ auto* store_type = mv->StoreType();
+ if (value_type != store_type) {
+ AddError(s, Store::kFromOperandOffset)
+ << "value type " << style::Type(value_type->FriendlyName())
+ << " does not match store type " << style::Type(store_type->FriendlyName());
}
}
}
@@ -1245,7 +1256,9 @@
if (auto* res = l->Result(0)) {
if (auto* el_ty = GetVectorPtrElementType(l, LoadVectorElement::kFromOperandOffset)) {
if (res->Type() != el_ty) {
- AddResultError(l, 0) << "result type does not match vector pointer element type";
+ AddResultError(l, 0) << "result type " << style::Type(res->Type()->FriendlyName())
+ << " does not match vector pointer element type "
+ << style::Type(el_ty->FriendlyName());
}
}
}
@@ -1260,7 +1273,9 @@
if (auto* el_ty = GetVectorPtrElementType(s, StoreVectorElement::kToOperandOffset)) {
if (value->Type() != el_ty) {
AddError(s, StoreVectorElement::kValueOperandOffset)
- << "value type does not match vector pointer element type";
+ << "value type " << style::Type(value->Type()->FriendlyName())
+ << " does not match vector pointer element type "
+ << style::Type(el_ty->FriendlyName());
}
}
}
@@ -1285,7 +1300,8 @@
}
}
- AddError(inst, idx) << "operand must be a pointer to vector, got " << type->FriendlyName();
+ AddError(inst, idx) << "operand must be a pointer to vector, got "
+ << style::Type(type->FriendlyName());
return nullptr;
}
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 864e24d..2619338 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -394,8 +394,9 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(),
- R"(:8:28 error: call: function parameter 1 is of type i32, but argument is of type f32
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:8:28 error: call: function parameter 1 is of type 'i32', but argument is of type 'f32'
%6:void = call %g, 1i, 2.0f, 3i
^^^^
@@ -631,7 +632,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:3:29 error: access: index out of bounds for type vec2<f32>
+ R"(:3:29 error: access: index out of bounds for type 'vec2<f32>'
%3:f32 = access %2, 1u, 3u
^^
@@ -667,7 +668,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:55 error: access: index out of bounds for type ptr<private, array<f32, 2>, read_write>
+ R"(:3:55 error: access: index out of bounds for type 'ptr<private, array<f32, 2>, read_write>'
%3:ptr<private, f32, read_write> = access %2, 1u, 3u
^^
@@ -701,7 +702,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(), R"(:3:25 error: access: type f32 cannot be indexed
+ EXPECT_EQ(res.Failure().reason.Str(), R"(:3:25 error: access: type 'f32' cannot be indexed
%3:f32 = access %2, 1u
^^
@@ -732,7 +733,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:3:51 error: access: type ptr<private, f32, read_write> cannot be indexed
+ R"(:3:51 error: access: type 'ptr<private, f32, read_write>' cannot be indexed
%3:ptr<private, f32, read_write> = access %2, 1u
^^
@@ -769,7 +770,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:8:25 error: access: type MyStruct cannot be dynamically indexed
+ R"(:8:25 error: access: type 'MyStruct' cannot be dynamically indexed
%4:i32 = access %2, %3
^^
@@ -812,7 +813,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:8:25 error: access: type ptr<private, MyStruct, read_write> cannot be dynamically indexed
+ R"(:8:25 error: access: type 'ptr<private, MyStruct, read_write>' cannot be dynamically indexed
%4:i32 = access %2, %3
^^
@@ -847,8 +848,9 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(),
- R"(:3:14 error: access: result of access chain is type f32 but instruction type is i32
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:3:14 error: access: result of access chain is type 'f32' but instruction type is 'i32'
%3:i32 = access %2, 1u, 1u
^^^^^^
@@ -880,7 +882,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:40 error: access: result of access chain is type ptr<private, f32, read_write> but instruction type is ptr<private, i32, read_write>
+ R"(:3:40 error: access: result of access chain is type 'ptr<private, f32, read_write>' but instruction type is 'ptr<private, i32, read_write>'
%3:ptr<private, i32, read_write> = access %2, 1u, 1u
^^^^^^
@@ -912,7 +914,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:14 error: access: result of access chain is type ptr<private, f32, read_write> but instruction type is f32
+ R"(:3:14 error: access: result of access chain is type 'ptr<private, f32, read_write>' but instruction type is 'f32'
%3:f32 = access %2, 1u, 1u
^^^^^^
@@ -1034,7 +1036,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:34 error: access: result of access chain is type ptr<storage, f32, read> but instruction type is ptr<uniform, f32, read>
+ R"(:3:34 error: access: result of access chain is type 'ptr<storage, f32, read>' but instruction type is 'ptr<uniform, f32, read>'
%3:ptr<uniform, f32, read> = access %2, 1u
^^^^^^
@@ -1066,7 +1068,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:40 error: access: result of access chain is type ptr<storage, f32, read> but instruction type is ptr<storage, f32, read_write>
+ R"(:3:40 error: access: result of access chain is type 'ptr<storage, f32, read>' but instruction type is 'ptr<storage, f32, read_write>'
%3:ptr<storage, f32, read_write> = access %2, 1u
^^^^^^
@@ -1197,7 +1199,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(), R"(:3:8 error: if: condition must be a `bool` type
+ EXPECT_EQ(res.Failure().reason.Str(), R"(:3:8 error: if: condition type must be 'bool'
if 1i [t: $B2, f: $B3] { # if_1
^^
@@ -1400,7 +1402,8 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(), R"(:3:41 error: var: initializer has incorrect type
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:3:41 error: var: initializer type 'i32' does not match store type 'f32'
%2:ptr<function, f32, read_write> = var, 1i
^^^
@@ -1488,7 +1491,8 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(), R"(:3:14 error: let: result type does not match value type
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:3:14 error: let: result type 'f32' does not match value type 'i32'
%2:f32 = let 1i
^^^
@@ -1813,7 +1817,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:3:5 error: unary: unary instruction result type (f32) does not match overload result type (i32)
+ R"(:3:5 error: unary: result value type 'f32' does not match complement result type 'i32'
%2:f32 = complement 2i
^^^^^^^^^^^^^^^^^^^^^^
@@ -1998,7 +2002,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:5:21 error: exit_if: argument type (f32) does not match control instruction type (i32)
+ R"(:5:21 error: exit_if: argument type 'f32' does not match control instruction type 'i32'
exit_if 1i, 2i # if_1
^^
@@ -2392,7 +2396,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:5:25 error: exit_switch: argument type (f32) does not match control instruction type (i32)
+ R"(:5:25 error: exit_switch: argument type 'f32' does not match control instruction type 'i32'
exit_switch 1i, 2i # switch_1
^^
@@ -2780,7 +2784,7 @@
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
- R"(:5:23 error: exit_loop: argument type (f32) does not match control instruction type (i32)
+ R"(:5:23 error: exit_loop: argument type 'f32' does not match control instruction type 'i32'
exit_loop 1i, 2i # loop_1
^^
@@ -3300,8 +3304,9 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
- EXPECT_EQ(res.Failure().reason.Str(),
- R"(:3:5 error: return: return value type does not match function return type
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:3:5 error: return: return value type 'f32' does not match function return type 'i32'
ret 42.0f
^^^^^^^^^
@@ -3392,7 +3397,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:4:19 error: load: result type does not match source store type
+ R"(:4:19 error: load: result type 'f32' does not match source store type 'i32'
%3:f32 = load %2
^^
@@ -3512,7 +3517,7 @@
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:4:15 error: store: value type does not match store type
+ R"(:4:15 error: store: value type 'u32' does not match store type 'i32'
store %2, 42u
^^^
@@ -3672,7 +3677,7 @@
$B1: {
^^^
-:4:37 error: store_vector_element: value type does not match vector pointer element type
+:4:37 error: store_vector_element: value type 'i32' does not match vector pointer element type 'f32'
store_vector_element %2, undef, 2i
^^
diff --git a/src/tint/lang/core/ir/value_test.cc b/src/tint/lang/core/ir/value_test.cc
index 43fd900..c8b7954 100644
--- a/src/tint/lang/core/ir/value_test.cc
+++ b/src/tint/lang/core/ir/value_test.cc
@@ -66,7 +66,7 @@
}
TEST_F(IR_ValueTest, Destroy_HasSource) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/core/ir/var_test.cc b/src/tint/lang/core/ir/var_test.cc
index e1759d9..88a1c69 100644
--- a/src/tint/lang/core/ir/var_test.cc
+++ b/src/tint/lang/core/ir/var_test.cc
@@ -41,7 +41,7 @@
using IR_VarTest = IRTestHelper;
TEST_F(IR_VarTest, Fail_NullType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Module mod;
Builder b{mod};
diff --git a/src/tint/lang/msl/validate/BUILD.bazel b/src/tint/lang/msl/validate/BUILD.bazel
index 7e5730a..2b76c9d 100644
--- a/src/tint/lang/msl/validate/BUILD.bazel
+++ b/src/tint/lang/msl/validate/BUILD.bazel
@@ -50,29 +50,9 @@
"validate.h",
],
deps = [
- "//src/tint/lang/core",
- "//src/tint/lang/core/constant",
- "//src/tint/lang/core/type",
- "//src/tint/lang/wgsl",
- "//src/tint/lang/wgsl/ast",
- "//src/tint/lang/wgsl/features",
- "//src/tint/lang/wgsl/program",
- "//src/tint/lang/wgsl/sem",
"//src/tint/utils/command",
- "//src/tint/utils/containers",
- "//src/tint/utils/diagnostic",
"//src/tint/utils/file",
- "//src/tint/utils/ice",
- "//src/tint/utils/id",
- "//src/tint/utils/macros",
- "//src/tint/utils/math",
- "//src/tint/utils/memory",
- "//src/tint/utils/reflection",
- "//src/tint/utils/result",
- "//src/tint/utils/rtti",
- "//src/tint/utils/symbol",
"//src/tint/utils/text",
- "//src/tint/utils/traits",
] + select({
":tint_build_is_mac": [
diff --git a/src/tint/lang/msl/validate/BUILD.cmake b/src/tint/lang/msl/validate/BUILD.cmake
index 698e899..3fcdcac 100644
--- a/src/tint/lang/msl/validate/BUILD.cmake
+++ b/src/tint/lang/msl/validate/BUILD.cmake
@@ -46,29 +46,9 @@
)
tint_target_add_dependencies(tint_lang_msl_validate lib
- tint_lang_core
- tint_lang_core_constant
- tint_lang_core_type
- tint_lang_wgsl
- tint_lang_wgsl_ast
- tint_lang_wgsl_features
- tint_lang_wgsl_program
- tint_lang_wgsl_sem
tint_utils_command
- tint_utils_containers
- tint_utils_diagnostic
tint_utils_file
- tint_utils_ice
- tint_utils_id
- tint_utils_macros
- tint_utils_math
- tint_utils_memory
- tint_utils_reflection
- tint_utils_result
- tint_utils_rtti
- tint_utils_symbol
tint_utils_text
- tint_utils_traits
)
if(TINT_BUILD_IS_MAC)
diff --git a/src/tint/lang/msl/validate/BUILD.gn b/src/tint/lang/msl/validate/BUILD.gn
index cbf7c61..902ed8d 100644
--- a/src/tint/lang/msl/validate/BUILD.gn
+++ b/src/tint/lang/msl/validate/BUILD.gn
@@ -44,29 +44,9 @@
"validate.h",
]
deps = [
- "${tint_src_dir}/lang/core",
- "${tint_src_dir}/lang/core/constant",
- "${tint_src_dir}/lang/core/type",
- "${tint_src_dir}/lang/wgsl",
- "${tint_src_dir}/lang/wgsl/ast",
- "${tint_src_dir}/lang/wgsl/features",
- "${tint_src_dir}/lang/wgsl/program",
- "${tint_src_dir}/lang/wgsl/sem",
"${tint_src_dir}/utils/command",
- "${tint_src_dir}/utils/containers",
- "${tint_src_dir}/utils/diagnostic",
"${tint_src_dir}/utils/file",
- "${tint_src_dir}/utils/ice",
- "${tint_src_dir}/utils/id",
- "${tint_src_dir}/utils/macros",
- "${tint_src_dir}/utils/math",
- "${tint_src_dir}/utils/memory",
- "${tint_src_dir}/utils/reflection",
- "${tint_src_dir}/utils/result",
- "${tint_src_dir}/utils/rtti",
- "${tint_src_dir}/utils/symbol",
"${tint_src_dir}/utils/text",
- "${tint_src_dir}/utils/traits",
]
if (tint_build_is_mac) {
diff --git a/src/tint/lang/msl/validate/validate.cc b/src/tint/lang/msl/validate/validate.cc
index 2230f54..0c960ed 100644
--- a/src/tint/lang/msl/validate/validate.cc
+++ b/src/tint/lang/msl/validate/validate.cc
@@ -27,8 +27,6 @@
#include "src/tint/lang/msl/validate/validate.h"
-#include "src/tint/lang/wgsl/ast/module.h"
-#include "src/tint/lang/wgsl/program/program.h"
#include "src/tint/utils/command/command.h"
#include "src/tint/utils/file/tmpfile.h"
diff --git a/src/tint/lang/msl/validate/validate.h b/src/tint/lang/msl/validate/validate.h
index d7b8acf..df2108a 100644
--- a/src/tint/lang/msl/validate/validate.h
+++ b/src/tint/lang/msl/validate/validate.h
@@ -31,13 +31,6 @@
#include <string>
#include <utility>
-#include "src/tint/lang/wgsl/ast/pipeline_stage.h"
-
-// Forward declarations
-namespace tint {
-class Program;
-} // namespace tint
-
namespace tint::msl::validate {
/// The version of MSL to validate against.
diff --git a/src/tint/lang/msl/writer/raise/builtin_polyfill.cc b/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
index e41330f..e44d1a6 100644
--- a/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
@@ -73,42 +73,32 @@
// Replace the builtins that we found.
for (auto* builtin : worklist) {
- core::ir::Value* replacement = nullptr;
switch (builtin->Func()) {
case core::BuiltinFn::kStorageBarrier:
- replacement = ThreadgroupBarrier(builtin, BarrierType::kDevice);
+ ThreadgroupBarrier(builtin, BarrierType::kDevice);
break;
case core::BuiltinFn::kWorkgroupBarrier:
- replacement = ThreadgroupBarrier(builtin, BarrierType::kThreadGroup);
+ ThreadgroupBarrier(builtin, BarrierType::kThreadGroup);
break;
case core::BuiltinFn::kTextureBarrier:
- replacement = ThreadgroupBarrier(builtin, BarrierType::kTexture);
+ ThreadgroupBarrier(builtin, BarrierType::kTexture);
break;
default:
break;
}
- TINT_ASSERT(replacement);
-
- // Replace the old builtin result with the new value.
- if (auto name = ir.NameOf(builtin->Result(0))) {
- ir.SetName(replacement, name);
- }
- builtin->Result(0)->ReplaceAllUsesWith(replacement);
- builtin->Destroy();
}
}
/// Replace a barrier builtin with the `threadgroupBarrier()` intrinsic.
/// @param builtin the builtin call instruction
/// @param type the barrier type
- /// @returns the replacement value
- core::ir::Value* ThreadgroupBarrier(core::ir::CoreBuiltinCall* builtin, BarrierType type) {
+ void ThreadgroupBarrier(core::ir::CoreBuiltinCall* builtin, BarrierType type) {
// Replace the builtin call with a call to the msl.threadgroup_barrier intrinsic.
auto args = Vector<core::ir::Value*, 1>{b.Constant(u32(type))};
- auto* call = b.Call<msl::ir::BuiltinCall>(
- builtin->Result(0)->Type(), msl::BuiltinFn::kThreadgroupBarrier, std::move(args));
+ auto* call = b.CallWithResult<msl::ir::BuiltinCall>(
+ builtin->DetachResult(), msl::BuiltinFn::kThreadgroupBarrier, std::move(args));
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
};
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.bazel b/src/tint/lang/spirv/reader/lower/BUILD.bazel
index 01fd572..1348c8d 100644
--- a/src/tint/lang/spirv/reader/lower/BUILD.bazel
+++ b/src/tint/lang/spirv/reader/lower/BUILD.bazel
@@ -40,10 +40,12 @@
name = "lower",
srcs = [
"lower.cc",
+ "shader_io.cc",
"vector_element_pointer.cc",
],
hdrs = [
"lower.h",
+ "shader_io.h",
"vector_element_pointer.h",
],
deps = [
@@ -52,6 +54,7 @@
"//src/tint/lang/core/constant",
"//src/tint/lang/core/intrinsic",
"//src/tint/lang/core/ir",
+ "//src/tint/lang/core/ir/transform/common",
"//src/tint/lang/core/type",
"//src/tint/utils/containers",
"//src/tint/utils/diagnostic",
@@ -74,6 +77,7 @@
name = "test",
alwayslink = True,
srcs = [
+ "shader_io_test.cc",
"vector_element_pointer_test.cc",
],
deps = [
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.cmake b/src/tint/lang/spirv/reader/lower/BUILD.cmake
index 5f0e866..98de660 100644
--- a/src/tint/lang/spirv/reader/lower/BUILD.cmake
+++ b/src/tint/lang/spirv/reader/lower/BUILD.cmake
@@ -41,6 +41,8 @@
tint_add_target(tint_lang_spirv_reader_lower lib
lang/spirv/reader/lower/lower.cc
lang/spirv/reader/lower/lower.h
+ lang/spirv/reader/lower/shader_io.cc
+ lang/spirv/reader/lower/shader_io.h
lang/spirv/reader/lower/vector_element_pointer.cc
lang/spirv/reader/lower/vector_element_pointer.h
)
@@ -51,6 +53,7 @@
tint_lang_core_constant
tint_lang_core_intrinsic
tint_lang_core_ir
+ tint_lang_core_ir_transform_common
tint_lang_core_type
tint_utils_containers
tint_utils_diagnostic
@@ -72,6 +75,7 @@
# Kind: test
################################################################################
tint_add_target(tint_lang_spirv_reader_lower_test test
+ lang/spirv/reader/lower/shader_io_test.cc
lang/spirv/reader/lower/vector_element_pointer_test.cc
)
diff --git a/src/tint/lang/spirv/reader/lower/BUILD.gn b/src/tint/lang/spirv/reader/lower/BUILD.gn
index b5f0342..37c4709 100644
--- a/src/tint/lang/spirv/reader/lower/BUILD.gn
+++ b/src/tint/lang/spirv/reader/lower/BUILD.gn
@@ -46,6 +46,8 @@
sources = [
"lower.cc",
"lower.h",
+ "shader_io.cc",
+ "shader_io.h",
"vector_element_pointer.cc",
"vector_element_pointer.h",
]
@@ -55,6 +57,7 @@
"${tint_src_dir}/lang/core/constant",
"${tint_src_dir}/lang/core/intrinsic",
"${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/ir/transform/common",
"${tint_src_dir}/lang/core/type",
"${tint_src_dir}/utils/containers",
"${tint_src_dir}/utils/diagnostic",
@@ -73,7 +76,10 @@
}
if (tint_build_unittests) {
tint_unittests_source_set("unittests") {
- sources = [ "vector_element_pointer_test.cc" ]
+ sources = [
+ "shader_io_test.cc",
+ "vector_element_pointer_test.cc",
+ ]
deps = [
"${tint_src_dir}:gmock_and_gtest",
"${tint_src_dir}/api/common",
diff --git a/src/tint/lang/spirv/reader/lower/lower.cc b/src/tint/lang/spirv/reader/lower/lower.cc
index 25f546c..433d0cd 100644
--- a/src/tint/lang/spirv/reader/lower/lower.cc
+++ b/src/tint/lang/spirv/reader/lower/lower.cc
@@ -28,6 +28,7 @@
#include "src/tint/lang/spirv/reader/lower/lower.h"
#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/spirv/reader/lower/shader_io.h"
#include "src/tint/lang/spirv/reader/lower/vector_element_pointer.h"
namespace tint::spirv::reader {
@@ -42,6 +43,7 @@
} while (false)
RUN_TRANSFORM(lower::VectorElementPointer, mod);
+ RUN_TRANSFORM(lower::ShaderIO, mod);
if (auto res = core::ir::ValidateAndDumpIfNeeded(mod, "end of lowering from SPIR-V");
res != Success) {
diff --git a/src/tint/lang/spirv/reader/lower/shader_io.cc b/src/tint/lang/spirv/reader/lower/shader_io.cc
new file mode 100644
index 0000000..a786791
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/shader_io.cc
@@ -0,0 +1,426 @@
+// 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/spirv/reader/lower/shader_io.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/transform/common/referenced_module_vars.h"
+#include "src/tint/lang/core/ir/validator.h"
+
+namespace tint::spirv::reader::lower {
+
+namespace {
+
+using namespace tint::core::fluent_types; // NOLINT
+
+/// 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()};
+
+ /// A map from block to its containing function.
+ Hashmap<core::ir::Block*, core::ir::Function*, 64> block_to_function{};
+
+ /// A map from each function to a map from input variable to parameter.
+ Hashmap<core::ir::Function*, Hashmap<core::ir::Var*, core::ir::Value*, 4>, 8>
+ function_parameter_map{};
+
+ /// The set of output variables that have been processed.
+ Hashset<core::ir::Var*, 4> output_variables{};
+
+ /// The mapping from functions to their transitively referenced output variables.
+ core::ir::ReferencedModuleVars referenced_output_vars{
+ ir, [](const core::ir::Var* var) {
+ auto* view = var->Result(0)->Type()->As<core::type::MemoryView>();
+ return view && view->AddressSpace() == core::AddressSpace::kOut;
+ }};
+
+ /// Process the module.
+ void Process() {
+ // Process outputs first, as that may introduce new functions that input variables need to
+ // be propagated through.
+ ProcessOutputs();
+ ProcessInputs();
+ }
+
+ /// Process output variables.
+ /// Changes output variables to the `private` address space and wraps entry points that produce
+ /// outputs with new functions that copy the outputs from the private variables to the return
+ /// value.
+ void ProcessOutputs() {
+ // Update entry point functions to return their outputs, using a wrapper function.
+ // Use a worklist as `ProcessEntryPointOutputs()` will add new functions.
+ Vector<core::ir::Function*, 4> entry_points;
+ for (auto& func : ir.functions) {
+ if (func->Stage() != core::ir::Function::PipelineStage::kUndefined) {
+ entry_points.Push(func);
+ }
+ }
+ for (auto& ep : entry_points) {
+ ProcessEntryPointOutputs(ep);
+ }
+
+ // Remove attributes from all of the original structs and module-scope output variables.
+ // This is done last as we need to copy attributes during `ProcessEntryPointOutputs()`.
+ for (auto& var : output_variables) {
+ var->SetAttributes({});
+ if (auto* str = var->Result(0)->Type()->UnwrapPtr()->As<core::type::Struct>()) {
+ for (auto* member : str->Members()) {
+ // TODO(crbug.com/tint/745): Remove the const_cast.
+ const_cast<core::type::StructMember*>(member)->SetAttributes({});
+ }
+ }
+ }
+ }
+
+ /// Process input variables.
+ /// Pass inputs down the call stack as parameters to any functions that need them.
+ void ProcessInputs() {
+ // Seed the block-to-function map with the function entry blocks.
+ for (auto& func : ir.functions) {
+ block_to_function.Add(func->Block(), func);
+ }
+
+ // Gather the list of all module-scope input variables.
+ Vector<core::ir::Var*, 4> inputs;
+ for (auto* global : *ir.root_block) {
+ if (auto* var = global->As<core::ir::Var>()) {
+ auto addrspace = var->Result(0)->Type()->As<core::type::Pointer>()->AddressSpace();
+ if (addrspace == core::AddressSpace::kIn) {
+ inputs.Push(var);
+ }
+ }
+ }
+
+ // Replace the input variables with function parameters.
+ for (auto* var : inputs) {
+ ReplaceInputPointerUses(var, var->Result(0));
+ var->Destroy();
+ }
+ }
+
+ /// Replace an output pointer address space to make it `private`.
+ /// @param value the output variable
+ void ReplaceOutputPointerAddressSpace(core::ir::InstructionResult* value) {
+ // Change the address space to `private`.
+ auto* old_ptr_type = value->Type();
+ auto* new_ptr_type = ty.ptr(core::AddressSpace::kPrivate, old_ptr_type->UnwrapPtr());
+ value->SetType(new_ptr_type);
+
+ // Update all uses of the module-scope variable.
+ value->ForEachUse([&](core::ir::Usage use) {
+ if (auto* access = use.instruction->As<core::ir::Access>()) {
+ ReplaceOutputPointerAddressSpace(access->Result(0));
+ } else if (!use.instruction->IsAnyOf<core::ir::Load, core::ir::LoadVectorElement,
+ core::ir::Store, core::ir::StoreVectorElement>()) {
+ TINT_UNREACHABLE()
+ << "unexpected instruction: " << use.instruction->TypeInfo().name;
+ }
+ });
+ }
+
+ /// Process the outputs of an entry point function, adding a wrapper function to forward outputs
+ /// through the return value.
+ /// @param ep the entry point
+ void ProcessEntryPointOutputs(core::ir::Function* ep) {
+ const auto& referenced_outputs = referenced_output_vars.TransitiveReferences(ep);
+ if (referenced_outputs.IsEmpty()) {
+ return;
+ }
+
+ // Add a wrapper function to return either a single value or a struct.
+ auto* wrapper = b.Function(ty.void_(), ep->Stage());
+ if (auto name = ir.NameOf(ep)) {
+ ir.SetName(ep, name.Name() + "_inner");
+ ir.SetName(wrapper, name);
+ }
+
+ // Call the original entry point and make it a regular function.
+ ep->SetStage(core::ir::Function::PipelineStage::kUndefined);
+ b.Append(wrapper->Block(), [&] { //
+ b.Call(ep);
+ });
+
+ // Collect all outputs into a list of struct member declarations.
+ // Also add instructions to load their final values in the wrapper function.
+ Vector<core::ir::Value*, 4> results;
+ Vector<core::type::Manager::StructMemberDesc, 4> output_descriptors;
+ auto add_output = [&](Symbol name, const core::type::Type* type,
+ core::type::StructMemberAttributes attributes) {
+ if (!name) {
+ name = ir.symbols.New();
+ }
+ output_descriptors.Push(core::type::Manager::StructMemberDesc{name, type, attributes});
+ };
+ for (auto* var : referenced_outputs) {
+ // Change the address space of the variable to private and update its uses, if we
+ // haven't already seen this variable.
+ if (output_variables.Add(var)) {
+ ReplaceOutputPointerAddressSpace(var->Result(0));
+ }
+
+ // Copy the variable attributes to the struct member.
+ const auto& original_attributes = var->Attributes();
+ core::type::StructMemberAttributes var_attributes;
+ var_attributes.invariant = original_attributes.invariant;
+ var_attributes.builtin = original_attributes.builtin;
+ var_attributes.location = original_attributes.location;
+ var_attributes.interpolation = original_attributes.interpolation;
+
+ auto var_type = var->Result(0)->Type()->UnwrapPtr();
+ if (auto* str = var_type->As<core::type::Struct>()) {
+ // Add an output for each member of the struct.
+ for (auto* member : str->Members()) {
+ // Use the base variable attributes if not specified directly on the member.
+ auto member_attributes = member->Attributes();
+ if (auto base_loc = var_attributes.location) {
+ // Location values increment from the base location value on the variable.
+ member_attributes.location = base_loc.value() + member->Index();
+ }
+ if (!member_attributes.interpolation) {
+ member_attributes.interpolation = var_attributes.interpolation;
+ }
+
+ add_output(member->Name(), member->Type(), std::move(member_attributes));
+
+ // Load the final result from the member of the original struct variable.
+ b.Append(wrapper->Block(), [&] { //
+ auto* access =
+ b.Access(ty.ptr<private_>(member->Type()), var, u32(member->Index()));
+ results.Push(b.Load(access)->Result(0));
+ });
+ }
+ } else {
+ // Load the final result from the original variable.
+ b.Append(wrapper->Block(), [&] {
+ results.Push(b.Load(var)->Result(0));
+
+ // If we're dealing with sample_mask, extract the scalar from the array.
+ if (var_attributes.builtin == core::BuiltinValue::kSampleMask) {
+ var_type = ty.u32();
+ results.Back() = b.Access(ty.u32(), results.Back(), u32(0))->Result(0);
+ }
+ });
+ add_output(ir.NameOf(var), var_type, std::move(var_attributes));
+ }
+ }
+
+ if (output_descriptors.Length() == 1) {
+ // Copy the output attributes to the function return.
+ const auto& attributes = output_descriptors[0].attributes;
+ wrapper->SetReturnInvariant(attributes.invariant);
+ if (attributes.builtin) {
+ wrapper->SetReturnBuiltin(attributes.builtin.value());
+ } else if (attributes.location) {
+ core::ir::Location loc;
+ loc.value = attributes.location.value();
+ loc.interpolation = attributes.interpolation;
+ wrapper->SetReturnLocation(std::move(loc));
+ }
+
+ // Return the output from the wrapper function.
+ wrapper->SetReturnType(output_descriptors[0].type);
+ b.Append(wrapper->Block(), [&] { //
+ b.Return(wrapper, results[0]);
+ });
+ } else {
+ // Create a struct to hold all of the output values.
+ auto* str = ty.Struct(ir.symbols.New(), std::move(output_descriptors));
+ wrapper->SetReturnType(str);
+
+ // Collect the output values and return them from the wrapper function.
+ b.Append(wrapper->Block(), [&] { //
+ b.Return(wrapper, b.Construct(str, std::move(results)));
+ });
+ }
+ }
+
+ /// Replace a use of an input pointer value.
+ /// @param var the originating input variable
+ /// @param value the input pointer value
+ void ReplaceInputPointerUses(core::ir::Var* var, core::ir::Value* value) {
+ Vector<core::ir::Instruction*, 8> to_destroy;
+ value->ForEachUse([&](core::ir::Usage use) {
+ auto* object = value;
+ if (object->Type()->Is<core::type::Pointer>()) {
+ // Get (or create) the function parameter that will replace the variable.
+ auto* func = ContainingFunction(use.instruction);
+ object = GetParameter(func, var);
+ }
+
+ Switch(
+ use.instruction,
+ [&](core::ir::Load* l) {
+ // Fold the load away and replace its uses with the new parameter.
+ l->Result(0)->ReplaceAllUsesWith(object);
+ to_destroy.Push(l);
+ },
+ [&](core::ir::LoadVectorElement* lve) {
+ // Replace the vector element load with an access instruction.
+ auto* access = b.AccessWithResult(lve->DetachResult(), object, lve->Index());
+ access->InsertBefore(lve);
+ to_destroy.Push(lve);
+ },
+ [&](core::ir::Access* a) {
+ if (!a->Indices().IsEmpty()) {
+ // Remove the pointer from the source and destination type.
+ a->SetOperand(core::ir::Access::kObjectOperandOffset, object);
+ a->Result(0)->SetType(a->Result(0)->Type()->UnwrapPtr());
+ ReplaceInputPointerUses(var, a->Result(0));
+ } else {
+ // Fold the access away and replace its uses.
+ ReplaceInputPointerUses(var, a->Result(0));
+ to_destroy.Push(a);
+ }
+ },
+ TINT_ICE_ON_NO_MATCH);
+ });
+
+ // Clean up orphaned instructions.
+ for (auto* inst : to_destroy) {
+ inst->Destroy();
+ }
+ }
+
+ /// Get the function that contains an instruction.
+ /// @param inst the instruction
+ /// @returns the function
+ core::ir::Function* ContainingFunction(core::ir::Instruction* inst) {
+ return block_to_function.GetOrAdd(inst->Block(), [&] { //
+ return ContainingFunction(inst->Block()->Parent());
+ });
+ }
+
+ /// Get or create a function parameter to replace a module-scope variable.
+ /// @param func the function
+ /// @param var the module-scope variable
+ /// @returns the function parameter
+ core::ir::Value* GetParameter(core::ir::Function* func, core::ir::Var* var) {
+ return function_parameter_map.GetOrAddZero(func).GetOrAdd(var, [&] {
+ const bool entry_point = func->Stage() != core::ir::Function::PipelineStage::kUndefined;
+ auto* var_type = var->Result(0)->Type()->UnwrapPtr();
+
+ // Use a scalar u32 for sample_mask builtins for entry point parameters.
+ if (entry_point && var->Attributes().builtin == core::BuiltinValue::kSampleMask) {
+ TINT_ASSERT(var_type->Is<core::type::Array>());
+ TINT_ASSERT(var_type->As<core::type::Array>()->ConstantCount() == 1u);
+ var_type = ty.u32();
+ }
+
+ // Create a new function parameter for the input.
+ auto* param = b.FunctionParam(var_type);
+ func->AppendParam(param);
+ if (auto name = ir.NameOf(var)) {
+ ir.SetName(param, name);
+ }
+
+ // Add attributes to the parameter if this is an entry point function.
+ if (entry_point) {
+ AddEntryPointParameterAttributes(param, var->Attributes());
+ }
+
+ // Update the callsites of this function.
+ func->ForEachUse([&](core::ir::Usage use) {
+ if (auto* call = use.instruction->As<core::ir::UserCall>()) {
+ // Recurse into the calling function.
+ auto* caller = ContainingFunction(call);
+ call->AppendArg(GetParameter(caller, var));
+ } else if (!use.instruction->Is<core::ir::Return>()) {
+ TINT_UNREACHABLE()
+ << "unexpected instruction: " << use.instruction->TypeInfo().name;
+ }
+ });
+
+ core::ir::Value* result = param;
+ if (entry_point && var->Attributes().builtin == core::BuiltinValue::kSampleMask) {
+ // Construct an array from the scalar sample_mask builtin value for entry points.
+ b.Prepend(func->Block(), [&] { //
+ result = b.Construct(var->Result(0)->Type()->UnwrapPtr(), param)->Result(0);
+ });
+ }
+ return result;
+ });
+ }
+
+ /// Add attributes to an entry point function parameter.
+ /// @param param the parameter
+ /// @param attributes the attributes
+ void AddEntryPointParameterAttributes(core::ir::FunctionParam* param,
+ const core::ir::IOAttributes& attributes) {
+ if (auto* str = param->Type()->UnwrapPtr()->As<core::type::Struct>()) {
+ for (auto* member : str->Members()) {
+ // Use the base variable attributes if not specified directly on the member.
+ auto member_attributes = member->Attributes();
+ if (auto base_loc = attributes.location) {
+ // Location values increment from the base location value on the variable.
+ member_attributes.location = base_loc.value() + member->Index();
+ }
+ if (!member_attributes.interpolation) {
+ member_attributes.interpolation = attributes.interpolation;
+ }
+ // TODO(crbug.com/tint/745): Remove the const_cast.
+ const_cast<core::type::StructMember*>(member)->SetAttributes(
+ std::move(member_attributes));
+ }
+ } else {
+ // Set attributes directly on the function parameter.
+ param->SetInvariant(attributes.invariant);
+ if (attributes.builtin) {
+ param->SetBuiltin(attributes.builtin.value());
+ } else if (attributes.location) {
+ core::ir::Location loc;
+ loc.value = attributes.location.value();
+ loc.interpolation = attributes.interpolation;
+ param->SetLocation(std::move(loc));
+ }
+ }
+ }
+};
+
+} // namespace
+
+Result<SuccessType> ShaderIO(core::ir::Module& ir) {
+ auto result = ValidateAndDumpIfNeeded(ir, "ShaderIO transform");
+ if (result != Success) {
+ return result.Failure();
+ }
+
+ State{ir}.Process();
+
+ return Success;
+}
+
+} // namespace tint::spirv::reader::lower
diff --git a/src/tint/lang/spirv/reader/lower/shader_io.h b/src/tint/lang/spirv/reader/lower/shader_io.h
new file mode 100644
index 0000000..8347585
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/shader_io.h
@@ -0,0 +1,48 @@
+// 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_SPIRV_READER_LOWER_SHADER_IO_H_
+#define SRC_TINT_LANG_SPIRV_READER_LOWER_SHADER_IO_H_
+
+#include "src/tint/utils/result/result.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Module;
+}
+
+namespace tint::spirv::reader::lower {
+
+/// ShaderIO is a transform that converts SPIR-V's style of shader IO (using global variables) into
+/// the form expected by Tint's core IR (using function parameters and return values).
+/// @param module the module to transform
+/// @returns success or failure
+Result<SuccessType> ShaderIO(core::ir::Module& module);
+
+} // namespace tint::spirv::reader::lower
+
+#endif // SRC_TINT_LANG_SPIRV_READER_LOWER_SHADER_IO_H_
diff --git a/src/tint/lang/spirv/reader/lower/shader_io_test.cc b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
new file mode 100644
index 0000000..d004762
--- /dev/null
+++ b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
@@ -0,0 +1,2267 @@
+// 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/spirv/reader/lower/shader_io.h"
+
+#include <utility>
+
+#include "src/tint/lang/core/ir/transform/helper_test.h"
+
+namespace tint::spirv::reader::lower {
+namespace {
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+class SpirvReader_ShaderIOTest : public core::ir::transform::TransformTest {
+ protected:
+ core::type::StructMemberAttributes BuiltinAttrs(core::BuiltinValue builtin) {
+ core::type::StructMemberAttributes attrs;
+ attrs.builtin = builtin;
+ return attrs;
+ }
+ core::type::StructMemberAttributes LocationAttrs(
+ uint32_t location,
+ std::optional<core::Interpolation> interpolation = std::nullopt) {
+ core::type::StructMemberAttributes attrs;
+ attrs.location = location;
+ attrs.interpolation = interpolation;
+ return attrs;
+ }
+};
+
+TEST_F(SpirvReader_ShaderIOTest, NoInputsOrOutputs) {
+ auto* ep = b.Function("foo", ty.void_());
+ ep->SetStage(core::ir::Function::PipelineStage::kCompute);
+
+ b.Append(ep->Block(), [&] { //
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+%foo = @compute func():void {
+ $B1: {
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs) {
+ auto* front_facing = b.Var("front_facing", ty.ptr(core::AddressSpace::kIn, ty.bool_()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kFrontFacing;
+ front_facing->SetAttributes(std::move(attributes));
+ }
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ auto* color1 = b.Var("color1", ty.ptr(core::AddressSpace::kIn, ty.f32()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 0;
+ color1->SetAttributes(std::move(attributes));
+ }
+ auto* color2 = b.Var("color2", ty.ptr(core::AddressSpace::kIn, ty.f32()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kSample};
+ color2->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(front_facing);
+ mod.root_block->Append(position);
+ mod.root_block->Append(color1);
+ mod.root_block->Append(color2);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ auto* ifelse = b.If(b.Load(front_facing));
+ b.Append(ifelse->True(), [&] {
+ auto* position_value = b.Load(position);
+ auto* color1_value = b.Load(color1);
+ auto* color2_value = b.Load(color2);
+ b.Multiply(ty.vec4<f32>(), position_value, b.Add(ty.f32(), color1_value, color2_value));
+ b.ExitIf(ifelse);
+ });
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %front_facing:ptr<__in, bool, read> = var @builtin(front_facing)
+ %position:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %color1:ptr<__in, f32, read> = var @location(0)
+ %color2:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %6:bool = load %front_facing
+ if %6 [t: $B3] { # if_1
+ $B3: { # true
+ %7:vec4<f32> = load %position
+ %8:f32 = load %color1
+ %9:f32 = load %color2
+ %10:f32 = add %8, %9
+ %11:vec4<f32> = mul %7, %10
+ exit_if # if_1
+ }
+ }
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = @fragment func(%front_facing:bool [@front_facing], %position:vec4<f32> [@invariant, @position], %color1:f32 [@location(0)], %color2:f32 [@location(1), @interpolate(linear, sample)]):void {
+ $B1: {
+ if %front_facing [t: $B2] { # if_1
+ $B2: { # true
+ %6:f32 = add %color1, %color2
+ %7:vec4<f32> = mul %position, %6
+ exit_if # if_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_UsedByHelper) {
+ auto* front_facing = b.Var("front_facing", ty.ptr(core::AddressSpace::kIn, ty.bool_()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kFrontFacing;
+ front_facing->SetAttributes(std::move(attributes));
+ }
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ auto* color1 = b.Var("color1", ty.ptr(core::AddressSpace::kIn, ty.f32()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 0;
+ color1->SetAttributes(std::move(attributes));
+ }
+ auto* color2 = b.Var("color2", ty.ptr(core::AddressSpace::kIn, ty.f32()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kSample};
+ color2->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(front_facing);
+ mod.root_block->Append(position);
+ mod.root_block->Append(color1);
+ mod.root_block->Append(color2);
+
+ // Inner function has an existing parameter.
+ auto* param = b.FunctionParam("existing_param", ty.f32());
+ auto* foo = b.Function("foo", ty.void_());
+ foo->SetParams({param});
+ b.Append(foo->Block(), [&] {
+ auto* ifelse = b.If(b.Load(front_facing));
+ b.Append(ifelse->True(), [&] {
+ auto* position_value = b.Load(position);
+ auto* color1_value = b.Load(color1);
+ auto* color2_value = b.Load(color2);
+ auto* add = b.Add(ty.f32(), color1_value, color2_value);
+ auto* mul = b.Multiply(ty.vec4<f32>(), position_value, add);
+ b.Divide(ty.vec4<f32>(), mul, param);
+ b.ExitIf(ifelse);
+ });
+ b.Return(foo);
+ });
+
+ // Intermediate function has no existing parameters.
+ auto* bar = b.Function("bar", ty.void_());
+ b.Append(bar->Block(), [&] {
+ b.Call(foo, 42_f);
+ b.Return(bar);
+ });
+
+ auto* ep = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ b.Call(bar);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %front_facing:ptr<__in, bool, read> = var @builtin(front_facing)
+ %position:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %color1:ptr<__in, f32, read> = var @location(0)
+ %color2:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
+}
+
+%foo = func(%existing_param:f32):void {
+ $B2: {
+ %7:bool = load %front_facing
+ if %7 [t: $B3] { # if_1
+ $B3: { # true
+ %8:vec4<f32> = load %position
+ %9:f32 = load %color1
+ %10:f32 = load %color2
+ %11:f32 = add %9, %10
+ %12:vec4<f32> = mul %8, %11
+ %13:vec4<f32> = div %12, %existing_param
+ exit_if # if_1
+ }
+ }
+ ret
+ }
+}
+%bar = func():void {
+ $B4: {
+ %15:void = call %foo, 42.0f
+ ret
+ }
+}
+%main = @fragment func():void {
+ $B5: {
+ %17:void = call %bar
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%existing_param:f32, %front_facing:bool, %position:vec4<f32>, %color1:f32, %color2:f32):void {
+ $B1: {
+ if %front_facing [t: $B2] { # if_1
+ $B2: { # true
+ %7:f32 = add %color1, %color2
+ %8:vec4<f32> = mul %position, %7
+ %9:vec4<f32> = div %8, %existing_param
+ exit_if # if_1
+ }
+ }
+ ret
+ }
+}
+%bar = func(%front_facing_1:bool, %position_1:vec4<f32>, %color1_1:f32, %color2_1:f32):void { # %front_facing_1: 'front_facing', %position_1: 'position', %color1_1: 'color1', %color2_1: 'color2'
+ $B3: {
+ %15:void = call %foo, 42.0f, %front_facing_1, %position_1, %color1_1, %color2_1
+ ret
+ }
+}
+%main = @fragment func(%front_facing_2:bool [@front_facing], %position_2:vec4<f32> [@invariant, @position], %color1_2:f32 [@location(0)], %color2_2:f32 [@location(1), @interpolate(linear, sample)]):void { # %front_facing_2: 'front_facing', %position_2: 'position', %color1_2: 'color1', %color2_2: 'color2'
+ $B4: {
+ %21:void = call %bar, %front_facing_2, %position_2, %color1_2, %color2_2
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_UsedEntryPointAndHelper) {
+ auto* gid = b.Var("gid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kGlobalInvocationId;
+ gid->SetAttributes(std::move(attributes));
+ }
+ auto* lid = b.Var("lid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kLocalInvocationId;
+ lid->SetAttributes(std::move(attributes));
+ }
+ auto* group_id = b.Var("group_id", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kWorkgroupId;
+ group_id->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(gid);
+ mod.root_block->Append(lid);
+ mod.root_block->Append(group_id);
+
+ // Use a subset of the inputs in the helper.
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* gid_value = b.Load(gid);
+ auto* lid_value = b.Load(lid);
+ b.Add(ty.vec3<u32>(), gid_value, lid_value);
+ b.Return(foo);
+ });
+
+ // Use a different subset of the inputs in the entry point.
+ auto* ep = b.Function("main1", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ b.Append(ep->Block(), [&] {
+ auto* group_value = b.Load(group_id);
+ auto* gid_value = b.Load(gid);
+ b.Add(ty.vec3<u32>(), group_value, gid_value);
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %gid:ptr<__in, vec3<u32>, read> = var @builtin(global_invocation_id)
+ %lid:ptr<__in, vec3<u32>, read> = var @builtin(local_invocation_id)
+ %group_id:ptr<__in, vec3<u32>, read> = var @builtin(workgroup_id)
+}
+
+%foo = func():void {
+ $B2: {
+ %5:vec3<u32> = load %gid
+ %6:vec3<u32> = load %lid
+ %7:vec3<u32> = add %5, %6
+ ret
+ }
+}
+%main1 = @compute func():void {
+ $B3: {
+ %9:vec3<u32> = load %group_id
+ %10:vec3<u32> = load %gid
+ %11:vec3<u32> = add %9, %10
+ %12:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%gid:vec3<u32>, %lid:vec3<u32>):void {
+ $B1: {
+ %4:vec3<u32> = add %gid, %lid
+ ret
+ }
+}
+%main1 = @compute func(%gid_1:vec3<u32> [@global_invocation_id], %lid_1:vec3<u32> [@local_invocation_id], %group_id:vec3<u32> [@workgroup_id]):void { # %gid_1: 'gid', %lid_1: 'lid'
+ $B2: {
+ %9:vec3<u32> = add %group_id, %gid_1
+ %10:void = call %foo, %gid_1, %lid_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_UsedEntryPointAndHelper_ForwardReference) {
+ auto* gid = b.Var("gid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kGlobalInvocationId;
+ gid->SetAttributes(std::move(attributes));
+ }
+ auto* lid = b.Var("lid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kLocalInvocationId;
+ lid->SetAttributes(std::move(attributes));
+ }
+ auto* group_id = b.Var("group_id", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kWorkgroupId;
+ group_id->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(gid);
+ mod.root_block->Append(lid);
+ mod.root_block->Append(group_id);
+
+ auto* ep = b.Function("main1", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ auto* foo = b.Function("foo", ty.void_());
+
+ // Use a subset of the inputs in the entry point.
+ b.Append(ep->Block(), [&] {
+ auto* group_value = b.Load(group_id);
+ auto* gid_value = b.Load(gid);
+ b.Add(ty.vec3<u32>(), group_value, gid_value);
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ // Use a different subset of the variables in the helper.
+ b.Append(foo->Block(), [&] {
+ auto* gid_value = b.Load(gid);
+ auto* lid_value = b.Load(lid);
+ b.Add(ty.vec3<u32>(), gid_value, lid_value);
+ b.Return(foo);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %gid:ptr<__in, vec3<u32>, read> = var @builtin(global_invocation_id)
+ %lid:ptr<__in, vec3<u32>, read> = var @builtin(local_invocation_id)
+ %group_id:ptr<__in, vec3<u32>, read> = var @builtin(workgroup_id)
+}
+
+%main1 = @compute func():void {
+ $B2: {
+ %5:vec3<u32> = load %group_id
+ %6:vec3<u32> = load %gid
+ %7:vec3<u32> = add %5, %6
+ %8:void = call %foo
+ ret
+ }
+}
+%foo = func():void {
+ $B3: {
+ %10:vec3<u32> = load %gid
+ %11:vec3<u32> = load %lid
+ %12:vec3<u32> = add %10, %11
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%main1 = @compute func(%gid:vec3<u32> [@global_invocation_id], %lid:vec3<u32> [@local_invocation_id], %group_id:vec3<u32> [@workgroup_id]):void {
+ $B1: {
+ %5:vec3<u32> = add %group_id, %gid
+ %6:void = call %foo, %gid, %lid
+ ret
+ }
+}
+%foo = func(%gid_1:vec3<u32>, %lid_1:vec3<u32>):void { # %gid_1: 'gid', %lid_1: 'lid'
+ $B2: {
+ %10:vec3<u32> = add %gid_1, %lid_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_UsedByMultipleEntryPoints) {
+ auto* gid = b.Var("gid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kGlobalInvocationId;
+ gid->SetAttributes(std::move(attributes));
+ }
+ auto* lid = b.Var("lid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kLocalInvocationId;
+ lid->SetAttributes(std::move(attributes));
+ }
+ auto* group_id = b.Var("group_id", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kWorkgroupId;
+ group_id->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(gid);
+ mod.root_block->Append(lid);
+ mod.root_block->Append(group_id);
+
+ // Use a subset of the inputs in the helper.
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* gid_value = b.Load(gid);
+ auto* lid_value = b.Load(lid);
+ b.Add(ty.vec3<u32>(), gid_value, lid_value);
+ b.Return(foo);
+ });
+
+ // Call the helper without directly referencing any inputs.
+ auto* ep1 = b.Function("main1", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ b.Append(ep1->Block(), [&] {
+ b.Call(foo);
+ b.Return(ep1);
+ });
+
+ // Reference another input and then call the helper.
+ auto* ep2 = b.Function("main2", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ b.Append(ep2->Block(), [&] {
+ auto* group_value = b.Load(group_id);
+ b.Add(ty.vec3<u32>(), group_value, group_value);
+ b.Call(foo);
+ b.Return(ep1);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %gid:ptr<__in, vec3<u32>, read> = var @builtin(global_invocation_id)
+ %lid:ptr<__in, vec3<u32>, read> = var @builtin(local_invocation_id)
+ %group_id:ptr<__in, vec3<u32>, read> = var @builtin(workgroup_id)
+}
+
+%foo = func():void {
+ $B2: {
+ %5:vec3<u32> = load %gid
+ %6:vec3<u32> = load %lid
+ %7:vec3<u32> = add %5, %6
+ ret
+ }
+}
+%main1 = @compute func():void {
+ $B3: {
+ %9:void = call %foo
+ ret
+ }
+}
+%main2 = @compute func():void {
+ $B4: {
+ %11:vec3<u32> = load %group_id
+ %12:vec3<u32> = add %11, %11
+ %13:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%gid:vec3<u32>, %lid:vec3<u32>):void {
+ $B1: {
+ %4:vec3<u32> = add %gid, %lid
+ ret
+ }
+}
+%main1 = @compute func(%gid_1:vec3<u32> [@global_invocation_id], %lid_1:vec3<u32> [@local_invocation_id]):void { # %gid_1: 'gid', %lid_1: 'lid'
+ $B2: {
+ %8:void = call %foo, %gid_1, %lid_1
+ ret
+ }
+}
+%main2 = @compute func(%gid_2:vec3<u32> [@global_invocation_id], %lid_2:vec3<u32> [@local_invocation_id], %group_id:vec3<u32> [@workgroup_id]):void { # %gid_2: 'gid', %lid_2: 'lid'
+ $B3: {
+ %13:vec3<u32> = add %group_id, %group_id
+ %14:void = call %foo, %gid_2, %lid_2
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Input_LoadVectorElement) {
+ auto* lid = b.Var("lid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kLocalInvocationId;
+ lid->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(lid);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ b.Append(ep->Block(), [&] {
+ b.LoadVectorElement(lid, 2_u);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %lid:ptr<__in, vec3<u32>, read> = var @builtin(local_invocation_id)
+}
+
+%foo = @compute func():void {
+ $B2: {
+ %3:u32 = load_vector_element %lid, 2u
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = @compute func(%lid:vec3<u32> [@local_invocation_id]):void {
+ $B1: {
+ %3:u32 = access %lid, 2u
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Input_AccessChains) {
+ auto* lid = b.Var("lid", ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kLocalInvocationId;
+ lid->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(lid);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+ b.Append(ep->Block(), [&] {
+ auto* access_1 = b.Access(ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()), lid);
+ auto* access_2 = b.Access(ty.ptr(core::AddressSpace::kIn, ty.vec3<u32>()), access_1);
+ auto* vec = b.Load(access_2);
+ auto* z = b.LoadVectorElement(access_2, 2_u);
+ b.Multiply<vec3<u32>>(vec, z);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %lid:ptr<__in, vec3<u32>, read> = var @builtin(local_invocation_id)
+}
+
+%foo = @compute func():void {
+ $B2: {
+ %3:ptr<__in, vec3<u32>, read> = access %lid
+ %4:ptr<__in, vec3<u32>, read> = access %3
+ %5:vec3<u32> = load %4
+ %6:u32 = load_vector_element %4, 2u
+ %7:vec3<u32> = mul %5, %6
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = @compute func(%lid:vec3<u32> [@local_invocation_id]):void {
+ $B1: {
+ %3:u32 = access %lid, 2u
+ %4:vec3<u32> = mul %lid, %3
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_Struct_LocationOnEachMember) {
+ auto* colors_str = ty.Struct(
+ mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ LocationAttrs(1),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ LocationAttrs(2u, core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kCentroid}),
+ },
+ });
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kIn, colors_str));
+ mod.root_block->Append(colors);
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* ptr = ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>());
+ auto* color1_value = b.Load(b.Access(ptr, colors, 0_u));
+ auto* color2_z_value = b.LoadVectorElement(b.Access(ptr, colors, 1_u), 2_u);
+ b.Multiply(ty.vec4<f32>(), color1_value, color2_z_value);
+ b.Return(foo);
+ });
+
+ auto* ep = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, centroid)
+}
+
+$B1: { # root
+ %colors:ptr<__in, Colors, read> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %3:ptr<__in, vec4<f32>, read> = access %colors, 0u
+ %4:vec4<f32> = load %3
+ %5:ptr<__in, vec4<f32>, read> = access %colors, 1u
+ %6:f32 = load_vector_element %5, 2u
+ %7:vec4<f32> = mul %4, %6
+ ret
+ }
+}
+%main = @fragment func():void {
+ $B3: {
+ %9:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, centroid)
+}
+
+%foo = func(%colors:Colors):void {
+ $B1: {
+ %3:vec4<f32> = access %colors, 0u
+ %4:vec4<f32> = access %colors, 1u
+ %5:f32 = access %4, 2u
+ %6:vec4<f32> = mul %3, %5
+ ret
+ }
+}
+%main = @fragment func(%colors_1:Colors):void { # %colors_1: 'colors'
+ $B2: {
+ %9:void = call %foo, %colors_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_Struct_LocationOnVariable) {
+ auto* colors_str =
+ ty.Struct(mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ core::type::StructMemberAttributes{
+ /* location */ std::nullopt,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */
+ core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid},
+ /* invariant */ false,
+ },
+ },
+ });
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kIn, colors_str));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ colors->SetAttributes(attributes);
+ }
+ mod.root_block->Append(colors);
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* ptr = ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>());
+ auto* color1_value = b.Load(b.Access(ptr, colors, 0_u));
+ auto* color2_z_value = b.LoadVectorElement(b.Access(ptr, colors, 1_u), 2_u);
+ b.Multiply(ty.vec4<f32>(), color1_value, color2_z_value);
+ b.Return(foo);
+ });
+
+ auto* ep = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0)
+ color2:vec4<f32> @offset(16), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %colors:ptr<__in, Colors, read> = var @location(1)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:ptr<__in, vec4<f32>, read> = access %colors, 0u
+ %4:vec4<f32> = load %3
+ %5:ptr<__in, vec4<f32>, read> = access %colors, 1u
+ %6:f32 = load_vector_element %5, 2u
+ %7:vec4<f32> = mul %4, %6
+ ret
+ }
+}
+%main = @fragment func():void {
+ $B3: {
+ %9:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(perspective, centroid)
+}
+
+%foo = func(%colors:Colors):void {
+ $B1: {
+ %3:vec4<f32> = access %colors, 0u
+ %4:vec4<f32> = access %colors, 1u
+ %5:f32 = access %4, 2u
+ %6:vec4<f32> = mul %3, %5
+ ret
+ }
+}
+%main = @fragment func(%colors_1:Colors):void { # %colors_1: 'colors'
+ $B2: {
+ %9:void = call %foo, %colors_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_Struct_InterpolateOnVariable) {
+ auto* colors_str = ty.Struct(
+ mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ LocationAttrs(1),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ LocationAttrs(2u, core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kSample}),
+ },
+ });
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kIn, colors_str));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid};
+ colors->SetAttributes(attributes);
+ }
+ mod.root_block->Append(colors);
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* ptr = ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>());
+ auto* color1_value = b.Load(b.Access(ptr, colors, 0_u));
+ auto* color2_z_value = b.LoadVectorElement(b.Access(ptr, colors, 1_u), 2_u);
+ b.Multiply(ty.vec4<f32>(), color1_value, color2_z_value);
+ b.Return(foo);
+ });
+
+ auto* ep = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, sample)
+}
+
+$B1: { # root
+ %colors:ptr<__in, Colors, read> = var @interpolate(perspective, centroid)
+}
+
+%foo = func():void {
+ $B2: {
+ %3:ptr<__in, vec4<f32>, read> = access %colors, 0u
+ %4:vec4<f32> = load %3
+ %5:ptr<__in, vec4<f32>, read> = access %colors, 1u
+ %6:f32 = load_vector_element %5, 2u
+ %7:vec4<f32> = mul %4, %6
+ ret
+ }
+}
+%main = @fragment func():void {
+ $B3: {
+ %9:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1), @interpolate(perspective, centroid)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, sample)
+}
+
+%foo = func(%colors:Colors):void {
+ $B1: {
+ %3:vec4<f32> = access %colors, 0u
+ %4:vec4<f32> = access %colors, 1u
+ %5:f32 = access %4, 2u
+ %6:vec4<f32> = mul %3, %5
+ ret
+ }
+}
+%main = @fragment func(%colors_1:Colors):void { # %colors_1: 'colors'
+ $B2: {
+ %9:void = call %foo, %colors_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_Struct_LoadWholeStruct) {
+ auto* colors_str = ty.Struct(
+ mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ LocationAttrs(1),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ LocationAttrs(2u, core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kCentroid}),
+ },
+ });
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kIn, colors_str));
+ mod.root_block->Append(colors);
+
+ auto* foo = b.Function("foo", ty.void_());
+ b.Append(foo->Block(), [&] {
+ auto* load = b.Load(colors);
+ auto* color1_value = b.Access<vec4<f32>>(load, 0_u);
+ auto* color2_z_value = b.Access<f32>(load, 1_u, 2_u);
+ b.Multiply(ty.vec4<f32>(), color1_value, color2_z_value);
+ b.Return(foo);
+ });
+
+ auto* ep = b.Function("main", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ b.Call(foo);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, centroid)
+}
+
+$B1: { # root
+ %colors:ptr<__in, Colors, read> = var
+}
+
+%foo = func():void {
+ $B2: {
+ %3:Colors = load %colors
+ %4:vec4<f32> = access %3, 0u
+ %5:f32 = access %3, 1u, 2u
+ %6:vec4<f32> = mul %4, %5
+ ret
+ }
+}
+%main = @fragment func():void {
+ $B3: {
+ %8:void = call %foo
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(linear, centroid)
+}
+
+%foo = func(%colors:Colors):void {
+ $B1: {
+ %3:vec4<f32> = access %colors, 0u
+ %4:f32 = access %colors, 1u, 2u
+ %5:vec4<f32> = mul %3, %4
+ ret
+ }
+}
+%main = @fragment func(%colors_1:Colors):void { # %colors_1: 'colors'
+ $B2: {
+ %8:void = call %foo, %colors_1
+ ret
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, SingleOutput_Builtin) {
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ position->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(position);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %position:ptr<__out, vec4<f32>, read_write> = var @builtin(position)
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %position:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+%foo = @vertex func():vec4<f32> [@position] {
+ $B3: {
+ %4:void = call %foo_inner
+ %5:vec4<f32> = load %position
+ ret %5
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, SingleOutput_Builtin_WithInvariant) {
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(position);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %position:ptr<__out, vec4<f32>, read_write> = var @invariant @builtin(position)
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %position:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+%foo = @vertex func():vec4<f32> [@invariant, @position] {
+ $B3: {
+ %4:void = call %foo_inner
+ %5:vec4<f32> = load %position
+ ret %5
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, SingleOutput_Location) {
+ auto* color = b.Var("color", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(color);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] { //
+ b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %color:ptr<__out, vec4<f32>, read_write> = var @location(1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %color:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ ret
+ }
+}
+%foo = @fragment func():vec4<f32> [@location(1)] {
+ $B3: {
+ %4:void = call %foo_inner
+ %5:vec4<f32> = load %color
+ ret %5
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, SingleOutput_Location_WithInterpolation) {
+ auto* color = b.Var("color", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid};
+ color->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(color);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] { //
+ b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %color:ptr<__out, vec4<f32>, read_write> = var @location(1) @interpolate(perspective, centroid)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %color:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ ret
+ }
+}
+%foo = @fragment func():vec4<f32> [@location(1), @interpolate(perspective, centroid)] {
+ $B3: {
+ %4:void = call %foo_inner
+ %5:vec4<f32> = load %color
+ ret %5
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, MultipleOutputs) {
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ auto* color1 = b.Var("color1", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color1->SetAttributes(std::move(attributes));
+ }
+ auto* color2 = b.Var("color2", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid};
+ color2->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(position);
+ mod.root_block->Append(color1);
+ mod.root_block->Append(color2);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(color1, b.Splat<vec4<f32>>(0.5_f, 4));
+ b.Store(color2, b.Splat<vec4<f32>>(0.25_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %position:ptr<__out, vec4<f32>, read_write> = var @invariant @builtin(position)
+ %color1:ptr<__out, vec4<f32>, read_write> = var @location(1)
+ %color2:ptr<__out, vec4<f32>, read_write> = var @location(1) @interpolate(perspective, centroid)
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ store %color1, vec4<f32>(0.5f)
+ store %color2, vec4<f32>(0.25f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_symbol = struct @align(16) {
+ position:vec4<f32> @offset(0), @invariant, @builtin(position)
+ color1:vec4<f32> @offset(16), @location(1)
+ color2:vec4<f32> @offset(32), @location(1), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %position:ptr<private, vec4<f32>, read_write> = var
+ %color1:ptr<private, vec4<f32>, read_write> = var
+ %color2:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ store %color1, vec4<f32>(0.5f)
+ store %color2, vec4<f32>(0.25f)
+ ret
+ }
+}
+%foo = @vertex func():tint_symbol {
+ $B3: {
+ %6:void = call %foo_inner
+ %7:vec4<f32> = load %position
+ %8:vec4<f32> = load %color1
+ %9:vec4<f32> = load %color2
+ %10:tint_symbol = construct %7, %8, %9
+ ret %10
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Outputs_Struct_LocationOnEachMember) {
+ auto* builtin_str =
+ ty.Struct(mod.symbols.New("Builtins"), Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("position"),
+ ty.vec4<f32>(),
+ BuiltinAttrs(core::BuiltinValue::kPosition),
+ },
+ });
+ auto* colors_str = ty.Struct(
+ mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ LocationAttrs(1),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ LocationAttrs(2u, core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid}),
+ },
+ });
+
+ auto* builtins = b.Var("builtins", ty.ptr(core::AddressSpace::kOut, builtin_str));
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kOut, colors_str));
+ mod.root_block->Append(builtins);
+ mod.root_block->Append(colors);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
+ b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
+ b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(1)
+ color2:vec4<f32> @offset(16), @location(2), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %builtins:ptr<__out, Builtins, read_write> = var
+ %colors:ptr<__out, Colors, read_write> = var
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ %4:ptr<__out, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<__out, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<__out, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0)
+ color2:vec4<f32> @offset(16)
+}
+
+tint_symbol = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+ color1:vec4<f32> @offset(16), @location(1)
+ color2:vec4<f32> @offset(32), @location(2), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %builtins:ptr<private, Builtins, read_write> = var
+ %colors:ptr<private, Colors, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ %4:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+%foo = @vertex func():tint_symbol {
+ $B3: {
+ %8:void = call %foo_inner
+ %9:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ %10:vec4<f32> = load %9
+ %11:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ %12:vec4<f32> = load %11
+ %13:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ %14:vec4<f32> = load %13
+ %15:tint_symbol = construct %10, %12, %14
+ ret %15
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Outputs_Struct_LocationOnVariable) {
+ auto* builtin_str =
+ ty.Struct(mod.symbols.New("Builtins"), Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("position"),
+ ty.vec4<f32>(),
+ BuiltinAttrs(core::BuiltinValue::kPosition),
+ },
+ });
+ auto* colors_str =
+ ty.Struct(mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ core::type::StructMemberAttributes{
+ /* location */ std::nullopt,
+ /* index */ std::nullopt,
+ /* color */ std::nullopt,
+ /* builtin */ std::nullopt,
+ /* interpolation */
+ core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid},
+ /* invariant */ false,
+ },
+ },
+ });
+
+ auto* builtins = b.Var("builtins", ty.ptr(core::AddressSpace::kOut, builtin_str));
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kOut, colors_str));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ colors->SetAttributes(attributes);
+ }
+ mod.root_block->Append(builtins);
+ mod.root_block->Append(colors);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
+ b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
+ b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0)
+ color2:vec4<f32> @offset(16), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %builtins:ptr<__out, Builtins, read_write> = var
+ %colors:ptr<__out, Colors, read_write> = var @location(1)
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ %4:ptr<__out, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<__out, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<__out, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0)
+ color2:vec4<f32> @offset(16)
+}
+
+tint_symbol = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+ color1:vec4<f32> @offset(16), @location(1)
+ color2:vec4<f32> @offset(32), @location(2), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %builtins:ptr<private, Builtins, read_write> = var
+ %colors:ptr<private, Colors, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ %4:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+%foo = @vertex func():tint_symbol {
+ $B3: {
+ %8:void = call %foo_inner
+ %9:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ %10:vec4<f32> = load %9
+ %11:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ %12:vec4<f32> = load %11
+ %13:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ %14:vec4<f32> = load %13
+ %15:tint_symbol = construct %10, %12, %14
+ ret %15
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Outputs_Struct_InterpolateOnVariable) {
+ auto* builtin_str =
+ ty.Struct(mod.symbols.New("Builtins"), Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("position"),
+ ty.vec4<f32>(),
+ BuiltinAttrs(core::BuiltinValue::kPosition),
+ },
+ });
+ auto* colors_str =
+ ty.Struct(mod.symbols.New("Colors"),
+ Vector{
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color1"),
+ ty.vec4<f32>(),
+ LocationAttrs(2),
+ },
+ core::type::Manager::StructMemberDesc{
+ mod.symbols.New("color2"),
+ ty.vec4<f32>(),
+ LocationAttrs(3, core::Interpolation{core::InterpolationType::kFlat}),
+ },
+ });
+
+ auto* builtins = b.Var("builtins", ty.ptr(core::AddressSpace::kOut, builtin_str));
+ auto* colors = b.Var("colors", ty.ptr(core::AddressSpace::kOut, colors_str));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid};
+ colors->SetAttributes(attributes);
+ }
+ mod.root_block->Append(builtins);
+ mod.root_block->Append(colors);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep->Block(), [&] { //
+ auto* ptr = ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>());
+ b.Store(b.Access(ptr, builtins, 0_u), b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(b.Access(ptr, colors, 0_u), b.Splat<vec4<f32>>(0.5_f, 4));
+ b.Store(b.Access(ptr, colors, 1_u), b.Splat<vec4<f32>>(0.25_f, 4));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0), @location(2)
+ color2:vec4<f32> @offset(16), @location(3), @interpolate(flat)
+}
+
+$B1: { # root
+ %builtins:ptr<__out, Builtins, read_write> = var
+ %colors:ptr<__out, Colors, read_write> = var @interpolate(perspective, centroid)
+}
+
+%foo = @vertex func():void {
+ $B2: {
+ %4:ptr<__out, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<__out, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<__out, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+Builtins = struct @align(16) {
+ position:vec4<f32> @offset(0)
+}
+
+Colors = struct @align(16) {
+ color1:vec4<f32> @offset(0)
+ color2:vec4<f32> @offset(16)
+}
+
+tint_symbol = struct @align(16) {
+ position:vec4<f32> @offset(0), @builtin(position)
+ color1:vec4<f32> @offset(16), @location(2), @interpolate(perspective, centroid)
+ color2:vec4<f32> @offset(32), @location(3), @interpolate(flat)
+}
+
+$B1: { # root
+ %builtins:ptr<private, Builtins, read_write> = var
+ %colors:ptr<private, Colors, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ %4:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ store %4, vec4<f32>(1.0f)
+ %5:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ store %5, vec4<f32>(0.5f)
+ %6:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ store %6, vec4<f32>(0.25f)
+ ret
+ }
+}
+%foo = @vertex func():tint_symbol {
+ $B3: {
+ %8:void = call %foo_inner
+ %9:ptr<private, vec4<f32>, read_write> = access %builtins, 0u
+ %10:vec4<f32> = load %9
+ %11:ptr<private, vec4<f32>, read_write> = access %colors, 0u
+ %12:vec4<f32> = load %11
+ %13:ptr<private, vec4<f32>, read_write> = access %colors, 1u
+ %14:vec4<f32> = load %13
+ %15:tint_symbol = construct %10, %12, %14
+ ret %15
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Outputs_UsedByMultipleEntryPoints) {
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ auto* color1 = b.Var("color1", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color1->SetAttributes(std::move(attributes));
+ }
+ auto* color2 = b.Var("color2", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ attributes.interpolation = core::Interpolation{core::InterpolationType::kPerspective,
+ core::InterpolationSampling::kCentroid};
+ color2->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(position);
+ mod.root_block->Append(color1);
+ mod.root_block->Append(color2);
+
+ auto* ep1 = b.Function("main1", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep1->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Return(ep1);
+ });
+
+ auto* ep2 = b.Function("main2", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep2->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(color1, b.Splat<vec4<f32>>(0.5_f, 4));
+ b.Return(ep2);
+ });
+
+ auto* ep3 = b.Function("main3", ty.void_(), core::ir::Function::PipelineStage::kVertex);
+ b.Append(ep3->Block(), [&] { //
+ b.Store(position, b.Splat<vec4<f32>>(1_f, 4));
+ b.Store(color2, b.Splat<vec4<f32>>(0.25_f, 4));
+ b.Return(ep3);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %position:ptr<__out, vec4<f32>, read_write> = var @invariant @builtin(position)
+ %color1:ptr<__out, vec4<f32>, read_write> = var @location(1)
+ %color2:ptr<__out, vec4<f32>, read_write> = var @location(1) @interpolate(perspective, centroid)
+}
+
+%main1 = @vertex func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+%main2 = @vertex func():void {
+ $B3: {
+ store %position, vec4<f32>(1.0f)
+ store %color1, vec4<f32>(0.5f)
+ ret
+ }
+}
+%main3 = @vertex func():void {
+ $B4: {
+ store %position, vec4<f32>(1.0f)
+ store %color2, vec4<f32>(0.25f)
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_symbol = struct @align(16) {
+ position:vec4<f32> @offset(0), @invariant, @builtin(position)
+ color1:vec4<f32> @offset(16), @location(1)
+}
+
+tint_symbol_1 = struct @align(16) {
+ position:vec4<f32> @offset(0), @invariant, @builtin(position)
+ color2:vec4<f32> @offset(16), @location(1), @interpolate(perspective, centroid)
+}
+
+$B1: { # root
+ %position:ptr<private, vec4<f32>, read_write> = var
+ %color1:ptr<private, vec4<f32>, read_write> = var
+ %color2:ptr<private, vec4<f32>, read_write> = var
+}
+
+%main1_inner = func():void {
+ $B2: {
+ store %position, vec4<f32>(1.0f)
+ ret
+ }
+}
+%main2_inner = func():void {
+ $B3: {
+ store %position, vec4<f32>(1.0f)
+ store %color1, vec4<f32>(0.5f)
+ ret
+ }
+}
+%main3_inner = func():void {
+ $B4: {
+ store %position, vec4<f32>(1.0f)
+ store %color2, vec4<f32>(0.25f)
+ ret
+ }
+}
+%main1 = @vertex func():vec4<f32> [@invariant, @position] {
+ $B5: {
+ %8:void = call %main1_inner
+ %9:vec4<f32> = load %position
+ ret %9
+ }
+}
+%main2 = @vertex func():tint_symbol {
+ $B6: {
+ %11:void = call %main2_inner
+ %12:vec4<f32> = load %position
+ %13:vec4<f32> = load %color1
+ %14:tint_symbol = construct %12, %13
+ ret %14
+ }
+}
+%main3 = @vertex func():tint_symbol_1 {
+ $B7: {
+ %16:void = call %main3_inner
+ %17:vec4<f32> = load %position
+ %18:vec4<f32> = load %color2
+ %19:tint_symbol_1 = construct %17, %18
+ ret %19
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Output_LoadAndStore) {
+ auto* color = b.Var("color", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(color);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] { //
+ b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+ auto* load = b.Load(color);
+ auto* mul = b.Multiply<vec4<f32>>(load, 2_f);
+ b.Store(color, mul);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %color:ptr<__out, vec4<f32>, read_write> = var @location(1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ %3:vec4<f32> = load %color
+ %4:vec4<f32> = mul %3, 2.0f
+ store %color, %4
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %color:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ %3:vec4<f32> = load %color
+ %4:vec4<f32> = mul %3, 2.0f
+ store %color, %4
+ ret
+ }
+}
+%foo = @fragment func():vec4<f32> [@location(1)] {
+ $B3: {
+ %6:void = call %foo_inner
+ %7:vec4<f32> = load %color
+ ret %7
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Output_LoadVectorElementAndStoreVectorElement) {
+ auto* color = b.Var("color", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(color);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] { //
+ b.Store(color, b.Splat<vec4<f32>>(1_f, 4));
+ auto* load = b.LoadVectorElement(color, 2_u);
+ auto* mul = b.Multiply<f32>(load, 2_f);
+ b.StoreVectorElement(color, 2_u, mul);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %color:ptr<__out, vec4<f32>, read_write> = var @location(1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ %3:f32 = load_vector_element %color, 2u
+ %4:f32 = mul %3, 2.0f
+ store_vector_element %color, 2u, %4
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %color:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ store %color, vec4<f32>(1.0f)
+ %3:f32 = load_vector_element %color, 2u
+ %4:f32 = mul %3, 2.0f
+ store_vector_element %color, 2u, %4
+ ret
+ }
+}
+%foo = @fragment func():vec4<f32> [@location(1)] {
+ $B3: {
+ %6:void = call %foo_inner
+ %7:vec4<f32> = load %color
+ ret %7
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Output_AccessChain) {
+ auto* color = b.Var("color", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1u;
+ color->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(color);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] { //
+ auto* access_1 = b.Access(ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()), color);
+ auto* access_2 = b.Access(ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()), access_1);
+ auto* load = b.LoadVectorElement(access_2, 2_u);
+ auto* mul = b.Multiply<vec4<f32>>(b.Splat<vec4<f32>>(1_f, 4), load);
+ b.Store(access_2, mul);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %color:ptr<__out, vec4<f32>, read_write> = var @location(1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %3:ptr<__out, vec4<f32>, read_write> = access %color
+ %4:ptr<__out, vec4<f32>, read_write> = access %3
+ %5:f32 = load_vector_element %4, 2u
+ %6:vec4<f32> = mul vec4<f32>(1.0f), %5
+ store %4, %6
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %color:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func():void {
+ $B2: {
+ %3:ptr<private, vec4<f32>, read_write> = access %color
+ %4:ptr<private, vec4<f32>, read_write> = access %3
+ %5:f32 = load_vector_element %4, 2u
+ %6:vec4<f32> = mul vec4<f32>(1.0f), %5
+ store %4, %6
+ ret
+ }
+}
+%foo = @fragment func():vec4<f32> [@location(1)] {
+ $B3: {
+ %8:void = call %foo_inner
+ %9:vec4<f32> = load %color
+ ret %9
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, Inputs_And_Outputs) {
+ auto* position = b.Var("position", ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kPosition;
+ attributes.invariant = true;
+ position->SetAttributes(std::move(attributes));
+ }
+ auto* color_in = b.Var("color_in", ty.ptr(core::AddressSpace::kIn, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 0;
+ color_in->SetAttributes(std::move(attributes));
+ }
+ auto* color_out_1 = b.Var("color_out_1", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 1;
+ color_out_1->SetAttributes(std::move(attributes));
+ }
+ auto* color_out_2 = b.Var("color_out_2", ty.ptr(core::AddressSpace::kOut, ty.vec4<f32>()));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.location = 2;
+ color_out_2->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(position);
+ mod.root_block->Append(color_in);
+ mod.root_block->Append(color_out_1);
+ mod.root_block->Append(color_out_2);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ auto* position_value = b.Load(position);
+ auto* color_in_value = b.Load(color_in);
+ b.Store(color_out_1, position_value);
+ b.Store(color_out_2, color_in_value);
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %position:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %color_in:ptr<__in, vec4<f32>, read> = var @location(0)
+ %color_out_1:ptr<__out, vec4<f32>, read_write> = var @location(1)
+ %color_out_2:ptr<__out, vec4<f32>, read_write> = var @location(2)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %6:vec4<f32> = load %position
+ %7:vec4<f32> = load %color_in
+ store %color_out_1, %6
+ store %color_out_2, %7
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_symbol = struct @align(16) {
+ color_out_1:vec4<f32> @offset(0), @location(1)
+ color_out_2:vec4<f32> @offset(16), @location(2)
+}
+
+$B1: { # root
+ %color_out_1:ptr<private, vec4<f32>, read_write> = var
+ %color_out_2:ptr<private, vec4<f32>, read_write> = var
+}
+
+%foo_inner = func(%position:vec4<f32>, %color_in:vec4<f32>):void {
+ $B2: {
+ store %color_out_1, %position
+ store %color_out_2, %color_in
+ ret
+ }
+}
+%foo = @fragment func(%position_1:vec4<f32> [@invariant, @position], %color_in_1:vec4<f32> [@location(0)]):tint_symbol { # %position_1: 'position', %color_in_1: 'color_in'
+ $B3: {
+ %9:void = call %foo_inner, %position_1, %color_in_1
+ %10:vec4<f32> = load %color_out_1
+ %11:vec4<f32> = load %color_out_2
+ %12:tint_symbol = construct %10, %11
+ ret %12
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Test that a sample mask array is converted to a scalar u32 for the entry point.
+TEST_F(SpirvReader_ShaderIOTest, SampleMask) {
+ auto* arr = ty.array<u32, 1>();
+ auto* mask_in = b.Var("mask_in", ty.ptr(core::AddressSpace::kIn, arr));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kSampleMask;
+ mask_in->SetAttributes(std::move(attributes));
+ }
+ auto* mask_out = b.Var("mask_out", ty.ptr(core::AddressSpace::kOut, arr));
+ {
+ core::ir::IOAttributes attributes;
+ attributes.builtin = core::BuiltinValue::kSampleMask;
+ mask_out->SetAttributes(std::move(attributes));
+ }
+ mod.root_block->Append(mask_in);
+ mod.root_block->Append(mask_out);
+
+ auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(ep->Block(), [&] {
+ auto* mask_value = b.Load(mask_in);
+ auto* doubled = b.Multiply(ty.u32(), b.Access(ty.u32(), mask_value, 0_u), 2_u);
+ b.Store(mask_out, b.Construct(arr, doubled));
+ b.Return(ep);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %mask_in:ptr<__in, array<u32, 1>, read> = var @builtin(sample_mask)
+ %mask_out:ptr<__out, array<u32, 1>, read_write> = var @builtin(sample_mask)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:array<u32, 1> = load %mask_in
+ %5:u32 = access %4, 0u
+ %6:u32 = mul %5, 2u
+ %7:array<u32, 1> = construct %6
+ store %mask_out, %7
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %mask_out:ptr<private, array<u32, 1>, read_write> = var
+}
+
+%foo_inner = func(%mask_in:array<u32, 1>):void {
+ $B2: {
+ %4:u32 = access %mask_in, 0u
+ %5:u32 = mul %4, 2u
+ %6:array<u32, 1> = construct %5
+ store %mask_out, %6
+ ret
+ }
+}
+%foo = @fragment func(%mask_in_1:u32 [@sample_mask]):u32 [@sample_mask] { # %mask_in_1: 'mask_in'
+ $B3: {
+ %9:array<u32, 1> = construct %mask_in_1
+ %10:void = call %foo_inner, %9
+ %11:array<u32, 1> = load %mask_out
+ %12:u32 = access %11, 0u
+ ret %12
+ }
+}
+)";
+
+ Run(ShaderIO);
+
+ EXPECT_EQ(expect, str());
+}
+
+} // namespace
+} // namespace tint::spirv::reader::lower
diff --git a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
index daf59df..948104d 100644
--- a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
@@ -131,9 +131,8 @@
Switch(
use.instruction,
[&](core::ir::Load* load) {
- auto* lve = b.LoadVectorElement(object, index);
+ auto* lve = b.LoadVectorElementWithResult(load->DetachResult(), object, index);
lve->InsertBefore(load);
- load->Result(0)->ReplaceAllUsesWith(lve->Result(0));
to_destroy.Push(load);
},
[&](core::ir::Store* store) {
diff --git a/src/tint/lang/spirv/reader/parser/struct_test.cc b/src/tint/lang/spirv/reader/parser/struct_test.cc
index 7c85914..f59236b 100644
--- a/src/tint/lang/spirv/reader/parser/struct_test.cc
+++ b/src/tint/lang/spirv/reader/parser/struct_test.cc
@@ -30,7 +30,7 @@
namespace tint::spirv::reader {
TEST_F(SpirvParserTest, Struct_Empty) {
- EXPECT_DEATH( //
+ EXPECT_DEATH_IF_SUPPORTED( //
{
auto assembly = Assemble(R"(
OpCapability Shader
diff --git a/src/tint/lang/spirv/reader/reader_test.cc b/src/tint/lang/spirv/reader/reader_test.cc
index d8ab351..5242819 100644
--- a/src/tint/lang/spirv/reader/reader_test.cc
+++ b/src/tint/lang/spirv/reader/reader_test.cc
@@ -154,5 +154,201 @@
)");
}
+TEST_F(SpirvReaderTest, ShaderInputs) {
+ auto got = Run(R"(
+ OpCapability Shader
+ OpCapability SampleRateShading
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %coord %colors
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %coord BuiltIn FragCoord
+ OpDecorate %colors Location 1
+ OpMemberDecorate %str 1 NoPerspective
+ %void = OpTypeVoid
+ %f32 = OpTypeFloat 32
+ %vec4f = OpTypeVector %f32 4
+ %fn_type = OpTypeFunction %void
+ %str = OpTypeStruct %vec4f %vec4f
+ %u32 = OpTypeInt 32 0
+ %u32_0 = OpConstant %u32 0
+ %u32_1 = OpConstant %u32 1
+
+%_ptr_Input_vec4f = OpTypePointer Input %vec4f
+ %_ptr_Input_str = OpTypePointer Input %str
+ %coord = OpVariable %_ptr_Input_vec4f Input
+ %colors = OpVariable %_ptr_Input_str Input
+
+ %main = OpFunction %void None %fn_type
+ %main_start = OpLabel
+ %access_a = OpAccessChain %_ptr_Input_vec4f %colors %u32_0
+ %access_b = OpAccessChain %_ptr_Input_vec4f %colors %u32_1
+ %a = OpLoad %vec4f %access_a
+ %b = OpLoad %vec4f %access_b
+ %c = OpLoad %vec4f %coord
+ %mul = OpFMul %vec4f %a %b
+ %add = OpFAdd %vec4f %mul %c
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(got, Success);
+ EXPECT_EQ(got, R"(
+tint_symbol_2 = struct @align(16) {
+ tint_symbol:vec4<f32> @offset(0), @location(1)
+ tint_symbol_1:vec4<f32> @offset(16), @location(2), @interpolate(linear, center)
+}
+
+%main = @fragment func(%2:vec4<f32> [@position], %3:tint_symbol_2):void {
+ $B1: {
+ %4:vec4<f32> = access %3, 0u
+ %5:vec4<f32> = access %3, 1u
+ %6:vec4<f32> = mul %4, %5
+ %7:vec4<f32> = add %6, %2
+ ret
+ }
+}
+)");
+}
+
+TEST_F(SpirvReaderTest, ShaderOutputs) {
+ auto got = Run(R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %depth %colors
+ OpExecutionMode %main OriginUpperLeft
+ OpExecutionMode %main DepthReplacing
+ OpDecorate %depth BuiltIn FragDepth
+ OpDecorate %colors Location 1
+ OpMemberDecorate %str 1 NoPerspective
+ %void = OpTypeVoid
+ %f32 = OpTypeFloat 32
+ %vec4f = OpTypeVector %f32 4
+ %fn_type = OpTypeFunction %void
+ %str = OpTypeStruct %vec4f %vec4f
+ %u32 = OpTypeInt 32 0
+ %u32_0 = OpConstant %u32 0
+ %u32_1 = OpConstant %u32 1
+ %f32_42 = OpConstant %f32 42.0
+ %f32_n1 = OpConstant %f32 -1.0
+ %f32_v4_a = OpConstantComposite %vec4f %f32_42 %f32_42 %f32_42 %f32_n1
+ %f32_v4_b = OpConstantComposite %vec4f %f32_n1 %f32_n1 %f32_n1 %f32_42
+
+%_ptr_Output_f32 = OpTypePointer Output %f32
+%_ptr_Output_vec4f = OpTypePointer Output %vec4f
+ %_ptr_Output_str = OpTypePointer Output %str
+ %depth = OpVariable %_ptr_Output_f32 Output
+ %colors = OpVariable %_ptr_Output_str Output
+
+ %main = OpFunction %void None %fn_type
+ %main_start = OpLabel
+ %access_a = OpAccessChain %_ptr_Output_vec4f %colors %u32_0
+ %access_b = OpAccessChain %_ptr_Output_vec4f %colors %u32_1
+ OpStore %access_a %f32_v4_a
+ OpStore %access_b %f32_v4_b
+ OpStore %depth %f32_42
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(got, Success);
+ EXPECT_EQ(got, R"(
+tint_symbol_2 = struct @align(16) {
+ tint_symbol:vec4<f32> @offset(0)
+ tint_symbol_1:vec4<f32> @offset(16)
+}
+
+tint_symbol_4 = struct @align(16) {
+ tint_symbol_3:f32 @offset(0), @builtin(frag_depth)
+ tint_symbol:vec4<f32> @offset(16), @location(1)
+ tint_symbol_1:vec4<f32> @offset(32), @location(2), @interpolate(linear, center)
+}
+
+$B1: { # root
+ %1:ptr<private, f32, read_write> = var
+ %2:ptr<private, tint_symbol_2, read_write> = var
+}
+
+%main_inner = func():void {
+ $B2: {
+ %4:ptr<private, vec4<f32>, read_write> = access %2, 0u
+ %5:ptr<private, vec4<f32>, read_write> = access %2, 1u
+ store %4, vec4<f32>(42.0f, 42.0f, 42.0f, -1.0f)
+ store %5, vec4<f32>(-1.0f, -1.0f, -1.0f, 42.0f)
+ store %1, 42.0f
+ ret
+ }
+}
+%main = @fragment func():tint_symbol_4 {
+ $B3: {
+ %7:void = call %main_inner
+ %8:f32 = load %1
+ %9:ptr<private, vec4<f32>, read_write> = access %2, 0u
+ %10:vec4<f32> = load %9
+ %11:ptr<private, vec4<f32>, read_write> = access %2, 1u
+ %12:vec4<f32> = load %11
+ %13:tint_symbol_4 = construct %8, %10, %12
+ ret %13
+ }
+}
+)");
+}
+
+TEST_F(SpirvReaderTest, SampleMask) {
+ auto got = Run(R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %mask_in %mask_out
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %mask_in BuiltIn SampleMask
+ OpDecorate %mask_out BuiltIn SampleMask
+ %void = OpTypeVoid
+ %fn_type = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %u32_0 = OpConstant %u32 0
+ %u32_1 = OpConstant %u32 1
+ %arr_u32 = OpTypeArray %u32 %u32_1
+
+%_ptr_Input_u32 = OpTypePointer Input %u32
+%_ptr_Input_arr_u32 = OpTypePointer Input %arr_u32
+%_ptr_Output_u32 = OpTypePointer Output %u32
+%_ptr_Output_arr_u32 = OpTypePointer Output %arr_u32
+ %mask_in = OpVariable %_ptr_Input_arr_u32 Input
+ %mask_out = OpVariable %_ptr_Output_arr_u32 Output
+
+ %main = OpFunction %void None %fn_type
+ %main_start = OpLabel
+ %mask_in_0 = OpAccessChain %_ptr_Input_u32 %mask_in %u32_0
+%mask_in_val = OpLoad %u32 %mask_in_0
+ %plus_one = OpIAdd %u32 %mask_in_val %u32_1
+ %mask_out_0 = OpAccessChain %_ptr_Output_u32 %mask_out %u32_0
+ OpStore %mask_out_0 %plus_one
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(got, Success);
+ EXPECT_EQ(got, R"(
+$B1: { # root
+ %1:ptr<private, array<u32, 1>, read_write> = var
+}
+
+%main_inner = func(%3:array<u32, 1>):void {
+ $B2: {
+ %4:u32 = access %3, 0u
+ %5:u32 = add %4, 1u
+ %6:ptr<private, u32, read_write> = access %1, 0u
+ store %6, %5
+ ret
+ }
+}
+%main = @fragment func(%8:u32 [@sample_mask]):u32 [@sample_mask] {
+ $B3: {
+ %9:array<u32, 1> = construct %8
+ %10:void = call %main_inner, %9
+ %11:array<u32, 1> = load %1
+ %12:u32 = access %11, 0u
+ ret %12
+ }
+}
+)");
+}
+
} // namespace
} // namespace tint::spirv::reader
diff --git a/src/tint/lang/spirv/validate/validate.h b/src/tint/lang/spirv/validate/validate.h
index 1e09477..9dab618 100644
--- a/src/tint/lang/spirv/validate/validate.h
+++ b/src/tint/lang/spirv/validate/validate.h
@@ -31,11 +31,6 @@
#include "spirv-tools/libspirv.hpp"
#include "src/tint/utils/result/result.h"
-// Forward declarations
-namespace tint {
-class Program;
-} // namespace tint
-
namespace tint::spirv::validate {
/// Validate checks that the provided SPIR-V passes validation.
diff --git a/src/tint/lang/spirv/writer/ast_printer/assign_test.cc b/src/tint/lang/spirv/writer/ast_printer/assign_test.cc
index 24ad65b..7da01f4 100644
--- a/src/tint/lang/spirv/writer/ast_printer/assign_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/assign_test.cc
@@ -66,7 +66,7 @@
}
TEST_F(SpirvASTPrinterTest, Assign_Var_OutsideFunction_IsError) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder pb;
diff --git a/src/tint/lang/spirv/writer/ast_printer/ast_if_test.cc b/src/tint/lang/spirv/writer/ast_printer/ast_if_test.cc
index 2404561..f8caeaa 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ast_if_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ast_if_test.cc
@@ -64,7 +64,7 @@
// if (true) {
// }
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder pb;
diff --git a/src/tint/lang/spirv/writer/ast_printer/builtin_texture_test.cc b/src/tint/lang/spirv/writer/ast_printer/builtin_texture_test.cc
index 4e6f84b..2105c9f 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builtin_texture_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/builtin_texture_test.cc
@@ -3774,9 +3774,8 @@
Validate(b);
}
-// TODO(dsinclair): This generates two fatal errors, but EXPECT_DEATH can only handle 1
-TEST_P(BuiltinTextureTest, DISABLED_OutsideFunction_IsError) {
- EXPECT_DEATH(
+TEST_P(BuiltinTextureTest, OutsideFunction_IsError) {
+ EXPECT_DEATH_IF_SUPPORTED(
{
auto param = GetParam();
diff --git a/src/tint/lang/spirv/writer/ast_printer/function_attribute_test.cc b/src/tint/lang/spirv/writer/ast_printer/function_attribute_test.cc
index 27e9a6e..7c5fdfb 100644
--- a/src/tint/lang/spirv/writer/ast_printer/function_attribute_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/function_attribute_test.cc
@@ -165,7 +165,7 @@
}
TEST_F(SpirvASTPrinterTest, Decoration_ExecutionMode_WorkgroupSize_OverridableConst) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder pb;
pb.Override("width", pb.ty.i32(), pb.Call<i32>(2_i), pb.Id(7_u));
@@ -185,7 +185,7 @@
}
TEST_F(SpirvASTPrinterTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder pb;
diff --git a/src/tint/lang/spirv/writer/ast_printer/ident_expression_test.cc b/src/tint/lang/spirv/writer/ast_printer/ident_expression_test.cc
index e65e8c9..54e18bb 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ident_expression_test.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ident_expression_test.cc
@@ -37,7 +37,7 @@
using SpirvASTPrinterTest = TestHelper;
TEST_F(SpirvASTPrinterTest, IdentifierExpression_GlobalConst) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder pb;
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index b45beac..70808f6 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -114,10 +114,9 @@
// Replace the builtins that we found.
for (auto* builtin : worklist) {
- core::ir::Value* replacement = nullptr;
switch (builtin->Func()) {
case core::BuiltinFn::kArrayLength:
- replacement = ArrayLength(builtin);
+ ArrayLength(builtin);
break;
case core::BuiltinFn::kAtomicAdd:
case core::BuiltinFn::kAtomicAnd:
@@ -130,30 +129,30 @@
case core::BuiltinFn::kAtomicStore:
case core::BuiltinFn::kAtomicSub:
case core::BuiltinFn::kAtomicXor:
- replacement = Atomic(builtin);
+ Atomic(builtin);
break;
case core::BuiltinFn::kDot:
- replacement = Dot(builtin);
+ Dot(builtin);
break;
case core::BuiltinFn::kDot4I8Packed:
case core::BuiltinFn::kDot4U8Packed:
- replacement = DotPacked4x8(builtin);
+ DotPacked4x8(builtin);
break;
case core::BuiltinFn::kSelect:
- replacement = Select(builtin);
+ Select(builtin);
break;
case core::BuiltinFn::kTextureDimensions:
- replacement = TextureDimensions(builtin);
+ TextureDimensions(builtin);
break;
case core::BuiltinFn::kTextureGather:
case core::BuiltinFn::kTextureGatherCompare:
- replacement = TextureGather(builtin);
+ TextureGather(builtin);
break;
case core::BuiltinFn::kTextureLoad:
- replacement = TextureLoad(builtin);
+ TextureLoad(builtin);
break;
case core::BuiltinFn::kTextureNumLayers:
- replacement = TextureNumLayers(builtin);
+ TextureNumLayers(builtin);
break;
case core::BuiltinFn::kTextureSample:
case core::BuiltinFn::kTextureSampleBias:
@@ -161,25 +160,17 @@
case core::BuiltinFn::kTextureSampleCompareLevel:
case core::BuiltinFn::kTextureSampleGrad:
case core::BuiltinFn::kTextureSampleLevel:
- replacement = TextureSample(builtin);
+ TextureSample(builtin);
break;
case core::BuiltinFn::kTextureStore:
- replacement = TextureStore(builtin);
+ TextureStore(builtin);
break;
case core::BuiltinFn::kQuantizeToF16:
- replacement = QuantizeToF16Vec(builtin);
+ QuantizeToF16Vec(builtin);
break;
default:
break;
}
- TINT_ASSERT(replacement);
-
- // Replace the old builtin result with the new value.
- if (auto name = ir.NameOf(builtin->Result(0))) {
- ir.SetName(replacement, name);
- }
- builtin->Result(0)->ReplaceAllUsesWith(replacement);
- builtin->Destroy();
}
}
@@ -192,8 +183,7 @@
/// Handle an `arrayLength()` builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* ArrayLength(core::ir::CoreBuiltinCall* builtin) {
+ void ArrayLength(core::ir::CoreBuiltinCall* builtin) {
// Strip away any let instructions to get to the original struct member access instruction.
auto* ptr = builtin->Args()[0]->As<core::ir::InstructionResult>();
while (auto* let = tint::As<core::ir::Let>(ptr->Instruction())) {
@@ -208,17 +198,16 @@
auto* const_idx = access->Indices()[0]->As<core::ir::Constant>();
// Replace the builtin call with a call to the spirv.array_length intrinsic.
- auto* call = b.Call<spirv::ir::BuiltinCall>(
- builtin->Result(0)->Type(), spirv::BuiltinFn::kArrayLength,
+ auto* call = b.CallWithResult<spirv::ir::BuiltinCall>(
+ builtin->DetachResult(), spirv::BuiltinFn::kArrayLength,
Vector{access->Object(), Literal(u32(const_idx->Value()->ValueAs<uint32_t>()))});
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
/// Handle an atomic*() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* Atomic(core::ir::CoreBuiltinCall* builtin) {
+ void Atomic(core::ir::CoreBuiltinCall* builtin) {
auto* result_ty = builtin->Result(0)->Type();
auto* pointer = builtin->Args()[0];
@@ -235,27 +224,29 @@
auto* memory_semantics = b.Constant(u32(SpvMemorySemanticsMaskNone));
// Helper to build the builtin call with the common operands.
- auto build = [&](const core::type::Type* type, enum spirv::BuiltinFn builtin_fn) {
- return b.Call<spirv::ir::BuiltinCall>(type, builtin_fn, pointer, memory,
- memory_semantics);
+ auto build = [&](enum spirv::BuiltinFn builtin_fn) {
+ return b.CallWithResult<spirv::ir::BuiltinCall>(builtin->DetachResult(), builtin_fn,
+ pointer, memory, memory_semantics);
};
// Create the replacement call instruction.
core::ir::Call* call = nullptr;
switch (builtin->Func()) {
case core::BuiltinFn::kAtomicAdd:
- call = build(result_ty, spirv::BuiltinFn::kAtomicIadd);
+ call = build(spirv::BuiltinFn::kAtomicIadd);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicAnd:
- call = build(result_ty, spirv::BuiltinFn::kAtomicAnd);
+ call = build(spirv::BuiltinFn::kAtomicAnd);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicCompareExchangeWeak: {
auto* cmp = builtin->Args()[1];
auto* value = builtin->Args()[2];
auto* int_ty = value->Type();
- call = build(int_ty, spirv::BuiltinFn::kAtomicCompareExchange);
+ call =
+ b.Call<spirv::ir::BuiltinCall>(int_ty, spirv::BuiltinFn::kAtomicCompareExchange,
+ pointer, memory, memory_semantics);
call->AppendArg(memory_semantics);
call->AppendArg(value);
call->AppendArg(cmp);
@@ -267,62 +258,60 @@
compare->InsertBefore(builtin);
// Construct the atomicCompareExchange result structure.
- call = b.Construct(
- core::type::CreateAtomicCompareExchangeResult(ty, ir.symbols, int_ty),
- Vector{original, compare->Result(0)});
+ call = b.ConstructWithResult(builtin->DetachResult(),
+ Vector{original, compare->Result(0)});
break;
}
case core::BuiltinFn::kAtomicExchange:
- call = build(result_ty, spirv::BuiltinFn::kAtomicExchange);
+ call = build(spirv::BuiltinFn::kAtomicExchange);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicLoad:
- call = build(result_ty, spirv::BuiltinFn::kAtomicLoad);
+ call = build(spirv::BuiltinFn::kAtomicLoad);
break;
case core::BuiltinFn::kAtomicOr:
- call = build(result_ty, spirv::BuiltinFn::kAtomicOr);
+ call = build(spirv::BuiltinFn::kAtomicOr);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicMax:
if (result_ty->is_signed_integer_scalar()) {
- call = build(result_ty, spirv::BuiltinFn::kAtomicSmax);
+ call = build(spirv::BuiltinFn::kAtomicSmax);
} else {
- call = build(result_ty, spirv::BuiltinFn::kAtomicUmax);
+ call = build(spirv::BuiltinFn::kAtomicUmax);
}
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicMin:
if (result_ty->is_signed_integer_scalar()) {
- call = build(result_ty, spirv::BuiltinFn::kAtomicSmin);
+ call = build(spirv::BuiltinFn::kAtomicSmin);
} else {
- call = build(result_ty, spirv::BuiltinFn::kAtomicUmin);
+ call = build(spirv::BuiltinFn::kAtomicUmin);
}
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicStore:
- call = build(result_ty, spirv::BuiltinFn::kAtomicStore);
+ call = build(spirv::BuiltinFn::kAtomicStore);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicSub:
- call = build(result_ty, spirv::BuiltinFn::kAtomicIsub);
+ call = build(spirv::BuiltinFn::kAtomicIsub);
call->AppendArg(builtin->Args()[1]);
break;
case core::BuiltinFn::kAtomicXor:
- call = build(result_ty, spirv::BuiltinFn::kAtomicXor);
+ call = build(spirv::BuiltinFn::kAtomicXor);
call->AppendArg(builtin->Args()[1]);
break;
default:
- return nullptr;
+ TINT_UNREACHABLE() << "unhandled atomic builtin";
}
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
/// Handle a `dot()` builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* Dot(core::ir::CoreBuiltinCall* builtin) {
+ void Dot(core::ir::CoreBuiltinCall* builtin) {
// OpDot only supports floating point operands, so we need to polyfill the integer case.
// TODO(crbug.com/tint/1267): If SPV_KHR_integer_dot_product is supported, use that instead.
if (builtin->Result(0)->Type()->is_integer_scalar()) {
@@ -344,38 +333,38 @@
}
});
}
- return sum->Result(0);
+ sum->SetResults(Vector{builtin->DetachResult()});
+ builtin->Destroy();
+ return;
}
// Replace the builtin call with a call to the spirv.dot intrinsic.
auto args = Vector<core::ir::Value*, 4>(builtin->Args());
- auto* call = b.Call<spirv::ir::BuiltinCall>(builtin->Result(0)->Type(),
- spirv::BuiltinFn::kDot, std::move(args));
+ auto* call = b.CallWithResult<spirv::ir::BuiltinCall>(
+ builtin->DetachResult(), spirv::BuiltinFn::kDot, std::move(args));
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
/// Handle a `dot4{I,U}8Packed()` builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* DotPacked4x8(core::ir::CoreBuiltinCall* builtin) {
+ void DotPacked4x8(core::ir::CoreBuiltinCall* builtin) {
// Replace the builtin call with a call to the spirv.{s,u}dot intrinsic.
- auto* type = builtin->Result(0)->Type();
auto is_signed = builtin->Func() == core::BuiltinFn::kDot4I8Packed;
auto inst = is_signed ? spirv::BuiltinFn::kSdot : spirv::BuiltinFn::kUdot;
auto args = Vector<core::ir::Value*, 3>(builtin->Args());
args.Push(Literal(u32(SpvPackedVectorFormatPackedVectorFormat4x8Bit)));
- auto* call = b.Call<spirv::ir::BuiltinCall>(type, inst, std::move(args));
+ auto* call = b.CallWithResult<spirv::ir::BuiltinCall>(builtin->DetachResult(), inst,
+ std::move(args));
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
/// Handle a `select()` builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* Select(core::ir::CoreBuiltinCall* builtin) {
+ void Select(core::ir::CoreBuiltinCall* builtin) {
// Argument order is different in SPIR-V: (condition, true_operand, false_operand).
Vector<core::ir::Value*, 4> args = {
builtin->Args()[2],
@@ -397,10 +386,10 @@
}
// Replace the builtin call with a call to the spirv.select intrinsic.
- auto* call = b.Call<spirv::ir::BuiltinCall>(builtin->Result(0)->Type(),
- spirv::BuiltinFn::kSelect, std::move(args));
+ auto* call = b.CallWithResult<spirv::ir::BuiltinCall>(
+ builtin->DetachResult(), spirv::BuiltinFn::kSelect, std::move(args));
call->InsertBefore(builtin);
- return call->Result(0);
+ builtin->Destroy();
}
/// ImageOperands represents the optional image operands for an image instruction.
@@ -494,8 +483,7 @@
/// Handle a textureSample*() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureSample(core::ir::CoreBuiltinCall* builtin) {
+ void TextureSample(core::ir::CoreBuiltinCall* builtin) {
// Helper to get the next argument from the call, or nullptr if there are no more arguments.
uint32_t arg_idx = 0;
auto next_arg = [&]() {
@@ -520,7 +508,7 @@
}
// Determine which SPIR-V function to use and which optional image operands are needed.
- enum spirv::BuiltinFn function;
+ enum spirv::BuiltinFn function = BuiltinFn::kNone;
core::ir::Value* depth = nullptr;
ImageOperands operands;
switch (builtin->Func()) {
@@ -556,7 +544,7 @@
operands.offset = next_arg();
break;
default:
- return nullptr;
+ TINT_UNREACHABLE() << "unhandled texture sample builtin";
}
// Start building the argument list for the function.
@@ -575,28 +563,25 @@
// Call the function.
// If this is a depth comparison, the result is always f32, otherwise vec4f.
auto* result_ty = depth ? static_cast<const core::type::Type*>(ty.f32()) : ty.vec4<f32>();
- auto* texture_call =
+ core::ir::Instruction* result =
b.Call<spirv::ir::BuiltinCall>(result_ty, function, std::move(function_args));
- texture_call->InsertBefore(builtin);
-
- auto* result = texture_call->Result(0);
+ result->InsertBefore(builtin);
// If this is not a depth comparison but we are sampling a depth texture, extract the first
// component to get the scalar f32 that SPIR-V expects.
if (!depth &&
texture_ty->IsAnyOf<core::type::DepthTexture, core::type::DepthMultisampledTexture>()) {
- auto* extract = b.Access(ty.f32(), result, 0_u);
- extract->InsertBefore(builtin);
- result = extract->Result(0);
+ result = b.Access(ty.f32(), result, 0_u);
+ result->InsertBefore(builtin);
}
- return result;
+ result->SetResults(Vector{builtin->DetachResult()});
+ builtin->Destroy();
}
/// Handle a textureGather*() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureGather(core::ir::CoreBuiltinCall* builtin) {
+ void TextureGather(core::ir::CoreBuiltinCall* builtin) {
// Helper to get the next argument from the call, or nullptr if there are no more arguments.
uint32_t arg_idx = 0;
auto next_arg = [&]() {
@@ -628,7 +613,7 @@
}
// Determine which SPIR-V function to use and which optional image operands are needed.
- enum spirv::BuiltinFn function;
+ enum spirv::BuiltinFn function = BuiltinFn::kNone;
core::ir::Value* depth = nullptr;
ImageOperands operands;
switch (builtin->Func()) {
@@ -642,7 +627,7 @@
operands.offset = next_arg();
break;
default:
- return nullptr;
+ TINT_UNIMPLEMENTED() << "unhandled texture gather builtin";
}
// Start building the argument list for the function.
@@ -661,17 +646,15 @@
AppendImageOperands(operands, function_args, builtin, /* requires_float_lod */ true);
// Call the function.
- auto* result_ty = builtin->Result(0)->Type();
- auto* texture_call =
- b.Call<spirv::ir::BuiltinCall>(result_ty, function, std::move(function_args));
+ auto* texture_call = b.CallWithResult<spirv::ir::BuiltinCall>(
+ builtin->DetachResult(), function, std::move(function_args));
texture_call->InsertBefore(builtin);
- return texture_call->Result(0);
+ builtin->Destroy();
}
/// Handle a textureLoad() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureLoad(core::ir::CoreBuiltinCall* builtin) {
+ void TextureLoad(core::ir::CoreBuiltinCall* builtin) {
// Helper to get the next argument from the call, or nullptr if there are no more arguments.
uint32_t arg_idx = 0;
auto next_arg = [&]() {
@@ -713,25 +696,23 @@
}
auto kind = texture_ty->Is<core::type::StorageTexture>() ? spirv::BuiltinFn::kImageRead
: spirv::BuiltinFn::kImageFetch;
- auto* texture_call =
+ core::ir::Instruction* result =
b.Call<spirv::ir::BuiltinCall>(result_ty, kind, std::move(builtin_args));
- texture_call->InsertBefore(builtin);
- auto* result = texture_call->Result(0);
+ result->InsertBefore(builtin);
// If we are expecting a scalar result, extract the first component.
if (expects_scalar_result) {
- auto* extract = b.Access(ty.f32(), result, 0_u);
- extract->InsertBefore(builtin);
- result = extract->Result(0);
+ result = b.Access(ty.f32(), result, 0_u);
+ result->InsertBefore(builtin);
}
- return result;
+ result->SetResults(Vector{builtin->DetachResult()});
+ builtin->Destroy();
}
/// Handle a textureStore() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureStore(core::ir::CoreBuiltinCall* builtin) {
+ void TextureStore(core::ir::CoreBuiltinCall* builtin) {
// Helper to get the next argument from the call, or nullptr if there are no more arguments.
uint32_t arg_idx = 0;
auto next_arg = [&]() {
@@ -764,13 +745,12 @@
auto* texture_call = b.Call<spirv::ir::BuiltinCall>(
ty.void_(), spirv::BuiltinFn::kImageWrite, std::move(function_args));
texture_call->InsertBefore(builtin);
- return texture_call->Result(0);
+ builtin->Destroy();
}
/// Handle a textureDimensions() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureDimensions(core::ir::CoreBuiltinCall* builtin) {
+ void TextureDimensions(core::ir::CoreBuiltinCall* builtin) {
// Helper to get the next argument from the call, or nullptr if there are no more arguments.
uint32_t arg_idx = 0;
auto next_arg = [&]() {
@@ -807,26 +787,23 @@
}
// Call the function.
- auto* texture_call =
+ core::ir::Instruction* result =
b.Call<spirv::ir::BuiltinCall>(result_ty, function, std::move(function_args));
- texture_call->InsertBefore(builtin);
-
- auto* result = texture_call->Result(0);
+ result->InsertBefore(builtin);
// Swizzle the first two components from the result for arrayed textures.
if (core::type::IsTextureArray(texture_ty->dim())) {
- auto* swizzle = b.Swizzle(builtin->Result(0)->Type(), result, {0, 1});
- swizzle->InsertBefore(builtin);
- result = swizzle->Result(0);
+ result = b.Swizzle(builtin->Result(0)->Type(), result, {0, 1});
+ result->InsertBefore(builtin);
}
- return result;
+ result->SetResults(Vector{builtin->DetachResult()});
+ builtin->Destroy();
}
/// Handle a textureNumLayers() builtin.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* TextureNumLayers(core::ir::CoreBuiltinCall* builtin) {
+ void TextureNumLayers(core::ir::CoreBuiltinCall* builtin) {
auto* texture = builtin->Args()[0];
auto* texture_ty = texture->Type()->As<core::type::Texture>();
@@ -850,16 +827,15 @@
texture_call->InsertBefore(builtin);
// Extract the third component to get the number of array layers.
- auto* extract = b.Access(ty.u32(), texture_call->Result(0), 2_u);
+ auto* extract = b.AccessWithResult(builtin->DetachResult(), texture_call->Result(0), 2_u);
extract->InsertBefore(builtin);
- return extract->Result(0);
+ builtin->Destroy();
}
/// Scalarize the vector form of a `quantizeToF16()` builtin.
/// See crbug.com/tint/1741.
/// @param builtin the builtin call instruction
- /// @returns the replacement value
- core::ir::Value* QuantizeToF16Vec(core::ir::CoreBuiltinCall* builtin) {
+ void QuantizeToF16Vec(core::ir::CoreBuiltinCall* builtin) {
auto* arg = builtin->Args()[0];
auto* vec = arg->Type()->As<core::type::Vector>();
TINT_ASSERT(vec);
@@ -873,9 +849,9 @@
el->InsertBefore(builtin);
scalar_call->InsertBefore(builtin);
}
- auto* construct = b.Construct(vec, std::move(args));
+ auto* construct = b.ConstructWithResult(builtin->DetachResult(), std::move(args));
construct->InsertBefore(builtin);
- return construct->Result(0);
+ builtin->Destroy();
}
};
diff --git a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
index 3b650a7..c5359f2 100644
--- a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
+++ b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
@@ -107,8 +107,8 @@
auto* result_ty = binary->Result(0)->Type();
if (result_ty->is_float_vector() && binary->Op() == core::BinaryOp::kMultiply) {
// Use OpVectorTimesScalar for floating point multiply.
- auto* vts =
- b.Call<spirv::ir::BuiltinCall>(result_ty, spirv::BuiltinFn::kVectorTimesScalar);
+ auto* vts = b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kVectorTimesScalar);
if (binary->LHS()->Type()->Is<core::type::Scalar>()) {
vts->AppendArg(binary->RHS());
vts->AppendArg(binary->LHS());
@@ -119,7 +119,6 @@
if (auto name = ir.NameOf(binary)) {
ir.SetName(vts->Result(0), name);
}
- binary->Result(0)->ReplaceAllUsesWith(vts->Result(0));
binary->ReplaceWith(vts);
binary->Destroy();
} else {
diff --git a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
index 487bac0..3091c09 100644
--- a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
+++ b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.cc
@@ -77,65 +77,63 @@
auto* rhs_ty = rhs->Type();
auto* ty = binary->Result(0)->Type();
- // Helper to replace the instruction with a new one.
- auto replace = [&](core::ir::Instruction* inst) {
- if (auto name = ir.NameOf(binary)) {
- ir.SetName(inst->Result(0), name);
- }
- binary->Result(0)->ReplaceAllUsesWith(inst->Result(0));
- binary->ReplaceWith(inst);
- binary->Destroy();
- };
-
- // Helper to replace the instruction with a column-wise operation.
- auto column_wise = [&](auto op) {
- auto* mat = ty->As<core::type::Matrix>();
- Vector<core::ir::Value*, 4> args;
- for (uint32_t col = 0; col < mat->columns(); col++) {
- b.InsertBefore(binary, [&] {
+ b.InsertBefore(binary, [&] {
+ // Helper to replace the instruction with a column-wise operation.
+ auto column_wise = [&](auto op) {
+ auto* mat = ty->As<core::type::Matrix>();
+ Vector<core::ir::Value*, 4> args;
+ for (uint32_t col = 0; col < mat->columns(); col++) {
auto* lhs_col = b.Access(mat->ColumnType(), lhs, u32(col));
auto* rhs_col = b.Access(mat->ColumnType(), rhs, u32(col));
auto* add = b.Binary(op, mat->ColumnType(), lhs_col, rhs_col);
args.Push(add->Result(0));
- });
- }
- replace(b.Construct(ty, std::move(args)));
- };
-
- switch (binary->Op()) {
- case core::BinaryOp::kAdd:
- column_wise(core::BinaryOp::kAdd);
- break;
- case core::BinaryOp::kSubtract:
- column_wise(core::BinaryOp::kSubtract);
- break;
- case core::BinaryOp::kMultiply:
- // Select the SPIR-V intrinsic that corresponds to the operation being performed.
- if (lhs_ty->Is<core::type::Matrix>()) {
- if (rhs_ty->Is<core::type::Scalar>()) {
- replace(b.Call<spirv::ir::BuiltinCall>(
- ty, spirv::BuiltinFn::kMatrixTimesScalar, lhs, rhs));
- } else if (rhs_ty->Is<core::type::Vector>()) {
- replace(b.Call<spirv::ir::BuiltinCall>(
- ty, spirv::BuiltinFn::kMatrixTimesVector, lhs, rhs));
- } else if (rhs_ty->Is<core::type::Matrix>()) {
- replace(b.Call<spirv::ir::BuiltinCall>(
- ty, spirv::BuiltinFn::kMatrixTimesMatrix, lhs, rhs));
- }
- } else {
- if (lhs_ty->Is<core::type::Scalar>()) {
- replace(b.Call<spirv::ir::BuiltinCall>(
- ty, spirv::BuiltinFn::kMatrixTimesScalar, rhs, lhs));
- } else if (lhs_ty->Is<core::type::Vector>()) {
- replace(b.Call<spirv::ir::BuiltinCall>(
- ty, spirv::BuiltinFn::kVectorTimesMatrix, lhs, rhs));
- }
}
- break;
+ b.ConstructWithResult(binary->DetachResult(), std::move(args));
+ };
- default:
- TINT_UNREACHABLE() << "unhandled matrix arithmetic instruction";
- }
+ switch (binary->Op()) {
+ case core::BinaryOp::kAdd:
+ column_wise(core::BinaryOp::kAdd);
+ break;
+ case core::BinaryOp::kSubtract:
+ column_wise(core::BinaryOp::kSubtract);
+ break;
+ case core::BinaryOp::kMultiply:
+ // Select the SPIR-V intrinsic that corresponds to the operation being
+ // performed.
+ if (lhs_ty->Is<core::type::Matrix>()) {
+ if (rhs_ty->Is<core::type::Scalar>()) {
+ b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kMatrixTimesScalar, lhs,
+ rhs);
+ } else if (rhs_ty->Is<core::type::Vector>()) {
+ b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kMatrixTimesVector, lhs,
+ rhs);
+ } else if (rhs_ty->Is<core::type::Matrix>()) {
+ b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kMatrixTimesMatrix, lhs,
+ rhs);
+ }
+ } else {
+ if (lhs_ty->Is<core::type::Scalar>()) {
+ b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kMatrixTimesScalar, rhs,
+ lhs);
+ } else if (lhs_ty->Is<core::type::Vector>()) {
+ b.CallWithResult<spirv::ir::BuiltinCall>(
+ binary->DetachResult(), spirv::BuiltinFn::kVectorTimesMatrix, lhs,
+ rhs);
+ }
+ }
+ break;
+
+ default:
+ TINT_UNREACHABLE() << "unhandled matrix arithmetic instruction";
+ }
+ });
+
+ binary->Destroy();
}
/// Replace a matrix convert instruction.
@@ -145,23 +143,19 @@
auto* in_mat = arg->Type()->As<core::type::Matrix>();
auto* out_mat = convert->Result(0)->Type()->As<core::type::Matrix>();
- // Extract and convert each column separately.
- Vector<core::ir::Value*, 4> args;
- for (uint32_t c = 0; c < out_mat->columns(); c++) {
- b.InsertBefore(convert, [&] {
+ b.InsertBefore(convert, [&] {
+ // Extract and convert each column separately.
+ Vector<core::ir::Value*, 4> args;
+ for (uint32_t c = 0; c < out_mat->columns(); c++) {
auto* col = b.Access(in_mat->ColumnType(), arg, u32(c));
auto* new_col = b.Convert(out_mat->ColumnType(), col);
args.Push(new_col->Result(0));
- });
- }
+ }
- // Reconstruct the result matrix from the converted columns.
- auto* construct = b.Construct(out_mat, std::move(args));
- if (auto name = ir.NameOf(convert)) {
- ir.SetName(construct->Result(0), name);
- }
- convert->Result(0)->ReplaceAllUsesWith(construct->Result(0));
- convert->ReplaceWith(construct);
+ // Reconstruct the result matrix from the converted columns.
+ b.ConstructWithResult(convert->DetachResult(), std::move(args));
+ });
+
convert->Destroy();
}
};
diff --git a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
index 409a52e..49655d5 100644
--- a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
+++ b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.cc
@@ -237,13 +237,11 @@
core::ir::Instruction* load = nullptr;
if (to_replace.vector_access_type) {
- load = b.LoadVectorElement(new_access->Result(0), vector_index);
+ load = b.LoadVectorElementWithResult(access->DetachResult(), new_access->Result(0),
+ vector_index);
} else {
- load = b.Load(new_access);
+ load = b.LoadWithResult(access->DetachResult(), new_access);
}
-
- // Replace all uses of the old access instruction with the loaded result.
- access->Result(0)->ReplaceAllUsesWith(load->Result(0));
access->ReplaceWith(load);
access->Destroy();
}
diff --git a/src/tint/lang/spirv/writer/writer_ir_fuzz.cc b/src/tint/lang/spirv/writer/writer_ir_fuzz.cc
index dcc848c..483dd77 100644
--- a/src/tint/lang/spirv/writer/writer_ir_fuzz.cc
+++ b/src/tint/lang/spirv/writer/writer_ir_fuzz.cc
@@ -28,6 +28,7 @@
#include "src/tint/lang/spirv/writer/writer.h"
#include "src/tint/cmd/fuzz/ir/fuzz.h"
+#include "src/tint/lang/core/ir/disassembly.h"
#include "src/tint/lang/spirv/validate/validate.h"
#include "src/tint/lang/spirv/writer/helpers/generate_bindings.h"
@@ -43,8 +44,10 @@
auto& spirv = output->spirv;
if (auto res = validate::Validate(Slice(spirv.data(), spirv.size()), SPV_ENV_VULKAN_1_1);
res != Success) {
- TINT_ICE() << "Output of SPIR-V writer failed to validate with SPIR-V Tools\n"
- << res.Failure();
+ TINT_ICE() << "output of SPIR-V writer failed to validate with SPIR-V Tools\n"
+ << res.Failure() << "\n\n"
+ << "IR:\n"
+ << core::ir::Disassemble(module).Plain();
}
}
diff --git a/src/tint/lang/wgsl/ast/assignment_statement_test.cc b/src/tint/lang/wgsl/ast/assignment_statement_test.cc
index 6c51920..7ba43d1 100644
--- a/src/tint/lang/wgsl/ast/assignment_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/assignment_statement_test.cc
@@ -64,7 +64,7 @@
}
TEST_F(AssignmentStatementTest, Assert_Null_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<AssignmentStatement>(nullptr, b.Expr(1_i));
@@ -73,7 +73,7 @@
}
TEST_F(AssignmentStatementTest, Assert_Null_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<AssignmentStatement>(b.Expr(1_i), nullptr);
@@ -82,7 +82,7 @@
}
TEST_F(AssignmentStatementTest, Assert_DifferentGenerationID_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -92,7 +92,7 @@
}
TEST_F(AssignmentStatementTest, Assert_DifferentGenerationID_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/binary_expression_test.cc b/src/tint/lang/wgsl/ast/binary_expression_test.cc
index 34e6e58..7dffd7b 100644
--- a/src/tint/lang/wgsl/ast/binary_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/binary_expression_test.cc
@@ -62,7 +62,7 @@
}
TEST_F(BinaryExpressionTest, Assert_Null_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<BinaryExpression>(core::BinaryOp::kEqual, nullptr, b.Expr("rhs"));
@@ -71,7 +71,7 @@
}
TEST_F(BinaryExpressionTest, Assert_Null_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<BinaryExpression>(core::BinaryOp::kEqual, b.Expr("lhs"), nullptr);
@@ -80,7 +80,7 @@
}
TEST_F(BinaryExpressionTest, Assert_DifferentGenerationID_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -90,7 +90,7 @@
}
TEST_F(BinaryExpressionTest, Assert_DifferentGenerationID_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/block_statement_test.cc b/src/tint/lang/wgsl/ast/block_statement_test.cc
index 6e00eaa..8540432 100644
--- a/src/tint/lang/wgsl/ast/block_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/block_statement_test.cc
@@ -72,7 +72,7 @@
}
TEST_F(BlockStatementTest, Assert_Null_Statement) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<BlockStatement>(tint::Vector<const Statement*, 1>{nullptr}, tint::Empty);
@@ -81,7 +81,7 @@
}
TEST_F(BlockStatementTest, Assert_DifferentGenerationID_Statement) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/break_if_statement_test.cc b/src/tint/lang/wgsl/ast/break_if_statement_test.cc
index 4c59976..c5e3e4b 100644
--- a/src/tint/lang/wgsl/ast/break_if_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/break_if_statement_test.cc
@@ -48,7 +48,7 @@
}
TEST_F(BreakIfStatementTest, Assert_Null_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.BreakIf(nullptr);
@@ -57,7 +57,7 @@
}
TEST_F(BreakIfStatementTest, Assert_DifferentGenerationID_Cond) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/builtin_attribute_test.cc b/src/tint/lang/wgsl/ast/builtin_attribute_test.cc
index 4bd547b..6a56995 100644
--- a/src/tint/lang/wgsl/ast/builtin_attribute_test.cc
+++ b/src/tint/lang/wgsl/ast/builtin_attribute_test.cc
@@ -39,7 +39,7 @@
}
TEST_F(BuiltinAttributeTest, Assert_Null_Builtin) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Builtin(nullptr);
@@ -48,7 +48,7 @@
}
TEST_F(BuiltinAttributeTest, Assert_DifferentGenerationID_Builtin) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/call_expression_test.cc b/src/tint/lang/wgsl/ast/call_expression_test.cc
index 7e6cab8..90bbc34 100644
--- a/src/tint/lang/wgsl/ast/call_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/call_expression_test.cc
@@ -91,7 +91,7 @@
}
TEST_F(CallExpressionTest, Assert_Null_Identifier) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Call(static_cast<Identifier*>(nullptr));
@@ -100,7 +100,7 @@
}
TEST_F(CallExpressionTest, Assert_Null_Param) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Call(b.Ident("func"), tint::Vector{
@@ -113,7 +113,7 @@
}
TEST_F(CallExpressionTest, Assert_DifferentGenerationID_Identifier) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -123,7 +123,7 @@
}
TEST_F(CallExpressionTest, Assert_DifferentGenerationID_Type) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -133,7 +133,7 @@
}
TEST_F(CallExpressionTest, Assert_DifferentGenerationID_Param) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/call_statement_test.cc b/src/tint/lang/wgsl/ast/call_statement_test.cc
index e9e8169..3c1a47f 100644
--- a/src/tint/lang/wgsl/ast/call_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/call_statement_test.cc
@@ -47,7 +47,7 @@
}
TEST_F(CallStatementTest, Assert_Null_Call) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.CallStmt(nullptr);
@@ -56,7 +56,7 @@
}
TEST_F(CallStatementTest, Assert_DifferentGenerationID_Call) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/case_statement_test.cc b/src/tint/lang/wgsl/ast/case_statement_test.cc
index 60df861..6425ac2 100644
--- a/src/tint/lang/wgsl/ast/case_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/case_statement_test.cc
@@ -99,7 +99,7 @@
}
TEST_F(CaseStatementTest, Assert_Null_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<CaseStatement>(tint::Vector{b.DefaultCaseSelector()}, nullptr);
@@ -108,7 +108,7 @@
}
TEST_F(CaseStatementTest, Assert_Null_Selector) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<CaseStatement>(tint::Vector<const ast::CaseSelector*, 1>{nullptr},
@@ -118,7 +118,7 @@
}
TEST_F(CaseStatementTest, Assert_DifferentGenerationID_Call) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -129,7 +129,7 @@
}
TEST_F(CaseStatementTest, Assert_DifferentGenerationID_Selector) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/clone_context_test.cc b/src/tint/lang/wgsl/ast/clone_context_test.cc
index 6bc1e6e..4d70dbc 100644
--- a/src/tint/lang/wgsl/ast/clone_context_test.cc
+++ b/src/tint/lang/wgsl/ast/clone_context_test.cc
@@ -1053,7 +1053,7 @@
TEST_F(ASTCloneContextTestNodeTest, CloneWithReplaceAll_SameTypeTwice) {
std::string Testnode_name = TypeInfo::Of<TestNode>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1070,7 +1070,7 @@
std::string Testnode_name = TypeInfo::Of<TestNode>().name;
std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1087,7 +1087,7 @@
std::string Testnode_name = TypeInfo::Of<TestNode>().name;
std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1103,7 +1103,7 @@
using ASTCloneContextTest = ::testing::Test;
TEST_F(ASTCloneContextTest, CloneWithReplaceAll_SymbolsTwice) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1181,7 +1181,7 @@
}
TEST_F(ASTCloneContextTest, GenerationIDs_Clone_ObjectNotOwnedBySrc) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder dst;
Program src(ProgramBuilder{});
@@ -1194,7 +1194,7 @@
}
TEST_F(ASTCloneContextTest, GenerationIDs_Clone_ObjectNotOwnedByDst) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder dst;
Program src(ProgramBuilder{});
diff --git a/src/tint/lang/wgsl/ast/color_attribute_test.cc b/src/tint/lang/wgsl/ast/color_attribute_test.cc
index c8dd840..990ee41 100644
--- a/src/tint/lang/wgsl/ast/color_attribute_test.cc
+++ b/src/tint/lang/wgsl/ast/color_attribute_test.cc
@@ -42,7 +42,7 @@
}
TEST_F(ColorAttributeTest, Assert_Null_Builtin) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Color(nullptr);
@@ -51,7 +51,7 @@
}
TEST_F(ColorAttributeTest, Assert_DifferentGenerationID_Color) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc b/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
index 36e302d..ee26804 100644
--- a/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/compound_assignment_statement_test.cc
@@ -68,7 +68,7 @@
}
TEST_F(CompoundAssignmentStatementTest, Assert_Null_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<CompoundAssignmentStatement>(nullptr, b.Expr(1_i), core::BinaryOp::kAdd);
@@ -77,7 +77,7 @@
}
TEST_F(CompoundAssignmentStatementTest, Assert_Null_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<CompoundAssignmentStatement>(b.Expr(1_i), nullptr, core::BinaryOp::kAdd);
@@ -86,7 +86,7 @@
}
TEST_F(CompoundAssignmentStatementTest, Assert_DifferentGenerationID_LHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -97,7 +97,7 @@
}
TEST_F(CompoundAssignmentStatementTest, Assert_DifferentGenerationID_RHS) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/const_assert_test.cc b/src/tint/lang/wgsl/ast/const_assert_test.cc
index 70e9dfd..0f94a51 100644
--- a/src/tint/lang/wgsl/ast/const_assert_test.cc
+++ b/src/tint/lang/wgsl/ast/const_assert_test.cc
@@ -59,7 +59,7 @@
}
TEST_F(ConstAssertTest, Assert_Null_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.ConstAssert(nullptr);
@@ -68,7 +68,7 @@
}
TEST_F(ConstAssertTest, Assert_DifferentGenerationID_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/diagnostic_control_test.cc b/src/tint/lang/wgsl/ast/diagnostic_control_test.cc
index 514b9ef..9873fb7 100644
--- a/src/tint/lang/wgsl/ast/diagnostic_control_test.cc
+++ b/src/tint/lang/wgsl/ast/diagnostic_control_test.cc
@@ -37,7 +37,7 @@
using DiagnosticControlTest = TestHelper;
TEST_F(DiagnosticControlTest, Assert_RuleNotNull) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
DiagnosticControl control(wgsl::DiagnosticSeverity::kWarning, nullptr);
diff --git a/src/tint/lang/wgsl/ast/diagnostic_rule_name_test.cc b/src/tint/lang/wgsl/ast/diagnostic_rule_name_test.cc
index 94ccc9c..387792c 100644
--- a/src/tint/lang/wgsl/ast/diagnostic_rule_name_test.cc
+++ b/src/tint/lang/wgsl/ast/diagnostic_rule_name_test.cc
@@ -41,7 +41,7 @@
}
TEST_F(DiagnosticRuleNameTest, Assert_NameNotTemplated) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<ast::DiagnosticRuleName>(b.Ident("name", "a", "b", "c"));
@@ -50,7 +50,7 @@
}
TEST_F(DiagnosticRuleNameTest, Assert_CategoryNotTemplated) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<ast::DiagnosticRuleName>(b.Ident("name"), b.Ident("category", "a", "b", "c"));
diff --git a/src/tint/lang/wgsl/ast/for_loop_statement_test.cc b/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
index a284597..d68ff74 100644
--- a/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/for_loop_statement_test.cc
@@ -73,7 +73,7 @@
}
TEST_F(ForLoopStatementTest, Assert_Null_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.For(nullptr, nullptr, nullptr, nullptr);
@@ -82,7 +82,7 @@
}
TEST_F(ForLoopStatementTest, Assert_DifferentGenerationID_Initializer) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -92,7 +92,7 @@
}
TEST_F(ForLoopStatementTest, Assert_DifferentGenerationID_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -102,7 +102,7 @@
}
TEST_F(ForLoopStatementTest, Assert_DifferentGenerationID_Continuing) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -112,7 +112,7 @@
}
TEST_F(ForLoopStatementTest, Assert_DifferentGenerationID_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/function_test.cc b/src/tint/lang/wgsl/ast/function_test.cc
index 96f7aa5..316d814 100644
--- a/src/tint/lang/wgsl/ast/function_test.cc
+++ b/src/tint/lang/wgsl/ast/function_test.cc
@@ -113,7 +113,7 @@
}
TEST_F(FunctionTest, Assert_NullName) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Func(static_cast<Identifier*>(nullptr), tint::Empty, b.ty.void_(), tint::Empty);
@@ -122,7 +122,7 @@
}
TEST_F(FunctionTest, Assert_TemplatedName) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Func(b.Ident("a", "b"), tint::Empty, b.ty.void_(), tint::Empty);
@@ -132,7 +132,7 @@
TEST_F(FunctionTest, Assert_NullParam) {
using ParamList = tint::Vector<const Parameter*, 2>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
ParamList params;
@@ -144,7 +144,7 @@
}
TEST_F(FunctionTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -154,7 +154,7 @@
}
TEST_F(FunctionTest, Assert_DifferentGenerationID_Param) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -168,7 +168,7 @@
}
TEST_F(FunctionTest, Assert_DifferentGenerationID_Attr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -181,7 +181,7 @@
}
TEST_F(FunctionTest, Assert_DifferentGenerationID_ReturnType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -191,7 +191,7 @@
}
TEST_F(FunctionTest, Assert_DifferentGenerationID_ReturnAttr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/identifier_expression_test.cc b/src/tint/lang/wgsl/ast/identifier_expression_test.cc
index ebf1916..8d3641e 100644
--- a/src/tint/lang/wgsl/ast/identifier_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/identifier_expression_test.cc
@@ -57,7 +57,7 @@
}
TEST_F(IdentifierExpressionTest, Assert_InvalidSymbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Expr("");
@@ -66,7 +66,7 @@
}
TEST_F(IdentifierExpressionTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/identifier_test.cc b/src/tint/lang/wgsl/ast/identifier_test.cc
index 8f238e7..2a71304 100644
--- a/src/tint/lang/wgsl/ast/identifier_test.cc
+++ b/src/tint/lang/wgsl/ast/identifier_test.cc
@@ -52,7 +52,7 @@
}
TEST_F(IdentifierTest, Assert_InvalidSymbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Ident("");
@@ -61,7 +61,7 @@
}
TEST_F(IdentifierTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/if_statement_test.cc b/src/tint/lang/wgsl/ast/if_statement_test.cc
index 4c738ad..7b37286 100644
--- a/src/tint/lang/wgsl/ast/if_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/if_statement_test.cc
@@ -59,7 +59,7 @@
}
TEST_F(IfStatementTest, Assert_Null_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.If(nullptr, b.Block());
@@ -68,7 +68,7 @@
}
TEST_F(IfStatementTest, Assert_Null_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.If(b.Expr(true), nullptr);
@@ -77,7 +77,7 @@
}
TEST_F(IfStatementTest, Assert_InvalidElse) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.If(b.Expr(true), b.Block(), b.Else(b.CallStmt(b.Call("foo"))));
@@ -86,7 +86,7 @@
}
TEST_F(IfStatementTest, Assert_DifferentGenerationID_Cond) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -96,7 +96,7 @@
}
TEST_F(IfStatementTest, Assert_DifferentGenerationID_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -106,7 +106,7 @@
}
TEST_F(IfStatementTest, Assert_DifferentGenerationID_ElseStatement) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/increment_decrement_statement_test.cc b/src/tint/lang/wgsl/ast/increment_decrement_statement_test.cc
index 869b522..70730d0 100644
--- a/src/tint/lang/wgsl/ast/increment_decrement_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/increment_decrement_statement_test.cc
@@ -66,7 +66,7 @@
}
TEST_F(IncrementDecrementStatementTest, Assert_DifferentGenerationID_Expr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/index_accessor_expression_test.cc b/src/tint/lang/wgsl/ast/index_accessor_expression_test.cc
index ff96f2a..7c1527a 100644
--- a/src/tint/lang/wgsl/ast/index_accessor_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/index_accessor_expression_test.cc
@@ -60,7 +60,7 @@
}
TEST_F(IndexAccessorExpressionTest, Assert_Null_Array) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.IndexAccessor(nullptr, b.Expr("idx"));
@@ -69,7 +69,7 @@
}
TEST_F(IndexAccessorExpressionTest, Assert_Null_Index) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.IndexAccessor(b.Expr("arr"), nullptr);
@@ -78,7 +78,7 @@
}
TEST_F(IndexAccessorExpressionTest, Assert_DifferentGenerationID_Array) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -88,7 +88,7 @@
}
TEST_F(IndexAccessorExpressionTest, Assert_DifferentGenerationID_Index) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/loop_statement_test.cc b/src/tint/lang/wgsl/ast/loop_statement_test.cc
index fc43b31..3afccfa 100644
--- a/src/tint/lang/wgsl/ast/loop_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/loop_statement_test.cc
@@ -93,7 +93,7 @@
}
TEST_F(LoopStatementTest, Assert_Null_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<LoopStatement>(nullptr, nullptr, tint::Empty);
@@ -102,7 +102,7 @@
}
TEST_F(LoopStatementTest, Assert_DifferentGenerationID_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -112,7 +112,7 @@
}
TEST_F(LoopStatementTest, Assert_DifferentGenerationID_Continuing) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/member_accessor_expression_test.cc b/src/tint/lang/wgsl/ast/member_accessor_expression_test.cc
index 22b1c38..28153d1 100644
--- a/src/tint/lang/wgsl/ast/member_accessor_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/member_accessor_expression_test.cc
@@ -55,7 +55,7 @@
}
TEST_F(MemberAccessorExpressionTest, Assert_Null_Struct) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<MemberAccessorExpression>(nullptr, b.Ident("member"));
@@ -64,7 +64,7 @@
}
TEST_F(MemberAccessorExpressionTest, Assert_Null_Member) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<MemberAccessorExpression>(b.Expr("struct"), nullptr);
@@ -73,7 +73,7 @@
}
TEST_F(MemberAccessorExpressionTest, Assert_DifferentGenerationID_Struct) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -83,7 +83,7 @@
}
TEST_F(MemberAccessorExpressionTest, Assert_DifferentGenerationID_Member) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -93,7 +93,7 @@
}
TEST_F(MemberAccessorExpressionTest, Assert_MemberNotTemplated) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<MemberAccessorExpression>(b.Expr("structure"),
diff --git a/src/tint/lang/wgsl/ast/module_test.cc b/src/tint/lang/wgsl/ast/module_test.cc
index e5949a6..36773ed 100644
--- a/src/tint/lang/wgsl/ast/module_test.cc
+++ b/src/tint/lang/wgsl/ast/module_test.cc
@@ -52,7 +52,7 @@
}
TEST_F(ModuleTest, Assert_Null_GlobalVariable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder builder;
builder.AST().AddGlobalVariable(nullptr);
@@ -61,7 +61,7 @@
}
TEST_F(ModuleTest, Assert_Null_TypeDecl) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder builder;
builder.AST().AddTypeDecl(nullptr);
@@ -70,7 +70,7 @@
}
TEST_F(ModuleTest, Assert_DifferentGenerationID_Function) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -81,7 +81,7 @@
}
TEST_F(ModuleTest, Assert_DifferentGenerationID_GlobalVariable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -91,7 +91,7 @@
}
TEST_F(ModuleTest, Assert_Null_Function) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder builder;
builder.AST().AddFunction(nullptr);
diff --git a/src/tint/lang/wgsl/ast/return_statement_test.cc b/src/tint/lang/wgsl/ast/return_statement_test.cc
index ed7c9e0..138a46c 100644
--- a/src/tint/lang/wgsl/ast/return_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/return_statement_test.cc
@@ -65,7 +65,7 @@
}
TEST_F(ReturnStatementTest, Assert_DifferentGenerationID_Expr) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/struct_member_test.cc b/src/tint/lang/wgsl/ast/struct_member_test.cc
index c1e429b..8a76d9f 100644
--- a/src/tint/lang/wgsl/ast/struct_member_test.cc
+++ b/src/tint/lang/wgsl/ast/struct_member_test.cc
@@ -58,7 +58,7 @@
}
TEST_F(StructMemberTest, Assert_Null_Name) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Member(static_cast<Identifier*>(nullptr), b.ty.i32());
@@ -67,7 +67,7 @@
}
TEST_F(StructMemberTest, Assert_Null_Type) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Member("a", Type{});
@@ -76,7 +76,7 @@
}
TEST_F(StructMemberTest, Assert_Null_Attribute) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Member("a", b.ty.i32(), tint::Vector{b.MemberSize(4_a), nullptr});
@@ -85,7 +85,7 @@
}
TEST_F(StructMemberTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -95,7 +95,7 @@
}
TEST_F(StructMemberTest, Assert_DifferentGenerationID_Attribute) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/struct_test.cc b/src/tint/lang/wgsl/ast/struct_test.cc
index d0e78de..b47e920 100644
--- a/src/tint/lang/wgsl/ast/struct_test.cc
+++ b/src/tint/lang/wgsl/ast/struct_test.cc
@@ -81,7 +81,7 @@
}
TEST_F(AstStructTest, Assert_Null_StructMember) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Structure(b.Sym("S"), tint::Vector{b.Member("a", b.ty.i32()), nullptr}, tint::Empty);
@@ -90,7 +90,7 @@
}
TEST_F(AstStructTest, Assert_Null_Attribute) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Structure(b.Sym("S"), tint::Vector{b.Member("a", b.ty.i32())},
@@ -100,7 +100,7 @@
}
TEST_F(AstStructTest, Assert_DifferentGenerationID_StructMember) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -110,7 +110,7 @@
}
TEST_F(AstStructTest, Assert_DifferentGenerationID_Attribute) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/switch_statement_test.cc b/src/tint/lang/wgsl/ast/switch_statement_test.cc
index fa1fc40..2105a6c 100644
--- a/src/tint/lang/wgsl/ast/switch_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/switch_statement_test.cc
@@ -88,7 +88,7 @@
TEST_F(SwitchStatementTest, Assert_Null_Condition) {
using CaseStatementList = tint::Vector<const CaseStatement*, 2>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
CaseStatementList cases;
@@ -101,7 +101,7 @@
TEST_F(SwitchStatementTest, Assert_Null_CaseStatement) {
using CaseStatementList = tint::Vector<const CaseStatement*, 2>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr}, tint::Empty,
@@ -111,7 +111,7 @@
}
TEST_F(SwitchStatementTest, Assert_DifferentGenerationID_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -129,7 +129,7 @@
}
TEST_F(SwitchStatementTest, Assert_DifferentGenerationID_CaseStatement) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/templated_identifier_test.cc b/src/tint/lang/wgsl/ast/templated_identifier_test.cc
index ded0e81..a47a905 100644
--- a/src/tint/lang/wgsl/ast/templated_identifier_test.cc
+++ b/src/tint/lang/wgsl/ast/templated_identifier_test.cc
@@ -63,7 +63,7 @@
}
TEST_F(TemplatedIdentifierTest, Assert_InvalidSymbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Expr("");
@@ -72,7 +72,7 @@
}
TEST_F(TemplatedIdentifierTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -82,7 +82,7 @@
}
TEST_F(TemplatedIdentifierTest, Assert_DifferentGenerationID_TemplateArg) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/transform/add_block_attribute_fuzz.cc b/src/tint/lang/wgsl/ast/transform/add_block_attribute_fuzz.cc
index 7898243..b9a5389 100644
--- a/src/tint/lang/wgsl/ast/transform/add_block_attribute_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/add_block_attribute_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = AddBlockAttribute{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "AddBlockAttribute returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "AddBlockAttribute returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_fuzz.cc b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_fuzz.cc
index cee5702..ff81441 100644
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = AddEmptyEntryPoint{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "AddEmptyEntryPoint returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "AddEmptyEntryPoint returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_fuzz.cc b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_fuzz.cc
index 525f57e..7b1f693 100644
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_fuzz.cc
@@ -72,6 +72,7 @@
if (auto result = ArrayLengthFromUniform{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "ArrayLengthFromUniform returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper_fuzz.cc b/src/tint/lang/wgsl/ast/transform/binding_remapper_fuzz.cc
index 2f27549..071e83d 100644
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/binding_remapper_fuzz.cc
@@ -78,7 +78,9 @@
DataMap outputs;
if (auto result = BindingRemapper{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "BindingRemapper returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "BindingRemapper returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_fuzz.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_fuzz.cc
index adce548..8694e3b 100644
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_fuzz.cc
@@ -38,7 +38,9 @@
DataMap outputs;
if (auto result = BuiltinPolyfill{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "BuiltinPolyfill returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "BuiltinPolyfill returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_fuzz.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_fuzz.cc
index 136291a..671a35b 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_fuzz.cc
@@ -40,6 +40,7 @@
if (auto result = CanonicalizeEntryPointIO{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "CanonicalizeEntryPointIO returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_fuzz.cc b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_fuzz.cc
index 2854240..6de6553 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_fuzz.cc
@@ -27,6 +27,7 @@
#include "src/tint/cmd/fuzz/wgsl/fuzz.h"
#include "src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h"
+#include "src/tint/lang/wgsl/program/program.h"
namespace tint::ast::transform {
namespace {
@@ -49,7 +50,9 @@
DataMap outputs;
if (auto result = ClampFragDepth{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "ClampFragDepth returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "ClampFragDepth returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper_fuzz.cc b/src/tint/lang/wgsl/ast/transform/demote_to_helper_fuzz.cc
index 180ab67..16e3833 100644
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/demote_to_helper_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = DemoteToHelper{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "DemoteToHelper returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "DemoteToHelper returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access_fuzz.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access_fuzz.cc
index 5123798..e1d1893 100644
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/direct_variable_access_fuzz.cc
@@ -40,6 +40,7 @@
if (auto result = DirectVariableAccess{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "DirectVariableAccess returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_fuzz.cc b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_fuzz.cc
index 378eb5f..1f5e153 100644
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_fuzz.cc
@@ -36,6 +36,7 @@
if (auto result = DisableUniformityAnalysis{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "DisableUniformityAnalysis returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_fuzz.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_fuzz.cc
index d017dbb..7776590 100644
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_fuzz.cc
@@ -36,6 +36,7 @@
if (auto result = ExpandCompoundAssignment{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "ExpandCompoundAssignment returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset_fuzz.cc b/src/tint/lang/wgsl/ast/transform/first_index_offset_fuzz.cc
index b7ed12e..2f3a607 100644
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/first_index_offset_fuzz.cc
@@ -59,7 +59,9 @@
DataMap outputs;
if (auto result = FirstIndexOffset{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "FirstIndexOffset returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "FirstIndexOffset returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants_fuzz.cc b/src/tint/lang/wgsl/ast/transform/fold_constants_fuzz.cc
index 4c48a90..668f7a5 100644
--- a/src/tint/lang/wgsl/ast/transform/fold_constants_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/fold_constants_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = FoldConstants{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "FoldConstants returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "FoldConstants returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_fuzz.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_fuzz.cc
index caddb35..4c6efeb 100644
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_fuzz.cc
@@ -95,6 +95,7 @@
if (auto result = MultiplanarExternalTexture{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "MultiplanarExternalTexture returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index_fuzz.cc b/src/tint/lang/wgsl/ast/transform/offset_first_index_fuzz.cc
index 04c13a2..f4c9faf 100644
--- a/src/tint/lang/wgsl/ast/transform/offset_first_index_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/offset_first_index_fuzz.cc
@@ -55,7 +55,9 @@
DataMap outputs;
if (auto result = OffsetFirstIndex{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "OffsetFirstIndex returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "OffsetFirstIndex returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/preserve_padding_fuzz.cc b/src/tint/lang/wgsl/ast/transform/preserve_padding_fuzz.cc
index 486a65a..963da24 100644
--- a/src/tint/lang/wgsl/ast/transform/preserve_padding_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/preserve_padding_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = PreservePadding{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "PreservePadding returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "PreservePadding returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_fuzz.cc b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_fuzz.cc
index bf928e6..ed3518f 100644
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_fuzz.cc
@@ -36,6 +36,7 @@
if (auto result = PromoteInitializersToLet{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "PromoteInitializersToLet returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_fuzz.cc b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_fuzz.cc
index 57a15f8..423d3ed 100644
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_fuzz.cc
@@ -54,6 +54,7 @@
if (auto result = PromoteSideEffectsToDecl{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "PromoteSideEffectsToDecl returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_fuzz.cc b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_fuzz.cc
index 049b57c..b49f1ed 100644
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_fuzz.cc
@@ -36,6 +36,7 @@
if (auto result = RemoveContinueInSwitch{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "RemoveContinueInSwitch returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies_fuzz.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies_fuzz.cc
index 92faf38..823fc6f 100644
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/remove_phonies_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = RemovePhonies{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "RemovePhonies returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "RemovePhonies returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/remove_unreachable_statements_fuzz.cc b/src/tint/lang/wgsl/ast/transform/remove_unreachable_statements_fuzz.cc
index c61d8aa..c803823 100644
--- a/src/tint/lang/wgsl/ast/transform/remove_unreachable_statements_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/remove_unreachable_statements_fuzz.cc
@@ -52,6 +52,7 @@
if (auto result = RemoveUnreachableStatements{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "RemoveUnreachableStatements returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point_fuzz.cc b/src/tint/lang/wgsl/ast/transform/single_entry_point_fuzz.cc
index 5cec2bb..5df7964 100644
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/single_entry_point_fuzz.cc
@@ -56,7 +56,9 @@
DataMap outputs;
if (auto result = SingleEntryPoint{}.Apply(program, inputs, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "SingleEntryPoint returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "SingleEntryPoint returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/std140_fuzz.cc b/src/tint/lang/wgsl/ast/transform/std140_fuzz.cc
index aedf74c..3606f96 100644
--- a/src/tint/lang/wgsl/ast/transform/std140_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/std140_fuzz.cc
@@ -57,7 +57,9 @@
DataMap outputs;
if (auto result = Std140{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "Std140 returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "Std140 returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/unshadow_fuzz.cc b/src/tint/lang/wgsl/ast/transform/unshadow_fuzz.cc
index 064ffaf..752a501 100644
--- a/src/tint/lang/wgsl/ast/transform/unshadow_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/unshadow_fuzz.cc
@@ -35,7 +35,9 @@
DataMap outputs;
if (auto result = Unshadow{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
- TINT_ICE() << "Unshadow returned invalid program:\n" << result->Diagnostics();
+ TINT_ICE() << "Unshadow returned invalid program:\n"
+ << Program::printer(*result) << "\n"
+ << result->Diagnostics();
}
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_fuzz.cc b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_fuzz.cc
index 407538c..f1c6e2d 100644
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_fuzz.cc
@@ -36,6 +36,7 @@
if (auto result = VectorizeScalarMatrixInitializers{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "VectorizeScalarMatrixInitializers returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_fuzz.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_fuzz.cc
index 2e3650d..b9c569d 100644
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_fuzz.cc
+++ b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_fuzz.cc
@@ -41,6 +41,7 @@
if (auto result = ZeroInitWorkgroupMemory{}.Apply(program, DataMap{}, outputs)) {
if (!result->IsValid()) {
TINT_ICE() << "ZeroInitWorkgroupMemory returned invalid program:\n"
+ << Program::printer(*result) << "\n"
<< result->Diagnostics();
}
}
diff --git a/src/tint/lang/wgsl/ast/unary_op_expression_test.cc b/src/tint/lang/wgsl/ast/unary_op_expression_test.cc
index 3ce46c9..45b2c52 100644
--- a/src/tint/lang/wgsl/ast/unary_op_expression_test.cc
+++ b/src/tint/lang/wgsl/ast/unary_op_expression_test.cc
@@ -58,7 +58,7 @@
}
TEST_F(UnaryOpExpressionTest, Assert_Null_Expression) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<UnaryOpExpression>(core::UnaryOp::kNot, nullptr);
@@ -67,7 +67,7 @@
}
TEST_F(UnaryOpExpressionTest, Assert_DifferentGenerationID_Expression) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/variable_decl_statement_test.cc b/src/tint/lang/wgsl/ast/variable_decl_statement_test.cc
index 1e3588d..3b4640c 100644
--- a/src/tint/lang/wgsl/ast/variable_decl_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/variable_decl_statement_test.cc
@@ -58,7 +58,7 @@
}
TEST_F(VariableDeclStatementTest, Assert_Null_Variable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.create<VariableDeclStatement>(nullptr);
@@ -67,7 +67,7 @@
}
TEST_F(VariableDeclStatementTest, Assert_DifferentGenerationID_Variable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/variable_test.cc b/src/tint/lang/wgsl/ast/variable_test.cc
index c85ec93..c24ab00 100644
--- a/src/tint/lang/wgsl/ast/variable_test.cc
+++ b/src/tint/lang/wgsl/ast/variable_test.cc
@@ -76,7 +76,7 @@
}
TEST_F(VariableTest, Assert_Null_Name) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Var(static_cast<Identifier*>(nullptr), b.ty.i32());
@@ -85,7 +85,7 @@
}
TEST_F(VariableTest, Assert_DifferentGenerationID_Symbol) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -95,7 +95,7 @@
}
TEST_F(VariableTest, Assert_DifferentGenerationID_Initializer) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/ast/while_statement_test.cc b/src/tint/lang/wgsl/ast/while_statement_test.cc
index e678d12..be9e07e 100644
--- a/src/tint/lang/wgsl/ast/while_statement_test.cc
+++ b/src/tint/lang/wgsl/ast/while_statement_test.cc
@@ -65,7 +65,7 @@
}
TEST_F(WhileStatementTest, Assert_Null_Cond) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
auto* body = b.Block();
@@ -75,7 +75,7 @@
}
TEST_F(WhileStatementTest, Assert_Null_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
auto* cond =
@@ -86,7 +86,7 @@
}
TEST_F(WhileStatementTest, Assert_DifferentGenerationID_Condition) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
@@ -96,7 +96,7 @@
}
TEST_F(WhileStatementTest, Assert_DifferentGenerationID_Body) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b1;
ProgramBuilder b2;
diff --git a/src/tint/lang/wgsl/program/clone_context_test.cc b/src/tint/lang/wgsl/program/clone_context_test.cc
index 936cb6b..527a6a2 100644
--- a/src/tint/lang/wgsl/program/clone_context_test.cc
+++ b/src/tint/lang/wgsl/program/clone_context_test.cc
@@ -1051,7 +1051,7 @@
TEST_F(ProgramCloneContextNodeTest, CloneWithReplaceAll_SameTypeTwice) {
std::string node_name = TypeInfo::Of<Node>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1068,7 +1068,7 @@
std::string node_name = TypeInfo::Of<Node>().name;
std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1085,7 +1085,7 @@
std::string node_name = TypeInfo::Of<Node>().name;
std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1101,7 +1101,7 @@
using ProgramCloneContextTest = ::testing::Test;
TEST_F(ProgramCloneContextTest, CloneWithReplaceAll_SymbolsTwice) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder cloned;
Program original;
@@ -1207,7 +1207,7 @@
}
TEST_F(ProgramCloneContextTest, GenerationIDs_Clone_ObjectNotOwnedBySrc) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder dst;
Program src(ProgramBuilder{});
@@ -1220,7 +1220,7 @@
}
TEST_F(ProgramCloneContextTest, GenerationIDs_Clone_ObjectNotOwnedByDst) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder dst;
Program src(ProgramBuilder{});
diff --git a/src/tint/lang/wgsl/program/program_test.cc b/src/tint/lang/wgsl/program/program_test.cc
index dff5b23..4c91d2d 100644
--- a/src/tint/lang/wgsl/program/program_test.cc
+++ b/src/tint/lang/wgsl/program/program_test.cc
@@ -65,7 +65,7 @@
}
TEST_F(ProgramTest, Assert_NullGlobalVariable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.AST().AddGlobalVariable(nullptr);
@@ -74,7 +74,7 @@
}
TEST_F(ProgramTest, Assert_NullTypeDecl) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.AST().AddTypeDecl(nullptr);
@@ -83,7 +83,7 @@
}
TEST_F(ProgramTest, Assert_Null_Function) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.AST().AddFunction(nullptr);
diff --git a/src/tint/lang/wgsl/reader/lower/lower.cc b/src/tint/lang/wgsl/reader/lower/lower.cc
index 0752652..33f1999 100644
--- a/src/tint/lang/wgsl/reader/lower/lower.cc
+++ b/src/tint/lang/wgsl/reader/lower/lower.cc
@@ -198,19 +198,16 @@
// call workgroupBarrier
b.InsertBefore(call, [&] {
b.Call(ty.void_(), core::BuiltinFn::kWorkgroupBarrier);
- auto* load = b.Load(call->Args()[0]);
- call->Result(0)->ReplaceAllUsesWith(load->Result(0));
+ b.LoadWithResult(call->DetachResult(), call->Args()[0]);
b.Call(ty.void_(), core::BuiltinFn::kWorkgroupBarrier);
});
break;
}
default: {
Vector<core::ir::Value*, 8> args(call->Args());
- auto* replacement =
- mod.allocators.instructions.Create<core::ir::CoreBuiltinCall>(
- call->Result(0), Convert(call->Func()), std::move(args));
+ auto* replacement = b.CallWithResult(call->DetachResult(),
+ Convert(call->Func()), std::move(args));
call->ReplaceWith(replacement);
- call->ClearResults();
break;
}
}
diff --git a/src/tint/lang/wgsl/resolver/resolver_test.cc b/src/tint/lang/wgsl/resolver/resolver_test.cc
index 152cbbe..c73a75b 100644
--- a/src/tint/lang/wgsl/resolver/resolver_test.cc
+++ b/src/tint/lang/wgsl/resolver/resolver_test.cc
@@ -2087,7 +2087,7 @@
}
TEST_F(ResolverTest, ASTNodeNotReached) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.Ident("ident");
@@ -2098,7 +2098,7 @@
}
TEST_F(ResolverTest, ASTNodeReachedTwice) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
auto* expr = b.Expr(1_i);
diff --git a/src/tint/lang/wgsl/resolver/validation_test.cc b/src/tint/lang/wgsl/resolver/validation_test.cc
index 5d0f51c..9c306f7 100644
--- a/src/tint/lang/wgsl/resolver/validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/validation_test.cc
@@ -133,7 +133,7 @@
}
TEST_F(ResolverValidationTest, UnhandledStmt) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.WrapInFunction(b.create<FakeStmt>());
@@ -164,7 +164,7 @@
}
TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
ProgramBuilder b;
b.WrapInFunction(b.create<FakeExpr>());
diff --git a/src/tint/lang/wgsl/writer/raise/raise.cc b/src/tint/lang/wgsl/writer/raise/raise.cc
index c576ac4..d06cb72 100644
--- a/src/tint/lang/wgsl/writer/raise/raise.cc
+++ b/src/tint/lang/wgsl/writer/raise/raise.cc
@@ -30,6 +30,7 @@
#include <utility>
#include "src/tint/lang/core/builtin_fn.h"
+#include "src/tint/lang/core/ir/builder.h"
#include "src/tint/lang/core/ir/core_builtin_call.h"
#include "src/tint/lang/core/ir/load.h"
#include "src/tint/lang/core/type/pointer.h"
@@ -175,16 +176,15 @@
TINT_ICE() << "unhandled builtin function: " << fn;
}
-void ReplaceBuiltinFnCall(core::ir::Module& mod, core::ir::CoreBuiltinCall* call) {
+void ReplaceBuiltinFnCall(core::ir::Builder& b, core::ir::CoreBuiltinCall* call) {
Vector<core::ir::Value*, 8> args(call->Args());
- auto* replacement = mod.allocators.instructions.Create<wgsl::ir::BuiltinCall>(
- call->Result(0), Convert(call->Func()), std::move(args));
+ auto* replacement = b.CallWithResult<wgsl::ir::BuiltinCall>(
+ call->DetachResult(), Convert(call->Func()), std::move(args));
call->ReplaceWith(replacement);
- call->ClearResults();
call->Destroy();
}
-void ReplaceWorkgroupBarrier(core::ir::Module& mod, core::ir::CoreBuiltinCall* call) {
+void ReplaceWorkgroupBarrier(core::ir::Builder& b, core::ir::CoreBuiltinCall* call) {
// Pattern match:
// call workgroupBarrier
// %value = load &ptr
@@ -196,14 +196,14 @@
if (!load || load->From()->Type()->As<core::type::Pointer>()->AddressSpace() !=
core::AddressSpace::kWorkgroup) {
// No match
- ReplaceBuiltinFnCall(mod, call);
+ ReplaceBuiltinFnCall(b, call);
return;
}
auto* post_load = As<core::ir::CoreBuiltinCall>(load->next.Get());
if (!post_load || post_load->Func() != core::BuiltinFn::kWorkgroupBarrier) {
// No match
- ReplaceBuiltinFnCall(mod, call);
+ ReplaceBuiltinFnCall(b, call);
return;
}
@@ -212,24 +212,24 @@
call->Destroy();
// Replace load with workgroupUniformLoad
- auto* replacement = mod.allocators.instructions.Create<wgsl::ir::BuiltinCall>(
- load->Result(0), wgsl::BuiltinFn::kWorkgroupUniformLoad, Vector{load->From()});
+ auto* replacement = b.CallWithResult<wgsl::ir::BuiltinCall>(
+ load->DetachResult(), wgsl::BuiltinFn::kWorkgroupUniformLoad, Vector{load->From()});
load->ReplaceWith(replacement);
- load->ClearResults();
load->Destroy();
}
} // namespace
Result<SuccessType> Raise(core::ir::Module& mod) {
+ core::ir::Builder b{mod};
for (auto* inst : mod.Instructions()) {
if (auto* call = inst->As<core::ir::CoreBuiltinCall>()) {
switch (call->Func()) {
case core::BuiltinFn::kWorkgroupBarrier:
- ReplaceWorkgroupBarrier(mod, call);
+ ReplaceWorkgroupBarrier(b, call);
break;
default:
- ReplaceBuiltinFnCall(mod, call);
+ ReplaceBuiltinFnCall(b, call);
break;
}
}
diff --git a/src/tint/lang/wgsl/writer/writer.cc b/src/tint/lang/wgsl/writer/writer.cc
index 92819c0..4013848 100644
--- a/src/tint/lang/wgsl/writer/writer.cc
+++ b/src/tint/lang/wgsl/writer/writer.cc
@@ -68,6 +68,14 @@
}
Result<Output> WgslFromIR(core::ir::Module& module, const ProgramOptions& options) {
+ auto res = ProgramFromIR(module, options);
+ if (res != Success) {
+ return res.Failure();
+ }
+ return Generate(res.Move(), Options{});
+}
+
+Result<Program> ProgramFromIR(core::ir::Module& module, const ProgramOptions& options) {
// core-dialect -> WGSL-dialect
if (auto res = Raise(module); res != Success) {
return res.Failure();
@@ -78,7 +86,7 @@
return Failure{program.Diagnostics()};
}
- return Generate(program, Options{});
+ return program;
}
} // namespace tint::wgsl::writer
diff --git a/src/tint/lang/wgsl/writer/writer.h b/src/tint/lang/wgsl/writer/writer.h
index 604ace2..a79ea4d 100644
--- a/src/tint/lang/wgsl/writer/writer.h
+++ b/src/tint/lang/wgsl/writer/writer.h
@@ -56,6 +56,11 @@
/// @returns the resulting WGSL, or failure
Result<Output> WgslFromIR(core::ir::Module& module, const ProgramOptions& options);
+/// Generate a Program from a core-dialect ir::Module.
+/// @param module the core-dialect ir::Module.
+/// @returns the resulting Program, or failure
+Result<Program> ProgramFromIR(core::ir::Module& module, const ProgramOptions& options);
+
} // namespace tint::wgsl::writer
#endif // SRC_TINT_LANG_WGSL_WRITER_WRITER_H_
diff --git a/src/tint/utils/containers/vector_test.cc b/src/tint/utils/containers/vector_test.cc
index f437ddb..c509cbb 100644
--- a/src/tint/utils/containers/vector_test.cc
+++ b/src/tint/utils/containers/vector_test.cc
@@ -2110,7 +2110,7 @@
}
TEST(TintVectorTest, AssertOOBs) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Vector vec{1};
[[maybe_unused]] int i = vec[1];
@@ -2121,7 +2121,7 @@
#if TINT_VECTOR_MUTATION_CHECKS_ENABLED
TEST(TintVectorTest, AssertPushWhileIterating) {
using V = Vector<int, 4>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
V vec;
vec.Push(1);
@@ -2136,7 +2136,7 @@
TEST(TintVectorTest, AssertPopWhileIterating) {
using V = Vector<int, 4>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
V vec;
vec.Push(1);
@@ -2151,7 +2151,7 @@
TEST(TintVectorTest, AssertClearWhileIterating) {
using V = Vector<int, 4>;
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
V vec;
vec.Push(1);
@@ -2446,7 +2446,7 @@
}
TEST(TintVectorRefTest, AssertOOBs) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Vector vec{1};
const VectorRef<int> vec_ref(vec);
diff --git a/src/tint/utils/diagnostic/diagnostic.h b/src/tint/utils/diagnostic/diagnostic.h
index 6471b5a..4b627df 100644
--- a/src/tint/utils/diagnostic/diagnostic.h
+++ b/src/tint/utils/diagnostic/diagnostic.h
@@ -185,6 +185,11 @@
return Add(std::move(error));
}
+ /// Ensures that the diagnostic list can fit an additional @p count diagnostics without
+ /// resizing. This is useful for ensuring that a reference returned by the AddX() methods is not
+ /// invalidated after another Add().
+ void ReserveAdditional(size_t count) { entries_.Reserve(entries_.Length() + count); }
+
/// @returns true iff the diagnostic list contains errors diagnostics (or of
/// higher severity).
bool ContainsErrors() const { return error_count_ > 0; }
diff --git a/src/tint/utils/ice/ice_test.cc b/src/tint/utils/ice/ice_test.cc
index 0afa5de..d0a992c 100644
--- a/src/tint/utils/ice/ice_test.cc
+++ b/src/tint/utils/ice/ice_test.cc
@@ -33,7 +33,7 @@
namespace {
TEST(ICETest_AssertTrue_Test, Unreachable) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
if ((true)) {
TINT_UNREACHABLE();
@@ -47,7 +47,7 @@
}
TEST(ICETest_AssertTrue_Test, AssertFalse) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
if ((true)) {
TINT_ASSERT(false);
diff --git a/src/tint/utils/rtti/switch_test.cc b/src/tint/utils/rtti/switch_test.cc
index 4ae0f95..e4241ff 100644
--- a/src/tint/utils/rtti/switch_test.cc
+++ b/src/tint/utils/rtti/switch_test.cc
@@ -230,7 +230,7 @@
}
TEST(Castable, SwitchMustMatch_NoMatchWithoutReturnValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
std::unique_ptr<Animal> frog = std::make_unique<Frog>();
Switch(
@@ -243,7 +243,7 @@
}
TEST(Castable, SwitchMustMatch_NoMatchWithReturnValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
std::unique_ptr<Animal> frog = std::make_unique<Frog>();
int res = Switch(
@@ -257,7 +257,7 @@
}
TEST(Castable, SwitchMustMatch_NullptrWithoutReturnValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
Switch(
static_cast<CastableBase*>(nullptr), //
@@ -269,7 +269,7 @@
}
TEST(Castable, SwitchMustMatch_NullptrWithReturnValue) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
int res = Switch(
static_cast<CastableBase*>(nullptr), //
diff --git a/src/tint/utils/symbol/symbol_table_test.cc b/src/tint/utils/symbol/symbol_table_test.cc
index 8421e1f..9328640 100644
--- a/src/tint/utils/symbol/symbol_table_test.cc
+++ b/src/tint/utils/symbol/symbol_table_test.cc
@@ -50,7 +50,7 @@
}
TEST_F(SymbolTableTest, AssertsForBlankString) {
- EXPECT_DEATH(
+ EXPECT_DEATH_IF_SUPPORTED(
{
auto generation_id = GenerationID::New();
SymbolTable s{generation_id};