[ir] Convert block instructions to a list.
This Cl moves the block instructions from a vector to a list. There will
be a lot of inserting and shuffling of these instructions when doing
transforms so this makes updating and removing simpler.
The block has several instructions added for working with the
instruction list, so, in effect, the block is at a high level an
instruction list container. There is a `begin` and `end` iterator on
block which iterates over the instructions.
Bug: tint:1718
Change-Id: I068801b390ca1e99baf22afb9712c12990c7f36d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/135181
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 006ed50..8239b44 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -2281,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",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 474b56e..0414bdc 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1484,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
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/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/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_literal_test.cc b/src/tint/ir/from_program_literal_test.cc
index f746bc0..b9c5528 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;
@@ -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());
@@ -118,12 +124,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());
@@ -154,12 +164,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());
@@ -188,12 +202,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());
@@ -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/instruction.h b/src/tint/ir/instruction.h
index 13bcc00..2a3ed57 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -41,6 +41,11 @@
/// @returns the block that owns this instruction
const ir::Block* Block() const { return block_; }
+ /// 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/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/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 6f97642..5b12094 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -333,7 +333,7 @@
}
void GeneratorImplIr::EmitRootBlock(const ir::Block* root_block) {
- for (auto* inst : root_block->Instructions()) {
+ for (auto* inst : *root_block) {
Switch(
inst, //
[&](const ir::Var* v) { return EmitVar(v); },
@@ -353,7 +353,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;
}
@@ -373,7 +373,7 @@
}
// Emit the instructions.
- for (auto* inst : block->Instructions()) {
+ for (auto* inst : *block) {
Switch(
inst, //
[&](const ir::Binary* b) { EmitBinary(b); }, //
@@ -448,11 +448,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);
}
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 338598e..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"
@@ -334,19 +334,19 @@
auto* func = b.CreateFunction("foo", mod.Types().void_());
auto* l = b.CreateLoop(utils::Vector{b.Constant(1_i)});
- func->StartTarget()->AddInstruction(l);
+ 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()->AddInstruction(inc);
- l->Body()->AddInstruction(b.Continue(l, utils::Vector{inc}));
+ 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()->AddInstruction(cmp);
- l->Continuing()->AddInstruction(b.BreakIf(cmp, l, utils::Vector{cont_param}));
+ 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"
@@ -380,23 +380,23 @@
auto* func = b.CreateFunction("foo", mod.Types().void_());
auto* l = b.CreateLoop(utils::Vector{b.Constant(1_i), b.Constant(false)});
- func->StartTarget()->AddInstruction(l);
+ 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()->AddInstruction(inc);
- l->Body()->AddInstruction(b.Continue(l, utils::Vector{inc, loop_param_b}));
+ 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()->AddInstruction(cmp);
+ l->Continuing()->Append(cmp);
auto* not_b = b.Not(b.ir.Types().bool_(), cont_param_b);
- l->Continuing()->AddInstruction(not_b);
- l->Continuing()->AddInstruction(b.BreakIf(cmp, l, utils::Vector{cont_param_a, not_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"
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 e7e4e7c..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});