Import Tint changes from Dawn
Changes:
- 0958c626d4067957c6b50d78962d812a1b758b33 [ir][spirv-writer] Emit struct types by James Price <jrprice@google.com>
- ec2bfb3abb587075216a82709de9d99d2e073d73 [tint] Fix implicit stride in array helper by James Price <jrprice@google.com>
- e2abb623b8dfc2e22f3962038c43c95731e26b0a [ir] Add instruction helper methods. by dan sinclair <dsinclair@chromium.org>
- ccc05b7c33769cba0f97edc8fe9d291f4f1de9cf [ir] Rename BuilderImpl tests. by dan sinclair <dsinclair@chromium.org>
- 792001a5fedc5572082a4cc31bd2111f0ed9355f [ir] Convert block instructions to a list. by dan sinclair <dsinclair@chromium.org>
- 829195972f5732b8738b8229a616315930d593be [tint][constant] Optimize Value::Equal() for splats by Ben Clayton <bclayton@google.com>
- 811054153f8668e54b68df03a8cdfcac00def1ae [tint][reader][wgsl]: Fix quadratic parsing of ___... by Ben Clayton <bclayton@google.com>
- 4a21f2436f2c3a4dc041926312c572eec17a4665 Fix tests which disable ice_reporter. by dan sinclair <dsinclair@chromium.org>
- 2f4983ce51b2bff3420760b7fc47e83a41376553 [tint][ir][spirv-writer] Implement loop phis by Ben Clayton <bclayton@google.com>
- 5d40159a743ab69dad377ff09c9e230b20009ea0 [tint][ir][spriv-writer] Rework ID allocation by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 0958c626d4067957c6b50d78962d812a1b758b33
Change-Id: I2ef4811afed7dd42886de6a0c9df4531e704d9be
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/135720
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index ce779bc..11f6a78 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1699,6 +1699,7 @@
"constant/manager_test.cc",
"constant/scalar_test.cc",
"constant/splat_test.cc",
+ "constant/value_test.cc",
]
}
@@ -2280,6 +2281,7 @@
sources = [
"ir/binary_test.cc",
"ir/bitcast_test.cc",
+ "ir/block_test.cc",
"ir/constant_test.cc",
"ir/discard_test.cc",
"ir/from_program_binary_test.cc",
@@ -2292,6 +2294,7 @@
"ir/from_program_test.cc",
"ir/from_program_unary_test.cc",
"ir/from_program_var_test.cc",
+ "ir/instruction_test.cc",
"ir/load_test.cc",
"ir/module_test.cc",
"ir/store_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 2ecb97a..40dd3ee 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -940,6 +940,7 @@
constant/manager_test.cc
constant/scalar_test.cc
constant/splat_test.cc
+ constant/value_test.cc
debug_test.cc
diagnostic/diagnostic_test.cc
diagnostic/formatter_test.cc
@@ -1483,6 +1484,7 @@
list(APPEND TINT_TEST_SRCS
ir/binary_test.cc
ir/bitcast_test.cc
+ ir/block_test.cc
ir/constant_test.cc
ir/discard_test.cc
ir/from_program_binary_test.cc
@@ -1495,6 +1497,7 @@
ir/from_program_test.cc
ir/from_program_unary_test.cc
ir/from_program_var_test.cc
+ ir/instruction_test.cc
ir/load_test.cc
ir/module_test.cc
ir/store_test.cc
diff --git a/src/tint/constant/splat_test.cc b/src/tint/constant/splat_test.cc
index 0e2f21e..e310d63 100644
--- a/src/tint/constant/splat_test.cc
+++ b/src/tint/constant/splat_test.cc
@@ -31,9 +31,9 @@
auto* fNeg0 = constants.Get(-0_f);
auto* fPos1 = constants.Get(1_f);
- auto* SpfPos0 = constants.Splat(vec3f, fPos0, 2);
- auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 2);
- auto* SpfPos1 = constants.Splat(vec3f, fPos1, 2);
+ auto* SpfPos0 = constants.Splat(vec3f, fPos0, 3);
+ auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 3);
+ auto* SpfPos1 = constants.Splat(vec3f, fPos1, 3);
EXPECT_TRUE(SpfPos0->AllZero());
EXPECT_FALSE(SpfNeg0->AllZero());
@@ -47,9 +47,9 @@
auto* fNeg0 = constants.Get(-0_f);
auto* fPos1 = constants.Get(1_f);
- auto* SpfPos0 = constants.Splat(vec3f, fPos0, 2);
- auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 2);
- auto* SpfPos1 = constants.Splat(vec3f, fPos1, 2);
+ auto* SpfPos0 = constants.Splat(vec3f, fPos0, 3);
+ auto* SpfNeg0 = constants.Splat(vec3f, fNeg0, 3);
+ auto* SpfPos1 = constants.Splat(vec3f, fPos1, 3);
EXPECT_TRUE(SpfPos0->AnyZero());
EXPECT_FALSE(SpfNeg0->AnyZero());
diff --git a/src/tint/constant/value.cc b/src/tint/constant/value.cc
index 7545731..989f2ba 100644
--- a/src/tint/constant/value.cc
+++ b/src/tint/constant/value.cc
@@ -14,6 +14,7 @@
#include "src/tint/constant/value.h"
+#include "src/tint/constant/splat.h"
#include "src/tint/switch.h"
#include "src/tint/type/array.h"
#include "src/tint/type/matrix.h"
@@ -30,51 +31,68 @@
/// Equal returns true if the constants `a` and `b` are of the same type and value.
bool Value::Equal(const constant::Value* b) const {
+ if (this == b) {
+ return true;
+ }
if (Hash() != b->Hash()) {
return false;
}
if (Type() != b->Type()) {
return false;
}
+
+ auto elements_equal = [&](size_t count) {
+ if (count == 0) {
+ return true;
+ }
+
+ // Avoid per-element comparisons if the constants are splats
+ bool a_is_splat = Is<Splat>();
+ bool b_is_splat = b->Is<Splat>();
+ if (a_is_splat && b_is_splat) {
+ return Index(0)->Equal(b->Index(0));
+ }
+
+ if (a_is_splat) {
+ auto* el_a = Index(0);
+ for (size_t i = 0; i < count; i++) {
+ if (!el_a->Equal(b->Index(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if (b_is_splat) {
+ auto* el_b = b->Index(0);
+ for (size_t i = 0; i < count; i++) {
+ if (!Index(i)->Equal(el_b)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Per-element comparison
+ for (size_t i = 0; i < count; i++) {
+ if (!Index(i)->Equal(b->Index(i))) {
+ return false;
+ }
+ }
+ return true;
+ };
+
return Switch(
Type(), //
- [&](const type::Vector* vec) {
- for (size_t i = 0; i < vec->Width(); i++) {
- if (!Index(i)->Equal(b->Index(i))) {
- return false;
- }
- }
- return true;
- },
- [&](const type::Matrix* mat) {
- for (size_t i = 0; i < mat->columns(); i++) {
- if (!Index(i)->Equal(b->Index(i))) {
- return false;
- }
- }
- return true;
- },
+ [&](const type::Vector* vec) { return elements_equal(vec->Width()); },
+ [&](const type::Matrix* mat) { return elements_equal(mat->columns()); },
+ [&](const type::Struct* str) { return elements_equal(str->Members().Length()); },
[&](const type::Array* arr) {
- if (auto count = arr->ConstantCount()) {
- for (size_t i = 0; i < count; i++) {
- if (!Index(i)->Equal(b->Index(i))) {
- return false;
- }
- }
- return true;
+ if (auto n = arr->ConstantCount()) {
+ return elements_equal(*n);
}
-
return false;
},
- [&](const type::Struct* str) {
- auto count = str->Members().Length();
- for (size_t i = 0; i < count; i++) {
- if (!Index(i)->Equal(b->Index(i))) {
- return false;
- }
- }
- return true;
- },
[&](Default) {
auto va = InternalValue();
auto vb = b->InternalValue();
diff --git a/src/tint/constant/value_test.cc b/src/tint/constant/value_test.cc
new file mode 100644
index 0000000..e715c21
--- /dev/null
+++ b/src/tint/constant/value_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/constant/splat.h"
+
+#include "src/tint/constant/scalar.h"
+#include "src/tint/constant/test_helper.h"
+
+namespace tint::constant {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+
+using ConstantTest_Value = TestHelper;
+
+TEST_F(ConstantTest_Value, Equal_Scalar_Scalar) {
+ EXPECT_TRUE(constants.Get(10_i)->Equal(constants.Get(10_i)));
+ EXPECT_FALSE(constants.Get(10_i)->Equal(constants.Get(20_i)));
+ EXPECT_FALSE(constants.Get(20_i)->Equal(constants.Get(10_i)));
+
+ EXPECT_TRUE(constants.Get(10_u)->Equal(constants.Get(10_u)));
+ EXPECT_FALSE(constants.Get(10_u)->Equal(constants.Get(20_u)));
+ EXPECT_FALSE(constants.Get(20_u)->Equal(constants.Get(10_u)));
+
+ EXPECT_TRUE(constants.Get(10_f)->Equal(constants.Get(10_f)));
+ EXPECT_FALSE(constants.Get(10_f)->Equal(constants.Get(20_f)));
+ EXPECT_FALSE(constants.Get(20_f)->Equal(constants.Get(10_f)));
+}
+
+TEST_F(ConstantTest_Value, Equal_Splat_Splat) {
+ auto* vec3f = create<type::Vector>(create<type::F32>(), 3u);
+
+ auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f), 3);
+ auto* vec3f_2_2_2 = constants.Splat(vec3f, constants.Get(2_f), 3);
+
+ EXPECT_TRUE(vec3f_1_1_1->Equal(vec3f_1_1_1));
+ EXPECT_FALSE(vec3f_2_2_2->Equal(vec3f_1_1_1));
+ EXPECT_FALSE(vec3f_1_1_1->Equal(vec3f_2_2_2));
+}
+
+TEST_F(ConstantTest_Value, Equal_Composite_Composite) {
+ auto* vec3f = create<type::Vector>(create<type::F32>(), 3u);
+
+ auto* vec3f_1_1_2 = constants.Composite(
+ vec3f, utils::Vector{constants.Get(1_f), constants.Get(1_f), constants.Get(2_f)});
+ auto* vec3f_1_2_1 = constants.Composite(
+ vec3f, utils::Vector{constants.Get(1_f), constants.Get(2_f), constants.Get(1_f)});
+
+ EXPECT_TRUE(vec3f_1_1_2->Equal(vec3f_1_1_2));
+ EXPECT_FALSE(vec3f_1_2_1->Equal(vec3f_1_1_2));
+ EXPECT_FALSE(vec3f_1_1_2->Equal(vec3f_1_2_1));
+}
+
+TEST_F(ConstantTest_Value, Equal_Splat_Composite) {
+ auto* vec3f = create<type::Vector>(create<type::F32>(), 3u);
+
+ auto* vec3f_1_1_1 = constants.Splat(vec3f, constants.Get(1_f), 3);
+ auto* vec3f_1_2_1 = constants.Composite(
+ vec3f, utils::Vector{constants.Get(1_f), constants.Get(2_f), constants.Get(1_f)});
+
+ EXPECT_TRUE(vec3f_1_1_1->Equal(vec3f_1_1_1));
+ EXPECT_FALSE(vec3f_1_2_1->Equal(vec3f_1_1_1));
+ EXPECT_FALSE(vec3f_1_1_1->Equal(vec3f_1_2_1));
+}
+
+} // namespace
+} // namespace tint::constant
diff --git a/src/tint/debug.h b/src/tint/debug.h
index 5caeb5d..8b8c4b3 100644
--- a/src/tint/debug.h
+++ b/src/tint/debug.h
@@ -119,4 +119,21 @@
} \
} while (false)
+/// TINT_ASSERT_OR_RETURN() is a macro for checking the expression is true, triggering a
+/// TINT_ICE if it is not and returning from the calling function.
+/// The ICE message contains the callsite's file and line.
+/// @warning: Unlike TINT_ICE() and TINT_UNREACHABLE(), TINT_ASSERT_OR_RETURN() does not
+/// append a message to an existing tint::diag::List. As such, TINT_ASSERT_OR_RETURN()
+/// may silently fail in builds where SetInternalCompilerErrorReporter() is not
+/// called. Only use in places where there's no sensible place to put proper
+/// error handling.
+#define TINT_ASSERT_OR_RETURN(system, condition) \
+ do { \
+ if (TINT_UNLIKELY(!(condition))) { \
+ tint::diag::List diagnostics; \
+ TINT_ICE(system, diagnostics) << "TINT_ASSERT(" #system ", " #condition ")"; \
+ return; \
+ } \
+ } while (false)
+
#endif // SRC_TINT_DEBUG_H_
diff --git a/src/tint/ir/binary_test.cc b/src/tint/ir/binary_test.cc
index 281d04b..4c4fab8 100644
--- a/src/tint/ir/binary_test.cc
+++ b/src/tint/ir/binary_test.cc
@@ -21,9 +21,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_InstructionTest = TestHelper;
+using IR_BinaryTest = TestHelper;
-TEST_F(IR_InstructionTest, CreateAnd) {
+TEST_F(IR_BinaryTest, CreateAnd) {
Module mod;
Builder b{mod};
@@ -44,7 +44,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateOr) {
+TEST_F(IR_BinaryTest, CreateOr) {
Module mod;
Builder b{mod};
@@ -64,7 +64,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateXor) {
+TEST_F(IR_BinaryTest, CreateXor) {
Module mod;
Builder b{mod};
@@ -84,7 +84,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateEqual) {
+TEST_F(IR_BinaryTest, CreateEqual) {
Module mod;
Builder b{mod};
@@ -104,7 +104,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateNotEqual) {
+TEST_F(IR_BinaryTest, CreateNotEqual) {
Module mod;
Builder b{mod};
@@ -124,7 +124,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateLessThan) {
+TEST_F(IR_BinaryTest, CreateLessThan) {
Module mod;
Builder b{mod};
@@ -144,7 +144,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateGreaterThan) {
+TEST_F(IR_BinaryTest, CreateGreaterThan) {
Module mod;
Builder b{mod};
@@ -164,7 +164,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateLessThanEqual) {
+TEST_F(IR_BinaryTest, CreateLessThanEqual) {
Module mod;
Builder b{mod};
@@ -184,7 +184,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
+TEST_F(IR_BinaryTest, CreateGreaterThanEqual) {
Module mod;
Builder b{mod};
@@ -204,7 +204,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateNot) {
+TEST_F(IR_BinaryTest, CreateNot) {
Module mod;
Builder b{mod};
const auto* inst = b.Not(mod.Types().bool_(), b.Constant(true));
@@ -223,7 +223,7 @@
EXPECT_FALSE(rhs->As<constant::Scalar<bool>>()->ValueAs<bool>());
}
-TEST_F(IR_InstructionTest, CreateShiftLeft) {
+TEST_F(IR_BinaryTest, CreateShiftLeft) {
Module mod;
Builder b{mod};
@@ -243,7 +243,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateShiftRight) {
+TEST_F(IR_BinaryTest, CreateShiftRight) {
Module mod;
Builder b{mod};
@@ -263,7 +263,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateAdd) {
+TEST_F(IR_BinaryTest, CreateAdd) {
Module mod;
Builder b{mod};
@@ -283,7 +283,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateSubtract) {
+TEST_F(IR_BinaryTest, CreateSubtract) {
Module mod;
Builder b{mod};
@@ -303,7 +303,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateMultiply) {
+TEST_F(IR_BinaryTest, CreateMultiply) {
Module mod;
Builder b{mod};
@@ -323,7 +323,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateDivide) {
+TEST_F(IR_BinaryTest, CreateDivide) {
Module mod;
Builder b{mod};
@@ -343,7 +343,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateModulo) {
+TEST_F(IR_BinaryTest, CreateModulo) {
Module mod;
Builder b{mod};
@@ -363,7 +363,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, Binary_Usage) {
+TEST_F(IR_BinaryTest, Binary_Usage) {
Module mod;
Builder b{mod};
const auto* inst = b.And(mod.Types().i32(), b.Constant(4_i), b.Constant(2_i));
@@ -379,7 +379,7 @@
EXPECT_EQ(inst->RHS()->Usage()[0], inst);
}
-TEST_F(IR_InstructionTest, Binary_Usage_DuplicateValue) {
+TEST_F(IR_BinaryTest, Binary_Usage_DuplicateValue) {
Module mod;
Builder b{mod};
auto val = b.Constant(4_i);
diff --git a/src/tint/ir/bitcast_test.cc b/src/tint/ir/bitcast_test.cc
index bf66e39..d63853f 100644
--- a/src/tint/ir/bitcast_test.cc
+++ b/src/tint/ir/bitcast_test.cc
@@ -22,9 +22,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_InstructionTest = TestHelper;
+using IR_BitcastTest = TestHelper;
-TEST_F(IR_InstructionTest, Bitcast) {
+TEST_F(IR_BitcastTest, Bitcast) {
Module mod;
Builder b{mod};
const auto* inst = b.Bitcast(mod.Types().i32(), b.Constant(4_i));
@@ -40,7 +40,7 @@
EXPECT_EQ(4_i, val->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, Bitcast_Usage) {
+TEST_F(IR_BitcastTest, Bitcast_Usage) {
Module mod;
Builder b{mod};
const auto* inst = b.Bitcast(mod.Types().i32(), b.Constant(4_i));
diff --git a/src/tint/ir/block.cc b/src/tint/ir/block.cc
index 4da1287..8942ea9 100644
--- a/src/tint/ir/block.cc
+++ b/src/tint/ir/block.cc
@@ -22,16 +22,141 @@
Block::~Block() = default;
-void Block::AddInstruction(Instruction* inst) {
- instructions_.Push(inst);
+void Block::Prepend(Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == nullptr);
+
inst->SetBlock(this);
+ instructions_.count += 1;
+
+ if (instructions_.first == nullptr) {
+ instructions_.first = inst;
+ instructions_.last = inst;
+ } else {
+ inst->next = instructions_.first;
+ instructions_.first->prev = inst;
+ instructions_.first = inst;
+ }
+}
+
+void Block::Append(Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == nullptr);
+
+ inst->SetBlock(this);
+ instructions_.count += 1;
+
+ if (instructions_.first == nullptr) {
+ instructions_.first = inst;
+ instructions_.last = inst;
+ } else {
+ inst->prev = instructions_.last;
+ instructions_.last->next = inst;
+ instructions_.last = inst;
+ }
+}
+
+void Block::InsertBefore(Instruction* before, Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, before);
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, before->Block() == this);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == nullptr);
+
+ inst->SetBlock(this);
+ instructions_.count += 1;
+
+ inst->next = before;
+ inst->prev = before->prev;
+ before->prev = inst;
+
+ if (inst->prev) {
+ inst->prev->next = inst;
+ }
+
+ if (before == instructions_.first) {
+ instructions_.first = inst;
+ }
+}
+
+void Block::InsertAfter(Instruction* after, Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, after);
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, after->Block() == this);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == nullptr);
+
+ inst->SetBlock(this);
+ instructions_.count += 1;
+
+ inst->prev = after;
+ inst->next = after->next;
+ after->next = inst;
+
+ if (inst->next) {
+ inst->next->prev = inst;
+ }
+ if (after == instructions_.last) {
+ instructions_.last = inst;
+ }
+}
+
+void Block::Replace(Instruction* target, Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, target);
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, target->Block() == this);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == nullptr);
+
+ inst->SetBlock(this);
+ target->SetBlock(nullptr);
+
+ inst->next = target->next;
+ inst->prev = target->prev;
+
+ target->next = nullptr;
+ target->prev = nullptr;
+
+ if (inst->next) {
+ inst->next->prev = inst;
+ }
+ if (inst->prev) {
+ inst->prev->next = inst;
+ }
+
+ if (target == instructions_.first) {
+ instructions_.first = inst;
+ }
+ if (target == instructions_.last) {
+ instructions_.last = inst;
+ }
+}
+
+void Block::Remove(Instruction* inst) {
+ TINT_ASSERT_OR_RETURN(IR, inst);
+ TINT_ASSERT_OR_RETURN(IR, inst->Block() == this);
+
+ inst->SetBlock(nullptr);
+ instructions_.count -= 1;
+
+ if (inst->prev) {
+ inst->prev->next = inst->next;
+ }
+ if (inst->next) {
+ inst->next->prev = inst->prev;
+ }
+ if (inst == instructions_.first) {
+ instructions_.first = inst->next;
+ }
+ if (inst == instructions_.last) {
+ instructions_.last = inst->prev;
+ }
+
+ inst->prev = nullptr;
+ inst->next = nullptr;
}
void Block::SetInstructions(utils::VectorRef<Instruction*> instructions) {
for (auto* i : instructions) {
- i->SetBlock(this);
+ Append(i);
}
- instructions_ = std::move(instructions);
}
} // namespace tint::ir
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index 20a2da7..6b1697a 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -35,7 +35,7 @@
/// @returns true if this is block has a branch target set
bool HasBranchTarget() const {
- return !instructions_.IsEmpty() && instructions_.Back()->Is<ir::Branch>();
+ return instructions_.last != nullptr && instructions_.last->Is<ir::Branch>();
}
/// @return the node this block branches to or nullptr if the block doesn't branch
@@ -43,7 +43,7 @@
if (!HasBranchTarget()) {
return nullptr;
}
- return instructions_.Back()->As<ir::Branch>();
+ return instructions_.last->As<ir::Branch>();
}
/// Sets the instructions in the block
@@ -51,11 +51,74 @@
void SetInstructions(utils::VectorRef<Instruction*> instructions);
/// @returns the instructions in the block
- utils::VectorRef<const Instruction*> Instructions() const { return instructions_; }
+ Instruction* Instructions() const { return instructions_.first; }
+ /// Iterator for the instructions inside a block
+ class Iterator {
+ public:
+ /// Constructor
+ /// @param inst the instruction to start iterating from
+ explicit Iterator(Instruction* inst) : inst_(inst) {}
+ ~Iterator() = default;
+
+ /// Dereference operator
+ /// @returns the instruction for this iterator
+ Instruction* operator*() const { return inst_; }
+
+ /// Comparison operator
+ /// @param itr to compare against
+ /// @returns true if this iterator and @p itr point to the same instruction
+ bool operator==(const Iterator& itr) const { return itr.inst_ == inst_; }
+
+ /// Not equal operator
+ /// @param itr to compare against
+ /// @returns true if this iterator and @p itr point to different instructions
+ bool operator!=(const Iterator& itr) const { return !(*this == itr); }
+
+ /// Increment operator
+ /// @returns this iterator advanced to the next element
+ Iterator& operator++() {
+ inst_ = inst_->next;
+ return *this;
+ }
+
+ private:
+ Instruction* inst_ = nullptr;
+ };
+
+ /// @returns the iterator pointing to the start of the instruction list
+ Iterator begin() const { return Iterator{instructions_.first}; }
+
+ /// @returns the ending iterator
+ Iterator end() const { return Iterator{nullptr}; }
+
+ /// Adds the instruction to the beginning of the block
+ /// @param inst the instruction to add
+ void Prepend(Instruction* inst);
/// Adds the instruction to the end of the block
/// @param inst the instruction to add
- void AddInstruction(Instruction* inst);
+ void Append(Instruction* inst);
+ /// Adds the new instruction before the given instruction
+ /// @param before the instruction to insert before
+ /// @param inst the instruction to insert
+ void InsertBefore(Instruction* before, Instruction* inst);
+ /// Adds the new instruction after the given instruction
+ /// @param after the instruction to insert after
+ /// @param inst the instruction to insert
+ void InsertAfter(Instruction* after, Instruction* inst);
+ /// Replaces the target instruction with the new instruction
+ /// @param target the instruction to replace
+ /// @param inst the instruction to insert
+ void Replace(Instruction* target, Instruction* inst);
+ /// Removes the target instruction
+ /// @param inst the instruction to remove
+ void Remove(Instruction* inst);
+
+ /// @returns true if the block contains no instructions
+ bool IsEmpty() const { return Length() == 0; }
+
+ /// @returns the number of instructions in the block
+ size_t Length() const { return instructions_.count; }
/// Sets the params to the block
/// @param params the params for the block
@@ -73,7 +136,12 @@
void AddInboundBranch(ir::Branch* node) { inbound_branches_.Push(node); }
private:
- utils::Vector<const Instruction*, 16> instructions_;
+ struct {
+ Instruction* first = nullptr;
+ Instruction* last = nullptr;
+ size_t count = 0;
+ } instructions_;
+
utils::Vector<const BlockParam*, 0> params_;
/// The list of branches into this node. This list maybe empty for several
diff --git a/src/tint/ir/block_test.cc b/src/tint/ir/block_test.cc
new file mode 100644
index 0000000..adce60f
--- /dev/null
+++ b/src/tint/ir/block_test.cc
@@ -0,0 +1,668 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ir/block.h"
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
+#include "src/tint/ir/builder.h"
+#include "src/tint/ir/module.h"
+
+namespace tint::ir {
+namespace {
+
+class IR_BlockTest : public ::testing::Test {
+ public:
+ Module mod;
+ Builder b{mod};
+};
+
+TEST_F(IR_BlockTest, SetInstructions) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst1, inst2, inst3});
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Append) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->Append(inst2);
+ blk->Append(inst3);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Prepend) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Prepend(inst3);
+ blk->Prepend(inst2);
+ blk->Prepend(inst1);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, InsertBefore_AtStart) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Append(inst2);
+ blk->InsertBefore(inst2, inst1);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(2u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, InsertBefore_Middle) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->Append(inst3);
+ blk->InsertBefore(inst3, inst2);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, InsertAfter_AtEnd) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->InsertAfter(inst1, inst2);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(2u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, InsertAfter_Middle) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->Append(inst3);
+ blk->InsertAfter(inst1, inst2);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Replace_Middle) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst3 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst1, inst4, inst3});
+ blk->Replace(inst4, inst2);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ ASSERT_EQ(inst3->Block(), blk);
+ EXPECT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(3u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst3);
+ ASSERT_EQ(inst->prev, inst2);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Replace_Start) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst4, inst2});
+ blk->Replace(inst4, inst1);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ EXPECT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(2u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Replace_End) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst1, inst4});
+ blk->Replace(inst4, inst2);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ ASSERT_EQ(inst2->Block(), blk);
+ EXPECT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(2u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Replace_OnlyNode) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst4});
+ blk->Replace(inst4, inst1);
+
+ ASSERT_EQ(inst1->Block(), blk);
+ EXPECT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(1u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Remove_Middle) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst1, inst4, inst2});
+ blk->Remove(inst4);
+
+ ASSERT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(2u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ inst = inst->next;
+
+ ASSERT_EQ(inst, inst2);
+ ASSERT_EQ(inst->prev, inst1);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Remove_Start) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst4, inst1});
+ blk->Remove(inst4);
+
+ ASSERT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(1u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Remove_End) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst1, inst4});
+ blk->Remove(inst4);
+
+ ASSERT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_FALSE(blk->IsEmpty());
+ EXPECT_EQ(1u, blk->Length());
+
+ auto* inst = blk->Instructions();
+ ASSERT_EQ(inst, inst1);
+ ASSERT_EQ(inst->prev, nullptr);
+ ASSERT_EQ(inst->next, nullptr);
+}
+
+TEST_F(IR_BlockTest, Remove_OnlyNode) {
+ auto* inst4 = b.CreateLoop();
+
+ auto* blk = b.CreateBlock();
+ blk->SetInstructions(utils::Vector{inst4});
+ blk->Remove(inst4);
+
+ ASSERT_EQ(inst4->Block(), nullptr);
+
+ EXPECT_TRUE(blk->IsEmpty());
+ EXPECT_EQ(0u, blk->Length());
+}
+
+TEST_F(IR_BlockTest, Fail_PrependNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* blk = b.CreateBlock();
+ blk->Prepend(nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_PrependAlreadyInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Prepend(inst1);
+
+ blk->Prepend(inst1);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_AppendNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* blk = b.CreateBlock();
+ blk->Append(nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_AppendAlreadyInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->Append(inst1);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertBeforeNullptrInst) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->InsertBefore(nullptr, inst1);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertBeforeInstNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->InsertBefore(inst1, nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertBeforeDifferentBlock) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ auto* blk2 = b.CreateBlock();
+ blk2->Append(inst1);
+ blk1->InsertBefore(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertBeforeAlreadyInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ blk1->Append(inst1);
+ blk1->Append(inst2);
+ blk1->InsertBefore(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertAfterNullptrInst) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->InsertAfter(nullptr, inst1);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertAfterInstNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->InsertAfter(inst1, nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertAfterDifferentBlock) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ auto* blk2 = b.CreateBlock();
+ blk2->Append(inst1);
+ blk1->InsertAfter(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_InsertAfterAlreadyInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ blk1->Append(inst1);
+ blk1->Append(inst2);
+ blk1->InsertAfter(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_ReplaceNullptrInst) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Replace(nullptr, inst1);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_ReplaceInstNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ blk->Replace(inst1, nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_ReplaceDifferentBlock) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ auto* blk2 = b.CreateBlock();
+ blk2->Append(inst1);
+ blk1->Replace(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_ReplaceAlreadyInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ blk1->Append(inst1);
+ blk1->Append(inst2);
+ blk1->Replace(inst1, inst2);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_RemoveNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* blk = b.CreateBlock();
+ blk->Remove(nullptr);
+ },
+ "internal compiler error");
+}
+
+TEST_F(IR_BlockTest, Fail_RemoveDifferentBlock) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk1 = b.CreateBlock();
+ auto* blk2 = b.CreateBlock();
+ blk2->Append(inst1);
+ blk1->Remove(inst1);
+ },
+ "internal compiler error");
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index c7155d5..6ca9356 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -70,7 +70,7 @@
}
void Disassembler::EmitBlockInstructions(const Block* b) {
- for (const auto* inst : b->Instructions()) {
+ for (const auto* inst : *b) {
Indent();
EmitInstruction(inst);
}
diff --git a/src/tint/ir/discard_test.cc b/src/tint/ir/discard_test.cc
index 054727f..a09c2e9 100644
--- a/src/tint/ir/discard_test.cc
+++ b/src/tint/ir/discard_test.cc
@@ -19,9 +19,9 @@
namespace tint::ir {
namespace {
-using IR_InstructionTest = TestHelper;
+using IR_DiscardTest = TestHelper;
-TEST_F(IR_InstructionTest, Discard) {
+TEST_F(IR_DiscardTest, Discard) {
Module mod;
Builder b{mod};
diff --git a/src/tint/ir/from_program.cc b/src/tint/ir/from_program.cc
index 4a442ee..a3c36e3 100644
--- a/src/tint/ir/from_program.cc
+++ b/src/tint/ir/from_program.cc
@@ -173,7 +173,7 @@
TINT_ASSERT(IR, current_block_);
TINT_ASSERT(IR, !current_block_->HasBranchTarget());
- current_block_->AddInstruction(br);
+ current_block_->Append(br);
current_block_ = nullptr;
}
@@ -487,7 +487,7 @@
return;
}
auto store = builder_.Store(lhs.Get(), rhs.Get());
- current_block_->AddInstruction(store);
+ current_block_->Append(store);
}
void EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
@@ -498,7 +498,7 @@
// Load from the LHS.
auto* lhs_value = builder_.Load(lhs.Get());
- current_block_->AddInstruction(lhs_value);
+ current_block_->Append(lhs_value);
auto* ty = lhs_value->Type();
@@ -511,10 +511,10 @@
} else {
inst = builder_.Subtract(ty, lhs_value, rhs);
}
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
auto store = builder_.Store(lhs.Get(), inst);
- current_block_->AddInstruction(store);
+ current_block_->Append(store);
}
void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) {
@@ -530,7 +530,7 @@
// Load from the LHS.
auto* lhs_value = builder_.Load(lhs.Get());
- current_block_->AddInstruction(lhs_value);
+ current_block_->Append(lhs_value);
auto* ty = lhs_value->Type();
@@ -580,10 +580,10 @@
TINT_ICE(IR, diagnostics_) << "missing binary operand type";
return;
}
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
auto store = builder_.Store(lhs.Get(), inst);
- current_block_->AddInstruction(store);
+ current_block_->Append(store);
}
void EmitBlock(const ast::BlockStatement* block) {
@@ -603,7 +603,7 @@
return;
}
auto* if_inst = builder_.CreateIf(reg.Get());
- current_block_->AddInstruction(if_inst);
+ current_block_->Append(if_inst);
{
ControlStackScope scope(this, if_inst);
@@ -638,7 +638,7 @@
void EmitLoop(const ast::LoopStatement* stmt) {
auto* loop_inst = builder_.CreateLoop();
- current_block_->AddInstruction(loop_inst);
+ current_block_->Append(loop_inst);
{
ControlStackScope scope(this, loop_inst);
@@ -682,7 +682,7 @@
void EmitWhile(const ast::WhileStatement* stmt) {
auto* loop_inst = builder_.CreateLoop();
- current_block_->AddInstruction(loop_inst);
+ current_block_->Append(loop_inst);
// Continue is always empty, just go back to the start
current_block_ = loop_inst->Continuing();
@@ -701,7 +701,7 @@
// Create an `if (cond) {} else {break;}` control flow
auto* if_inst = builder_.CreateIf(reg.Get());
- current_block_->AddInstruction(if_inst);
+ current_block_->Append(if_inst);
current_block_ = if_inst->True();
SetBranch(builder_.ExitIf(if_inst));
@@ -723,7 +723,7 @@
void EmitForLoop(const ast::ForLoopStatement* stmt) {
auto* loop_inst = builder_.CreateLoop();
- current_block_->AddInstruction(loop_inst);
+ current_block_->Append(loop_inst);
// Make sure the initializer ends up in a contained scope
scopes_.Push();
@@ -748,7 +748,7 @@
// Create an `if (cond) {} else {break;}` control flow
auto* if_inst = builder_.CreateIf(reg.Get());
- current_block_->AddInstruction(if_inst);
+ current_block_->Append(if_inst);
current_block_ = if_inst->True();
SetBranch(builder_.ExitIf(if_inst));
@@ -783,7 +783,7 @@
return;
}
auto* switch_inst = builder_.CreateSwitch(reg.Get());
- current_block_->AddInstruction(switch_inst);
+ current_block_->Append(switch_inst);
{
ControlStackScope scope(this, switch_inst);
@@ -856,7 +856,7 @@
// figuring out the multi-level exit that is triggered.
void EmitDiscard(const ast::DiscardStatement*) {
auto* inst = builder_.Discard();
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
}
void EmitBreakIf(const ast::BreakIfStatement* stmt) {
@@ -914,7 +914,7 @@
// If this expression maps to sem::Load, insert a load instruction to get the result.
if (result && sem->Is<sem::Load>()) {
auto* load = builder_.Load(result.Get());
- current_block_->AddInstruction(load);
+ current_block_->Append(load);
return load;
}
@@ -940,7 +940,7 @@
}
val->SetInitializer(init.Get());
}
- current_block_->AddInstruction(val);
+ current_block_->Append(val);
if (auto* gv = sem->As<sem::GlobalVariable>(); gv && var->HasBindingPoint()) {
val->SetBindingPoint(gv->BindingPoint().value().group,
@@ -1012,7 +1012,7 @@
break;
}
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
return inst;
}
@@ -1036,7 +1036,7 @@
}
auto* if_inst = builder_.CreateIf(lhs.Get());
- current_block_->AddInstruction(if_inst);
+ current_block_->Append(if_inst);
auto* result = builder_.BlockParam(builder_.ir.Types().bool_());
if_inst->Merge()->SetParams(utils::Vector{result});
@@ -1157,7 +1157,7 @@
return utils::Failure;
}
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
return inst;
}
@@ -1171,7 +1171,7 @@
auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
auto* inst = builder_.Bitcast(ty, val.Get());
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
return inst;
}
@@ -1235,7 +1235,7 @@
if (inst == nullptr) {
return utils::Failure;
}
- current_block_->AddInstruction(inst);
+ current_block_->Append(inst);
return inst;
}
diff --git a/src/tint/ir/from_program_binary_test.cc b/src/tint/ir/from_program_binary_test.cc
index 5f7bcb2..2be72e1 100644
--- a/src/tint/ir/from_program_binary_test.cc
+++ b/src/tint/ir/from_program_binary_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramBinaryTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Add) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Add(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -49,7 +49,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Increment) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Increment) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = Increment("v1");
WrapInFunction(expr);
@@ -73,7 +73,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundAdd) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundAdd) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kAdd);
WrapInFunction(expr);
@@ -97,7 +97,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Subtract) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Subtract) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Sub(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -120,7 +120,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Decrement) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Decrement) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.i32());
auto* expr = Decrement("v1");
WrapInFunction(expr);
@@ -144,7 +144,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundSubtract) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundSubtract) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kSubtract);
WrapInFunction(expr);
@@ -168,7 +168,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Multiply) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Multiply) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Mul(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -191,7 +191,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundMultiply) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundMultiply) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kMultiply);
WrapInFunction(expr);
@@ -215,7 +215,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Div) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Div) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Div(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -238,7 +238,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundDiv) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundDiv) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kDivide);
WrapInFunction(expr);
@@ -262,7 +262,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Modulo) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Modulo) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Mod(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -285,7 +285,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundModulo) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundModulo) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kModulo);
WrapInFunction(expr);
@@ -309,7 +309,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_And) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_And) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = And(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -332,7 +332,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundAnd) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundAnd) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.bool_());
auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kAnd);
WrapInFunction(expr);
@@ -356,7 +356,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Or) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Or) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Or(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -379,7 +379,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundOr) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundOr) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.bool_());
auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kOr);
WrapInFunction(expr);
@@ -403,7 +403,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Xor) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Xor) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Xor(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -426,7 +426,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundXor) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundXor) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kXor);
WrapInFunction(expr);
@@ -450,7 +450,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalAnd) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_LogicalAnd) {
Func("my_func", utils::Empty, ty.bool_(), utils::Vector{Return(true)});
auto* expr = If(LogicalAnd(Call("my_func"), false), Block());
WrapInFunction(expr);
@@ -502,7 +502,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalOr) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_LogicalOr) {
Func("my_func", utils::Empty, ty.bool_(), utils::Vector{Return(true)});
auto* expr = If(LogicalOr(Call("my_func"), true), Block());
WrapInFunction(expr);
@@ -554,7 +554,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Equal) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Equal) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Equal(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -577,7 +577,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_NotEqual) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_NotEqual) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = NotEqual(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -600,7 +600,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThan) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_LessThan) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = LessThan(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -623,7 +623,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThan) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_GreaterThan) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = GreaterThan(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -646,7 +646,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThanEqual) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_LessThanEqual) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = LessThanEqual(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -669,7 +669,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThanEqual) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_GreaterThanEqual) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = GreaterThanEqual(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -692,7 +692,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftLeft) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_ShiftLeft) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Shl(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -715,7 +715,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundShiftLeft) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundShiftLeft) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftLeft);
WrapInFunction(expr);
@@ -739,7 +739,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftRight) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_ShiftRight) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(0_u)});
auto* expr = Shr(Call("my_func"), 4_u);
WrapInFunction(expr);
@@ -762,7 +762,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundShiftRight) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_CompoundShiftRight) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.u32());
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftRight);
WrapInFunction(expr);
@@ -786,7 +786,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Compound) {
Func("my_func", utils::Empty, ty.f32(), utils::Vector{Return(0_f)});
auto* expr = LogicalAnd(LessThan(Call("my_func"), 2_f),
@@ -831,7 +831,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound_WithConstEval) {
+TEST_F(IR_FromProgramBinaryTest, EmitExpression_Binary_Compound_WithConstEval) {
Func("my_func", utils::Vector{Param("p", ty.bool_())}, ty.bool_(), utils::Vector{Return(true)});
auto* expr = Call("my_func", LogicalAnd(LessThan(2.4_f, 2_f),
GreaterThan(2.5_f, Div(10_f, Mul(2.3_f, 9.4_f)))));
diff --git a/src/tint/ir/from_program_builtin_test.cc b/src/tint/ir/from_program_builtin_test.cc
index 4d21b48..7eafa17 100644
--- a/src/tint/ir/from_program_builtin_test.cc
+++ b/src/tint/ir/from_program_builtin_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramBuiltinTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitExpression_Builtin) {
+TEST_F(IR_FromProgramBuiltinTest, EmitExpression_Builtin) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
auto* expr = Call("asin", i);
WrapInFunction(expr);
diff --git a/src/tint/ir/from_program_call_test.cc b/src/tint/ir/from_program_call_test.cc
index d305855..1aa224c 100644
--- a/src/tint/ir/from_program_call_test.cc
+++ b/src/tint/ir/from_program_call_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramCallTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitExpression_Bitcast) {
+TEST_F(IR_FromProgramCallTest, EmitExpression_Bitcast) {
Func("my_func", utils::Empty, ty.f32(), utils::Vector{Return(0_f)});
auto* expr = Bitcast<f32>(Call("my_func"));
@@ -50,7 +50,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitStatement_Discard) {
+TEST_F(IR_FromProgramCallTest, EmitStatement_Discard) {
auto* expr = Discard();
Func("test_function", {}, ty.void_(), expr,
utils::Vector{
@@ -69,7 +69,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitStatement_UserFunction) {
+TEST_F(IR_FromProgramCallTest, EmitStatement_UserFunction) {
Func("my_func", utils::Vector{Param("p", ty.f32())}, ty.void_(), utils::Empty);
auto* stmt = CallStmt(Call("my_func", Mul(2_a, 3_a)));
@@ -91,7 +91,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Convert) {
+TEST_F(IR_FromProgramCallTest, EmitExpression_Convert) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_i));
auto* expr = Call(ty.f32(), i);
WrapInFunction(expr);
@@ -114,7 +114,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_ConstructEmpty) {
+TEST_F(IR_FromProgramCallTest, EmitExpression_ConstructEmpty) {
auto* expr = vec3(ty.f32());
GlobalVar("i", builtin::AddressSpace::kPrivate, expr);
@@ -129,7 +129,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Construct) {
+TEST_F(IR_FromProgramCallTest, EmitExpression_Construct) {
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
auto* expr = vec3(ty.f32(), 2_f, 3_f, i);
WrapInFunction(expr);
diff --git a/src/tint/ir/from_program_function_test.cc b/src/tint/ir/from_program_function_test.cc
index 57e78a3..a130281 100644
--- a/src/tint/ir/from_program_function_test.cc
+++ b/src/tint/ir/from_program_function_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramFunctionTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitFunction_Vertex) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_Vertex) {
Func("test", utils::Empty, ty.vec4<f32>(), utils::Vector{Return(vec4<f32>(0_f, 0_f, 0_f, 0_f))},
utils::Vector{Stage(ast::PipelineStage::kVertex)},
utils::Vector{Builtin(builtin::BuiltinValue::kPosition)});
@@ -42,7 +42,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_Fragment) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_Fragment) {
Func("test", utils::Empty, ty.void_(), utils::Empty,
utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -57,7 +57,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_Compute) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_Compute) {
Func("test", utils::Empty, ty.void_(), utils::Empty,
utils::Vector{Stage(ast::PipelineStage::kCompute), WorkgroupSize(8_i, 4_i, 2_i)});
@@ -73,7 +73,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_Return) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_Return) {
Func("test", utils::Empty, ty.vec3<f32>(), utils::Vector{Return(vec3<f32>(0_f, 0_f, 0_f))},
utils::Empty);
@@ -88,7 +88,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnPosition) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnPosition) {
Func("test", utils::Empty, ty.vec4<f32>(), utils::Vector{Return(vec4<f32>(1_f, 2_f, 3_f, 4_f))},
utils::Vector{Stage(ast::PipelineStage::kVertex)},
utils::Vector{Builtin(builtin::BuiltinValue::kPosition)});
@@ -104,7 +104,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnPositionInvariant) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnPositionInvariant) {
Func("test", utils::Empty, ty.vec4<f32>(), utils::Vector{Return(vec4<f32>(1_f, 2_f, 3_f, 4_f))},
utils::Vector{Stage(ast::PipelineStage::kVertex)},
utils::Vector{Builtin(builtin::BuiltinValue::kPosition), Invariant()});
@@ -121,7 +121,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnLocation) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnLocation) {
Func("test", utils::Empty, ty.vec4<f32>(), utils::Vector{Return(vec4<f32>(1_f, 2_f, 3_f, 4_f))},
utils::Vector{Stage(ast::PipelineStage::kFragment)}, utils::Vector{Location(1_i)});
@@ -137,7 +137,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnLocation_Interpolate) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnLocation_Interpolate) {
Func("test", utils::Empty, ty.vec4<f32>(), utils::Vector{Return(vec4<f32>(1_f, 2_f, 3_f, 4_f))},
utils::Vector{Stage(ast::PipelineStage::kFragment)},
utils::Vector{Location(1_i), Interpolate(builtin::InterpolationType::kLinear,
@@ -156,7 +156,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnFragDepth) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnFragDepth) {
Func("test", utils::Empty, ty.f32(), utils::Vector{Return(1_f)},
utils::Vector{Stage(ast::PipelineStage::kFragment)},
utils::Vector{Builtin(builtin::BuiltinValue::kFragDepth)});
@@ -172,7 +172,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitFunction_ReturnSampleMask) {
+TEST_F(IR_FromProgramFunctionTest, EmitFunction_ReturnSampleMask) {
Func("test", utils::Empty, ty.u32(), utils::Vector{Return(1_u)},
utils::Vector{Stage(ast::PipelineStage::kFragment)},
utils::Vector{Builtin(builtin::BuiltinValue::kSampleMask)});
diff --git a/src/tint/ir/from_program_literal_test.cc b/src/tint/ir/from_program_literal_test.cc
index f746bc0..1ea9706 100644
--- a/src/tint/ir/from_program_literal_test.cc
+++ b/src/tint/ir/from_program_literal_test.cc
@@ -26,13 +26,13 @@
namespace {
const Value* GlobalVarInitializer(const Module& m) {
- const auto instr = m.root_block->Instructions();
-
- if (instr.Length() == 0u) {
+ if (m.root_block->Length() == 0u) {
ADD_FAILURE() << "m.root_block has no instruction";
return nullptr;
}
- auto* var = instr[0]->As<ir::Var>();
+
+ const auto instr = m.root_block->Instructions();
+ auto* var = instr->As<ir::Var>();
if (!var) {
ADD_FAILURE() << "m.root_block.instructions[0] was not a var";
return nullptr;
@@ -42,9 +42,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramLiteralTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_Bool_True) {
auto* expr = Expr(true);
GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr);
@@ -58,7 +58,7 @@
EXPECT_TRUE(val->As<constant::Scalar<bool>>()->ValueAs<bool>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_Bool_False) {
auto* expr = Expr(false);
GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr);
@@ -72,7 +72,7 @@
EXPECT_FALSE(val->As<constant::Scalar<bool>>()->ValueAs<bool>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_Deduped) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_Bool_Deduped) {
GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, Expr(true));
GlobalVar("b", ty.bool_(), builtin::AddressSpace::kPrivate, Expr(false));
GlobalVar("c", ty.bool_(), builtin::AddressSpace::kPrivate, Expr(true));
@@ -81,14 +81,20 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- auto instr = m.Get().root_block->Instructions();
- auto* var_a = instr[0]->As<ir::Var>();
+ auto itr = m.Get().root_block->begin();
+ auto* var_a = (*itr)->As<ir::Var>();
+ ++itr;
+
ASSERT_NE(var_a, nullptr);
- auto* var_b = instr[1]->As<ir::Var>();
+ auto* var_b = (*itr)->As<ir::Var>();
+ ++itr;
+
ASSERT_NE(var_b, nullptr);
- auto* var_c = instr[2]->As<ir::Var>();
+ auto* var_c = (*itr)->As<ir::Var>();
+ ++itr;
+
ASSERT_NE(var_c, nullptr);
- auto* var_d = instr[3]->As<ir::Var>();
+ auto* var_d = (*itr)->As<ir::Var>();
ASSERT_NE(var_d, nullptr);
ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
@@ -96,7 +102,7 @@
ASSERT_NE(var_a->Initializer(), var_b->Initializer());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_F32) {
auto* expr = Expr(1.2_f);
GlobalVar("a", ty.f32(), builtin::AddressSpace::kPrivate, expr);
@@ -110,7 +116,7 @@
EXPECT_EQ(1.2_f, val->As<constant::Scalar<f32>>()->ValueAs<f32>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_F32_Deduped) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_F32_Deduped) {
GlobalVar("a", ty.f32(), builtin::AddressSpace::kPrivate, Expr(1.2_f));
GlobalVar("b", ty.f32(), builtin::AddressSpace::kPrivate, Expr(1.25_f));
GlobalVar("c", ty.f32(), builtin::AddressSpace::kPrivate, Expr(1.2_f));
@@ -118,19 +124,23 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- auto instr = m.Get().root_block->Instructions();
- auto* var_a = instr[0]->As<ir::Var>();
+ auto itr = m.Get().root_block->begin();
+ auto* var_a = (*itr)->As<ir::Var>();
ASSERT_NE(var_a, nullptr);
- auto* var_b = instr[1]->As<ir::Var>();
+ ++itr;
+
+ auto* var_b = (*itr)->As<ir::Var>();
ASSERT_NE(var_b, nullptr);
- auto* var_c = instr[2]->As<ir::Var>();
+ ++itr;
+
+ auto* var_c = (*itr)->As<ir::Var>();
ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->Initializer(), var_b->Initializer());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_F16) {
Enable(builtin::Extension::kF16);
auto* expr = Expr(1.2_h);
GlobalVar("a", ty.f16(), builtin::AddressSpace::kPrivate, expr);
@@ -145,7 +155,7 @@
EXPECT_EQ(1.2_h, val->As<constant::Scalar<f16>>()->ValueAs<f32>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_F16_Deduped) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_F16_Deduped) {
Enable(builtin::Extension::kF16);
GlobalVar("a", ty.f16(), builtin::AddressSpace::kPrivate, Expr(1.2_h));
GlobalVar("b", ty.f16(), builtin::AddressSpace::kPrivate, Expr(1.25_h));
@@ -154,19 +164,23 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- auto instr = m.Get().root_block->Instructions();
- auto* var_a = instr[0]->As<ir::Var>();
+ auto itr = m.Get().root_block->begin();
+ auto* var_a = (*itr)->As<ir::Var>();
ASSERT_NE(var_a, nullptr);
- auto* var_b = instr[1]->As<ir::Var>();
+ ++itr;
+
+ auto* var_b = (*itr)->As<ir::Var>();
ASSERT_NE(var_b, nullptr);
- auto* var_c = instr[2]->As<ir::Var>();
+ ++itr;
+
+ auto* var_c = (*itr)->As<ir::Var>();
ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->Initializer(), var_b->Initializer());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_I32) {
auto* expr = Expr(-2_i);
GlobalVar("a", ty.i32(), builtin::AddressSpace::kPrivate, expr);
@@ -180,7 +194,7 @@
EXPECT_EQ(-2_i, val->As<constant::Scalar<i32>>()->ValueAs<f32>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_I32_Deduped) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_I32_Deduped) {
GlobalVar("a", ty.i32(), builtin::AddressSpace::kPrivate, Expr(-2_i));
GlobalVar("b", ty.i32(), builtin::AddressSpace::kPrivate, Expr(2_i));
GlobalVar("c", ty.i32(), builtin::AddressSpace::kPrivate, Expr(-2_i));
@@ -188,19 +202,23 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- auto instr = m.Get().root_block->Instructions();
- auto* var_a = instr[0]->As<ir::Var>();
+ auto itr = m.Get().root_block->begin();
+ auto* var_a = (*itr)->As<ir::Var>();
ASSERT_NE(var_a, nullptr);
- auto* var_b = instr[1]->As<ir::Var>();
+ ++itr;
+
+ auto* var_b = (*itr)->As<ir::Var>();
ASSERT_NE(var_b, nullptr);
- auto* var_c = instr[2]->As<ir::Var>();
+ ++itr;
+
+ auto* var_c = (*itr)->As<ir::Var>();
ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->Initializer(), var_b->Initializer());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_U32) {
auto* expr = Expr(2_u);
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr);
@@ -214,7 +232,7 @@
EXPECT_EQ(2_u, val->As<constant::Scalar<u32>>()->ValueAs<f32>());
}
-TEST_F(IR_BuilderImplTest, EmitLiteral_U32_Deduped) {
+TEST_F(IR_FromProgramLiteralTest, EmitLiteral_U32_Deduped) {
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, Expr(2_u));
GlobalVar("b", ty.u32(), builtin::AddressSpace::kPrivate, Expr(3_u));
GlobalVar("c", ty.u32(), builtin::AddressSpace::kPrivate, Expr(2_u));
@@ -222,12 +240,16 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- auto instr = m.Get().root_block->Instructions();
- auto* var_a = instr[0]->As<ir::Var>();
+ auto itr = m.Get().root_block->begin();
+ auto* var_a = (*itr)->As<ir::Var>();
ASSERT_NE(var_a, nullptr);
- auto* var_b = instr[1]->As<ir::Var>();
+ ++itr;
+
+ auto* var_b = (*itr)->As<ir::Var>();
ASSERT_NE(var_b, nullptr);
- auto* var_c = instr[2]->As<ir::Var>();
+ ++itr;
+
+ auto* var_c = (*itr)->As<ir::Var>();
ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
diff --git a/src/tint/ir/from_program_materialize_test.cc b/src/tint/ir/from_program_materialize_test.cc
index 1262579..07b49c1 100644
--- a/src/tint/ir/from_program_materialize_test.cc
+++ b/src/tint/ir/from_program_materialize_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramMaterializeTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitExpression_MaterializedCall) {
+TEST_F(IR_FromProgramMaterializeTest, EmitExpression_MaterializedCall) {
auto* expr = Return(Call("trunc", 2.5_f));
Func("test_function", {}, ty.f32(), expr, utils::Empty);
diff --git a/src/tint/ir/from_program_store_test.cc b/src/tint/ir/from_program_store_test.cc
index 20ea290..daeed37 100644
--- a/src/tint/ir/from_program_store_test.cc
+++ b/src/tint/ir/from_program_store_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramStoreTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitStatement_Assign) {
+TEST_F(IR_FromProgramStoreTest, EmitStatement_Assign) {
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate);
auto* expr = Assign("a", 4_u);
diff --git a/src/tint/ir/from_program_test.cc b/src/tint/ir/from_program_test.cc
index 404b6c3..d7825c5 100644
--- a/src/tint/ir/from_program_test.cc
+++ b/src/tint/ir/from_program_test.cc
@@ -51,9 +51,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramTest = TestHelper;
-TEST_F(IR_BuilderImplTest, Func) {
+TEST_F(IR_FromProgramTest, Func) {
Func("f", utils::Empty, ty.void_(), utils::Empty);
auto m = Build();
@@ -74,7 +74,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithParam) {
+TEST_F(IR_FromProgramTest, Func_WithParam) {
Func("f", utils::Vector{Param("a", ty.u32())}, ty.u32(), utils::Vector{Return("a")});
auto m = Build();
@@ -95,7 +95,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithMultipleParam) {
+TEST_F(IR_FromProgramTest, Func_WithMultipleParam) {
Func("f", utils::Vector{Param("a", ty.u32()), Param("b", ty.i32()), Param("c", ty.bool_())},
ty.void_(), utils::Empty);
@@ -117,7 +117,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EntryPoint) {
+TEST_F(IR_FromProgramTest, EntryPoint) {
Func("f", utils::Empty, ty.void_(), utils::Empty,
utils::Vector{Stage(ast::PipelineStage::kFragment)});
@@ -127,7 +127,7 @@
EXPECT_EQ(m->functions[0]->Stage(), Function::PipelineStage::kFragment);
}
-TEST_F(IR_BuilderImplTest, IfStatement) {
+TEST_F(IR_FromProgramTest, IfStatement) {
auto* ast_if = If(true, Block(), Else(Block()));
WrapInFunction(ast_if);
@@ -167,7 +167,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
+TEST_F(IR_FromProgramTest, IfStatement_TrueReturns) {
auto* ast_if = If(true, Block(Return()));
WrapInFunction(ast_if);
@@ -207,7 +207,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
+TEST_F(IR_FromProgramTest, IfStatement_FalseReturns) {
auto* ast_if = If(true, Block(), Else(Block(Return())));
WrapInFunction(ast_if);
@@ -247,7 +247,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
+TEST_F(IR_FromProgramTest, IfStatement_BothReturn) {
auto* ast_if = If(true, Block(Return()), Else(Block(Return())));
WrapInFunction(ast_if);
@@ -282,7 +282,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
+TEST_F(IR_FromProgramTest, IfStatement_JumpChainToMerge) {
auto* ast_loop = Loop(Block(Break()));
auto* ast_if = If(true, Block(ast_loop));
WrapInFunction(ast_if);
@@ -330,7 +330,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
+TEST_F(IR_FromProgramTest, Loop_WithBreak) {
auto* ast_loop = Loop(Block(Break()));
WrapInFunction(ast_loop);
@@ -342,7 +342,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(0u, flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(1u, flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
@@ -364,7 +364,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
+TEST_F(IR_FromProgramTest, Loop_WithContinue) {
auto* ast_if = If(true, Block(Break()));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
@@ -379,7 +379,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(2u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
@@ -424,7 +424,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
+TEST_F(IR_FromProgramTest, Loop_WithContinuing_BreakIf) {
auto* ast_break_if = BreakIf(true);
auto* ast_loop = Loop(Block(), Block(ast_break_if));
WrapInFunction(ast_loop);
@@ -437,7 +437,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(2u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Merge()->InboundBranches().Length());
@@ -464,7 +464,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_Continuing_Body_Scope) {
+TEST_F(IR_FromProgramTest, Loop_Continuing_Body_Scope) {
auto* a = Decl(Let("a", Expr(true)));
auto* ast_break_if = BreakIf("a");
auto* ast_loop = Loop(Block(a), Block(ast_break_if));
@@ -497,7 +497,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
+TEST_F(IR_FromProgramTest, Loop_WithReturn) {
auto* ast_if = If(true, Block(Return()));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
@@ -511,7 +511,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(2u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
@@ -551,7 +551,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
+TEST_F(IR_FromProgramTest, Loop_WithOnlyReturn) {
auto* ast_loop = Loop(Block(Return(), Continue()));
WrapInFunction(ast_loop, If(true, Block(Return())));
@@ -563,7 +563,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(0u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Merge()->InboundBranches().Length());
@@ -580,7 +580,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
+TEST_F(IR_FromProgramTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
// Note, even though there is code in the loop merge (specifically, the
// `ast_if` below), it doesn't get emitted as there is no way to reach the
// loop merge due to the loop itself doing a `return`. This is why the
@@ -601,7 +601,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(0u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Merge()->InboundBranches().Length());
@@ -618,7 +618,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
+TEST_F(IR_FromProgramTest, Loop_WithIf_BothBranchesBreak) {
auto* ast_if = If(true, Block(Break()), Else(Block(Break())));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
@@ -632,7 +632,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(0u, loop_flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(1u, loop_flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
@@ -667,7 +667,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Loop_Nested) {
+TEST_F(IR_FromProgramTest, Loop_Nested) {
auto* ast_if_a = If(true, Block(Break()));
auto* ast_if_b = If(true, Block(Continue()));
auto* ast_if_c = BreakIf(true);
@@ -789,7 +789,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, While) {
+TEST_F(IR_FromProgramTest, While) {
auto* ast_while = While(false, Block());
WrapInFunction(ast_while);
@@ -805,7 +805,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(1u, flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(2u, flow->Body()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
@@ -850,7 +850,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, While_Return) {
+TEST_F(IR_FromProgramTest, While_Return) {
auto* ast_while = While(true, Block(Return()));
WrapInFunction(ast_while);
@@ -866,7 +866,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(1u, flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(2u, flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->True()->InboundBranches().Length());
@@ -912,7 +912,7 @@
}
// TODO(dsinclair): Enable when variable declarations and increment are supported
-TEST_F(IR_BuilderImplTest, DISABLED_For) {
+TEST_F(IR_FromProgramTest, DISABLED_For) {
// for(var i: 0; i < 10; i++) {
// }
//
@@ -950,7 +950,7 @@
EXPECT_EQ(Disassemble(m), R"()");
}
-TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
+TEST_F(IR_FromProgramTest, For_NoInitCondOrContinuing) {
auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
WrapInFunction(ast_for);
@@ -962,7 +962,7 @@
ASSERT_EQ(1u, m.functions.Length());
- EXPECT_EQ(0u, flow->Body()->InboundBranches().Length());
+ EXPECT_EQ(1u, flow->Body()->InboundBranches().Length());
EXPECT_EQ(0u, flow->Continuing()->InboundBranches().Length());
EXPECT_EQ(1u, flow->Merge()->InboundBranches().Length());
@@ -984,7 +984,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Switch) {
+TEST_F(IR_FromProgramTest, Switch) {
auto* ast_switch = Switch(
1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block()),
Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
@@ -1049,7 +1049,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Switch_MultiSelector) {
+TEST_F(IR_FromProgramTest, Switch_MultiSelector) {
auto* ast_switch = Switch(
1_i,
utils::Vector{Case(
@@ -1100,7 +1100,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
+TEST_F(IR_FromProgramTest, Switch_OnlyDefault) {
auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
WrapInFunction(ast_switch);
@@ -1139,7 +1139,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
+TEST_F(IR_FromProgramTest, Switch_WithBreak) {
auto* ast_switch = Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)},
Block(Break(), If(true, Block(Return())))),
DefaultCase(Block())});
@@ -1192,7 +1192,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
+TEST_F(IR_FromProgramTest, Switch_AllReturn) {
auto* ast_switch =
Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block(Return())),
DefaultCase(Block(Return()))});
@@ -1242,7 +1242,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_Phony) {
+TEST_F(IR_FromProgramTest, Emit_Phony) {
Func("b", utils::Empty, ty.i32(), Return(1_i));
WrapInFunction(Ignore(Call("b")));
@@ -1264,7 +1264,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithParam_WithAttribute_Invariant) {
+TEST_F(IR_FromProgramTest, Func_WithParam_WithAttribute_Invariant) {
Func(
"f",
utils::Vector{Param("a", ty.vec4<f32>(),
@@ -1284,7 +1284,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithParam_WithAttribute_Location) {
+TEST_F(IR_FromProgramTest, Func_WithParam_WithAttribute_Location) {
Func("f", utils::Vector{Param("a", ty.f32(), utils::Vector{Location(2_i)})}, ty.f32(),
utils::Vector{Return("a")}, utils::Vector{Stage(ast::PipelineStage::kFragment)},
utils::Vector{Location(1_i)});
@@ -1301,7 +1301,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithParam_WithAttribute_Location_WithInterpolation_LinearCentroid) {
+TEST_F(IR_FromProgramTest, Func_WithParam_WithAttribute_Location_WithInterpolation_LinearCentroid) {
Func("f",
utils::Vector{Param(
"a", ty.f32(),
@@ -1323,7 +1323,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Func_WithParam_WithAttribute_Location_WithInterpolation_Flat) {
+TEST_F(IR_FromProgramTest, Func_WithParam_WithAttribute_Location_WithInterpolation_Flat) {
Func("f",
utils::Vector{
Param("a", ty.f32(),
diff --git a/src/tint/ir/from_program_unary_test.cc b/src/tint/ir/from_program_unary_test.cc
index b83e5d5..a7af3dd 100644
--- a/src/tint/ir/from_program_unary_test.cc
+++ b/src/tint/ir/from_program_unary_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramUnaryTest = TestHelper;
-TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Not) {
+TEST_F(IR_FromProgramUnaryTest, EmitExpression_Unary_Not) {
Func("my_func", utils::Empty, ty.bool_(), utils::Vector{Return(false)});
auto* expr = Not(Call("my_func"));
WrapInFunction(expr);
@@ -49,7 +49,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Complement) {
+TEST_F(IR_FromProgramUnaryTest, EmitExpression_Unary_Complement) {
Func("my_func", utils::Empty, ty.u32(), utils::Vector{Return(1_u)});
auto* expr = Complement(Call("my_func"));
WrapInFunction(expr);
@@ -72,7 +72,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Negation) {
+TEST_F(IR_FromProgramUnaryTest, EmitExpression_Unary_Negation) {
Func("my_func", utils::Empty, ty.i32(), utils::Vector{Return(1_i)});
auto* expr = Negation(Call("my_func"));
WrapInFunction(expr);
@@ -95,7 +95,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Unary_AddressOf) {
+TEST_F(IR_FromProgramUnaryTest, EmitExpression_Unary_AddressOf) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.i32());
auto* expr = Decl(Let("v2", AddressOf("v1")));
@@ -117,7 +117,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Indirection) {
+TEST_F(IR_FromProgramUnaryTest, EmitExpression_Unary_Indirection) {
GlobalVar("v1", builtin::AddressSpace::kPrivate, ty.i32());
utils::Vector stmts = {
Decl(Let("v3", AddressOf("v1"))),
diff --git a/src/tint/ir/from_program_var_test.cc b/src/tint/ir/from_program_var_test.cc
index 91b8b8f..5f6e78c 100644
--- a/src/tint/ir/from_program_var_test.cc
+++ b/src/tint/ir/from_program_var_test.cc
@@ -24,9 +24,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_BuilderImplTest = TestHelper;
+using IR_FromProgramVarTest = TestHelper;
-TEST_F(IR_BuilderImplTest, Emit_GlobalVar_NoInit) {
+TEST_F(IR_FromProgramVarTest, Emit_GlobalVar_NoInit) {
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate);
auto m = Build();
@@ -40,7 +40,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_GlobalVar_Init) {
+TEST_F(IR_FromProgramVarTest, Emit_GlobalVar_Init) {
auto* expr = Expr(2_u);
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr);
@@ -55,7 +55,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_GlobalVar_GroupBinding) {
+TEST_F(IR_FromProgramVarTest, Emit_GlobalVar_GroupBinding) {
GlobalVar("a", ty.u32(), builtin::AddressSpace::kStorage,
utils::Vector{Group(2_u), Binding(3_u)});
@@ -70,7 +70,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_Var_NoInit) {
+TEST_F(IR_FromProgramVarTest, Emit_Var_NoInit) {
auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction);
WrapInFunction(a);
@@ -87,7 +87,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_Var_Init_Constant) {
+TEST_F(IR_FromProgramVarTest, Emit_Var_Init_Constant) {
auto* expr = Expr(2_u);
auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction, expr);
WrapInFunction(a);
@@ -105,7 +105,7 @@
)");
}
-TEST_F(IR_BuilderImplTest, Emit_Var_Init_NonConstant) {
+TEST_F(IR_FromProgramVarTest, Emit_Var_Init_NonConstant) {
auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction);
auto* b = Var("b", ty.u32(), builtin::AddressSpace::kFunction, Add("a", 2_u));
WrapInFunction(a, b);
diff --git a/src/tint/ir/instruction.cc b/src/tint/ir/instruction.cc
index e54b13f..22e3904 100644
--- a/src/tint/ir/instruction.cc
+++ b/src/tint/ir/instruction.cc
@@ -14,6 +14,9 @@
#include "src/tint/ir/instruction.h"
+#include "src/tint/debug.h"
+#include "src/tint/ir/block.h"
+
TINT_INSTANTIATE_TYPEINFO(tint::ir::Instruction);
namespace tint::ir {
@@ -22,4 +25,27 @@
Instruction::~Instruction() = default;
+void Instruction::InsertBefore(Instruction* before) {
+ TINT_ASSERT_OR_RETURN(IR, before);
+ TINT_ASSERT_OR_RETURN(IR, before->Block() != nullptr);
+ before->Block()->InsertBefore(before, this);
+}
+
+void Instruction::InsertAfter(Instruction* after) {
+ TINT_ASSERT_OR_RETURN(IR, after);
+ TINT_ASSERT_OR_RETURN(IR, after->Block() != nullptr);
+ after->Block()->InsertAfter(after, this);
+}
+
+void Instruction::Replace(Instruction* replacement) {
+ TINT_ASSERT_OR_RETURN(IR, replacement);
+ TINT_ASSERT_OR_RETURN(IR, Block() != nullptr);
+ Block()->Replace(this, replacement);
+}
+
+void Instruction::Remove() {
+ TINT_ASSERT_OR_RETURN(IR, Block() != nullptr);
+ Block()->Remove(this);
+}
+
} // namespace tint::ir
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index 13bcc00..a79b3da 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -37,10 +37,26 @@
/// @returns the block that owns this instruction
ir::Block* Block() { return block_; }
-
/// @returns the block that owns this instruction
const ir::Block* Block() const { return block_; }
+ /// Adds the new instruction before the given instruction in the owning block
+ /// @param before the instruction to insert before
+ void InsertBefore(Instruction* before);
+ /// Adds the new instruction after the given instruction in the owning block
+ /// @param after the instruction to insert after
+ void InsertAfter(Instruction* after);
+ /// Replaces this instruction with @p replacement in the owning block owning this instruction
+ /// @param replacement the instruction to replace with
+ void Replace(Instruction* replacement);
+ /// Removes this instruction from the owning block
+ void Remove();
+
+ /// Pointer to the next instruction in the list
+ Instruction* next = nullptr;
+ /// Pointer to the previous instruction in the list
+ Instruction* prev = nullptr;
+
protected:
/// Constructor
Instruction();
diff --git a/src/tint/ir/instruction_test.cc b/src/tint/ir/instruction_test.cc
new file mode 100644
index 0000000..3fb8ab0
--- /dev/null
+++ b/src/tint/ir/instruction_test.cc
@@ -0,0 +1,162 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
+#include "src/tint/ir/block.h"
+#include "src/tint/ir/builder.h"
+#include "src/tint/ir/module.h"
+
+namespace tint::ir {
+namespace {
+
+class IR_InstructionTest : public ::testing::Test {
+ public:
+ Module mod;
+ Builder b{mod};
+};
+
+TEST_F(IR_InstructionTest, InsertBefore) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst2);
+ inst1->InsertBefore(inst2);
+ EXPECT_EQ(2u, blk->Length());
+ EXPECT_EQ(inst1->Block(), blk);
+}
+
+TEST_F(IR_InstructionTest, Fail_InsertBeforeNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ inst1->InsertBefore(nullptr);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, Fail_InsertBeforeNotInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ inst1->InsertBefore(inst2);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, InsertAfter) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst2);
+ inst1->InsertAfter(inst2);
+ EXPECT_EQ(2u, blk->Length());
+ EXPECT_EQ(inst1->Block(), blk);
+}
+
+TEST_F(IR_InstructionTest, Fail_InsertAfterNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ inst1->InsertAfter(nullptr);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, Fail_InsertAfterNotInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ inst1->InsertAfter(inst2);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, Replace) {
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst2);
+ inst2->Replace(inst1);
+ EXPECT_EQ(1u, blk->Length());
+ EXPECT_EQ(inst1->Block(), blk);
+ EXPECT_EQ(inst2->Block(), nullptr);
+}
+
+TEST_F(IR_InstructionTest, Fail_ReplaceNullptr) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ inst1->Replace(nullptr);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, Fail_ReplaceNotInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ auto* inst2 = b.CreateLoop();
+ inst1->Replace(inst2);
+ },
+ "");
+}
+
+TEST_F(IR_InstructionTest, Remove) {
+ auto* inst1 = b.CreateLoop();
+ auto* blk = b.CreateBlock();
+ blk->Append(inst1);
+ EXPECT_EQ(1u, blk->Length());
+
+ inst1->Remove();
+ EXPECT_EQ(0u, blk->Length());
+ EXPECT_EQ(inst1->Block(), nullptr);
+}
+
+TEST_F(IR_InstructionTest, Fail_RemoveNotInserted) {
+ EXPECT_FATAL_FAILURE(
+ {
+ Module mod;
+ Builder b{mod};
+
+ auto* inst1 = b.CreateLoop();
+ inst1->Remove();
+ },
+ "");
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/load_test.cc b/src/tint/ir/load_test.cc
index 72359e7..02bc65b 100644
--- a/src/tint/ir/load_test.cc
+++ b/src/tint/ir/load_test.cc
@@ -21,9 +21,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_InstructionTest = TestHelper;
+using IR_LoadTest = TestHelper;
-TEST_F(IR_InstructionTest, CreateLoad) {
+TEST_F(IR_LoadTest, CreateLoad) {
Module mod;
Builder b{mod};
@@ -41,7 +41,7 @@
EXPECT_EQ(inst->From(), var);
}
-TEST_F(IR_InstructionTest, Load_Usage) {
+TEST_F(IR_LoadTest, Load_Usage) {
Module mod;
Builder b{mod};
diff --git a/src/tint/ir/loop.cc b/src/tint/ir/loop.cc
index 5b3e603..06764c8 100644
--- a/src/tint/ir/loop.cc
+++ b/src/tint/ir/loop.cc
@@ -28,6 +28,8 @@
TINT_ASSERT(IR, body_);
TINT_ASSERT(IR, continuing_);
TINT_ASSERT(IR, merge_);
+
+ body_->AddInboundBranch(this);
}
Loop::~Loop() = default;
diff --git a/src/tint/ir/store_test.cc b/src/tint/ir/store_test.cc
index 1906cc6..bc68a98 100644
--- a/src/tint/ir/store_test.cc
+++ b/src/tint/ir/store_test.cc
@@ -21,9 +21,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_InstructionTest = TestHelper;
+using IR_StoreTest = TestHelper;
-TEST_F(IR_InstructionTest, CreateStore) {
+TEST_F(IR_StoreTest, CreateStore) {
Module mod;
Builder b{mod};
@@ -41,7 +41,7 @@
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, Store_Usage) {
+TEST_F(IR_StoreTest, Store_Usage) {
Module mod;
Builder b{mod};
diff --git a/src/tint/ir/to_program.cc b/src/tint/ir/to_program.cc
index 08436f3..33c9674 100644
--- a/src/tint/ir/to_program.cc
+++ b/src/tint/ir/to_program.cc
@@ -122,7 +122,7 @@
while (block) {
TINT_ASSERT(IR, block->HasBranchTarget());
- for (auto* inst : block->Instructions()) {
+ for (auto* inst : *block) {
auto stmt = Stmt(inst);
if (TINT_UNLIKELY(!stmt)) {
return nullptr;
@@ -157,14 +157,14 @@
}
auto* false_blk = i->False();
- if (false_blk->Instructions().Length() > 1 ||
- (false_blk->Instructions().Length() == 1 && false_blk->HasBranchTarget() &&
- !false_blk->Branch()->Is<ir::ExitIf>())) {
+ if (false_blk->Length() > 1 || (false_blk->Length() == 1 && false_blk->HasBranchTarget() &&
+ !false_blk->Branch()->Is<ir::ExitIf>())) {
// If the else target is an `if` which has a merge target that just bounces to the outer
// if merge target then emit an 'else if' instead of a block statement for the else.
- if (auto* inst = i->False()->Instructions().Front()->As<ir::If>()) {
- if (auto* br = inst->Merge()->Branch()->As<ir::ExitIf>(); br && br->If() == i) {
- auto* f = If(inst);
+ if (auto* inst = i->False()->Instructions(); inst && inst->As<ir::If>()) {
+ auto* if_ = inst->As<ir::If>();
+ if (auto* br = if_->Merge()->Branch()->As<ir::ExitIf>(); br && br->If() == i) {
+ auto* f = If(if_);
if (!f) {
return nullptr;
}
diff --git a/src/tint/ir/unary_test.cc b/src/tint/ir/unary_test.cc
index 92ad24c..d6e5d50 100644
--- a/src/tint/ir/unary_test.cc
+++ b/src/tint/ir/unary_test.cc
@@ -21,9 +21,9 @@
using namespace tint::number_suffixes; // NOLINT
-using IR_InstructionTest = TestHelper;
+using IR_UnaryTest = TestHelper;
-TEST_F(IR_InstructionTest, CreateComplement) {
+TEST_F(IR_UnaryTest, CreateComplement) {
Module mod;
Builder b{mod};
auto* inst = b.Complement(mod.Types().i32(), b.Constant(4_i));
@@ -37,7 +37,7 @@
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, CreateNegation) {
+TEST_F(IR_UnaryTest, CreateNegation) {
Module mod;
Builder b{mod};
auto* inst = b.Negation(mod.Types().i32(), b.Constant(4_i));
@@ -51,7 +51,7 @@
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
}
-TEST_F(IR_InstructionTest, Unary_Usage) {
+TEST_F(IR_UnaryTest, Unary_Usage) {
Module mod;
Builder b{mod};
auto* inst = b.Negation(mod.Types().i32(), b.Constant(4_i));
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 4ed4786..304a545 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -994,12 +994,11 @@
// Consume continuing codepoint
advance(n);
- }
- if (at(start) == '_') {
- // Check for an underscore on its own (special token), or a
- // double-underscore (not allowed).
- if ((pos() == start + 1) || (at(start + 1) == '_')) {
+ if (pos() - start == 2 && substr(start, 2) == "__") {
+ // Identifiers prefixed with two or more underscores are not allowed.
+ // We check for these in the loop to bail early and prevent quadratic parse time for
+ // long sequences of ____.
set_pos(start);
return {};
}
@@ -1008,8 +1007,8 @@
auto str = substr(start, pos() - start);
end_source(source);
- if (auto t = check_keyword(source, str); t.has_value() && !t->IsUninitialized()) {
- return t;
+ if (auto t = parse_keyword(str); t.has_value()) {
+ return Token{t.value(), source, str};
}
return Token{Token::Type::kIdentifier, source, str};
@@ -1194,92 +1193,95 @@
return Token{type, source};
}
-std::optional<Token> Lexer::check_keyword(const Source& source, std::string_view str) {
+std::optional<Token::Type> Lexer::parse_keyword(std::string_view str) {
if (str == "alias") {
- return Token{Token::Type::kAlias, source, "alias"};
+ return Token::Type::kAlias;
}
if (str == "bitcast") {
- return Token{Token::Type::kBitcast, source, "bitcast"};
+ return Token::Type::kBitcast;
}
if (str == "break") {
- return Token{Token::Type::kBreak, source, "break"};
+ return Token::Type::kBreak;
}
if (str == "case") {
- return Token{Token::Type::kCase, source, "case"};
+ return Token::Type::kCase;
}
if (str == "const") {
- return Token{Token::Type::kConst, source, "const"};
+ return Token::Type::kConst;
}
if (str == "const_assert") {
- return Token{Token::Type::kConstAssert, source, "const_assert"};
+ return Token::Type::kConstAssert;
}
if (str == "continue") {
- return Token{Token::Type::kContinue, source, "continue"};
+ return Token::Type::kContinue;
}
if (str == "continuing") {
- return Token{Token::Type::kContinuing, source, "continuing"};
+ return Token::Type::kContinuing;
}
if (str == "diagnostic") {
- return Token{Token::Type::kDiagnostic, source, "diagnostic"};
+ return Token::Type::kDiagnostic;
}
if (str == "discard") {
- return Token{Token::Type::kDiscard, source, "discard"};
+ return Token::Type::kDiscard;
}
if (str == "default") {
- return Token{Token::Type::kDefault, source, "default"};
+ return Token::Type::kDefault;
}
if (str == "else") {
- return Token{Token::Type::kElse, source, "else"};
+ return Token::Type::kElse;
}
if (str == "enable") {
- return Token{Token::Type::kEnable, source, "enable"};
+ return Token::Type::kEnable;
}
if (str == "fallthrough") {
- return Token{Token::Type::kFallthrough, source, "fallthrough"};
+ return Token::Type::kFallthrough;
}
if (str == "false") {
- return Token{Token::Type::kFalse, source, "false"};
+ return Token::Type::kFalse;
}
if (str == "fn") {
- return Token{Token::Type::kFn, source, "fn"};
+ return Token::Type::kFn;
}
if (str == "for") {
- return Token{Token::Type::kFor, source, "for"};
+ return Token::Type::kFor;
}
if (str == "if") {
- return Token{Token::Type::kIf, source, "if"};
+ return Token::Type::kIf;
}
if (str == "let") {
- return Token{Token::Type::kLet, source, "let"};
+ return Token::Type::kLet;
}
if (str == "loop") {
- return Token{Token::Type::kLoop, source, "loop"};
+ return Token::Type::kLoop;
}
if (str == "override") {
- return Token{Token::Type::kOverride, source, "override"};
+ return Token::Type::kOverride;
}
if (str == "return") {
- return Token{Token::Type::kReturn, source, "return"};
+ return Token::Type::kReturn;
}
if (str == "requires") {
- return Token{Token::Type::kRequires, source, "requires"};
+ return Token::Type::kRequires;
}
if (str == "struct") {
- return Token{Token::Type::kStruct, source, "struct"};
+ return Token::Type::kStruct;
}
if (str == "switch") {
- return Token{Token::Type::kSwitch, source, "switch"};
+ return Token::Type::kSwitch;
}
if (str == "true") {
- return Token{Token::Type::kTrue, source, "true"};
+ return Token::Type::kTrue;
}
if (str == "var") {
- return Token{Token::Type::kVar, source, "var"};
+ return Token::Type::kVar;
}
if (str == "while") {
- return Token{Token::Type::kWhile, source, "while"};
+ return Token::Type::kWhile;
}
- return {};
+ if (str == "_") {
+ return Token::Type::kUnderscore;
+ }
+ return std::nullopt;
}
} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/lexer.h b/src/tint/reader/wgsl/lexer.h
index e56a79a..e0231d8 100644
--- a/src/tint/reader/wgsl/lexer.h
+++ b/src/tint/reader/wgsl/lexer.h
@@ -53,7 +53,7 @@
size_t prefix_count,
int32_t base);
- std::optional<Token> check_keyword(const Source&, std::string_view);
+ std::optional<Token::Type> parse_keyword(std::string_view);
/// The try_* methods have the following in common:
/// - They assume there is at least one character to be consumed,
diff --git a/src/tint/type/manager.cc b/src/tint/type/manager.cc
index 4a98045..136f910 100644
--- a/src/tint/type/manager.cc
+++ b/src/tint/type/manager.cc
@@ -128,15 +128,18 @@
const type::Array* Manager::array(const type::Type* elem_ty,
uint32_t count,
uint32_t stride /* = 0*/) {
+ uint32_t implicit_stride = utils::RoundUp(elem_ty->Align(), elem_ty->Size());
if (stride == 0) {
- stride = elem_ty->Align();
+ stride = implicit_stride;
}
+ TINT_ASSERT(Type, stride >= implicit_stride);
+
return Get<type::Array>(/* element type */ elem_ty,
/* element count */ Get<ConstantArrayCount>(count),
/* array alignment */ elem_ty->Align(),
/* array size */ count * stride,
/* element stride */ stride,
- /* implicit stride */ elem_ty->Align());
+ /* implicit stride */ implicit_stride);
}
const type::Array* Manager::runtime_array(const type::Type* elem_ty, uint32_t stride /* = 0 */) {
diff --git a/src/tint/writer/spirv/builder_assign_test.cc b/src/tint/writer/spirv/builder_assign_test.cc
index cab0ed4..e37563a 100644
--- a/src/tint/writer/spirv/builder_assign_test.cc
+++ b/src/tint/writer/spirv/builder_assign_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
#include "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"
@@ -52,23 +53,23 @@
}
TEST_F(BuilderTest, Assign_Var_OutsideFunction_IsError) {
- auto* v = GlobalVar("var", ty.f32(), builtin::AddressSpace::kPrivate);
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder pb;
- auto* assign = Assign("var", Expr(1_f));
+ auto* v = pb.GlobalVar("var", pb.ty.f32(), builtin::AddressSpace::kPrivate);
- WrapInFunction(assign);
+ auto* assign = pb.Assign("var", pb.Expr(1_f));
- spirv::Builder& b = Build();
+ pb.WrapInFunction(assign);
- EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.Diagnostics();
- ASSERT_FALSE(b.has_error()) << b.Diagnostics();
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- tint::SetInternalCompilerErrorReporter(nullptr);
-
- EXPECT_FALSE(b.GenerateAssignStatement(assign)) << b.Diagnostics();
- EXPECT_TRUE(b.has_error());
- EXPECT_THAT(b.Diagnostics().str(),
- ::testing::HasSubstr("trying to add SPIR-V instruction 62 outside a function"));
+ b->GenerateGlobalVariable(v);
+ b->GenerateAssignStatement(assign);
+ },
+ "trying to add SPIR-V instruction 62 outside a function");
}
TEST_F(BuilderTest, Assign_Var_ZeroInitializer) {
diff --git a/src/tint/writer/spirv/builder_builtin_texture_test.cc b/src/tint/writer/spirv/builder_builtin_texture_test.cc
index 9d45519..6709f35 100644
--- a/src/tint/writer/spirv/builder_builtin_texture_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_texture_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
#include "src/tint/ast/builtin_texture_helper_test.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/stage_attribute.h"
@@ -3761,34 +3762,38 @@
Validate(b);
}
-TEST_P(BuiltinTextureTest, OutsideFunction_IsError) {
- auto param = GetParam();
+// TODO(dsinclair): This generates two fatal errors, but expect_fatal_failure can only handle 1
+TEST_P(BuiltinTextureTest, DISABLED_OutsideFunction_IsError) {
+ EXPECT_FATAL_FAILURE(
+ {
+ auto param = GetParam();
- // The point of this test is to try to generate the texture
- // builtin call outside a function.
+ // The point of this test is to try to generate the texture
+ // builtin call outside a function.
- auto* texture = param.BuildTextureVariable(this);
- auto* sampler = param.BuildSamplerVariable(this);
+ ProgramBuilder pb;
- auto* call = Call(param.function, param.args(this));
- auto* stmt = param.returns_value ? static_cast<const ast::Statement*>(Assign(Phony(), call))
- : static_cast<const ast::Statement*>(CallStmt(call));
+ auto* texture = param.BuildTextureVariable(&pb);
+ auto* sampler = param.BuildSamplerVariable(&pb);
- Func("func", utils::Empty, ty.void_(), utils::Vector{stmt},
- utils::Vector{
- Stage(ast::PipelineStage::kFragment),
- });
+ auto* call = pb.Call(param.function, param.args(&pb));
+ auto* stmt = param.returns_value
+ ? static_cast<const ast::Statement*>(pb.Assign(pb.Phony(), call))
+ : static_cast<const ast::Statement*>(pb.CallStmt(call));
- spirv::Builder& b = Build();
+ pb.Func("func", utils::Empty, pb.ty.void_(), utils::Vector{stmt},
+ utils::Vector{
+ pb.Stage(ast::PipelineStage::kFragment),
+ });
- tint::SetInternalCompilerErrorReporter(nullptr);
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- ASSERT_TRUE(b.GenerateGlobalVariable(texture)) << b.Diagnostics();
- ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.Diagnostics();
- EXPECT_EQ(b.GenerateExpression(call), 0u);
- EXPECT_THAT(b.Diagnostics().str(),
- ::testing::HasSubstr("Internal error: trying to add SPIR-V instruction "));
- EXPECT_THAT(b.Diagnostics().str(), ::testing::HasSubstr(" outside a function"));
+ b->GenerateGlobalVariable(texture);
+ b->GenerateGlobalVariable(sampler);
+ b->GenerateExpression(call);
+ },
+ "Internal error: trying to add SPIR-V instruction ");
}
} // namespace
diff --git a/src/tint/writer/spirv/builder_function_attribute_test.cc b/src/tint/writer/spirv/builder_function_attribute_test.cc
index c215f21..c47429b 100644
--- a/src/tint/writer/spirv/builder_function_attribute_test.cc
+++ b/src/tint/writer/spirv/builder_function_attribute_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
#include "src/tint/ast/stage_attribute.h"
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/writer/spirv/spv_dump.h"
@@ -151,44 +152,44 @@
}
TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_OverridableConst) {
- Override("width", ty.i32(), Call<i32>(2_i), Id(7_u));
- Override("height", ty.i32(), Call<i32>(3_i), Id(8_u));
- Override("depth", ty.i32(), Call<i32>(4_i), Id(9_u));
- auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
- utils::Vector{
- WorkgroupSize("width", "height", "depth"),
- Stage(ast::PipelineStage::kCompute),
- });
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder pb;
+ pb.Override("width", pb.ty.i32(), pb.Call<i32>(2_i), pb.Id(7_u));
+ pb.Override("height", pb.ty.i32(), pb.Call<i32>(3_i), pb.Id(8_u));
+ pb.Override("depth", pb.ty.i32(), pb.Call<i32>(4_i), pb.Id(9_u));
+ auto* func = pb.Func("main", utils::Empty, pb.ty.void_(), utils::Empty,
+ utils::Vector{
+ pb.WorkgroupSize("width", "height", "depth"),
+ pb.Stage(ast::PipelineStage::kCompute),
+ });
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- spirv::Builder& b = Build();
-
- tint::SetInternalCompilerErrorReporter(nullptr);
-
- EXPECT_FALSE(b.GenerateExecutionModes(func, 3)) << b.Diagnostics();
- EXPECT_THAT(
- b.Diagnostics().str(),
- ::testing::HasSubstr(
- "override-expressions should have been removed with the SubstituteOverride transform"));
+ b->GenerateExecutionModes(func, 3);
+ },
+ "override-expressions should have been removed with the SubstituteOverride transform");
}
TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
- Override("height", ty.i32(), Call<i32>(2_i), Id(7_u));
- GlobalConst("depth", ty.i32(), Call<i32>(3_i));
- auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
- utils::Vector{
- WorkgroupSize(4_i, "height", "depth"),
- Stage(ast::PipelineStage::kCompute),
- });
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder pb;
- spirv::Builder& b = Build();
+ pb.Override("height", pb.ty.i32(), pb.Call<i32>(2_i), pb.Id(7_u));
+ pb.GlobalConst("depth", pb.ty.i32(), pb.Call<i32>(3_i));
+ auto* func = pb.Func("main", utils::Empty, pb.ty.void_(), utils::Empty,
+ utils::Vector{
+ pb.WorkgroupSize(4_i, "height", "depth"),
+ pb.Stage(ast::PipelineStage::kCompute),
+ });
- tint::SetInternalCompilerErrorReporter(nullptr);
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- EXPECT_FALSE(b.GenerateExecutionModes(func, 3)) << b.Diagnostics();
- EXPECT_THAT(
- b.Diagnostics().str(),
- ::testing::HasSubstr(
- "override-expressions should have been removed with the SubstituteOverride transform"));
+ b->GenerateExecutionModes(func, 3);
+ },
+ "override-expressions should have been removed with the SubstituteOverride transform");
}
TEST_F(BuilderTest, Decoration_ExecutionMode_MultipleFragment) {
diff --git a/src/tint/writer/spirv/builder_ident_expression_test.cc b/src/tint/writer/spirv/builder_ident_expression_test.cc
index 3917028..5ab7c19 100644
--- a/src/tint/writer/spirv/builder_ident_expression_test.cc
+++ b/src/tint/writer/spirv/builder_ident_expression_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "gtest/gtest-spi.h"
#include "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"
@@ -23,21 +24,24 @@
using BuilderTest = TestHelper;
TEST_F(BuilderTest, IdentifierExpression_GlobalConst) {
- auto* init = vec3<f32>(1_f, 1_f, 3_f);
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder pb;
- auto* v = GlobalConst("c", ty.vec3<f32>(), init);
+ auto* init = pb.vec3<f32>(1_f, 1_f, 3_f);
- auto* expr = Expr("c");
- WrapInFunction(expr);
+ auto* v = pb.GlobalConst("c", pb.ty.vec3<f32>(), init);
- spirv::Builder& b = Build();
+ auto* expr = pb.Expr("c");
+ pb.WrapInFunction(expr);
- EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.Diagnostics();
- ASSERT_FALSE(b.has_error()) << b.Diagnostics();
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- EXPECT_EQ(DumpInstructions(b.Module().Types()), R"()");
-
- EXPECT_EQ(b.GenerateIdentifierExpression(expr), 0u);
+ b->GenerateGlobalVariable(v);
+ b->GenerateIdentifierExpression(expr);
+ },
+ "internal compiler error: unable to find ID for variable: c");
}
TEST_F(BuilderTest, IdentifierExpression_GlobalVar) {
diff --git a/src/tint/writer/spirv/builder_if_test.cc b/src/tint/writer/spirv/builder_if_test.cc
index 3bd4bb2..fb34a68 100644
--- a/src/tint/writer/spirv/builder_if_test.cc
+++ b/src/tint/writer/spirv/builder_if_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
#include "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"
@@ -51,19 +52,20 @@
// if (true) {
// }
- auto* block = Block();
- auto* expr = If(true, block);
- WrapInFunction(expr);
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder pb;
- spirv::Builder& b = Build();
+ auto* block = pb.Block();
+ auto* expr = pb.If(true, block);
+ pb.WrapInFunction(expr);
- tint::SetInternalCompilerErrorReporter(nullptr);
+ auto program = std::make_unique<Program>(std::move(pb));
+ auto b = std::make_unique<spirv::Builder>(program.get());
- EXPECT_FALSE(b.GenerateIfStatement(expr)) << b.Diagnostics();
- EXPECT_TRUE(b.has_error());
- EXPECT_THAT(b.Diagnostics().str(),
- ::testing::HasSubstr(
- "Internal error: trying to add SPIR-V instruction 247 outside a function"));
+ b->GenerateIfStatement(expr);
+ },
+ "Internal error: trying to add SPIR-V instruction 247 outside a function");
}
TEST_F(BuilderTest, If_WithStatements) {
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 8ae01c8..89e6a1f 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -47,6 +47,7 @@
#include "src/tint/type/i32.h"
#include "src/tint/type/matrix.h"
#include "src/tint/type/pointer.h"
+#include "src/tint/type/struct.h"
#include "src/tint/type/type.h"
#include "src/tint/type/u32.h"
#include "src/tint/type/vector.h"
@@ -225,6 +226,7 @@
spv::Op::OpTypePointer,
{id, U32Operand(StorageClass(ptr->AddressSpace())), Type(ptr->StoreType())});
},
+ [&](const type::Struct* str) { EmitStructType(id, str); },
[&](Default) {
TINT_ICE(Writer, diagnostics_) << "unhandled type: " << ty->FriendlyName();
});
@@ -237,12 +239,7 @@
value, //
[&](const ir::Constant* constant) { return Constant(constant); },
[&](const ir::Value*) {
- auto id = values_.Find(value);
- if (TINT_UNLIKELY(!id)) {
- TINT_ICE(Writer, diagnostics_) << "missing result ID for value";
- return 0u;
- }
- return *id;
+ return values_.GetOrCreate(value, [&] { return module_.NextId(); });
});
}
@@ -250,10 +247,49 @@
return block_labels_.GetOrCreate(block, [&]() { return module_.NextId(); });
}
+void GeneratorImplIr::EmitStructType(uint32_t id, const type::Struct* str) {
+ // Helper to return `type` or a potentially nested array element type within `type` as a matrix
+ // type, or nullptr if no such matrix type is present.
+ auto get_nested_matrix_type = [&](const type::Type* type) {
+ while (auto* arr = type->As<type::Array>()) {
+ type = arr->ElemType();
+ }
+ return type->As<type::Matrix>();
+ };
+
+ OperandList operands = {id};
+ for (auto* member : str->Members()) {
+ operands.push_back(Type(member->Type()));
+
+ // Generate struct member offset decoration.
+ module_.PushAnnot(
+ spv::Op::OpMemberDecorate,
+ {operands[0], member->Index(), U32Operand(SpvDecorationOffset), member->Offset()});
+
+ // Emit matrix layout decorations if necessary.
+ if (auto* matrix_type = get_nested_matrix_type(member->Type())) {
+ const uint32_t effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
+ module_.PushAnnot(spv::Op::OpMemberDecorate,
+ {id, member->Index(), U32Operand(SpvDecorationColMajor)});
+ module_.PushAnnot(spv::Op::OpMemberDecorate,
+ {id, member->Index(), U32Operand(SpvDecorationMatrixStride),
+ Operand(effective_row_count * matrix_type->type()->Size())});
+ }
+
+ if (member->Name().IsValid()) {
+ module_.PushDebug(spv::Op::OpMemberName,
+ {operands[0], member->Index(), Operand(member->Name().Name())});
+ }
+ }
+ module_.PushType(spv::Op::OpTypeStruct, std::move(operands));
+
+ if (str->Name().IsValid()) {
+ module_.PushDebug(spv::Op::OpName, {operands[0], Operand(str->Name().Name())});
+ }
+}
+
void GeneratorImplIr::EmitFunction(const ir::Function* func) {
- // Make an ID for the function.
- auto id = module_.NextId();
- values_.Add(func, id);
+ auto id = Value(func);
// Emit the function name.
module_.PushDebug(spv::Op::OpName, {id, Operand(ir_->NameOf(func).Name())});
@@ -272,9 +308,8 @@
// Generate function parameter declarations and add their type IDs to the function signature.
for (auto* param : func->Params()) {
auto param_type_id = Type(param->Type());
- auto param_id = module_.NextId();
+ auto param_id = Value(param);
params.push_back(Instruction(spv::Op::OpFunctionParameter, {param_type_id, param_id}));
- values_.Add(param, param_id);
function_type.param_type_ids.Push(param_type_id);
if (auto name = ir_->NameOf(param)) {
module_.PushDebug(spv::Op::OpName, {param_id, Operand(name.Name())});
@@ -341,16 +376,14 @@
}
void GeneratorImplIr::EmitRootBlock(const ir::Block* root_block) {
- for (auto* inst : root_block->Instructions()) {
- auto result = Switch(
+ for (auto* inst : *root_block) {
+ Switch(
inst, //
[&](const ir::Var* v) { return EmitVar(v); },
[&](Default) {
TINT_ICE(Writer, diagnostics_)
<< "unimplemented root block instruction: " << inst->TypeInfo().name;
- return 0u;
});
- values_.Add(inst, result);
}
}
@@ -363,7 +396,7 @@
// If there are no instructions in the block, it's a dead end, so we shouldn't be able to get
// here to begin with.
- if (block->Instructions().IsEmpty()) {
+ if (block->IsEmpty()) {
current_function_.push_inst(spv::Op::OpUnreachable, {});
return;
}
@@ -371,9 +404,7 @@
// Emit Phi nodes for all the incoming block parameters
for (size_t param_idx = 0; param_idx < block->Params().Length(); param_idx++) {
auto* param = block->Params()[param_idx];
- auto id = module_.NextId();
- values_.Add(param, id);
- OperandList ops{Type(param->Type()), id};
+ OperandList ops{Type(param->Type()), Value(param)};
for (auto* incoming : block->InboundBranches()) {
auto* arg = incoming->Args()[param_idx];
@@ -385,40 +416,23 @@
}
// Emit the instructions.
- for (auto* inst : block->Instructions()) {
- auto result = Switch(
- inst, //
- [&](const ir::Binary* b) { return EmitBinary(b); },
- [&](const ir::Builtin* b) { return EmitBuiltin(b); },
- [&](const ir::Load* l) { return EmitLoad(l); },
- [&](const ir::Loop* l) {
- EmitLoop(l);
- return 0u;
- },
- [&](const ir::Switch* sw) {
- EmitSwitch(sw);
- return 0u;
- },
- [&](const ir::Store* s) {
- EmitStore(s);
- return 0u;
- },
- [&](const ir::UserCall* c) { return EmitUserCall(c); },
- [&](const ir::Var* v) { return EmitVar(v); },
- [&](const ir::If* i) {
- EmitIf(i);
- return 0u;
- },
- [&](const ir::Branch* b) {
- EmitBranch(b);
- return 0u;
- },
+ for (auto* inst : *block) {
+ Switch(
+ inst, //
+ [&](const ir::Binary* b) { EmitBinary(b); }, //
+ [&](const ir::Builtin* b) { EmitBuiltin(b); }, //
+ [&](const ir::Load* l) { EmitLoad(l); }, //
+ [&](const ir::Loop* l) { EmitLoop(l); }, //
+ [&](const ir::Switch* sw) { EmitSwitch(sw); }, //
+ [&](const ir::Store* s) { EmitStore(s); }, //
+ [&](const ir::UserCall* c) { EmitUserCall(c); }, //
+ [&](const ir::Var* v) { EmitVar(v); }, //
+ [&](const ir::If* i) { EmitIf(i); }, //
+ [&](const ir::Branch* b) { EmitBranch(b); }, //
[&](Default) {
TINT_ICE(Writer, diagnostics_)
<< "unimplemented instruction: " << inst->TypeInfo().name;
- return 0u;
});
- values_.Add(inst, result);
}
}
@@ -477,11 +491,11 @@
uint32_t merge_label = Label(merge_block);
uint32_t true_label = merge_label;
uint32_t false_label = merge_label;
- if (true_block->Instructions().Length() > 1 || !merge_block->Params().IsEmpty() ||
+ if (true_block->Length() > 1 || !merge_block->Params().IsEmpty() ||
(true_block->HasBranchTarget() && !true_block->Branch()->Is<ir::ExitIf>())) {
true_label = Label(true_block);
}
- if (false_block->Instructions().Length() > 1 || !merge_block->Params().IsEmpty() ||
+ if (false_block->Length() > 1 || !merge_block->Params().IsEmpty() ||
(false_block->HasBranchTarget() && !false_block->Branch()->Is<ir::ExitIf>())) {
false_label = Label(false_block);
}
@@ -504,8 +518,8 @@
EmitBlock(merge_block);
}
-uint32_t GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
- auto id = module_.NextId();
+void GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
+ auto id = Value(binary);
auto* lhs_ty = binary->LHS()->Type();
// Determine the opcode.
@@ -603,14 +617,20 @@
// Emit the instruction.
current_function_.push_inst(
op, {Type(binary->Type()), id, Value(binary->LHS()), Value(binary->RHS())});
-
- return id;
}
-uint32_t GeneratorImplIr::EmitBuiltin(const ir::Builtin* builtin) {
- auto id = module_.NextId();
+void GeneratorImplIr::EmitBuiltin(const ir::Builtin* builtin) {
auto* result_ty = builtin->Type();
+ if (builtin->Func() == builtin::Function::kAbs &&
+ result_ty->is_unsigned_integer_scalar_or_vector()) {
+ // abs() is a no-op for unsigned integers.
+ values_.Add(builtin, Value(builtin->Args()[0]));
+ return;
+ }
+
+ auto id = Value(builtin);
+
spv::Op op = spv::Op::Max;
OperandList operands = {Type(result_ty), id};
@@ -634,9 +654,6 @@
glsl_ext_inst(GLSLstd450FAbs);
} else if (result_ty->is_signed_integer_scalar_or_vector()) {
glsl_ext_inst(GLSLstd450SAbs);
- } else if (result_ty->is_unsigned_integer_scalar_or_vector()) {
- // abs() is a no-op for unsigned integers.
- return Value(builtin->Args()[0]);
}
break;
case builtin::Function::kMax:
@@ -659,7 +676,6 @@
break;
default:
TINT_ICE(Writer, diagnostics_) << "unimplemented builtin function: " << builtin->Func();
- return 0u;
}
TINT_ASSERT(Writer, op != spv::Op::Max);
@@ -670,14 +686,11 @@
// Emit the instruction.
current_function_.push_inst(op, operands);
-
- return id;
}
-uint32_t GeneratorImplIr::EmitLoad(const ir::Load* load) {
- auto id = module_.NextId();
- current_function_.push_inst(spv::Op::OpLoad, {Type(load->Type()), id, Value(load->From())});
- return id;
+void GeneratorImplIr::EmitLoad(const ir::Load* load) {
+ current_function_.push_inst(spv::Op::OpLoad,
+ {Type(load->Type()), Value(load), Value(load->From())});
}
void GeneratorImplIr::EmitLoop(const ir::Loop* loop) {
@@ -754,18 +767,17 @@
current_function_.push_inst(spv::Op::OpStore, {Value(store->To()), Value(store->From())});
}
-uint32_t GeneratorImplIr::EmitUserCall(const ir::UserCall* call) {
- auto id = module_.NextId();
- OperandList operands = {Type(call->Type()), id, values_.Get(call->Func()).value()};
+void GeneratorImplIr::EmitUserCall(const ir::UserCall* call) {
+ auto id = Value(call);
+ OperandList operands = {Type(call->Type()), id, Value(call->Func())};
for (auto* arg : call->Args()) {
operands.push_back(Value(arg));
}
current_function_.push_inst(spv::Op::OpFunctionCall, operands);
- return id;
}
-uint32_t GeneratorImplIr::EmitVar(const ir::Var* var) {
- auto id = module_.NextId();
+void GeneratorImplIr::EmitVar(const ir::Var* var) {
+ auto id = Value(var);
auto* ptr = var->Type()->As<type::Pointer>();
TINT_ASSERT(Writer, ptr);
auto ty = Type(ptr);
@@ -803,7 +815,6 @@
default: {
TINT_ICE(Writer, diagnostics_)
<< "unimplemented variable address space " << ptr->AddressSpace();
- return 0u;
}
}
@@ -811,8 +822,6 @@
if (auto name = ir_->NameOf(var)) {
module_.PushDebug(spv::Op::OpName, {id, Operand(name.Name())});
}
-
- return id;
}
} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.h b/src/tint/writer/spirv/ir/generator_impl_ir.h
index 5793325..7c3105b 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.h
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.h
@@ -31,6 +31,7 @@
namespace tint::ir {
class Binary;
class Block;
+class BlockParam;
class Branch;
class Builtin;
class If;
@@ -45,6 +46,7 @@
class Var;
} // namespace tint::ir
namespace tint::type {
+class Struct;
class Type;
} // namespace tint::type
@@ -96,6 +98,11 @@
/// @returns the ID of the block's label
uint32_t Label(const ir::Block* block);
+ /// Emit a struct type.
+ /// @param id the result ID to use
+ /// @param str the struct type to emit
+ void EmitStructType(uint32_t id, const type::Struct* str);
+
/// Emit a function.
/// @param func the function to emit
void EmitFunction(const ir::Function* func);
@@ -119,18 +126,15 @@
/// Emit a binary instruction.
/// @param binary the binary instruction to emit
- /// @returns the result ID of the instruction
- uint32_t EmitBinary(const ir::Binary* binary);
+ void EmitBinary(const ir::Binary* binary);
/// Emit a builtin function call instruction.
/// @param call the builtin call instruction to emit
- /// @returns the result ID of the instruction
- uint32_t EmitBuiltin(const ir::Builtin* call);
+ void EmitBuiltin(const ir::Builtin* call);
/// Emit a load instruction.
/// @param load the load instruction to emit
- /// @returns the result ID of the instruction
- uint32_t EmitLoad(const ir::Load* load);
+ void EmitLoad(const ir::Load* load);
/// Emit a loop instruction.
/// @param loop the loop instruction to emit
@@ -146,13 +150,11 @@
/// Emit a user call instruction.
/// @param call the user call instruction to emit
- /// @returns the result ID of the instruction
- uint32_t EmitUserCall(const ir::UserCall* call);
+ void EmitUserCall(const ir::UserCall* call);
/// Emit a var instruction.
/// @param var the var instruction to emit
- /// @returns the result ID of the instruction
- uint32_t EmitVar(const ir::Var* var);
+ void EmitVar(const ir::Var* var);
/// Emit a branch instruction.
/// @param b the branch instruction to emit
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_builtin_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_builtin_test.cc
index f62848e..3bc06c1 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_builtin_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_builtin_test.cc
@@ -76,10 +76,10 @@
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
-%6 = OpConstant %2 1
+%5 = OpConstant %2 1
%1 = OpFunction %2 None %3
%4 = OpLabel
-OpReturnValue %6
+OpReturnValue %5
OpFunctionEnd
)");
}
@@ -95,12 +95,12 @@
%3 = OpTypeInt 32 0
%2 = OpTypeVector %3 2
%4 = OpTypeFunction %2
-%8 = OpConstant %3 42
-%9 = OpConstant %3 10
-%7 = OpConstantComposite %2 %8 %9
+%7 = OpConstant %3 42
+%8 = OpConstant %3 10
+%6 = OpConstantComposite %2 %7 %8
%1 = OpFunction %2 None %4
%5 = OpLabel
-OpReturnValue %7
+OpReturnValue %6
OpFunctionEnd
)");
}
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_if_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_if_test.cc
index 1c9b9ac..8d88852 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_if_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_if_test.cc
@@ -161,9 +161,9 @@
%3 = OpTypeFunction %2
%9 = OpTypeBool
%8 = OpConstantTrue %9
-%11 = OpTypeInt 32 1
-%12 = OpConstant %11 10
-%13 = OpConstant %11 20
+%10 = OpTypeInt 32 1
+%12 = OpConstant %10 10
+%13 = OpConstant %10 20
%1 = OpFunction %2 None %3
%4 = OpLabel
OpSelectionMerge %5 None
@@ -173,8 +173,8 @@
%7 = OpLabel
OpBranch %5
%5 = OpLabel
-%10 = OpPhi %11 %12 %6 %13 %7
-OpReturnValue %10
+%11 = OpPhi %10 %12 %6 %13 %7
+OpReturnValue %11
OpFunctionEnd
)");
}
@@ -277,9 +277,9 @@
%3 = OpTypeFunction %2
%9 = OpTypeBool
%8 = OpConstantTrue %9
-%11 = OpTypeInt 32 1
-%12 = OpConstant %11 10
-%13 = OpConstant %11 20
+%10 = OpTypeInt 32 1
+%12 = OpConstant %10 10
+%13 = OpConstant %10 20
%15 = OpConstantFalse %9
%1 = OpFunction %2 None %3
%4 = OpLabel
@@ -290,9 +290,9 @@
%7 = OpLabel
OpBranch %5
%5 = OpLabel
-%10 = OpPhi %11 %12 %6 %13 %7
+%11 = OpPhi %10 %12 %6 %13 %7
%14 = OpPhi %9 %8 %6 %15 %7
-OpReturnValue %10
+OpReturnValue %11
OpFunctionEnd
)");
}
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_loop_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_loop_test.cc
index a32a751..577d9d2 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_loop_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_loop_test.cc
@@ -24,11 +24,11 @@
auto* loop = b.CreateLoop();
- loop->Body()->AddInstruction(b.Continue(loop));
- loop->Continuing()->AddInstruction(b.BreakIf(b.Constant(true), loop));
- loop->Merge()->AddInstruction(b.Return(func));
+ loop->Body()->Append(b.Continue(loop));
+ loop->Continuing()->Append(b.BreakIf(b.Constant(true), loop));
+ loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -58,10 +58,10 @@
auto* loop = b.CreateLoop();
- loop->Body()->AddInstruction(b.ExitLoop(loop));
- loop->Merge()->AddInstruction(b.Return(func));
+ loop->Body()->Append(b.ExitLoop(loop));
+ loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -89,15 +89,15 @@
auto* loop = b.CreateLoop();
auto* cond_break = b.CreateIf(b.Constant(true));
- cond_break->True()->AddInstruction(b.ExitLoop(loop));
- cond_break->False()->AddInstruction(b.ExitIf(cond_break));
- cond_break->Merge()->AddInstruction(b.Continue(loop));
+ cond_break->True()->Append(b.ExitLoop(loop));
+ cond_break->False()->Append(b.ExitIf(cond_break));
+ cond_break->Merge()->Append(b.Continue(loop));
- loop->Body()->AddInstruction(cond_break);
- loop->Continuing()->AddInstruction(b.NextIteration(loop));
- loop->Merge()->AddInstruction(b.Return(func));
+ loop->Body()->Append(cond_break);
+ loop->Continuing()->Append(b.NextIteration(loop));
+ loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -132,15 +132,15 @@
auto* loop = b.CreateLoop();
auto* cond_break = b.CreateIf(b.Constant(true));
- cond_break->True()->AddInstruction(b.Continue(loop));
- cond_break->False()->AddInstruction(b.ExitIf(cond_break));
- cond_break->Merge()->AddInstruction(b.ExitLoop(loop));
+ cond_break->True()->Append(b.Continue(loop));
+ cond_break->False()->Append(b.ExitIf(cond_break));
+ cond_break->Merge()->Append(b.ExitLoop(loop));
- loop->Body()->AddInstruction(cond_break);
- loop->Continuing()->AddInstruction(b.NextIteration(loop));
- loop->Merge()->AddInstruction(b.Return(func));
+ loop->Body()->Append(cond_break);
+ loop->Continuing()->Append(b.NextIteration(loop));
+ loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -176,9 +176,9 @@
auto* loop = b.CreateLoop();
- loop->Body()->AddInstruction(b.Return(func));
+ loop->Body()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -207,11 +207,11 @@
auto* result = b.Equal(mod.Types().i32(), b.Constant(1_i), b.Constant(2_i));
- loop->Body()->AddInstruction(result);
- loop->Continuing()->AddInstruction(b.BreakIf(result, loop));
- loop->Merge()->AddInstruction(b.Return(func));
+ loop->Body()->Append(result);
+ loop->Continuing()->Append(b.BreakIf(result, loop));
+ loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(loop);
+ func->StartTarget()->Append(loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -242,15 +242,15 @@
auto* outer_loop = b.CreateLoop();
auto* inner_loop = b.CreateLoop();
- inner_loop->Body()->AddInstruction(b.ExitLoop(inner_loop));
- inner_loop->Continuing()->AddInstruction(b.NextIteration(inner_loop));
- inner_loop->Merge()->AddInstruction(b.Continue(outer_loop));
+ inner_loop->Body()->Append(b.ExitLoop(inner_loop));
+ inner_loop->Continuing()->Append(b.NextIteration(inner_loop));
+ inner_loop->Merge()->Append(b.Continue(outer_loop));
- outer_loop->Body()->AddInstruction(inner_loop);
- outer_loop->Continuing()->AddInstruction(b.BreakIf(b.Constant(true), outer_loop));
- outer_loop->Merge()->AddInstruction(b.Return(func));
+ outer_loop->Body()->Append(inner_loop);
+ outer_loop->Continuing()->Append(b.BreakIf(b.Constant(true), outer_loop));
+ outer_loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(outer_loop);
+ func->StartTarget()->Append(outer_loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -289,15 +289,15 @@
auto* outer_loop = b.CreateLoop();
auto* inner_loop = b.CreateLoop();
- inner_loop->Body()->AddInstruction(b.Continue(inner_loop));
- inner_loop->Continuing()->AddInstruction(b.BreakIf(b.Constant(true), inner_loop));
- inner_loop->Merge()->AddInstruction(b.BreakIf(b.Constant(true), outer_loop));
+ inner_loop->Body()->Append(b.Continue(inner_loop));
+ inner_loop->Continuing()->Append(b.BreakIf(b.Constant(true), inner_loop));
+ inner_loop->Merge()->Append(b.BreakIf(b.Constant(true), outer_loop));
- outer_loop->Body()->AddInstruction(b.Continue(outer_loop));
- outer_loop->Continuing()->AddInstruction(inner_loop);
- outer_loop->Merge()->AddInstruction(b.Return(func));
+ outer_loop->Body()->Append(b.Continue(outer_loop));
+ outer_loop->Continuing()->Append(inner_loop);
+ outer_loop->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(outer_loop);
+ func->StartTarget()->Append(outer_loop);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -330,5 +330,105 @@
)");
}
+TEST_F(SpvGeneratorImplTest, Loop_Phi_SingleValue) {
+ auto* func = b.CreateFunction("foo", mod.Types().void_());
+
+ auto* l = b.CreateLoop(utils::Vector{b.Constant(1_i)});
+ func->StartTarget()->Append(l);
+
+ auto* loop_param = b.BlockParam(b.ir.Types().i32());
+ l->Body()->SetParams(utils::Vector{loop_param});
+ auto* inc = b.Add(b.ir.Types().i32(), loop_param, b.Constant(1_i));
+ l->Body()->Append(inc);
+ l->Body()->Append(b.Continue(l, utils::Vector{inc}));
+
+ auto* cont_param = b.BlockParam(b.ir.Types().i32());
+ l->Continuing()->SetParams(utils::Vector{cont_param});
+ auto* cmp = b.GreaterThan(b.ir.Types().bool_(), cont_param, b.Constant(5_i));
+ l->Continuing()->Append(cmp);
+ l->Continuing()->Append(b.BreakIf(cmp, l, utils::Vector{cont_param}));
+
+ generator_.EmitFunction(func);
+ EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%9 = OpTypeInt 32 1
+%11 = OpConstant %9 1
+%16 = OpTypeBool
+%17 = OpConstant %9 5
+%1 = OpFunction %2 None %3
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpLoopMerge %8 %7 None
+OpBranch %6
+%6 = OpLabel
+%10 = OpPhi %9 %11 %12 %13 %7
+%14 = OpIAdd %9 %10 %11
+OpBranch %7
+%7 = OpLabel
+%13 = OpPhi %9 %14 %5
+%15 = OpSGreaterThan %16 %13 %17
+OpBranchConditional %15 %8 %5
+%8 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)");
+}
+
+TEST_F(SpvGeneratorImplTest, Loop_Phi_MultipleValue) {
+ auto* func = b.CreateFunction("foo", mod.Types().void_());
+
+ auto* l = b.CreateLoop(utils::Vector{b.Constant(1_i), b.Constant(false)});
+ func->StartTarget()->Append(l);
+
+ auto* loop_param_a = b.BlockParam(b.ir.Types().i32());
+ auto* loop_param_b = b.BlockParam(b.ir.Types().bool_());
+ l->Body()->SetParams(utils::Vector{loop_param_a, loop_param_b});
+ auto* inc = b.Add(b.ir.Types().i32(), loop_param_a, b.Constant(1_i));
+ l->Body()->Append(inc);
+ l->Body()->Append(b.Continue(l, utils::Vector{inc, loop_param_b}));
+
+ auto* cont_param_a = b.BlockParam(b.ir.Types().i32());
+ auto* cont_param_b = b.BlockParam(b.ir.Types().bool_());
+ l->Continuing()->SetParams(utils::Vector{cont_param_a, cont_param_b});
+ auto* cmp = b.GreaterThan(b.ir.Types().bool_(), cont_param_a, b.Constant(5_i));
+ l->Continuing()->Append(cmp);
+ auto* not_b = b.Not(b.ir.Types().bool_(), cont_param_b);
+ l->Continuing()->Append(not_b);
+ l->Continuing()->Append(b.BreakIf(cmp, l, utils::Vector{cont_param_a, not_b}));
+
+ generator_.EmitFunction(func);
+ EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%9 = OpTypeInt 32 1
+%11 = OpConstant %9 1
+%14 = OpTypeBool
+%16 = OpConstantFalse %14
+%21 = OpConstant %9 5
+%1 = OpFunction %2 None %3
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpLoopMerge %8 %7 None
+OpBranch %6
+%6 = OpLabel
+%10 = OpPhi %9 %11 %12 %13 %7
+%15 = OpPhi %14 %16 %12 %17 %7
+%18 = OpIAdd %9 %10 %11
+OpBranch %7
+%7 = OpLabel
+%13 = OpPhi %9 %18 %5
+%19 = OpPhi %14 %15 %5
+%20 = OpSGreaterThan %14 %13 %21
+%17 = OpLogicalEqual %14 %19 %16
+OpBranchConditional %20 %8 %5
+%8 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)");
+}
+
} // namespace
} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_switch_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_switch_test.cc
index 1c4d5d0..2773ae0 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_switch_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_switch_test.cc
@@ -25,11 +25,11 @@
auto* swtch = b.CreateSwitch(b.Constant(42_i));
auto* def_case = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector()});
- def_case->AddInstruction(b.ExitSwitch(swtch));
+ def_case->Append(b.ExitSwitch(swtch));
- swtch->Merge()->AddInstruction(b.Return(func));
+ swtch->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(swtch);
+ func->StartTarget()->Append(swtch);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -55,17 +55,17 @@
auto* swtch = b.CreateSwitch(b.Constant(42_i));
auto* case_a = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)}});
- case_a->AddInstruction(b.ExitSwitch(swtch));
+ case_a->Append(b.ExitSwitch(swtch));
auto* case_b = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)}});
- case_b->AddInstruction(b.ExitSwitch(swtch));
+ case_b->Append(b.ExitSwitch(swtch));
auto* def_case = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector()});
- def_case->AddInstruction(b.ExitSwitch(swtch));
+ def_case->Append(b.ExitSwitch(swtch));
- swtch->Merge()->AddInstruction(b.Return(func));
+ swtch->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(swtch);
+ func->StartTarget()->Append(swtch);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -96,19 +96,19 @@
auto* case_a = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)},
ir::Switch::CaseSelector{b.Constant(3_i)}});
- case_a->AddInstruction(b.ExitSwitch(swtch));
+ case_a->Append(b.ExitSwitch(swtch));
auto* case_b = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)},
ir::Switch::CaseSelector{b.Constant(4_i)}});
- case_b->AddInstruction(b.ExitSwitch(swtch));
+ case_b->Append(b.ExitSwitch(swtch));
auto* def_case = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(5_i)},
ir::Switch::CaseSelector()});
- def_case->AddInstruction(b.ExitSwitch(swtch));
+ def_case->Append(b.ExitSwitch(swtch));
- swtch->Merge()->AddInstruction(b.Return(func));
+ swtch->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(swtch);
+ func->StartTarget()->Append(swtch);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -138,15 +138,15 @@
auto* swtch = b.CreateSwitch(b.Constant(42_i));
auto* case_a = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)}});
- case_a->AddInstruction(b.Return(func));
+ case_a->Append(b.Return(func));
auto* case_b = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)}});
- case_b->AddInstruction(b.Return(func));
+ case_b->Append(b.Return(func));
auto* def_case = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector()});
- def_case->AddInstruction(b.Return(func));
+ def_case->Append(b.Return(func));
- func->StartTarget()->AddInstruction(swtch);
+ func->StartTarget()->Append(swtch);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -176,19 +176,19 @@
auto* swtch = b.CreateSwitch(b.Constant(42_i));
auto* cond_break = b.CreateIf(b.Constant(true));
- cond_break->True()->AddInstruction(b.ExitSwitch(swtch));
- cond_break->False()->AddInstruction(b.ExitIf(cond_break));
- cond_break->Merge()->AddInstruction(b.Return(func));
+ cond_break->True()->Append(b.ExitSwitch(swtch));
+ cond_break->False()->Append(b.ExitIf(cond_break));
+ cond_break->Merge()->Append(b.Return(func));
auto* case_a = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)}});
- case_a->AddInstruction(cond_break);
+ case_a->Append(cond_break);
auto* def_case = b.CreateCase(swtch, utils::Vector{ir::Switch::CaseSelector()});
- def_case->AddInstruction(b.ExitSwitch(swtch));
+ def_case->Append(b.ExitSwitch(swtch));
- swtch->Merge()->AddInstruction(b.Return(func));
+ swtch->Merge()->Append(b.Return(func));
- func->StartTarget()->AddInstruction(swtch);
+ func->StartTarget()->Append(swtch);
generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@@ -225,13 +225,13 @@
auto* s = b.CreateSwitch(b.Constant(42_i));
auto* case_a = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)},
ir::Switch::CaseSelector{nullptr}});
- case_a->AddInstruction(b.ExitSwitch(s, utils::Vector{b.Constant(10_i)}));
+ case_a->Append(b.ExitSwitch(s, utils::Vector{b.Constant(10_i)}));
auto* case_b = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)}});
- case_b->AddInstruction(b.ExitSwitch(s, utils::Vector{b.Constant(20_i)}));
+ case_b->Append(b.ExitSwitch(s, utils::Vector{b.Constant(20_i)}));
s->Merge()->SetParams(utils::Vector{merge_param});
- s->Merge()->AddInstruction(b.Return(func));
+ s->Merge()->Append(b.Return(func));
func->StartTarget()->SetInstructions(utils::Vector{s});
@@ -264,13 +264,13 @@
auto* s = b.CreateSwitch(b.Constant(42_i));
auto* case_a = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)},
ir::Switch::CaseSelector{nullptr}});
- case_a->AddInstruction(b.Return(func, utils::Vector{b.Constant(10_i)}));
+ case_a->Append(b.Return(func, utils::Vector{b.Constant(10_i)}));
auto* case_b = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)}});
- case_b->AddInstruction(b.ExitSwitch(s, utils::Vector{b.Constant(20_i)}));
+ case_b->Append(b.ExitSwitch(s, utils::Vector{b.Constant(20_i)}));
s->Merge()->SetParams(utils::Vector{b.BlockParam(b.ir.Types().i32())});
- s->Merge()->AddInstruction(b.Return(func));
+ s->Merge()->Append(b.Return(func));
func->StartTarget()->SetInstructions(utils::Vector{s});
@@ -306,13 +306,13 @@
auto* s = b.CreateSwitch(b.Constant(42_i));
auto* case_a = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(1_i)},
ir::Switch::CaseSelector{nullptr}});
- case_a->AddInstruction(b.ExitSwitch(s, utils::Vector{b.Constant(10_i), b.Constant(true)}));
+ case_a->Append(b.ExitSwitch(s, utils::Vector{b.Constant(10_i), b.Constant(true)}));
auto* case_b = b.CreateCase(s, utils::Vector{ir::Switch::CaseSelector{b.Constant(2_i)}});
- case_b->AddInstruction(b.ExitSwitch(s, utils::Vector{b.Constant(20_i), b.Constant(false)}));
+ case_b->Append(b.ExitSwitch(s, utils::Vector{b.Constant(20_i), b.Constant(false)}));
s->Merge()->SetParams(utils::Vector{merge_param_0, merge_param_1});
- s->Merge()->AddInstruction(b.Return(func, utils::Vector{merge_param_0}));
+ s->Merge()->Append(b.Return(func, utils::Vector{merge_param_0}));
func->StartTarget()->SetInstructions(utils::Vector{s});
@@ -324,9 +324,9 @@
%6 = OpConstant %7 42
%11 = OpConstant %7 10
%12 = OpConstant %7 20
-%14 = OpTypeBool
-%15 = OpConstantTrue %14
-%16 = OpConstantFalse %14
+%13 = OpTypeBool
+%15 = OpConstantTrue %13
+%16 = OpConstantFalse %13
%1 = OpFunction %2 None %3
%4 = OpLabel
OpSelectionMerge %9 None
@@ -337,7 +337,7 @@
OpBranch %9
%9 = OpLabel
%10 = OpPhi %7 %11 %5 %12 %8
-%13 = OpPhi %14 %15 %5 %16 %8
+%14 = OpPhi %13 %15 %5 %16 %8
OpReturnValue %10
OpFunctionEnd
)");
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
index 7b2142c..d6c654f 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
@@ -149,6 +149,22 @@
EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), "OpDecorate %1 ArrayStride 16\n");
}
+TEST_F(SpvGeneratorImplTest, Type_Array_NestedArray) {
+ auto* arr = mod.Types().array(mod.Types().array(mod.Types().f32(), 64u), 4u);
+ auto id = generator_.Type(arr);
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(),
+ "%3 = OpTypeFloat 32\n"
+ "%5 = OpTypeInt 32 0\n"
+ "%4 = OpConstant %5 64\n"
+ "%2 = OpTypeArray %3 %4\n"
+ "%6 = OpConstant %5 4\n"
+ "%1 = OpTypeArray %2 %6\n");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Annots()),
+ "OpDecorate %2 ArrayStride 4\n"
+ "OpDecorate %1 ArrayStride 256\n");
+}
+
TEST_F(SpvGeneratorImplTest, Type_RuntimeArray_DefaultStride) {
auto* arr = mod.Types().runtime_array(mod.Types().f32());
auto id = generator_.Type(arr);
@@ -169,6 +185,76 @@
EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), "OpDecorate %1 ArrayStride 16\n");
}
+TEST_F(SpvGeneratorImplTest, Type_Struct) {
+ auto* str = mod.Types().Get<type::Struct>(
+ mod.symbols.Register("MyStruct"),
+ utils::Vector{
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("a"), mod.Types().f32(), 0u,
+ 0u, 4u, 4u, type::StructMemberAttributes{}),
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("b"),
+ mod.Types().vec4(mod.Types().i32()), 1u, 16u, 16u,
+ 16u, type::StructMemberAttributes{}),
+ },
+ 16u, 32u, 32u);
+ auto id = generator_.Type(str);
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), R"(%2 = OpTypeFloat 32
+%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 4
+%1 = OpTypeStruct %2 %3
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), R"(OpMemberDecorate %1 0 Offset 0
+OpMemberDecorate %1 1 Offset 16
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Debug()), R"(OpMemberName %1 0 "a"
+OpMemberName %1 1 "b"
+OpName %1 "MyStruct"
+)");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_Struct_MatrixLayout) {
+ auto* str = mod.Types().Get<type::Struct>(
+ mod.symbols.Register("MyStruct"),
+ utils::Vector{
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("m"),
+ mod.Types().mat3x3(mod.Types().f32()), 0u, 0u, 16u,
+ 48u, type::StructMemberAttributes{}),
+ // Matrices nested inside arrays need layout decorations on the struct member too.
+ mod.Types().Get<type::StructMember>(
+ mod.symbols.Register("arr"),
+ mod.Types().array(mod.Types().array(mod.Types().mat2x4(mod.Types().f16()), 4), 4),
+ 1u, 64u, 8u, 64u, type::StructMemberAttributes{}),
+ },
+ 16u, 128u, 128u);
+ auto id = generator_.Type(str);
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypeMatrix %3 3
+%9 = OpTypeFloat 16
+%8 = OpTypeVector %9 4
+%7 = OpTypeMatrix %8 2
+%11 = OpTypeInt 32 0
+%10 = OpConstant %11 4
+%6 = OpTypeArray %7 %10
+%5 = OpTypeArray %6 %10
+%1 = OpTypeStruct %2 %5
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), R"(OpMemberDecorate %1 0 Offset 0
+OpMemberDecorate %1 0 ColMajor
+OpMemberDecorate %1 0 MatrixStride 16
+OpDecorate %6 ArrayStride 16
+OpDecorate %5 ArrayStride 64
+OpMemberDecorate %1 1 Offset 64
+OpMemberDecorate %1 1 ColMajor
+OpMemberDecorate %1 1 MatrixStride 8
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Debug()), R"(OpMemberName %1 0 "m"
+OpMemberName %1 1 "arr"
+OpName %1 "MyStruct"
+)");
+}
+
// Test that we can emit multiple types.
// Includes types with the same opcode but different parameters.
TEST_F(SpvGeneratorImplTest, Type_Multiple) {