[ir] Add instruction helper methods.
This Cl adds some helper methods to `instruction` for working with the
block insertion points.
Bug: tint:1718
Change-Id: I928b4d535932ef5c6f6f358180cad5b4d5e49aee
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/135340
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 8239b44..11f6a78 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -2294,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 0414bdc..40dd3ee 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1497,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/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/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/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 2a3ed57..a79b3da 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -37,10 +37,21 @@
/// @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
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/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/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));