[ir] Shift back to accessors.

Some of the IR classes require setters in order to update dependant
information. In order to keep the IR access symmetrical this CL moves
the IR back to using accessors and private fields.

Bug: tint:1718
Change-Id: I101edda004671e07c4594bdcae4b1576e5771782
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/133640
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2023-05-18 22:16:08 +00:00 committed by Dawn LUCI CQ
parent 3cd9b53e88
commit 24cb81116d
57 changed files with 1219 additions and 1050 deletions

View File

@ -2265,6 +2265,7 @@ if (tint_build_unittests) {
"ir/store_test.cc", "ir/store_test.cc",
"ir/test_helper.h", "ir/test_helper.h",
"ir/to_program_roundtrip_test.cc", "ir/to_program_roundtrip_test.cc",
"ir/transform/add_empty_entry_point_test.cc",
"ir/unary_test.cc", "ir/unary_test.cc",
] ]

View File

@ -1476,6 +1476,7 @@ if(TINT_BUILD_TESTS)
ir/module_test.cc ir/module_test.cc
ir/store_test.cc ir/store_test.cc
ir/test_helper.h ir/test_helper.h
ir/transform/add_empty_entry_point_test.cc
ir/unary_test.cc ir/unary_test.cc
) )
endif() endif()

View File

@ -19,8 +19,8 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Binary);
namespace tint::ir { namespace tint::ir {
Binary::Binary(Kind k, const type::Type* res_ty, Value* lhs, Value* rhs) Binary::Binary(enum Kind kind, const type::Type* res_ty, Value* lhs, Value* rhs)
: kind(k), result_type(res_ty), lhs_(lhs), rhs_(rhs) { : kind_(kind), result_type_(res_ty), lhs_(lhs), rhs_(rhs) {
TINT_ASSERT(IR, lhs); TINT_ASSERT(IR, lhs);
TINT_ASSERT(IR, rhs); TINT_ASSERT(IR, rhs);
lhs_->AddUsage(this); lhs_->AddUsage(this);

View File

@ -51,7 +51,7 @@ class Binary : public utils::Castable<Binary, Instruction> {
/// @param type the result type /// @param type the result type
/// @param lhs the lhs of the instruction /// @param lhs the lhs of the instruction
/// @param rhs the rhs of the instruction /// @param rhs the rhs of the instruction
Binary(Kind kind, const type::Type* type, Value* lhs, Value* rhs); Binary(enum Kind kind, const type::Type* type, Value* lhs, Value* rhs);
Binary(const Binary& inst) = delete; Binary(const Binary& inst) = delete;
Binary(Binary&& inst) = delete; Binary(Binary&& inst) = delete;
~Binary() override; ~Binary() override;
@ -59,8 +59,11 @@ class Binary : public utils::Castable<Binary, Instruction> {
Binary& operator=(const Binary& inst) = delete; Binary& operator=(const Binary& inst) = delete;
Binary& operator=(Binary&& inst) = delete; Binary& operator=(Binary&& inst) = delete;
/// @returns the kind of the binary instruction
enum Kind Kind() const { return kind_; }
/// @returns the type of the value /// @returns the type of the value
const type::Type* Type() const override { return result_type; } const type::Type* Type() const override { return result_type_; }
/// @returns the left-hand-side value for the instruction /// @returns the left-hand-side value for the instruction
const Value* LHS() const { return lhs_; } const Value* LHS() const { return lhs_; }
@ -68,15 +71,11 @@ class Binary : public utils::Castable<Binary, Instruction> {
/// @returns the right-hand-side value for the instruction /// @returns the right-hand-side value for the instruction
const Value* RHS() const { return rhs_; } const Value* RHS() const { return rhs_; }
/// the kind of binary instruction
Kind kind = Kind::kAdd;
/// the result type of the instruction
const type::Type* result_type = nullptr;
private: private:
Value* lhs_ = nullptr; enum Kind kind_;
Value* rhs_ = nullptr; const type::Type* result_type_;
Value* lhs_;
Value* rhs_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -30,17 +30,16 @@ TEST_F(IR_InstructionTest, CreateAnd) {
const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kAnd); EXPECT_EQ(inst->Kind(), Binary::Kind::kAnd);
ASSERT_NE(inst->result_type, nullptr);
ASSERT_NE(inst->Type(), nullptr); ASSERT_NE(inst->Type(), nullptr);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -52,15 +51,15 @@ TEST_F(IR_InstructionTest, CreateOr) {
const auto* inst = b.Or(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Or(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kOr); EXPECT_EQ(inst->Kind(), Binary::Kind::kOr);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -72,15 +71,15 @@ TEST_F(IR_InstructionTest, CreateXor) {
const auto* inst = b.Xor(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Xor(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kXor); EXPECT_EQ(inst->Kind(), Binary::Kind::kXor);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -92,15 +91,15 @@ TEST_F(IR_InstructionTest, CreateEqual) {
const auto* inst = b.Equal(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Equal(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kEqual); EXPECT_EQ(inst->Kind(), Binary::Kind::kEqual);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -112,15 +111,15 @@ TEST_F(IR_InstructionTest, CreateNotEqual) {
const auto* inst = b.NotEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.NotEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kNotEqual); EXPECT_EQ(inst->Kind(), Binary::Kind::kNotEqual);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -132,15 +131,15 @@ TEST_F(IR_InstructionTest, CreateLessThan) {
const auto* inst = b.LessThan(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.LessThan(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kLessThan); EXPECT_EQ(inst->Kind(), Binary::Kind::kLessThan);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -153,15 +152,15 @@ TEST_F(IR_InstructionTest, CreateGreaterThan) {
b.GreaterThan(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); b.GreaterThan(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kGreaterThan); EXPECT_EQ(inst->Kind(), Binary::Kind::kGreaterThan);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -174,15 +173,15 @@ TEST_F(IR_InstructionTest, CreateLessThanEqual) {
b.LessThanEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); b.LessThanEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kLessThanEqual); EXPECT_EQ(inst->Kind(), Binary::Kind::kLessThanEqual);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -195,15 +194,15 @@ TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
b.GreaterThanEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i)); b.GreaterThanEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kGreaterThanEqual); EXPECT_EQ(inst->Kind(), Binary::Kind::kGreaterThanEqual);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -214,15 +213,15 @@ TEST_F(IR_InstructionTest, CreateNot) {
const auto* inst = b.Not(b.ir.types.Get<type::Bool>(), b.Constant(true)); const auto* inst = b.Not(b.ir.types.Get<type::Bool>(), b.Constant(true));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kEqual); EXPECT_EQ(inst->Kind(), Binary::Kind::kEqual);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<bool>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<bool>>());
EXPECT_TRUE(lhs->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_TRUE(lhs->As<constant::Scalar<bool>>()->ValueAs<bool>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<bool>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<bool>>());
EXPECT_FALSE(rhs->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_FALSE(rhs->As<constant::Scalar<bool>>()->ValueAs<bool>());
} }
@ -234,15 +233,15 @@ TEST_F(IR_InstructionTest, CreateShiftLeft) {
const auto* inst = b.ShiftLeft(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.ShiftLeft(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kShiftLeft); EXPECT_EQ(inst->Kind(), Binary::Kind::kShiftLeft);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -254,15 +253,15 @@ TEST_F(IR_InstructionTest, CreateShiftRight) {
const auto* inst = b.ShiftRight(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.ShiftRight(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kShiftRight); EXPECT_EQ(inst->Kind(), Binary::Kind::kShiftRight);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -274,15 +273,15 @@ TEST_F(IR_InstructionTest, CreateAdd) {
const auto* inst = b.Add(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Add(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kAdd); EXPECT_EQ(inst->Kind(), Binary::Kind::kAdd);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -294,15 +293,15 @@ TEST_F(IR_InstructionTest, CreateSubtract) {
const auto* inst = b.Subtract(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Subtract(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kSubtract); EXPECT_EQ(inst->Kind(), Binary::Kind::kSubtract);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -314,15 +313,15 @@ TEST_F(IR_InstructionTest, CreateMultiply) {
const auto* inst = b.Multiply(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Multiply(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kMultiply); EXPECT_EQ(inst->Kind(), Binary::Kind::kMultiply);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -334,15 +333,15 @@ TEST_F(IR_InstructionTest, CreateDivide) {
const auto* inst = b.Divide(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Divide(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kDivide); EXPECT_EQ(inst->Kind(), Binary::Kind::kDivide);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -354,15 +353,15 @@ TEST_F(IR_InstructionTest, CreateModulo) {
const auto* inst = b.Modulo(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.Modulo(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
ASSERT_TRUE(inst->Is<Binary>()); ASSERT_TRUE(inst->Is<Binary>());
EXPECT_EQ(inst->kind, Binary::Kind::kModulo); EXPECT_EQ(inst->Kind(), Binary::Kind::kModulo);
ASSERT_TRUE(inst->LHS()->Is<Constant>()); ASSERT_TRUE(inst->LHS()->Is<Constant>());
auto lhs = inst->LHS()->As<Constant>()->value; auto lhs = inst->LHS()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
ASSERT_TRUE(inst->RHS()->Is<Constant>()); ASSERT_TRUE(inst->RHS()->Is<Constant>());
auto rhs = inst->RHS()->As<Constant>()->value; auto rhs = inst->RHS()->As<Constant>()->Value();
ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(rhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -372,7 +371,7 @@ TEST_F(IR_InstructionTest, Binary_Usage) {
Builder b{mod}; Builder b{mod};
const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i)); const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
EXPECT_EQ(inst->kind, Binary::Kind::kAnd); EXPECT_EQ(inst->Kind(), Binary::Kind::kAnd);
ASSERT_NE(inst->LHS(), nullptr); ASSERT_NE(inst->LHS(), nullptr);
ASSERT_EQ(inst->LHS()->Usage().Length(), 1u); ASSERT_EQ(inst->LHS()->Usage().Length(), 1u);
@ -389,7 +388,7 @@ TEST_F(IR_InstructionTest, Binary_Usage_DuplicateValue) {
auto val = b.Constant(4_i); auto val = b.Constant(4_i);
const auto* inst = b.And(b.ir.types.Get<type::I32>(), val, val); const auto* inst = b.And(b.ir.types.Get<type::I32>(), val, val);
EXPECT_EQ(inst->kind, Binary::Kind::kAnd); EXPECT_EQ(inst->Kind(), Binary::Kind::kAnd);
ASSERT_EQ(inst->LHS(), inst->RHS()); ASSERT_EQ(inst->LHS(), inst->RHS());
ASSERT_NE(inst->LHS(), nullptr); ASSERT_NE(inst->LHS(), nullptr);

View File

@ -32,9 +32,10 @@ TEST_F(IR_InstructionTest, Bitcast) {
ASSERT_TRUE(inst->Is<ir::Bitcast>()); ASSERT_TRUE(inst->Is<ir::Bitcast>());
ASSERT_NE(inst->Type(), nullptr); ASSERT_NE(inst->Type(), nullptr);
ASSERT_EQ(inst->args.Length(), 1u); const auto args = inst->Args();
ASSERT_TRUE(inst->args[0]->Is<Constant>()); ASSERT_EQ(args.Length(), 1u);
auto val = inst->args[0]->As<Constant>()->value; ASSERT_TRUE(args[0]->Is<Constant>());
auto val = args[0]->As<Constant>()->Value();
ASSERT_TRUE(val->Is<constant::Scalar<i32>>()); ASSERT_TRUE(val->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, val->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, val->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -44,10 +45,11 @@ TEST_F(IR_InstructionTest, Bitcast_Usage) {
Builder b{mod}; Builder b{mod};
const auto* inst = b.Bitcast(b.ir.types.Get<type::I32>(), b.Constant(4_i)); const auto* inst = b.Bitcast(b.ir.types.Get<type::I32>(), b.Constant(4_i));
ASSERT_EQ(inst->args.Length(), 1u); const auto args = inst->Args();
ASSERT_NE(inst->args[0], nullptr); ASSERT_EQ(args.Length(), 1u);
ASSERT_EQ(inst->args[0]->Usage().Length(), 1u); ASSERT_NE(args[0], nullptr);
EXPECT_EQ(inst->args[0]->Usage()[0], inst); ASSERT_EQ(args[0]->Usage().Length(), 1u);
EXPECT_EQ(args[0]->Usage()[0], inst);
} }
} // namespace } // namespace

View File

@ -22,4 +22,13 @@ Block::Block() : Base() {}
Block::~Block() = default; Block::~Block() = default;
void Block::BranchTo(FlowNode* to, utils::VectorRef<Value*> args) {
TINT_ASSERT(IR, to);
branch_.target = to;
branch_.args = args;
if (to) {
to->AddInboundBranch(this);
}
}
} // namespace tint::ir } // namespace tint::ir

View File

@ -15,6 +15,8 @@
#ifndef SRC_TINT_IR_BLOCK_H_ #ifndef SRC_TINT_IR_BLOCK_H_
#define SRC_TINT_IR_BLOCK_H_ #define SRC_TINT_IR_BLOCK_H_
#include <utility>
#include "src/tint/ir/block_param.h" #include "src/tint/ir/block_param.h"
#include "src/tint/ir/branch.h" #include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h" #include "src/tint/ir/flow_node.h"
@ -30,20 +32,48 @@ class Block : public utils::Castable<Block, FlowNode> {
public: public:
/// Constructor /// Constructor
Block(); Block();
Block(const Block&) = delete;
Block(Block&&) = delete;
~Block() override; ~Block() override;
/// @returns true if this is a dead block. This can happen in the case like a loop merge block Block& operator=(const Block&) = delete;
/// which is never reached. Block& operator=(Block&&) = delete;
bool IsDead() const override { return branch.target == nullptr; }
/// The node this block branches too. /// Sets the blocks branch target to the given node.
Branch branch = {}; /// @param to the node to branch too
/// @param args the branch arguments
void BranchTo(FlowNode* to, utils::VectorRef<Value*> args = {});
/// The instructions in the block /// @returns true if this is block has a branch target set
utils::Vector<const Instruction*, 16> instructions; bool HasBranchTarget() const override { return branch_.target != nullptr; }
/// The parameters passed into the block /// @return the node this block branches too.
utils::Vector<const BlockParam*, 0> params; const ir::Branch& Branch() const { return branch_; }
/// Sets the instructions in the block
/// @param instructions the instructions to set
void SetInstructions(utils::VectorRef<const Instruction*> instructions) {
instructions_ = std::move(instructions);
}
/// @returns the instructions in the block
utils::VectorRef<const Instruction*> Instructions() const { return instructions_; }
/// @returns the instructions in the block
utils::Vector<const Instruction*, 16>& Instructions() { return instructions_; }
/// Sets the params to the block
/// @param params the params for the block
void SetParams(utils::VectorRef<const BlockParam*> params) { params_ = std::move(params); }
/// @returns the params to the block
utils::Vector<const BlockParam*, 0>& Params() { return params_; }
/// @return the parameters passed into the block
utils::VectorRef<const BlockParam*> Params() const { return params_; }
private:
ir::Branch branch_ = {};
utils::Vector<const Instruction*, 16> instructions_;
utils::Vector<const BlockParam*, 0> params_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::BlockParam);
namespace tint::ir { namespace tint::ir {
BlockParam::BlockParam(const type::Type* ty) : type(ty) {} BlockParam::BlockParam(const type::Type* ty) : type_(ty) {}
BlockParam::~BlockParam() = default; BlockParam::~BlockParam() = default;

View File

@ -34,10 +34,11 @@ class BlockParam : public utils::Castable<BlockParam, Value> {
BlockParam& operator=(BlockParam&& inst) = delete; BlockParam& operator=(BlockParam&& inst) = delete;
/// @returns the type of the var /// @returns the type of the var
const type::Type* Type() const override { return type; } const type::Type* Type() const override { return type_; }
private:
/// the result type of the instruction /// the result type of the instruction
const type::Type* type = nullptr; const type::Type* type_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -32,7 +32,7 @@ ir::Block* Builder::CreateRootBlockIfNeeded() {
// Everything in the module scope must have been const-eval's, so everything will go into a // Everything in the module scope must have been const-eval's, so everything will go into a
// single block. So, we can create the root terminator for the root-block now. // single block. So, we can create the root terminator for the root-block now.
ir.root_block->branch.target = CreateRootTerminator(); ir.root_block->BranchTo(CreateRootTerminator());
} }
return ir.root_block; return ir.root_block;
} }
@ -56,11 +56,11 @@ Function* Builder::CreateFunction(Symbol name,
TINT_ASSERT(IR, return_type); TINT_ASSERT(IR, return_type);
auto* ir_func = ir.flow_nodes.Create<Function>(name, return_type, stage, wg_size); auto* ir_func = ir.flow_nodes.Create<Function>(name, return_type, stage, wg_size);
ir_func->start_target = CreateBlock(); ir_func->SetStartTarget(CreateBlock());
ir_func->end_target = CreateFunctionTerminator(); ir_func->SetEndTarget(CreateFunctionTerminator());
// Function is always branching into the start target // Function is always branching into the Start().target
ir_func->start_target->inbound_branches.Push(ir_func); ir_func->StartTarget()->AddInboundBranch(ir_func);
return ir_func; return ir_func;
} }
@ -69,53 +69,48 @@ If* Builder::CreateIf(Value* condition) {
TINT_ASSERT(IR, condition); TINT_ASSERT(IR, condition);
auto* ir_if = ir.flow_nodes.Create<If>(condition); auto* ir_if = ir.flow_nodes.Create<If>(condition);
ir_if->true_.target = CreateBlock(); ir_if->True().target = CreateBlock();
ir_if->false_.target = CreateBlock(); ir_if->False().target = CreateBlock();
ir_if->merge.target = CreateBlock(); ir_if->Merge().target = CreateBlock();
// An if always branches to both the true and false block. // An if always branches to both the true and false block.
ir_if->true_.target->inbound_branches.Push(ir_if); ir_if->True().target->AddInboundBranch(ir_if);
ir_if->false_.target->inbound_branches.Push(ir_if); ir_if->False().target->AddInboundBranch(ir_if);
return ir_if; return ir_if;
} }
Loop* Builder::CreateLoop() { Loop* Builder::CreateLoop() {
auto* ir_loop = ir.flow_nodes.Create<Loop>(); auto* ir_loop = ir.flow_nodes.Create<Loop>();
ir_loop->start.target = CreateBlock(); ir_loop->Start().target = CreateBlock();
ir_loop->continuing.target = CreateBlock(); ir_loop->Continuing().target = CreateBlock();
ir_loop->merge.target = CreateBlock(); ir_loop->Merge().target = CreateBlock();
// A loop always branches to the start block. // A loop always branches to the start block.
ir_loop->start.target->inbound_branches.Push(ir_loop); ir_loop->Start().target->AddInboundBranch(ir_loop);
return ir_loop; return ir_loop;
} }
Switch* Builder::CreateSwitch(Value* condition) { Switch* Builder::CreateSwitch(Value* condition) {
auto* ir_switch = ir.flow_nodes.Create<Switch>(condition); auto* ir_switch = ir.flow_nodes.Create<Switch>(condition);
ir_switch->merge.target = CreateBlock(); ir_switch->Merge().target = CreateBlock();
return ir_switch; return ir_switch;
} }
Block* Builder::CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors) { Block* Builder::CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors) {
s->cases.Push(Switch::Case{selectors, {CreateBlock(), utils::Empty}}); s->Cases().Push(Switch::Case{selectors, {CreateBlock(), utils::Empty}});
Block* b = s->cases.Back().start.target->As<Block>(); Block* b = s->Cases().Back().Start().target->As<Block>();
// Switch branches into the case block // Switch branches into the case block
b->inbound_branches.Push(s); b->AddInboundBranch(s);
return b; return b;
} }
void Builder::Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args) { Binary* Builder::CreateBinary(enum Binary::Kind kind,
TINT_ASSERT(IR, from); const type::Type* type,
TINT_ASSERT(IR, to); Value* lhs,
from->branch.target = to; Value* rhs) {
from->branch.args = args;
to->inbound_branches.Push(from);
}
Binary* Builder::CreateBinary(Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs) {
return ir.values.Create<ir::Binary>(kind, type, lhs, rhs); return ir.values.Create<ir::Binary>(kind, type, lhs, rhs);
} }
@ -183,7 +178,7 @@ Binary* Builder::Modulo(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs); return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs);
} }
Unary* Builder::CreateUnary(Unary::Kind kind, const type::Type* type, Value* val) { Unary* Builder::CreateUnary(enum Unary::Kind kind, const type::Type* type, Value* val) {
return ir.values.Create<ir::Unary>(kind, type, val); return ir.values.Create<ir::Unary>(kind, type, val);
} }

View File

@ -98,12 +98,6 @@ class Builder {
/// @returns the start block for the case flow node /// @returns the start block for the case flow node
Block* CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors); Block* CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors);
/// Branches the given block to the given flow node.
/// @param from the block to branch from
/// @param to the node to branch too
/// @param args arguments to the branch
void Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args = {});
/// Creates a constant::Value /// Creates a constant::Value
/// @param args the arguments /// @param args the arguments
/// @returns the new constant value /// @returns the new constant value
@ -161,7 +155,7 @@ class Builder {
/// @param lhs the left-hand-side of the operation /// @param lhs the left-hand-side of the operation
/// @param rhs the right-hand-side of the operation /// @param rhs the right-hand-side of the operation
/// @returns the operation /// @returns the operation
Binary* CreateBinary(Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs); Binary* CreateBinary(enum Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs);
/// Creates an And operation /// Creates an And operation
/// @param type the result type of the expression /// @param type the result type of the expression
@ -280,7 +274,7 @@ class Builder {
/// @param type the result type of the binary expression /// @param type the result type of the binary expression
/// @param val the value of the operation /// @param val the value of the operation
/// @returns the operation /// @returns the operation
Unary* CreateUnary(Unary::Kind kind, const type::Type* type, Value* val); Unary* CreateUnary(enum Unary::Kind kind, const type::Type* type, Value* val);
/// Creates a Complement operation /// Creates a Complement operation
/// @param type the result type of the expression /// @param type the result type of the expression

View File

@ -21,8 +21,8 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Call);
namespace tint::ir { namespace tint::ir {
Call::Call(const type::Type* res_ty, utils::VectorRef<Value*> arguments) Call::Call(const type::Type* res_ty, utils::VectorRef<Value*> arguments)
: result_type(res_ty), args(std::move(arguments)) { : result_type_(res_ty), args_(std::move(arguments)) {
for (auto* arg : args) { for (auto* arg : args_) {
arg->AddUsage(this); arg->AddUsage(this);
} }
} }

View File

@ -31,13 +31,10 @@ class Call : public utils::Castable<Call, Instruction> {
Call& operator=(Call&& inst) = delete; Call& operator=(Call&& inst) = delete;
/// @returns the type of the value /// @returns the type of the value
const type::Type* Type() const override { return result_type; } const type::Type* Type() const override { return result_type_; }
/// The instruction type /// @returns the call arguments
const type::Type* result_type = nullptr; utils::VectorRef<Value*> Args() const { return args_; }
/// The constructor arguments
utils::Vector<Value*, 1> args;
protected: protected:
/// Constructor /// Constructor
@ -46,6 +43,10 @@ class Call : public utils::Castable<Call, Instruction> {
/// @param result_type the result type /// @param result_type the result type
/// @param args the constructor arguments /// @param args the constructor arguments
Call(const type::Type* result_type, utils::VectorRef<Value*> args); Call(const type::Type* result_type, utils::VectorRef<Value*> args);
private:
const type::Type* result_type_;
utils::Vector<Value*, 1> args_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Constant);
namespace tint::ir { namespace tint::ir {
Constant::Constant(const constant::Value* val) : value(val) {} Constant::Constant(const constant::Value* val) : value_(val) {}
Constant::~Constant() = default; Constant::~Constant() = default;

View File

@ -26,13 +26,21 @@ class Constant : public utils::Castable<Constant, Value> {
/// Constructor /// Constructor
/// @param val the value stored in the constant /// @param val the value stored in the constant
explicit Constant(const constant::Value* val); explicit Constant(const constant::Value* val);
Constant(const Constant&) = delete;
Constant(Constant&&) = delete;
~Constant() override; ~Constant() override;
/// @returns the type of the constant Constant& operator=(const Constant&) = delete;
const type::Type* Type() const override { return value->Type(); } Constant& operator=(Constant&&) = delete;
/// The constants value /// @returns the constants value
const constant::Value* const value; const constant::Value* Value() const { return value_; }
/// @returns the type of the constant
const type::Type* Type() const override { return value_->Type(); }
private:
const constant::Value* const value_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -30,13 +30,13 @@ TEST_F(IR_ConstantTest, f32) {
utils::StringStream str; utils::StringStream str;
auto* c = b.Constant(1.2_f); auto* c = b.Constant(1.2_f);
EXPECT_EQ(1.2_f, c->value->As<constant::Scalar<f32>>()->ValueAs<f32>()); EXPECT_EQ(1.2_f, c->Value()->As<constant::Scalar<f32>>()->ValueAs<f32>());
EXPECT_TRUE(c->value->Is<constant::Scalar<f32>>()); EXPECT_TRUE(c->Value()->Is<constant::Scalar<f32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f16>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f16>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<i32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<i32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<u32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<u32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<bool>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<bool>>());
} }
TEST_F(IR_ConstantTest, f16) { TEST_F(IR_ConstantTest, f16) {
@ -46,13 +46,13 @@ TEST_F(IR_ConstantTest, f16) {
utils::StringStream str; utils::StringStream str;
auto* c = b.Constant(1.1_h); auto* c = b.Constant(1.1_h);
EXPECT_EQ(1.1_h, c->value->As<constant::Scalar<f16>>()->ValueAs<f16>()); EXPECT_EQ(1.1_h, c->Value()->As<constant::Scalar<f16>>()->ValueAs<f16>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f32>>());
EXPECT_TRUE(c->value->Is<constant::Scalar<f16>>()); EXPECT_TRUE(c->Value()->Is<constant::Scalar<f16>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<i32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<i32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<u32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<u32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<bool>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<bool>>());
} }
TEST_F(IR_ConstantTest, i32) { TEST_F(IR_ConstantTest, i32) {
@ -62,13 +62,13 @@ TEST_F(IR_ConstantTest, i32) {
utils::StringStream str; utils::StringStream str;
auto* c = b.Constant(1_i); auto* c = b.Constant(1_i);
EXPECT_EQ(1_i, c->value->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(1_i, c->Value()->As<constant::Scalar<i32>>()->ValueAs<i32>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f16>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f16>>());
EXPECT_TRUE(c->value->Is<constant::Scalar<i32>>()); EXPECT_TRUE(c->Value()->Is<constant::Scalar<i32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<u32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<u32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<bool>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<bool>>());
} }
TEST_F(IR_ConstantTest, u32) { TEST_F(IR_ConstantTest, u32) {
@ -78,13 +78,13 @@ TEST_F(IR_ConstantTest, u32) {
utils::StringStream str; utils::StringStream str;
auto* c = b.Constant(2_u); auto* c = b.Constant(2_u);
EXPECT_EQ(2_u, c->value->As<constant::Scalar<u32>>()->ValueAs<u32>()); EXPECT_EQ(2_u, c->Value()->As<constant::Scalar<u32>>()->ValueAs<u32>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f16>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f16>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<i32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<i32>>());
EXPECT_TRUE(c->value->Is<constant::Scalar<u32>>()); EXPECT_TRUE(c->Value()->Is<constant::Scalar<u32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<bool>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<bool>>());
} }
TEST_F(IR_ConstantTest, bool) { TEST_F(IR_ConstantTest, bool) {
@ -95,19 +95,19 @@ TEST_F(IR_ConstantTest, bool) {
utils::StringStream str; utils::StringStream str;
auto* c = b.Constant(false); auto* c = b.Constant(false);
EXPECT_FALSE(c->value->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_FALSE(c->Value()->As<constant::Scalar<bool>>()->ValueAs<bool>());
} }
{ {
utils::StringStream str; utils::StringStream str;
auto c = b.Constant(true); auto c = b.Constant(true);
EXPECT_TRUE(c->value->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_TRUE(c->Value()->As<constant::Scalar<bool>>()->ValueAs<bool>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<f16>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<f16>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<i32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<i32>>());
EXPECT_FALSE(c->value->Is<constant::Scalar<u32>>()); EXPECT_FALSE(c->Value()->Is<constant::Scalar<u32>>());
EXPECT_TRUE(c->value->Is<constant::Scalar<bool>>()); EXPECT_TRUE(c->Value()->Is<constant::Scalar<bool>>());
} }
} }

View File

@ -60,81 +60,81 @@ std::string Debug::AsDotGraph(const Module* mod) {
if (node_to_name.count(b) == 0) { if (node_to_name.count(b) == 0) {
out << name_for(b) << R"( [label="block"])" << std::endl; out << name_for(b) << R"( [label="block"])" << std::endl;
} }
out << name_for(b) << " -> " << name_for(b->branch.target); out << name_for(b) << " -> " << name_for(b->Branch().target);
// Dashed lines to merge blocks // Dashed lines to merge blocks
if (merge_nodes.count(b->branch.target) != 0) { if (merge_nodes.count(b->Branch().target) != 0) {
out << " [style=dashed]"; out << " [style=dashed]";
} }
out << std::endl; out << std::endl;
Graph(b->branch.target); Graph(b->Branch().target);
}, },
[&](const ir::Switch* s) { [&](const ir::Switch* s) {
out << name_for(s) << R"( [label="switch"])" << std::endl; out << name_for(s) << R"( [label="switch"])" << std::endl;
out << name_for(s->merge.target) << R"( [label="switch merge"])" << std::endl; out << name_for(s->Merge().target) << R"( [label="switch merge"])" << std::endl;
merge_nodes.insert(s->merge.target); merge_nodes.insert(s->Merge().target);
size_t i = 0; size_t i = 0;
for (const auto& c : s->cases) { for (const auto& c : s->Cases()) {
out << name_for(c.start.target) out << name_for(c.Start().target)
<< R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl; << R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
} }
out << name_for(s) << " -> {"; out << name_for(s) << " -> {";
for (const auto& c : s->cases) { for (const auto& c : s->Cases()) {
if (&c != &(s->cases[0])) { if (&c != &(s->Cases().Front())) {
out << ", "; out << ", ";
} }
out << name_for(c.start.target); out << name_for(c.Start().target);
} }
out << "}" << std::endl; out << "}" << std::endl;
for (const auto& c : s->cases) { for (const auto& c : s->Cases()) {
Graph(c.start.target); Graph(c.Start().target);
} }
Graph(s->merge.target); Graph(s->Merge().target);
}, },
[&](const ir::If* i) { [&](const ir::If* i) {
out << name_for(i) << R"( [label="if"])" << std::endl; out << name_for(i) << R"( [label="if"])" << std::endl;
out << name_for(i->true_.target) << R"( [label="true"])" << std::endl; out << name_for(i->True().target) << R"( [label="true"])" << std::endl;
out << name_for(i->false_.target) << R"( [label="false"])" << std::endl; out << name_for(i->False().target) << R"( [label="false"])" << std::endl;
out << name_for(i->merge.target) << R"( [label="if merge"])" << std::endl; out << name_for(i->Merge().target) << R"( [label="if merge"])" << std::endl;
merge_nodes.insert(i->merge.target); merge_nodes.insert(i->Merge().target);
out << name_for(i) << " -> {"; out << name_for(i) << " -> {";
out << name_for(i->true_.target) << ", " << name_for(i->false_.target); out << name_for(i->True().target) << ", " << name_for(i->False().target);
out << "}" << std::endl; out << "}" << std::endl;
// Subgraph if true/false branches so they draw on the same line // Subgraph if true/false branches so they draw on the same line
out << "subgraph sub_" << name_for(i) << " {" << std::endl; out << "subgraph sub_" << name_for(i) << " {" << std::endl;
out << R"(rank="same")" << std::endl; out << R"(rank="same")" << std::endl;
out << name_for(i->true_.target) << std::endl; out << name_for(i->True().target) << std::endl;
out << name_for(i->false_.target) << std::endl; out << name_for(i->False().target) << std::endl;
out << "}" << std::endl; out << "}" << std::endl;
Graph(i->true_.target); Graph(i->True().target);
Graph(i->false_.target); Graph(i->False().target);
Graph(i->merge.target); Graph(i->Merge().target);
}, },
[&](const ir::Loop* l) { [&](const ir::Loop* l) {
out << name_for(l) << R"( [label="loop"])" << std::endl; out << name_for(l) << R"( [label="loop"])" << std::endl;
out << name_for(l->start.target) << R"( [label="start"])" << std::endl; out << name_for(l->Start().target) << R"( [label="start"])" << std::endl;
out << name_for(l->continuing.target) << R"( [label="continuing"])" << std::endl; out << name_for(l->Continuing().target) << R"( [label="continuing"])" << std::endl;
out << name_for(l->merge.target) << R"( [label="loop merge"])" << std::endl; out << name_for(l->Merge().target) << R"( [label="loop merge"])" << std::endl;
merge_nodes.insert(l->merge.target); merge_nodes.insert(l->Merge().target);
// Subgraph the continuing and merge so they get drawn on the same line // Subgraph the continuing and merge so they get drawn on the same line
out << "subgraph sub_" << name_for(l) << " {" << std::endl; out << "subgraph sub_" << name_for(l) << " {" << std::endl;
out << R"(rank="same")" << std::endl; out << R"(rank="same")" << std::endl;
out << name_for(l->continuing.target) << std::endl; out << name_for(l->Continuing().target) << std::endl;
out << name_for(l->merge.target) << std::endl; out << name_for(l->Merge().target) << std::endl;
out << "}" << std::endl; out << "}" << std::endl;
out << name_for(l) << " -> " << name_for(l->start.target) << std::endl; out << name_for(l) << " -> " << name_for(l->Start().target) << std::endl;
Graph(l->start.target); Graph(l->Start().target);
Graph(l->continuing.target); Graph(l->Continuing().target);
Graph(l->merge.target); Graph(l->Merge().target);
}, },
[&](const ir::FunctionTerminator*) { [&](const ir::FunctionTerminator*) {
// Already done // Already done
@ -145,10 +145,10 @@ std::string Debug::AsDotGraph(const Module* mod) {
for (const auto* func : mod->functions) { for (const auto* func : mod->functions) {
// Cluster each function to label and draw a box around it. // Cluster each function to label and draw a box around it.
out << "subgraph cluster_" << name_for(func) << " {" << std::endl; out << "subgraph cluster_" << name_for(func) << " {" << std::endl;
out << R"(label=")" << func->name.Name() << R"(")" << std::endl; out << R"(label=")" << func->Name().Name() << R"(")" << std::endl;
out << name_for(func->start_target) << R"( [label="start"])" << std::endl; out << name_for(func->StartTarget()) << R"( [label="start"])" << std::endl;
out << name_for(func->end_target) << R"( [label="end"])" << std::endl; out << name_for(func->EndTarget()) << R"( [label="end"])" << std::endl;
Graph(func->start_target); Graph(func->StartTarget());
out << "}" << std::endl; out << "}" << std::endl;
} }
out << "}"; out << "}";

View File

@ -81,7 +81,7 @@ utils::StringStream& Disassembler::Indent() {
} }
void Disassembler::EmitBlockInstructions(const Block* b) { void Disassembler::EmitBlockInstructions(const Block* b) {
for (const auto* inst : b->instructions) { for (const auto* inst : b->Instructions()) {
Indent(); Indent();
EmitInstruction(inst); EmitInstruction(inst);
out_ << std::endl; out_ << std::endl;
@ -114,31 +114,31 @@ void Disassembler::Walk(const FlowNode* node) {
[&](const ir::Function* f) { [&](const ir::Function* f) {
TINT_SCOPED_ASSIGNMENT(in_function_, true); TINT_SCOPED_ASSIGNMENT(in_function_, true);
Indent() << "%fn" << IdOf(f) << " = func " << f->name.Name() << "("; Indent() << "%fn" << IdOf(f) << " = func " << f->Name().Name() << "(";
for (auto* p : f->params) { for (auto* p : f->Params()) {
if (p != f->params.Front()) { if (p != f->Params().Front()) {
out_ << ", "; out_ << ", ";
} }
out_ << "%" << IdOf(p) << ":" << p->type->FriendlyName(); out_ << "%" << IdOf(p) << ":" << p->Type()->FriendlyName();
} }
out_ << "):" << f->return_type->FriendlyName(); out_ << "):" << f->ReturnType()->FriendlyName();
if (f->pipeline_stage != Function::PipelineStage::kUndefined) { if (f->Stage() != Function::PipelineStage::kUndefined) {
out_ << " [@" << f->pipeline_stage; out_ << " [@" << f->Stage();
if (f->workgroup_size) { if (f->WorkgroupSize()) {
auto arr = f->workgroup_size.value(); auto arr = f->WorkgroupSize().value();
out_ << " @workgroup_size(" << arr[0] << ", " << arr[1] << ", " << arr[2] out_ << " @workgroup_size(" << arr[0] << ", " << arr[1] << ", " << arr[2]
<< ")"; << ")";
} }
if (!f->return_attributes.IsEmpty()) { if (!f->ReturnAttributes().IsEmpty()) {
out_ << " ra:"; out_ << " ra:";
for (auto attr : f->return_attributes) { for (auto attr : f->ReturnAttributes()) {
out_ << " @" << attr; out_ << " @" << attr;
if (attr == Function::ReturnAttribute::kLocation) { if (attr == Function::ReturnAttribute::kLocation) {
out_ << "(" << f->return_location.value() << ")"; out_ << "(" << f->ReturnLocation().value() << ")";
} }
} }
} }
@ -149,23 +149,23 @@ void Disassembler::Walk(const FlowNode* node) {
{ {
ScopedIndent func_indent(indent_size_); ScopedIndent func_indent(indent_size_);
ScopedStopNode scope(stop_nodes_, f->end_target); ScopedStopNode scope(stop_nodes_, f->EndTarget());
Walk(f->start_target); Walk(f->StartTarget());
} }
out_ << "} "; out_ << "} ";
Walk(f->end_target); Walk(f->EndTarget());
}, },
[&](const ir::Block* b) { [&](const ir::Block* b) {
// If this block is dead, nothing to do // If this block is dead, nothing to do
if (b->IsDead()) { if (!b->HasBranchTarget()) {
return; return;
} }
Indent() << "%fn" << IdOf(b) << " = block"; Indent() << "%fn" << IdOf(b) << " = block";
if (!b->params.IsEmpty()) { if (!b->Params().IsEmpty()) {
out_ << " ("; out_ << " (";
for (auto* p : b->params) { for (const auto* p : b->Params()) {
if (p != b->params.Front()) { if (p != b->Params().Front()) {
out_ << ", "; out_ << ", ";
} }
EmitValue(p); EmitValue(p);
@ -181,20 +181,20 @@ void Disassembler::Walk(const FlowNode* node) {
Indent() << "}"; Indent() << "}";
std::string suffix = ""; std::string suffix = "";
if (b->branch.target->Is<FunctionTerminator>()) { if (b->Branch().target->Is<FunctionTerminator>()) {
out_ << " -> %func_end"; out_ << " -> %func_end";
suffix = "return"; suffix = "return";
} else if (b->branch.target->Is<RootTerminator>()) { } else if (b->Branch().target->Is<RootTerminator>()) {
// Nothing to do // Nothing to do
} else { } else {
out_ << " -> " out_ << " -> "
<< "%fn" << IdOf(b->branch.target); << "%fn" << IdOf(b->Branch().target);
suffix = "branch"; suffix = "branch";
} }
if (!b->branch.args.IsEmpty()) { if (!b->Branch().args.IsEmpty()) {
out_ << " "; out_ << " ";
for (const auto* v : b->branch.args) { for (const auto* v : b->Branch().args) {
if (v != b->branch.args.Front()) { if (v != b->Branch().args.Front()) {
out_ << ", "; out_ << ", ";
} }
EmitValue(v); EmitValue(v);
@ -205,18 +205,18 @@ void Disassembler::Walk(const FlowNode* node) {
} }
out_ << std::endl; out_ << std::endl;
if (!b->branch.target->Is<FunctionTerminator>()) { if (!b->Branch().target->Is<FunctionTerminator>()) {
out_ << std::endl; out_ << std::endl;
} }
Walk(b->branch.target); Walk(b->Branch().target);
}, },
[&](const ir::Switch* s) { [&](const ir::Switch* s) {
Indent() << "%fn" << IdOf(s) << " = switch "; Indent() << "%fn" << IdOf(s) << " = switch ";
EmitValue(s->condition); EmitValue(s->Condition());
out_ << " ["; out_ << " [";
for (const auto& c : s->cases) { for (const auto& c : s->Cases()) {
if (&c != &s->cases.Front()) { if (&c != &s->Cases().Front()) {
out_ << ", "; out_ << ", ";
} }
out_ << "c: ("; out_ << "c: (";
@ -231,17 +231,17 @@ void Disassembler::Walk(const FlowNode* node) {
EmitValue(selector.val); EmitValue(selector.val);
} }
} }
out_ << ", %fn" << IdOf(c.start.target) << ")"; out_ << ", %fn" << IdOf(c.Start().target) << ")";
} }
if (s->merge.target->IsConnected()) { if (s->Merge().target->IsConnected()) {
out_ << ", m: %fn" << IdOf(s->merge.target); out_ << ", m: %fn" << IdOf(s->Merge().target);
} }
out_ << "]" << std::endl; out_ << "]" << std::endl;
{ {
ScopedIndent switch_indent(indent_size_); ScopedIndent switch_indent(indent_size_);
ScopedStopNode scope(stop_nodes_, s->merge.target); ScopedStopNode scope(stop_nodes_, s->Merge().target);
for (const auto& c : s->cases) { for (const auto& c : s->Cases()) {
Indent() << "# case "; Indent() << "# case ";
for (const auto& selector : c.selectors) { for (const auto& selector : c.selectors) {
if (&selector != &c.selectors.Front()) { if (&selector != &c.selectors.Front()) {
@ -255,86 +255,86 @@ void Disassembler::Walk(const FlowNode* node) {
} }
} }
out_ << std::endl; out_ << std::endl;
Walk(c.start.target); Walk(c.Start().target);
} }
} }
if (s->merge.target->IsConnected()) { if (s->Merge().target->IsConnected()) {
Indent() << "# switch merge" << std::endl; Indent() << "# switch merge" << std::endl;
Walk(s->merge.target); Walk(s->Merge().target);
} }
}, },
[&](const ir::If* i) { [&](const ir::If* i) {
Indent() << "%fn" << IdOf(i) << " = if "; Indent() << "%fn" << IdOf(i) << " = if ";
EmitValue(i->condition); EmitValue(i->Condition());
bool has_true = !i->true_.target->IsDead(); bool has_true = i->True().target->HasBranchTarget();
bool has_false = !i->false_.target->IsDead(); bool has_false = i->False().target->HasBranchTarget();
out_ << " ["; out_ << " [";
if (has_true) { if (has_true) {
out_ << "t: %fn" << IdOf(i->true_.target); out_ << "t: %fn" << IdOf(i->True().target);
} }
if (has_false) { if (has_false) {
if (has_true) { if (has_true) {
out_ << ", "; out_ << ", ";
} }
out_ << "f: %fn" << IdOf(i->false_.target); out_ << "f: %fn" << IdOf(i->False().target);
} }
if (i->merge.target->IsConnected()) { if (i->Merge().target->IsConnected()) {
out_ << ", m: %fn" << IdOf(i->merge.target); out_ << ", m: %fn" << IdOf(i->Merge().target);
} }
out_ << "]" << std::endl; out_ << "]" << std::endl;
{ {
ScopedIndent if_indent(indent_size_); ScopedIndent if_indent(indent_size_);
ScopedStopNode scope(stop_nodes_, i->merge.target); ScopedStopNode scope(stop_nodes_, i->Merge().target);
if (has_true) { if (has_true) {
Indent() << "# true branch" << std::endl; Indent() << "# true branch" << std::endl;
Walk(i->true_.target); Walk(i->True().target);
} }
if (has_false) { if (has_false) {
Indent() << "# false branch" << std::endl; Indent() << "# false branch" << std::endl;
Walk(i->false_.target); Walk(i->False().target);
} }
} }
if (i->merge.target->IsConnected()) { if (i->Merge().target->IsConnected()) {
Indent() << "# if merge" << std::endl; Indent() << "# if merge" << std::endl;
Walk(i->merge.target); Walk(i->Merge().target);
} }
}, },
[&](const ir::Loop* l) { [&](const ir::Loop* l) {
Indent() << "%fn" << IdOf(l) << " = loop [s: %fn" << IdOf(l->start.target); Indent() << "%fn" << IdOf(l) << " = loop [s: %fn" << IdOf(l->Start().target);
if (l->continuing.target->IsConnected()) { if (l->Continuing().target->IsConnected()) {
out_ << ", c: %fn" << IdOf(l->continuing.target); out_ << ", c: %fn" << IdOf(l->Continuing().target);
} }
if (l->merge.target->IsConnected()) { if (l->Merge().target->IsConnected()) {
out_ << ", m: %fn" << IdOf(l->merge.target); out_ << ", m: %fn" << IdOf(l->Merge().target);
} }
out_ << "]" << std::endl; out_ << "]" << std::endl;
{ {
ScopedStopNode loop_scope(stop_nodes_, l->merge.target); ScopedStopNode loop_scope(stop_nodes_, l->Merge().target);
ScopedIndent loop_indent(indent_size_); ScopedIndent loop_indent(indent_size_);
{ {
ScopedStopNode inner_scope(stop_nodes_, l->continuing.target); ScopedStopNode inner_scope(stop_nodes_, l->Continuing().target);
Indent() << "# loop start" << std::endl; Indent() << "# loop start" << std::endl;
Walk(l->start.target); Walk(l->Start().target);
} }
if (l->continuing.target->IsConnected()) { if (l->Continuing().target->IsConnected()) {
Indent() << "# loop continuing" << std::endl; Indent() << "# loop continuing" << std::endl;
Walk(l->continuing.target); Walk(l->Continuing().target);
} }
} }
if (l->merge.target->IsConnected()) { if (l->Merge().target->IsConnected()) {
Indent() << "# loop merge" << std::endl; Indent() << "# loop merge" << std::endl;
Walk(l->merge.target); Walk(l->Merge().target);
} }
}, },
[&](const ir::FunctionTerminator*) { [&](const ir::FunctionTerminator*) {
@ -407,7 +407,7 @@ void Disassembler::EmitValue(const Value* val) {
} }
}); });
}; };
emit(constant->value); emit(constant->Value());
}, },
[&](const ir::Instruction* i) { out_ << "%" << IdOf(i); }, [&](const ir::Instruction* i) { out_ << "%" << IdOf(i); },
[&](const ir::BlockParam* p) { [&](const ir::BlockParam* p) {
@ -445,18 +445,18 @@ void Disassembler::EmitInstruction(const Instruction* inst) {
[&](const ir::Load* l) { [&](const ir::Load* l) {
EmitValueWithType(l); EmitValueWithType(l);
out_ << " = load "; out_ << " = load ";
EmitValue(l->from); EmitValue(l->From());
}, },
[&](const ir::Store* s) { [&](const ir::Store* s) {
out_ << "store "; out_ << "store ";
EmitValue(s->to); EmitValue(s->To());
out_ << ", "; out_ << ", ";
EmitValue(s->from); EmitValue(s->From());
}, },
[&](const ir::UserCall* uc) { [&](const ir::UserCall* uc) {
EmitValueWithType(uc); EmitValueWithType(uc);
out_ << " = call " << uc->name.Name(); out_ << " = call " << uc->Name().Name();
if (uc->args.Length() > 0) { if (!uc->Args().IsEmpty()) {
out_ << ", "; out_ << ", ";
} }
EmitArgs(uc); EmitArgs(uc);
@ -464,16 +464,16 @@ void Disassembler::EmitInstruction(const Instruction* inst) {
[&](const ir::Var* v) { [&](const ir::Var* v) {
EmitValueWithType(v); EmitValueWithType(v);
out_ << " = var"; out_ << " = var";
if (v->initializer) { if (v->Initializer()) {
out_ << ", "; out_ << ", ";
EmitValue(v->initializer); EmitValue(v->Initializer());
} }
}); });
} }
void Disassembler::EmitArgs(const Call* call) { void Disassembler::EmitArgs(const Call* call) {
bool first = true; bool first = true;
for (const auto* arg : call->args) { for (const auto* arg : call->Args()) {
if (!first) { if (!first) {
out_ << ", "; out_ << ", ";
} }
@ -485,7 +485,7 @@ void Disassembler::EmitArgs(const Call* call) {
void Disassembler::EmitBinary(const Binary* b) { void Disassembler::EmitBinary(const Binary* b) {
EmitValueWithType(b); EmitValueWithType(b);
out_ << " = "; out_ << " = ";
switch (b->kind) { switch (b->Kind()) {
case Binary::Kind::kAdd: case Binary::Kind::kAdd:
out_ << "add"; out_ << "add";
break; break;
@ -544,7 +544,7 @@ void Disassembler::EmitBinary(const Binary* b) {
void Disassembler::EmitUnary(const Unary* u) { void Disassembler::EmitUnary(const Unary* u) {
EmitValueWithType(u); EmitValueWithType(u);
out_ << " = "; out_ << " = ";
switch (u->kind) { switch (u->Kind()) {
case Unary::Kind::kComplement: case Unary::Kind::kComplement:
out_ << "complement"; out_ << "complement";
break; break;

View File

@ -25,22 +25,30 @@ class FlowNode : public utils::Castable<FlowNode> {
public: public:
~FlowNode() override; ~FlowNode() override;
/// The list of flow nodes which branch into this node. This list maybe empty for several
/// reasons:
/// - Node is a start node
/// - Node is a merge target outside control flow (if that returns in both branches)
/// - Node is a continue target outside control flow (loop that returns)
utils::Vector<FlowNode*, 2> inbound_branches;
/// @returns true if this node has inbound branches and branches out /// @returns true if this node has inbound branches and branches out
bool IsConnected() const { return !IsDead() && !inbound_branches.IsEmpty(); } bool IsConnected() const { return HasBranchTarget() && !inbound_branches_.IsEmpty(); }
/// @returns true if the node does not branch out /// @returns true if the node has a branch target
virtual bool IsDead() const { return false; } virtual bool HasBranchTarget() const { return false; }
/// @returns the inbound branch list for the flow node
utils::VectorRef<FlowNode*> InboundBranches() const { return inbound_branches_; }
/// Adds the given node to the inbound branches
/// @param node the node to add
void AddInboundBranch(FlowNode* node) { inbound_branches_.Push(node); }
protected: protected:
/// Constructor /// Constructor
FlowNode(); FlowNode();
private:
/// The list of flow nodes which branch into this node. This list maybe empty for several
/// reasons:
/// - Node is a start node
/// - Node is a merge target outside control flow (e.g. an if that returns in both branches)
/// - Node is a continue target outside control flow (e.g. a loop that returns)
utils::Vector<FlowNode*, 2> inbound_branches_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -98,17 +98,13 @@ namespace {
using ResultType = utils::Result<Module, diag::List>; using ResultType = utils::Result<Module, diag::List>;
bool IsBranched(const Block* b) {
return b->branch.target != nullptr;
}
bool IsConnected(const FlowNode* b) { bool IsConnected(const FlowNode* b) {
// Function is always connected as it's the start. // Function is always connected as it's the start.
if (b->Is<ir::Function>()) { if (b->Is<ir::Function>()) {
return true; return true;
} }
for (auto* parent : b->inbound_branches) { for (auto* parent : b->InboundBranches()) {
if (IsConnected(parent)) { if (IsConnected(parent)) {
return true; return true;
} }
@ -184,14 +180,14 @@ class Impl {
void BranchTo(FlowNode* node, utils::VectorRef<Value*> args = {}) { void BranchTo(FlowNode* node, utils::VectorRef<Value*> args = {}) {
TINT_ASSERT(IR, current_flow_block_); TINT_ASSERT(IR, current_flow_block_);
TINT_ASSERT(IR, !IsBranched(current_flow_block_)); TINT_ASSERT(IR, !current_flow_block_->HasBranchTarget());
builder_.Branch(current_flow_block_, node, args); current_flow_block_->BranchTo(node, args);
current_flow_block_ = nullptr; current_flow_block_ = nullptr;
} }
void BranchToIfNeeded(FlowNode* node) { void BranchToIfNeeded(FlowNode* node) {
if (!current_flow_block_ || IsBranched(current_flow_block_)) { if (!current_flow_block_ || current_flow_block_->HasBranchTarget()) {
return; return;
} }
BranchTo(node); BranchTo(node);
@ -271,20 +267,17 @@ class Impl {
if (ast_func->IsEntryPoint()) { if (ast_func->IsEntryPoint()) {
switch (ast_func->PipelineStage()) { switch (ast_func->PipelineStage()) {
case ast::PipelineStage::kVertex: case ast::PipelineStage::kVertex:
ir_func->pipeline_stage = Function::PipelineStage::kVertex; ir_func->SetStage(Function::PipelineStage::kVertex);
break; break;
case ast::PipelineStage::kFragment: case ast::PipelineStage::kFragment:
ir_func->pipeline_stage = Function::PipelineStage::kFragment; ir_func->SetStage(Function::PipelineStage::kFragment);
break; break;
case ast::PipelineStage::kCompute: { case ast::PipelineStage::kCompute: {
ir_func->pipeline_stage = Function::PipelineStage::kCompute; ir_func->SetStage(Function::PipelineStage::kCompute);
auto wg_size = sem->WorkgroupSize(); auto wg_size = sem->WorkgroupSize();
ir_func->workgroup_size = { ir_func->SetWorkgroupSize(wg_size[0].value(), wg_size[1].value_or(1),
wg_size[0].value(), wg_size[2].value_or(1));
wg_size[1].value_or(1),
wg_size[2].value_or(1),
};
break; break;
} }
default: { default: {
@ -293,14 +286,15 @@ class Impl {
} }
} }
utils::Vector<Function::ReturnAttribute, 1> return_attributes;
for (auto* attr : ast_func->return_type_attributes) { for (auto* attr : ast_func->return_type_attributes) {
tint::Switch( tint::Switch(
attr, // attr, //
[&](const ast::LocationAttribute*) { [&](const ast::LocationAttribute*) {
ir_func->return_attributes.Push(Function::ReturnAttribute::kLocation); return_attributes.Push(Function::ReturnAttribute::kLocation);
}, },
[&](const ast::InvariantAttribute*) { [&](const ast::InvariantAttribute*) {
ir_func->return_attributes.Push(Function::ReturnAttribute::kInvariant); return_attributes.Push(Function::ReturnAttribute::kInvariant);
}, },
[&](const ast::BuiltinAttribute* b) { [&](const ast::BuiltinAttribute* b) {
if (auto* ident_sem = if (auto* ident_sem =
@ -309,16 +303,13 @@ class Impl {
->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>()) { ->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>()) {
switch (ident_sem->Value()) { switch (ident_sem->Value()) {
case builtin::BuiltinValue::kPosition: case builtin::BuiltinValue::kPosition:
ir_func->return_attributes.Push( return_attributes.Push(Function::ReturnAttribute::kPosition);
Function::ReturnAttribute::kPosition);
break; break;
case builtin::BuiltinValue::kFragDepth: case builtin::BuiltinValue::kFragDepth:
ir_func->return_attributes.Push( return_attributes.Push(Function::ReturnAttribute::kFragDepth);
Function::ReturnAttribute::kFragDepth);
break; break;
case builtin::BuiltinValue::kSampleMask: case builtin::BuiltinValue::kSampleMask:
ir_func->return_attributes.Push( return_attributes.Push(Function::ReturnAttribute::kSampleMask);
Function::ReturnAttribute::kSampleMask);
break; break;
default: default:
TINT_ICE(IR, diagnostics_) TINT_ICE(IR, diagnostics_)
@ -332,8 +323,9 @@ class Impl {
} }
}); });
} }
ir_func->SetReturnAttributes(return_attributes);
} }
ir_func->return_location = sem->ReturnLocation(); ir_func->SetReturnLocation(sem->ReturnLocation());
scopes_.Push(); scopes_.Push();
TINT_DEFER(scopes_.Pop()); TINT_DEFER(scopes_.Pop());
@ -348,17 +340,17 @@ class Impl {
builder_.ir.SetName(param, p->name->symbol.NameView()); builder_.ir.SetName(param, p->name->symbol.NameView());
params.Push(param); params.Push(param);
} }
ir_func->params = std::move(params); ir_func->SetParams(params);
{ {
FlowStackScope scope(this, ir_func); FlowStackScope scope(this, ir_func);
current_flow_block_ = ir_func->start_target; current_flow_block_ = ir_func->StartTarget();
EmitBlock(ast_func->body); EmitBlock(ast_func->body);
// If the branch target has already been set then a `return` was called. Only set in the // If the branch target has already been set then a `return` was called. Only set in the
// case where `return` wasn't called. // case where `return` wasn't called.
BranchToIfNeeded(current_function_->end_target); BranchToIfNeeded(current_function_->EndTarget());
} }
TINT_ASSERT(IR, flow_stack_.IsEmpty()); TINT_ASSERT(IR, flow_stack_.IsEmpty());
@ -372,7 +364,7 @@ class Impl {
// If the current flow block has a branch target then the rest of the statements in this // If the current flow block has a branch target then the rest of the statements in this
// block are dead code. Skip them. // block are dead code. Skip them.
if (!current_flow_block_ || IsBranched(current_flow_block_)) { if (!current_flow_block_ || current_flow_block_->HasBranchTarget()) {
break; break;
} }
} }
@ -427,7 +419,7 @@ class Impl {
return; return;
} }
auto store = builder_.Store(lhs.Get(), rhs.Get()); auto store = builder_.Store(lhs.Get(), rhs.Get());
current_flow_block_->instructions.Push(store); current_flow_block_->Instructions().Push(store);
} }
void EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) { void EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
@ -438,7 +430,7 @@ class Impl {
// Load from the LHS. // Load from the LHS.
auto* lhs_value = builder_.Load(lhs.Get()); auto* lhs_value = builder_.Load(lhs.Get());
current_flow_block_->instructions.Push(lhs_value); current_flow_block_->Instructions().Push(lhs_value);
auto* ty = lhs_value->Type(); auto* ty = lhs_value->Type();
@ -451,10 +443,10 @@ class Impl {
} else { } else {
inst = builder_.Subtract(ty, lhs_value, rhs); inst = builder_.Subtract(ty, lhs_value, rhs);
} }
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
auto store = builder_.Store(lhs.Get(), inst); auto store = builder_.Store(lhs.Get(), inst);
current_flow_block_->instructions.Push(store); current_flow_block_->Instructions().Push(store);
} }
void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) { void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) {
@ -470,7 +462,7 @@ class Impl {
// Load from the LHS. // Load from the LHS.
auto* lhs_value = builder_.Load(lhs.Get()); auto* lhs_value = builder_.Load(lhs.Get());
current_flow_block_->instructions.Push(lhs_value); current_flow_block_->Instructions().Push(lhs_value);
auto* ty = lhs_value->Type(); auto* ty = lhs_value->Type();
@ -520,10 +512,10 @@ class Impl {
TINT_ICE(IR, diagnostics_) << "missing binary operand type"; TINT_ICE(IR, diagnostics_) << "missing binary operand type";
return; return;
} }
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
auto store = builder_.Store(lhs.Get(), inst); auto store = builder_.Store(lhs.Get(), inst);
current_flow_block_->instructions.Push(store); current_flow_block_->Instructions().Push(store);
} }
void EmitBlock(const ast::BlockStatement* block) { void EmitBlock(const ast::BlockStatement* block) {
@ -551,27 +543,27 @@ class Impl {
{ {
FlowStackScope scope(this, if_node); FlowStackScope scope(this, if_node);
current_flow_block_ = if_node->true_.target->As<Block>(); current_flow_block_ = if_node->True().target->As<Block>();
EmitBlock(stmt->body); EmitBlock(stmt->body);
// If the true branch did not execute control flow, then go to the merge target // If the true branch did not execute control flow, then go to the Merge().target
BranchToIfNeeded(if_node->merge.target); BranchToIfNeeded(if_node->Merge().target);
current_flow_block_ = if_node->false_.target->As<Block>(); current_flow_block_ = if_node->False().target->As<Block>();
if (stmt->else_statement) { if (stmt->else_statement) {
EmitStatement(stmt->else_statement); EmitStatement(stmt->else_statement);
} }
// If the false branch did not execute control flow, then go to the merge target // If the false branch did not execute control flow, then go to the Merge().target
BranchToIfNeeded(if_node->merge.target); BranchToIfNeeded(if_node->Merge().target);
} }
current_flow_block_ = nullptr; current_flow_block_ = nullptr;
// If both branches went somewhere, then they both returned, continued or broke. So, there // If both branches went somewhere, then they both returned, continued or broke. So, there
// is no need for the if merge-block and there is nothing to branch to the merge block // is no need for the if merge-block and there is nothing to branch to the merge block
// anyway. // anyway.
if (IsConnected(if_node->merge.target)) { if (IsConnected(if_node->Merge().target)) {
current_flow_block_ = if_node->merge.target->As<Block>(); current_flow_block_ = if_node->Merge().target->As<Block>();
} }
} }
@ -585,7 +577,7 @@ class Impl {
{ {
FlowStackScope scope(this, loop_node); FlowStackScope scope(this, loop_node);
current_flow_block_ = loop_node->start.target->As<Block>(); current_flow_block_ = loop_node->Start().target->As<Block>();
// The loop doesn't use EmitBlock because it needs the scope stack to not get popped // The loop doesn't use EmitBlock because it needs the scope stack to not get popped
// until after the continuing block. // until after the continuing block.
@ -594,21 +586,22 @@ class Impl {
EmitStatements(stmt->body->statements); EmitStatements(stmt->body->statements);
// The current block didn't `break`, `return` or `continue`, go to the continuing block. // The current block didn't `break`, `return` or `continue`, go to the continuing block.
BranchToIfNeeded(loop_node->continuing.target); BranchToIfNeeded(loop_node->Continuing().target);
current_flow_block_ = loop_node->continuing.target->As<Block>(); current_flow_block_ = loop_node->Continuing().target->As<Block>();
if (stmt->continuing) { if (stmt->continuing) {
EmitBlock(stmt->continuing); EmitBlock(stmt->continuing);
} }
// Branch back to the start node if the continue target didn't branch out already // Branch back to the start node if the continue target didn't branch out already
BranchToIfNeeded(loop_node->start.target); BranchToIfNeeded(loop_node->Start().target);
} }
// The loop merge can get disconnected if the loop returns directly, or the continuing // The loop merge can get disconnected if the loop returns directly, or the continuing
// target branches, eventually, to the merge, but nothing branched to the continuing target. // target branches, eventually, to the merge, but nothing branched to the
current_flow_block_ = loop_node->merge.target->As<Block>(); // Continuing().target.
if (!IsConnected(loop_node->merge.target)) { current_flow_block_ = loop_node->Merge().target->As<Block>();
if (!IsConnected(loop_node->Merge().target)) {
current_flow_block_ = nullptr; current_flow_block_ = nullptr;
} }
} }
@ -616,9 +609,8 @@ class Impl {
void EmitWhile(const ast::WhileStatement* stmt) { void EmitWhile(const ast::WhileStatement* stmt) {
auto* loop_node = builder_.CreateLoop(); auto* loop_node = builder_.CreateLoop();
// Continue is always empty, just go back to the start // Continue is always empty, just go back to the start
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>()); TINT_ASSERT(IR, loop_node->Continuing().target->Is<Block>());
builder_.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target, loop_node->Continuing().target->As<Block>()->BranchTo(loop_node->Start().target);
utils::Empty);
BranchTo(loop_node); BranchTo(loop_node);
@ -627,9 +619,9 @@ class Impl {
{ {
FlowStackScope scope(this, loop_node); FlowStackScope scope(this, loop_node);
current_flow_block_ = loop_node->start.target->As<Block>(); current_flow_block_ = loop_node->Start().target->As<Block>();
// Emit the while condition into the start target of the loop // Emit the while condition into the Start().target of the loop
auto reg = EmitExpression(stmt->condition); auto reg = EmitExpression(stmt->condition);
if (!reg) { if (!reg) {
return; return;
@ -637,31 +629,24 @@ class Impl {
// Create an `if (cond) {} else {break;}` control flow // Create an `if (cond) {} else {break;}` control flow
auto* if_node = builder_.CreateIf(reg.Get()); auto* if_node = builder_.CreateIf(reg.Get());
TINT_ASSERT(IR, if_node->true_.target->Is<Block>()); if_node->True().target->As<Block>()->BranchTo(if_node->Merge().target);
builder_.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, if_node->False().target->As<Block>()->BranchTo(loop_node->Merge().target);
utils::Empty);
TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
builder_.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target,
utils::Empty);
BranchTo(if_node); BranchTo(if_node);
current_flow_block_ = if_node->merge.target->As<Block>(); current_flow_block_ = if_node->Merge().target->As<Block>();
EmitBlock(stmt->body); EmitBlock(stmt->body);
BranchToIfNeeded(loop_node->continuing.target); BranchToIfNeeded(loop_node->Continuing().target);
} }
// The while loop always has a path to the merge target as the break statement comes before // The while loop always has a path to the Merge().target as the break statement comes
// anything inside the loop. // before anything inside the loop.
current_flow_block_ = loop_node->merge.target->As<Block>(); current_flow_block_ = loop_node->Merge().target->As<Block>();
} }
void EmitForLoop(const ast::ForLoopStatement* stmt) { void EmitForLoop(const ast::ForLoopStatement* stmt) {
auto* loop_node = builder_.CreateLoop(); auto* loop_node = builder_.CreateLoop();
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>()); loop_node->Continuing().target->As<Block>()->BranchTo(loop_node->Start().target);
builder_.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
utils::Empty);
// Make sure the initializer ends up in a contained scope // Make sure the initializer ends up in a contained scope
scopes_.Push(); scopes_.Push();
@ -679,7 +664,7 @@ class Impl {
{ {
FlowStackScope scope(this, loop_node); FlowStackScope scope(this, loop_node);
current_flow_block_ = loop_node->start.target->As<Block>(); current_flow_block_ = loop_node->Start().target->As<Block>();
if (stmt->condition) { if (stmt->condition) {
// Emit the condition into the target target of the loop // Emit the condition into the target target of the loop
@ -690,30 +675,25 @@ class Impl {
// Create an `if (cond) {} else {break;}` control flow // Create an `if (cond) {} else {break;}` control flow
auto* if_node = builder_.CreateIf(reg.Get()); auto* if_node = builder_.CreateIf(reg.Get());
TINT_ASSERT(IR, if_node->true_.target->Is<Block>()); if_node->True().target->As<Block>()->BranchTo(if_node->Merge().target);
builder_.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, if_node->False().target->As<Block>()->BranchTo(loop_node->Merge().target);
utils::Empty);
TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
builder_.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target,
utils::Empty);
BranchTo(if_node); BranchTo(if_node);
current_flow_block_ = if_node->merge.target->As<Block>(); current_flow_block_ = if_node->Merge().target->As<Block>();
} }
EmitBlock(stmt->body); EmitBlock(stmt->body);
BranchToIfNeeded(loop_node->continuing.target); BranchToIfNeeded(loop_node->Continuing().target);
if (stmt->continuing) { if (stmt->continuing) {
current_flow_block_ = loop_node->continuing.target->As<Block>(); current_flow_block_ = loop_node->Continuing().target->As<Block>();
EmitStatement(stmt->continuing); EmitStatement(stmt->continuing);
} }
} }
// The while loop always has a path to the merge target as the break statement comes before // The while loop always has a path to the Merge().target as the break statement comes
// anything inside the loop. // before anything inside the loop.
current_flow_block_ = loop_node->merge.target->As<Block>(); current_flow_block_ = loop_node->Merge().target->As<Block>();
} }
void EmitSwitch(const ast::SwitchStatement* stmt) { void EmitSwitch(const ast::SwitchStatement* stmt) {
@ -745,13 +725,13 @@ class Impl {
current_flow_block_ = builder_.CreateCase(switch_node, selectors); current_flow_block_ = builder_.CreateCase(switch_node, selectors);
EmitBlock(c->Body()->Declaration()); EmitBlock(c->Body()->Declaration());
BranchToIfNeeded(switch_node->merge.target); BranchToIfNeeded(switch_node->Merge().target);
} }
} }
current_flow_block_ = nullptr; current_flow_block_ = nullptr;
if (IsConnected(switch_node->merge.target)) { if (IsConnected(switch_node->Merge().target)) {
current_flow_block_ = switch_node->merge.target->As<Block>(); current_flow_block_ = switch_node->Merge().target->As<Block>();
} }
} }
@ -765,7 +745,7 @@ class Impl {
ret_value.Push(ret.Get()); ret_value.Push(ret.Get());
} }
BranchTo(current_function_->end_target, std::move(ret_value)); BranchTo(current_function_->EndTarget(), std::move(ret_value));
} }
void EmitBreak(const ast::BreakStatement*) { void EmitBreak(const ast::BreakStatement*) {
@ -773,9 +753,9 @@ class Impl {
TINT_ASSERT(IR, current_control); TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) { if (auto* c = current_control->As<Loop>()) {
BranchTo(c->merge.target); BranchTo(c->Merge().target);
} else if (auto* s = current_control->As<Switch>()) { } else if (auto* s = current_control->As<Switch>()) {
BranchTo(s->merge.target); BranchTo(s->Merge().target);
} else { } else {
TINT_UNREACHABLE(IR, diagnostics_); TINT_UNREACHABLE(IR, diagnostics_);
} }
@ -786,7 +766,7 @@ class Impl {
TINT_ASSERT(IR, current_control); TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) { if (auto* c = current_control->As<Loop>()) {
BranchTo(c->continuing.target); BranchTo(c->Continuing().target);
} else { } else {
TINT_UNREACHABLE(IR, diagnostics_); TINT_UNREACHABLE(IR, diagnostics_);
} }
@ -798,7 +778,7 @@ class Impl {
// figuring out the multi-level exit that is triggered. // figuring out the multi-level exit that is triggered.
void EmitDiscard(const ast::DiscardStatement*) { void EmitDiscard(const ast::DiscardStatement*) {
auto* inst = builder_.Discard(); auto* inst = builder_.Discard();
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
} }
void EmitBreakIf(const ast::BreakIfStatement* stmt) { void EmitBreakIf(const ast::BreakIfStatement* stmt) {
@ -819,17 +799,17 @@ class Impl {
auto* loop = current_control->As<Loop>(); auto* loop = current_control->As<Loop>();
current_flow_block_ = if_node->true_.target->As<Block>(); current_flow_block_ = if_node->True().target->As<Block>();
BranchTo(loop->merge.target); BranchTo(loop->Merge().target);
current_flow_block_ = if_node->false_.target->As<Block>(); current_flow_block_ = if_node->False().target->As<Block>();
BranchTo(if_node->merge.target); BranchTo(if_node->Merge().target);
current_flow_block_ = if_node->merge.target->As<Block>(); current_flow_block_ = if_node->Merge().target->As<Block>();
// The `break-if` has to be the last item in the continuing block. The false branch of the // The `break-if` has to be the last item in the continuing block. The false branch of the
// `break-if` will always take us back to the start of the loop. // `break-if` will always take us back to the start of the loop.
BranchTo(loop->start.target); BranchTo(loop->Start().target);
} }
utils::Result<Value*> EmitExpression(const ast::Expression* expr) { utils::Result<Value*> EmitExpression(const ast::Expression* expr) {
@ -876,7 +856,7 @@ class Impl {
// If this expression maps to sem::Load, insert a load instruction to get the result. // If this expression maps to sem::Load, insert a load instruction to get the result.
if (result && sem->Is<sem::Load>()) { if (result && sem->Is<sem::Load>()) {
auto* load = builder_.Load(result.Get()); auto* load = builder_.Load(result.Get());
current_flow_block_->instructions.Push(load); current_flow_block_->Instructions().Push(load);
return load; return load;
} }
@ -895,14 +875,14 @@ class Impl {
ref->Access()); ref->Access());
auto* val = builder_.Declare(ty); auto* val = builder_.Declare(ty);
current_flow_block_->instructions.Push(val); current_flow_block_->Instructions().Push(val);
if (v->initializer) { if (v->initializer) {
auto init = EmitExpression(v->initializer); auto init = EmitExpression(v->initializer);
if (!init) { if (!init) {
return; return;
} }
val->initializer = init.Get(); val->SetInitializer(init.Get());
} }
// Store the declaration so we can get the instruction to store too // Store the declaration so we can get the instruction to store too
scopes_.Set(v->name->symbol, val); scopes_.Set(v->name->symbol, val);
@ -969,7 +949,7 @@ class Impl {
break; break;
} }
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
return inst; return inst;
} }
@ -996,7 +976,7 @@ class Impl {
BranchTo(if_node); BranchTo(if_node);
auto* result = builder_.BlockParam(builder_.ir.types.Get<type::Bool>()); auto* result = builder_.BlockParam(builder_.ir.types.Get<type::Bool>());
if_node->merge.target->As<Block>()->params.Push(result); if_node->Merge().target->As<Block>()->SetParams(utils::Vector{result});
utils::Result<Value*> rhs; utils::Result<Value*> rhs;
{ {
@ -1010,17 +990,17 @@ class Impl {
if (expr->op == ast::BinaryOp::kLogicalAnd) { if (expr->op == ast::BinaryOp::kLogicalAnd) {
// If the lhs is false, then that is the result we want to pass to the merge block // If the lhs is false, then that is the result we want to pass to the merge block
// as our argument // as our argument
current_flow_block_ = if_node->false_.target->As<Block>(); current_flow_block_ = if_node->False().target->As<Block>();
BranchTo(if_node->merge.target, std::move(alt_args)); BranchTo(if_node->Merge().target, std::move(alt_args));
current_flow_block_ = if_node->true_.target->As<Block>(); current_flow_block_ = if_node->True().target->As<Block>();
} else { } else {
// If the lhs is true, then that is the result we want to pass to the merge block // If the lhs is true, then that is the result we want to pass to the merge block
// as our argument // as our argument
current_flow_block_ = if_node->true_.target->As<Block>(); current_flow_block_ = if_node->True().target->As<Block>();
BranchTo(if_node->merge.target, std::move(alt_args)); BranchTo(if_node->Merge().target, std::move(alt_args));
current_flow_block_ = if_node->false_.target->As<Block>(); current_flow_block_ = if_node->False().target->As<Block>();
} }
rhs = EmitExpression(expr->rhs); rhs = EmitExpression(expr->rhs);
@ -1030,9 +1010,9 @@ class Impl {
utils::Vector<Value*, 1> args; utils::Vector<Value*, 1> args;
args.Push(rhs.Get()); args.Push(rhs.Get());
BranchTo(if_node->merge.target, std::move(args)); BranchTo(if_node->Merge().target, std::move(args));
} }
current_flow_block_ = if_node->merge.target->As<Block>(); current_flow_block_ = if_node->Merge().target->As<Block>();
return result; return result;
} }
@ -1114,7 +1094,7 @@ class Impl {
return utils::Failure; return utils::Failure;
} }
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
return inst; return inst;
} }
@ -1128,7 +1108,7 @@ class Impl {
auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx); auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
auto* inst = builder_.Bitcast(ty, val.Get()); auto* inst = builder_.Bitcast(ty, val.Get());
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
return inst; return inst;
} }
@ -1191,7 +1171,7 @@ class Impl {
if (inst == nullptr) { if (inst == nullptr) {
return utils::Failure; return utils::Failure;
} }
current_flow_block_->instructions.Push(inst); current_flow_block_->Instructions().Push(inst);
return inst; return inst;
} }

View File

@ -25,17 +25,19 @@
namespace tint::ir { namespace tint::ir {
namespace { namespace {
Value* GlobalVarInitializer(const Module& m) { const Value* GlobalVarInitializer(const Module& m) {
if (m.root_block->instructions.Length() == 0u) { const auto instr = m.root_block->Instructions();
if (instr.Length() == 0u) {
ADD_FAILURE() << "m.root_block has no instruction"; ADD_FAILURE() << "m.root_block has no instruction";
return nullptr; return nullptr;
} }
auto* var = m.root_block->instructions[0]->As<ir::Var>(); auto* var = instr[0]->As<ir::Var>();
if (!var) { if (!var) {
ADD_FAILURE() << "m.root_block.instructions[0] was not a var"; ADD_FAILURE() << "m.root_block.instructions[0] was not a var";
return nullptr; return nullptr;
} }
return var->initializer; return var->Initializer();
} }
using namespace tint::number_suffixes; // NOLINT using namespace tint::number_suffixes; // NOLINT
@ -51,7 +53,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<bool>>()); EXPECT_TRUE(val->Is<constant::Scalar<bool>>());
EXPECT_TRUE(val->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_TRUE(val->As<constant::Scalar<bool>>()->ValueAs<bool>());
} }
@ -65,7 +67,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<bool>>()); EXPECT_TRUE(val->Is<constant::Scalar<bool>>());
EXPECT_FALSE(val->As<constant::Scalar<bool>>()->ValueAs<bool>()); EXPECT_FALSE(val->As<constant::Scalar<bool>>()->ValueAs<bool>());
} }
@ -79,18 +81,19 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_Deduped) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* var_a = m.Get().root_block->instructions[0]->As<ir::Var>(); auto instr = m.Get().root_block->Instructions();
auto* var_a = instr[0]->As<ir::Var>();
ASSERT_NE(var_a, nullptr); ASSERT_NE(var_a, nullptr);
auto* var_b = m.Get().root_block->instructions[1]->As<ir::Var>(); auto* var_b = instr[1]->As<ir::Var>();
ASSERT_NE(var_b, nullptr); ASSERT_NE(var_b, nullptr);
auto* var_c = m.Get().root_block->instructions[2]->As<ir::Var>(); auto* var_c = instr[2]->As<ir::Var>();
ASSERT_NE(var_c, nullptr); ASSERT_NE(var_c, nullptr);
auto* var_d = m.Get().root_block->instructions[3]->As<ir::Var>(); auto* var_d = instr[3]->As<ir::Var>();
ASSERT_NE(var_d, nullptr); ASSERT_NE(var_d, nullptr);
ASSERT_EQ(var_a->initializer, var_c->initializer); ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_EQ(var_b->initializer, var_d->initializer); ASSERT_EQ(var_b->Initializer(), var_d->Initializer());
ASSERT_NE(var_a->initializer, var_b->initializer); ASSERT_NE(var_a->Initializer(), var_b->Initializer());
} }
TEST_F(IR_BuilderImplTest, EmitLiteral_F32) { TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
@ -102,7 +105,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<f32>>()); EXPECT_TRUE(val->Is<constant::Scalar<f32>>());
EXPECT_EQ(1.2_f, val->As<constant::Scalar<f32>>()->ValueAs<f32>()); EXPECT_EQ(1.2_f, val->As<constant::Scalar<f32>>()->ValueAs<f32>());
} }
@ -115,15 +118,16 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F32_Deduped) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* var_a = m.Get().root_block->instructions[0]->As<ir::Var>(); auto instr = m.Get().root_block->Instructions();
auto* var_a = instr[0]->As<ir::Var>();
ASSERT_NE(var_a, nullptr); ASSERT_NE(var_a, nullptr);
auto* var_b = m.Get().root_block->instructions[1]->As<ir::Var>(); auto* var_b = instr[1]->As<ir::Var>();
ASSERT_NE(var_b, nullptr); ASSERT_NE(var_b, nullptr);
auto* var_c = m.Get().root_block->instructions[2]->As<ir::Var>(); auto* var_c = instr[2]->As<ir::Var>();
ASSERT_NE(var_c, nullptr); ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->initializer, var_c->initializer); ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->initializer, var_b->initializer); ASSERT_NE(var_a->Initializer(), var_b->Initializer());
} }
TEST_F(IR_BuilderImplTest, EmitLiteral_F16) { TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
@ -136,7 +140,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<f16>>()); EXPECT_TRUE(val->Is<constant::Scalar<f16>>());
EXPECT_EQ(1.2_h, val->As<constant::Scalar<f16>>()->ValueAs<f32>()); EXPECT_EQ(1.2_h, val->As<constant::Scalar<f16>>()->ValueAs<f32>());
} }
@ -150,15 +154,16 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F16_Deduped) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* var_a = m.Get().root_block->instructions[0]->As<ir::Var>(); auto instr = m.Get().root_block->Instructions();
auto* var_a = instr[0]->As<ir::Var>();
ASSERT_NE(var_a, nullptr); ASSERT_NE(var_a, nullptr);
auto* var_b = m.Get().root_block->instructions[1]->As<ir::Var>(); auto* var_b = instr[1]->As<ir::Var>();
ASSERT_NE(var_b, nullptr); ASSERT_NE(var_b, nullptr);
auto* var_c = m.Get().root_block->instructions[2]->As<ir::Var>(); auto* var_c = instr[2]->As<ir::Var>();
ASSERT_NE(var_c, nullptr); ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->initializer, var_c->initializer); ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->initializer, var_b->initializer); ASSERT_NE(var_a->Initializer(), var_b->Initializer());
} }
TEST_F(IR_BuilderImplTest, EmitLiteral_I32) { TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
@ -170,7 +175,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<i32>>()); EXPECT_TRUE(val->Is<constant::Scalar<i32>>());
EXPECT_EQ(-2_i, val->As<constant::Scalar<i32>>()->ValueAs<f32>()); EXPECT_EQ(-2_i, val->As<constant::Scalar<i32>>()->ValueAs<f32>());
} }
@ -183,15 +188,16 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_I32_Deduped) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* var_a = m.Get().root_block->instructions[0]->As<ir::Var>(); auto instr = m.Get().root_block->Instructions();
auto* var_a = instr[0]->As<ir::Var>();
ASSERT_NE(var_a, nullptr); ASSERT_NE(var_a, nullptr);
auto* var_b = m.Get().root_block->instructions[1]->As<ir::Var>(); auto* var_b = instr[1]->As<ir::Var>();
ASSERT_NE(var_b, nullptr); ASSERT_NE(var_b, nullptr);
auto* var_c = m.Get().root_block->instructions[2]->As<ir::Var>(); auto* var_c = instr[2]->As<ir::Var>();
ASSERT_NE(var_c, nullptr); ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->initializer, var_c->initializer); ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->initializer, var_b->initializer); ASSERT_NE(var_a->Initializer(), var_b->Initializer());
} }
TEST_F(IR_BuilderImplTest, EmitLiteral_U32) { TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
@ -203,7 +209,7 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
auto* init = GlobalVarInitializer(m.Get()); auto* init = GlobalVarInitializer(m.Get());
ASSERT_TRUE(Is<Constant>(init)); ASSERT_TRUE(Is<Constant>(init));
auto* val = init->As<Constant>()->value; auto* val = init->As<Constant>()->Value();
EXPECT_TRUE(val->Is<constant::Scalar<u32>>()); EXPECT_TRUE(val->Is<constant::Scalar<u32>>());
EXPECT_EQ(2_u, val->As<constant::Scalar<u32>>()->ValueAs<f32>()); EXPECT_EQ(2_u, val->As<constant::Scalar<u32>>()->ValueAs<f32>());
} }
@ -216,15 +222,16 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_U32_Deduped) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* var_a = m.Get().root_block->instructions[0]->As<ir::Var>(); auto instr = m.Get().root_block->Instructions();
auto* var_a = instr[0]->As<ir::Var>();
ASSERT_NE(var_a, nullptr); ASSERT_NE(var_a, nullptr);
auto* var_b = m.Get().root_block->instructions[1]->As<ir::Var>(); auto* var_b = instr[1]->As<ir::Var>();
ASSERT_NE(var_b, nullptr); ASSERT_NE(var_b, nullptr);
auto* var_c = m.Get().root_block->instructions[2]->As<ir::Var>(); auto* var_c = instr[2]->As<ir::Var>();
ASSERT_NE(var_c, nullptr); ASSERT_NE(var_c, nullptr);
ASSERT_EQ(var_a->initializer, var_c->initializer); ASSERT_EQ(var_a->Initializer(), var_c->Initializer());
ASSERT_NE(var_a->initializer, var_b->initializer); ASSERT_NE(var_a->Initializer(), var_b->Initializer());
} }
} // namespace } // namespace

View File

@ -63,13 +63,13 @@ TEST_F(IR_BuilderImplTest, Func) {
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* f = m->functions[0]; auto* f = m->functions[0];
ASSERT_NE(f->start_target, nullptr); ASSERT_NE(f->StartTarget(), nullptr);
ASSERT_NE(f->end_target, nullptr); ASSERT_NE(f->EndTarget(), nullptr);
EXPECT_EQ(1u, f->start_target->inbound_branches.Length()); EXPECT_EQ(1u, f->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, f->end_target->inbound_branches.Length()); EXPECT_EQ(1u, f->EndTarget()->InboundBranches().Length());
EXPECT_EQ(m->functions[0]->pipeline_stage, Function::PipelineStage::kUndefined); EXPECT_EQ(m->functions[0]->Stage(), Function::PipelineStage::kUndefined);
EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f():void { EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f():void {
%fn2 = block { %fn2 = block {
@ -88,13 +88,13 @@ TEST_F(IR_BuilderImplTest, Func_WithParam) {
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* f = m->functions[0]; auto* f = m->functions[0];
ASSERT_NE(f->start_target, nullptr); ASSERT_NE(f->StartTarget(), nullptr);
ASSERT_NE(f->end_target, nullptr); ASSERT_NE(f->EndTarget(), nullptr);
EXPECT_EQ(1u, f->start_target->inbound_branches.Length()); EXPECT_EQ(1u, f->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, f->end_target->inbound_branches.Length()); EXPECT_EQ(1u, f->EndTarget()->InboundBranches().Length());
EXPECT_EQ(m->functions[0]->pipeline_stage, Function::PipelineStage::kUndefined); EXPECT_EQ(m->functions[0]->Stage(), Function::PipelineStage::kUndefined);
EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f(%a:u32):u32 { EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f(%a:u32):u32 {
%fn2 = block { %fn2 = block {
@ -114,13 +114,13 @@ TEST_F(IR_BuilderImplTest, Func_WithMultipleParam) {
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* f = m->functions[0]; auto* f = m->functions[0];
ASSERT_NE(f->start_target, nullptr); ASSERT_NE(f->StartTarget(), nullptr);
ASSERT_NE(f->end_target, nullptr); ASSERT_NE(f->EndTarget(), nullptr);
EXPECT_EQ(1u, f->start_target->inbound_branches.Length()); EXPECT_EQ(1u, f->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, f->end_target->inbound_branches.Length()); EXPECT_EQ(1u, f->EndTarget()->InboundBranches().Length());
EXPECT_EQ(m->functions[0]->pipeline_stage, Function::PipelineStage::kUndefined); EXPECT_EQ(m->functions[0]->Stage(), Function::PipelineStage::kUndefined);
EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f(%a:u32, %b:i32, %c:bool):void { EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f(%a:u32, %b:i32, %c:bool):void {
%fn2 = block { %fn2 = block {
@ -137,7 +137,7 @@ TEST_F(IR_BuilderImplTest, EntryPoint) {
auto m = Build(); auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
EXPECT_EQ(m->functions[0]->pipeline_stage, Function::PipelineStage::kFragment); EXPECT_EQ(m->functions[0]->Stage(), Function::PipelineStage::kFragment);
} }
TEST_F(IR_BuilderImplTest, IfStatement) { TEST_F(IR_BuilderImplTest, IfStatement) {
@ -148,19 +148,19 @@ TEST_F(IR_BuilderImplTest, IfStatement) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::If>(m.Get()); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(flow->true_.target, nullptr); ASSERT_NE(flow->True().target, nullptr);
ASSERT_NE(flow->false_.target, nullptr); ASSERT_NE(flow->False().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->False().target->InboundBranches().Length());
EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -192,19 +192,19 @@ TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::If>(m.Get()); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(flow->true_.target, nullptr); ASSERT_NE(flow->True().target, nullptr);
ASSERT_NE(flow->false_.target, nullptr); ASSERT_NE(flow->False().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -235,19 +235,19 @@ TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::If>(m.Get()); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(flow->true_.target, nullptr); ASSERT_NE(flow->True().target, nullptr);
ASSERT_NE(flow->false_.target, nullptr); ASSERT_NE(flow->False().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -278,19 +278,19 @@ TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::If>(m.Get()); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(flow->true_.target, nullptr); ASSERT_NE(flow->True().target, nullptr);
ASSERT_NE(flow->false_.target, nullptr); ASSERT_NE(flow->False().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->False().target->InboundBranches().Length());
EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -318,15 +318,15 @@ TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow, nullptr); ASSERT_NE(loop_flow, nullptr);
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -367,19 +367,19 @@ TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(flow->start.target, nullptr); ASSERT_NE(flow->Start().target, nullptr);
ASSERT_NE(flow->continuing.target, nullptr); ASSERT_NE(flow->Continuing().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -408,28 +408,28 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
auto* if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -475,28 +475,28 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(break_if_flow->true_.target, nullptr); ASSERT_NE(break_if_flow->True().target, nullptr);
ASSERT_NE(break_if_flow->false_.target, nullptr); ASSERT_NE(break_if_flow->False().target, nullptr);
ASSERT_NE(break_if_flow->merge.target, nullptr); ASSERT_NE(break_if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, break_if_flow->inbound_branches.Length()); EXPECT_EQ(1u, break_if_flow->InboundBranches().Length());
EXPECT_EQ(1u, break_if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, break_if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, break_if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, break_if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, break_if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, break_if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -586,28 +586,28 @@ TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
auto* if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -648,19 +648,19 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -693,25 +693,25 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(break_if_flow->true_.target, nullptr); ASSERT_NE(break_if_flow->True().target, nullptr);
ASSERT_NE(break_if_flow->false_.target, nullptr); ASSERT_NE(break_if_flow->False().target, nullptr);
ASSERT_NE(break_if_flow->merge.target, nullptr); ASSERT_NE(break_if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
// This is 1 because only the loop branch happens. The subsequent if return is dead code. // This is 1 because only the loop branch happens. The subsequent if return is dead code.
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -736,28 +736,28 @@ TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(loop_flow->start.target, nullptr); ASSERT_NE(loop_flow->Start().target, nullptr);
ASSERT_NE(loop_flow->continuing.target, nullptr); ASSERT_NE(loop_flow->Continuing().target, nullptr);
ASSERT_NE(loop_flow->merge.target, nullptr); ASSERT_NE(loop_flow->Merge().target, nullptr);
auto* if_flow = FindSingleFlowNode<ir::If>(m.Get()); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(0u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -807,96 +807,96 @@ TEST_F(IR_BuilderImplTest, Loop_Nested) {
auto block_exit = [&](const ir::FlowNode* node) -> const ir::FlowNode* { auto block_exit = [&](const ir::FlowNode* node) -> const ir::FlowNode* {
if (auto* block = As<ir::Block>(node)) { if (auto* block = As<ir::Block>(node)) {
return block->branch.target; return block->Branch().target;
} }
return nullptr; return nullptr;
}; };
auto* loop_flow_a = As<ir::Loop>(m->functions[0]->start_target->branch.target); auto* loop_flow_a = As<ir::Loop>(m->functions[0]->StartTarget()->Branch().target);
ASSERT_NE(loop_flow_a, nullptr); ASSERT_NE(loop_flow_a, nullptr);
ASSERT_NE(loop_flow_a->start.target, nullptr); ASSERT_NE(loop_flow_a->Start().target, nullptr);
ASSERT_NE(loop_flow_a->continuing.target, nullptr); ASSERT_NE(loop_flow_a->Continuing().target, nullptr);
ASSERT_NE(loop_flow_a->merge.target, nullptr); ASSERT_NE(loop_flow_a->Merge().target, nullptr);
auto* loop_flow_b = As<ir::Loop>(block_exit(loop_flow_a->start.target)); auto* loop_flow_b = As<ir::Loop>(block_exit(loop_flow_a->Start().target));
ASSERT_NE(loop_flow_b, nullptr); ASSERT_NE(loop_flow_b, nullptr);
ASSERT_NE(loop_flow_b->start.target, nullptr); ASSERT_NE(loop_flow_b->Start().target, nullptr);
ASSERT_NE(loop_flow_b->continuing.target, nullptr); ASSERT_NE(loop_flow_b->Continuing().target, nullptr);
ASSERT_NE(loop_flow_b->merge.target, nullptr); ASSERT_NE(loop_flow_b->Merge().target, nullptr);
auto* if_flow_a = As<ir::If>(block_exit(loop_flow_b->start.target)); auto* if_flow_a = As<ir::If>(block_exit(loop_flow_b->Start().target));
ASSERT_NE(if_flow_a, nullptr); ASSERT_NE(if_flow_a, nullptr);
ASSERT_NE(if_flow_a->true_.target, nullptr); ASSERT_NE(if_flow_a->True().target, nullptr);
ASSERT_NE(if_flow_a->false_.target, nullptr); ASSERT_NE(if_flow_a->False().target, nullptr);
ASSERT_NE(if_flow_a->merge.target, nullptr); ASSERT_NE(if_flow_a->Merge().target, nullptr);
auto* if_flow_b = As<ir::If>(block_exit(if_flow_a->merge.target)); auto* if_flow_b = As<ir::If>(block_exit(if_flow_a->Merge().target));
ASSERT_NE(if_flow_b, nullptr); ASSERT_NE(if_flow_b, nullptr);
ASSERT_NE(if_flow_b->true_.target, nullptr); ASSERT_NE(if_flow_b->True().target, nullptr);
ASSERT_NE(if_flow_b->false_.target, nullptr); ASSERT_NE(if_flow_b->False().target, nullptr);
ASSERT_NE(if_flow_b->merge.target, nullptr); ASSERT_NE(if_flow_b->Merge().target, nullptr);
auto* loop_flow_c = As<ir::Loop>(block_exit(loop_flow_b->continuing.target)); auto* loop_flow_c = As<ir::Loop>(block_exit(loop_flow_b->Continuing().target));
ASSERT_NE(loop_flow_c, nullptr); ASSERT_NE(loop_flow_c, nullptr);
ASSERT_NE(loop_flow_c->start.target, nullptr); ASSERT_NE(loop_flow_c->Start().target, nullptr);
ASSERT_NE(loop_flow_c->continuing.target, nullptr); ASSERT_NE(loop_flow_c->Continuing().target, nullptr);
ASSERT_NE(loop_flow_c->merge.target, nullptr); ASSERT_NE(loop_flow_c->Merge().target, nullptr);
auto* loop_flow_d = As<ir::Loop>(block_exit(loop_flow_c->merge.target)); auto* loop_flow_d = As<ir::Loop>(block_exit(loop_flow_c->Merge().target));
ASSERT_NE(loop_flow_d, nullptr); ASSERT_NE(loop_flow_d, nullptr);
ASSERT_NE(loop_flow_d->start.target, nullptr); ASSERT_NE(loop_flow_d->Start().target, nullptr);
ASSERT_NE(loop_flow_d->continuing.target, nullptr); ASSERT_NE(loop_flow_d->Continuing().target, nullptr);
ASSERT_NE(loop_flow_d->merge.target, nullptr); ASSERT_NE(loop_flow_d->Merge().target, nullptr);
auto* if_flow_c = As<ir::If>(block_exit(loop_flow_d->continuing.target)); auto* if_flow_c = As<ir::If>(block_exit(loop_flow_d->Continuing().target));
ASSERT_NE(if_flow_c, nullptr); ASSERT_NE(if_flow_c, nullptr);
ASSERT_NE(if_flow_c->true_.target, nullptr); ASSERT_NE(if_flow_c->True().target, nullptr);
ASSERT_NE(if_flow_c->false_.target, nullptr); ASSERT_NE(if_flow_c->False().target, nullptr);
ASSERT_NE(if_flow_c->merge.target, nullptr); ASSERT_NE(if_flow_c->Merge().target, nullptr);
auto* if_flow_d = As<ir::If>(block_exit(loop_flow_b->merge.target)); auto* if_flow_d = As<ir::If>(block_exit(loop_flow_b->Merge().target));
ASSERT_NE(if_flow_d, nullptr); ASSERT_NE(if_flow_d, nullptr);
ASSERT_NE(if_flow_d->true_.target, nullptr); ASSERT_NE(if_flow_d->True().target, nullptr);
ASSERT_NE(if_flow_d->false_.target, nullptr); ASSERT_NE(if_flow_d->False().target, nullptr);
ASSERT_NE(if_flow_d->merge.target, nullptr); ASSERT_NE(if_flow_d->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, loop_flow_a->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_a->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow_a->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_a->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_a->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_a->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_a->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_a->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_b->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_b->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow_b->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_b->Start().target->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow_b->continuing.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_b->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_b->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_b->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_c->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_c->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow_c->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_c->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, loop_flow_c->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, loop_flow_c->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_c->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_c->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_d->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_d->InboundBranches().Length());
EXPECT_EQ(2u, loop_flow_d->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_d->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_d->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_d->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, loop_flow_d->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow_d->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_a->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_a->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_a->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_a->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_a->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_a->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_a->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_a->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_b->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_b->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_b->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_b->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_b->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_b->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_b->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_b->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_c->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_c->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_c->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_c->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_c->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_c->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_c->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_c->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_d->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_d->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_d->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_d->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_d->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_d->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow_d->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow_d->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->StartTarget()->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1015,28 +1015,28 @@ TEST_F(IR_BuilderImplTest, While) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(flow->start.target, nullptr); ASSERT_NE(flow->Start().target, nullptr);
ASSERT_NE(flow->continuing.target, nullptr); ASSERT_NE(flow->Continuing().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr); ASSERT_NE(flow->Start().target->As<ir::Block>()->Branch().target, nullptr);
ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>()); ASSERT_TRUE(flow->Start().target->As<ir::Block>()->Branch().target->Is<ir::If>());
auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>(); auto* if_flow = flow->Start().target->As<ir::Block>()->Branch().target->As<ir::If>();
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1081,28 +1081,28 @@ TEST_F(IR_BuilderImplTest, While_Return) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(flow->start.target, nullptr); ASSERT_NE(flow->Start().target, nullptr);
ASSERT_NE(flow->continuing.target, nullptr); ASSERT_NE(flow->Continuing().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr); ASSERT_NE(flow->Start().target->As<ir::Block>()->Branch().target, nullptr);
ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>()); ASSERT_TRUE(flow->Start().target->As<ir::Block>()->Branch().target->Is<ir::If>());
auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>(); auto* if_flow = flow->Start().target->As<ir::Block>()->Branch().target->As<ir::If>();
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1155,28 +1155,28 @@ TEST_F(IR_BuilderImplTest, DISABLED_For) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(flow->start.target, nullptr); ASSERT_NE(flow->Start().target, nullptr);
ASSERT_NE(flow->continuing.target, nullptr); ASSERT_NE(flow->Continuing().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr); ASSERT_NE(flow->Start().target->As<ir::Block>()->Branch().target, nullptr);
ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>()); ASSERT_TRUE(flow->Start().target->As<ir::Block>()->Branch().target->Is<ir::If>());
auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>(); auto* if_flow = flow->Start().target->As<ir::Block>()->Branch().target->As<ir::If>();
ASSERT_NE(if_flow->true_.target, nullptr); ASSERT_NE(if_flow->True().target, nullptr);
ASSERT_NE(if_flow->false_.target, nullptr); ASSERT_NE(if_flow->False().target, nullptr);
ASSERT_NE(if_flow->merge.target, nullptr); ASSERT_NE(if_flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->True().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->False().target->InboundBranches().Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), R"()"); EXPECT_EQ(Disassemble(m.Get()), R"()");
} }
@ -1189,18 +1189,18 @@ TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Loop>(m.Get()); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(flow->start.target, nullptr); ASSERT_NE(flow->Start().target, nullptr);
ASSERT_NE(flow->continuing.target, nullptr); ASSERT_NE(flow->Continuing().target, nullptr);
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Start().target->InboundBranches().Length());
EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->Continuing().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1231,31 +1231,33 @@ TEST_F(IR_BuilderImplTest, Switch) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Switch>(m.Get()); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(3u, flow->cases.Length());
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length()); auto cases = flow->Cases();
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_EQ(3u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(0_i, EXPECT_EQ(0_i,
flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[0].selectors[0].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length()); ASSERT_EQ(1u, cases[1].selectors.Length());
ASSERT_TRUE(flow->cases[1].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(cases[1].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(1_i, EXPECT_EQ(1_i,
flow->cases[1].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[1].selectors[0].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[2].selectors.Length()); ASSERT_EQ(1u, cases[2].selectors.Length());
EXPECT_TRUE(flow->cases[2].selectors[0].IsDefault()); EXPECT_TRUE(cases[2].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[0].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[1].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[2].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[2].Start().target->InboundBranches().Length());
EXPECT_EQ(3u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(3u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1295,27 +1297,28 @@ TEST_F(IR_BuilderImplTest, Switch_MultiSelector) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Switch>(m.Get()); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, flow->cases.Length());
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
ASSERT_EQ(3u, flow->cases[0].selectors.Length()); auto cases = flow->Cases();
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_EQ(1u, cases.Length());
ASSERT_EQ(3u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(0_i, EXPECT_EQ(0_i,
flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[0].selectors[0].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_TRUE(flow->cases[0].selectors[1].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(cases[0].selectors[1].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(1_i, EXPECT_EQ(1_i,
flow->cases[0].selectors[1].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[0].selectors[1].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
EXPECT_TRUE(flow->cases[0].selectors[2].IsDefault()); EXPECT_TRUE(cases[0].selectors[2].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[0].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1343,19 +1346,20 @@ TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Switch>(m.Get()); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(1u, flow->cases.Length());
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length()); auto cases = flow->Cases();
EXPECT_TRUE(flow->cases[0].selectors[0].IsDefault()); ASSERT_EQ(1u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
EXPECT_TRUE(cases[0].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[0].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1385,26 +1389,27 @@ TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
ASSERT_TRUE(m) << (!m ? m.Failure() : ""); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* flow = FindSingleFlowNode<ir::Switch>(m.Get()); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(2u, flow->cases.Length());
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length()); auto cases = flow->Cases();
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_EQ(2u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(0_i, EXPECT_EQ(0_i,
flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[0].selectors[0].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length()); ASSERT_EQ(1u, cases[1].selectors.Length());
EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault()); EXPECT_TRUE(cases[1].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[0].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[1].Start().target->InboundBranches().Length());
EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->Merge().target->InboundBranches().Length());
// This is 1 because the if is dead-code eliminated and the return doesn't happen. // This is 1 because the if is dead-code eliminated and the return doesn't happen.
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {
@ -1441,25 +1446,26 @@ TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
ASSERT_EQ(FindSingleFlowNode<ir::If>(m.Get()), nullptr); ASSERT_EQ(FindSingleFlowNode<ir::If>(m.Get()), nullptr);
auto* flow = FindSingleFlowNode<ir::Switch>(m.Get()); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->Merge().target, nullptr);
ASSERT_EQ(2u, flow->cases.Length());
ASSERT_EQ(1u, m->functions.Length()); ASSERT_EQ(1u, m->functions.Length());
auto* func = m->functions[0]; auto* func = m->functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length()); auto cases = flow->Cases();
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_EQ(2u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
EXPECT_EQ(0_i, EXPECT_EQ(0_i,
flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf()); cases[0].selectors[0].val->Value()->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length()); ASSERT_EQ(1u, cases[1].selectors.Length());
EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault()); EXPECT_TRUE(cases[1].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[0].Start().target->InboundBranches().Length());
EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length()); EXPECT_EQ(1u, cases[1].Start().target->InboundBranches().Length());
EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->Merge().target->InboundBranches().Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->EndTarget()->InboundBranches().Length());
EXPECT_EQ(Disassemble(m.Get()), EXPECT_EQ(Disassemble(m.Get()),
R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] { R"(%fn1 = func test_function():void [@compute @workgroup_size(1, 1, 1)] {

View File

@ -18,11 +18,11 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Function);
namespace tint::ir { namespace tint::ir {
Function::Function(Symbol n, Function::Function(Symbol name,
type::Type* rt, type::Type* rt,
PipelineStage stage, PipelineStage stage,
std::optional<std::array<uint32_t, 3>> wg_size) std::optional<std::array<uint32_t, 3>> wg_size)
: Base(), name(n), pipeline_stage(stage), workgroup_size(wg_size), return_type(rt) {} : Base(), name_(name), return_type_(rt), pipeline_stage_(stage), workgroup_size_(wg_size) {}
Function::~Function() = default; Function::~Function() = default;

View File

@ -17,6 +17,7 @@
#include <array> #include <array>
#include <optional> #include <optional>
#include <utility>
#include "src/tint/ir/flow_node.h" #include "src/tint/ir/flow_node.h"
#include "src/tint/ir/function_param.h" #include "src/tint/ir/function_param.h"
@ -71,32 +72,81 @@ class Function : public utils::Castable<Function, FlowNode> {
type::Type* rt, type::Type* rt,
PipelineStage stage = PipelineStage::kUndefined, PipelineStage stage = PipelineStage::kUndefined,
std::optional<std::array<uint32_t, 3>> wg_size = {}); std::optional<std::array<uint32_t, 3>> wg_size = {});
Function(Function&&) = delete;
Function(const Function&) = delete;
~Function() override; ~Function() override;
/// The function name Function& operator=(Function&&) = delete;
Symbol name; Function& operator=(const Function&) = delete;
/// The pipeline stage for the function, `kUndefined` if the function is not an entry point /// @returns the function name
PipelineStage pipeline_stage = PipelineStage::kUndefined; Symbol Name() const { return name_; }
/// If this is a `compute` entry point, holds the workgroup size information /// Sets the function stage
std::optional<std::array<uint32_t, 3>> workgroup_size; /// @param stage the stage to set
void SetStage(PipelineStage stage) { pipeline_stage_ = stage; }
/// The function return type /// @returns the function pipeline stage
const type::Type* return_type = nullptr; PipelineStage Stage() const { return pipeline_stage_; }
/// The function return attributes if any
utils::Vector<ReturnAttribute, 1> return_attributes;
/// If the return attribute is `kLocation` this stores the location value.
std::optional<uint32_t> return_location;
/// The parameters to the function /// Sets the workgroup size
utils::Vector<FunctionParam*, 1> params; /// @param x the x size
/// @param y the y size
/// @param z the z size
void SetWorkgroupSize(uint32_t x, uint32_t y, uint32_t z) { workgroup_size_ = {x, y, z}; }
/// The start target is the first block in a function. /// @returns the workgroup size information
Block* start_target = nullptr; std::optional<std::array<uint32_t, 3>> WorkgroupSize() const { return workgroup_size_; }
/// The end target is the end of the function. It is used as the branch target if a return is
/// encountered in the function. /// @returns the return type for the function
FunctionTerminator* end_target = nullptr; const type::Type* ReturnType() const { return return_type_; }
/// Sets the return attributes
/// @param attrs the attributes to set
void SetReturnAttributes(utils::VectorRef<ReturnAttribute> attrs) {
return_attributes_ = std::move(attrs);
}
/// @returns the return attributes
utils::VectorRef<ReturnAttribute> ReturnAttributes() const { return return_attributes_; }
/// Sets the return location
/// @param loc the location to set
void SetReturnLocation(std::optional<uint32_t> loc) { return_location_ = loc; }
/// @returns the return location
std::optional<uint32_t> ReturnLocation() const { return return_location_; }
/// Sets the function parameters
/// @param params the function paramters
void SetParams(utils::VectorRef<FunctionParam*> params) { params_ = std::move(params); }
/// @returns the function parameters
utils::VectorRef<FunctionParam*> Params() const { return params_; }
/// Sets the start target for the function
/// @param target the start target
void SetStartTarget(Block* target) { start_target_ = target; }
/// @returns the function start target
Block* StartTarget() const { return start_target_; }
/// Sets the end target for the function
/// @param target the end target
void SetEndTarget(FunctionTerminator* target) { end_target_ = target; }
/// @returns the function end target
FunctionTerminator* EndTarget() const { return end_target_; }
private:
Symbol name_;
const type::Type* return_type_;
PipelineStage pipeline_stage_;
std::optional<std::array<uint32_t, 3>> workgroup_size_;
utils::Vector<ReturnAttribute, 1> return_attributes_;
std::optional<uint32_t> return_location_;
utils::Vector<FunctionParam*, 1> params_;
Block* start_target_ = nullptr;
FunctionTerminator* end_target_ = nullptr;
}; };
utils::StringStream& operator<<(utils::StringStream& out, Function::PipelineStage value); utils::StringStream& operator<<(utils::StringStream& out, Function::PipelineStage value);

View File

@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::FunctionParam);
namespace tint::ir { namespace tint::ir {
FunctionParam::FunctionParam(const type::Type* ty) : type(ty) {} FunctionParam::FunctionParam(const type::Type* ty) : type_(ty) {}
FunctionParam::~FunctionParam() = default; FunctionParam::~FunctionParam() = default;

View File

@ -34,10 +34,11 @@ class FunctionParam : public utils::Castable<FunctionParam, Value> {
FunctionParam& operator=(FunctionParam&& inst) = delete; FunctionParam& operator=(FunctionParam&& inst) = delete;
/// @returns the type of the var /// @returns the type of the var
const type::Type* Type() const override { return type; } const type::Type* Type() const override { return type_; }
private:
/// The type of the parameter /// The type of the parameter
const type::Type* type; const type::Type* type_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::If);
namespace tint::ir { namespace tint::ir {
If::If(Value* cond) : Base(), condition(cond) {} If::If(Value* cond) : Base(), condition_(cond) {}
If::~If() = default; If::~If() = default;

View File

@ -32,17 +32,36 @@ class If : public utils::Castable<If, FlowNode> {
/// Constructor /// Constructor
/// @param cond the if condition /// @param cond the if condition
explicit If(Value* cond); explicit If(Value* cond);
If(const If&) = delete;
If(If&&) = delete;
~If() override; ~If() override;
/// The true branch block If& operator=(const If&) = delete;
If& operator=(If&&) = delete;
/// @returns the if condition
const Value* Condition() const { return condition_; }
/// @returns the true branch block
const Branch& True() const { return true_; }
/// @returns the true branch block
Branch& True() { return true_; }
/// @returns the false branch block
const Branch& False() const { return false_; }
/// @returns the false branch block
Branch& False() { return false_; }
/// @returns the merge branch block
const Branch& Merge() const { return merge_; }
/// @returns the merge branch block
Branch& Merge() { return merge_; }
private:
Branch true_ = {}; Branch true_ = {};
/// The false branch block
Branch false_ = {}; Branch false_ = {};
/// An block to converge the true/false branches. The block always exists, but there maybe no Branch merge_ = {};
/// branches into it. (e.g. if both branches `return`) Value* condition_;
Branch merge = {};
/// Value holding the condition result
const Value* condition = nullptr;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -19,10 +19,10 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Load);
namespace tint::ir { namespace tint::ir {
Load::Load(const type::Type* type, Value* f) : Base(), result_type(type), from(f) { Load::Load(const type::Type* type, Value* f) : Base(), result_type_(type), from_(f) {
TINT_ASSERT(IR, result_type); TINT_ASSERT(IR, result_type_);
TINT_ASSERT(IR, from); TINT_ASSERT(IR, from_);
from->AddUsage(this); from_->AddUsage(this);
} }
Load::~Load() = default; Load::~Load() = default;

View File

@ -35,13 +35,14 @@ class Load : public utils::Castable<Load, Instruction> {
Load& operator=(Load&& inst) = delete; Load& operator=(Load&& inst) = delete;
/// @returns the type of the value /// @returns the type of the value
const type::Type* Type() const override { return result_type; } const type::Type* Type() const override { return result_type_; }
/// the result type of the instruction /// @returns the avlue being loaded from
const type::Type* result_type = nullptr; Value* From() const { return from_; }
/// the value being loaded private:
Value* from = nullptr; const type::Type* result_type_;
Value* from_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -33,12 +33,12 @@ TEST_F(IR_InstructionTest, CreateLoad) {
const auto* inst = b.Load(var); const auto* inst = b.Load(var);
ASSERT_TRUE(inst->Is<Load>()); ASSERT_TRUE(inst->Is<Load>());
ASSERT_EQ(inst->from, var); ASSERT_EQ(inst->From(), var);
EXPECT_EQ(inst->Type(), store_type); EXPECT_EQ(inst->Type(), store_type);
ASSERT_TRUE(inst->from->Is<ir::Var>()); ASSERT_TRUE(inst->From()->Is<ir::Var>());
EXPECT_EQ(inst->from, var); EXPECT_EQ(inst->From(), var);
} }
TEST_F(IR_InstructionTest, Load_Usage) { TEST_F(IR_InstructionTest, Load_Usage) {
@ -50,9 +50,9 @@ TEST_F(IR_InstructionTest, Load_Usage) {
store_type, builtin::AddressSpace::kFunction, builtin::Access::kReadWrite)); store_type, builtin::AddressSpace::kFunction, builtin::Access::kReadWrite));
const auto* inst = b.Load(var); const auto* inst = b.Load(var);
ASSERT_NE(inst->from, nullptr); ASSERT_NE(inst->From(), nullptr);
ASSERT_EQ(inst->from->Usage().Length(), 1u); ASSERT_EQ(inst->From()->Usage().Length(), 1u);
EXPECT_EQ(inst->from->Usage()[0], inst); EXPECT_EQ(inst->From()->Usage()[0], inst);
} }
} // namespace } // namespace

View File

@ -26,16 +26,32 @@ class Loop : public utils::Castable<Loop, FlowNode> {
public: public:
/// Constructor /// Constructor
Loop(); Loop();
Loop(const Loop&) = delete;
Loop(Loop&&) = delete;
~Loop() override; ~Loop() override;
/// The start block is the first block in a loop. Loop& operator=(const Loop&) = delete;
Branch start = {}; Loop& operator=(Loop&&) = delete;
/// The continue target of the block.
Branch continuing = {}; /// @returns the switch start branch
/// The loop merge target. If the `loop` does a `return` then this block may not actually const Branch& Start() const { return start_; }
/// end up in the control flow. We need it if the loop does a `break` we know where to break /// @returns the switch start branch
/// too. Branch& Start() { return start_; }
Branch merge = {};
/// @returns the switch continuing branch
const Branch& Continuing() const { return continuing_; }
/// @returns the switch continuing branch
Branch& Continuing() { return continuing_; }
/// @returns the switch merge branch
const Branch& Merge() const { return merge_; }
/// @returns the switch merge branch
Branch& Merge() { return merge_; }
private:
Branch start_ = {};
Branch continuing_ = {};
Branch merge_ = {};
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -25,7 +25,12 @@ class RootTerminator : public utils::Castable<RootTerminator, FlowNode> {
public: public:
/// Constructor /// Constructor
RootTerminator(); RootTerminator();
RootTerminator(const RootTerminator&) = delete;
RootTerminator(RootTerminator&&) = delete;
~RootTerminator() override; ~RootTerminator() override;
RootTerminator& operator=(const RootTerminator&) = delete;
RootTerminator& operator=(RootTerminator&&) = delete;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -19,11 +19,11 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Store);
namespace tint::ir { namespace tint::ir {
Store::Store(Value* t, Value* f) : Base(), to(t), from(f) { Store::Store(Value* to, Value* from) : Base(), to_(to), from_(from) {
TINT_ASSERT(IR, to); TINT_ASSERT(IR, to_);
TINT_ASSERT(IR, from); TINT_ASSERT(IR, from_);
to->AddUsage(this); to_->AddUsage(this);
from->AddUsage(this); from_->AddUsage(this);
} }
Store::~Store() = default; Store::~Store() = default;

View File

@ -34,10 +34,15 @@ class Store : public utils::Castable<Store, Instruction> {
Store& operator=(const Store& inst) = delete; Store& operator=(const Store& inst) = delete;
Store& operator=(Store&& inst) = delete; Store& operator=(Store&& inst) = delete;
/// the value being stored to /// @returns the value being stored too
Value* to = nullptr; Value* To() const { return to_; }
/// the value being stored
Value* from = nullptr; /// @returns the value being stored
Value* From() const { return from_; }
private:
Value* to_;
Value* from_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -33,10 +33,10 @@ TEST_F(IR_InstructionTest, CreateStore) {
const auto* inst = b.Store(to, b.Constant(4_i)); const auto* inst = b.Store(to, b.Constant(4_i));
ASSERT_TRUE(inst->Is<Store>()); ASSERT_TRUE(inst->Is<Store>());
ASSERT_EQ(inst->to, to); ASSERT_EQ(inst->To(), to);
ASSERT_TRUE(inst->from->Is<Constant>()); ASSERT_TRUE(inst->From()->Is<Constant>());
auto lhs = inst->from->As<Constant>()->value; auto lhs = inst->From()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -48,13 +48,13 @@ TEST_F(IR_InstructionTest, Store_Usage) {
auto* to = b.Discard(); auto* to = b.Discard();
const auto* inst = b.Store(to, b.Constant(4_i)); const auto* inst = b.Store(to, b.Constant(4_i));
ASSERT_NE(inst->to, nullptr); ASSERT_NE(inst->To(), nullptr);
ASSERT_EQ(inst->to->Usage().Length(), 1u); ASSERT_EQ(inst->To()->Usage().Length(), 1u);
EXPECT_EQ(inst->to->Usage()[0], inst); EXPECT_EQ(inst->To()->Usage()[0], inst);
ASSERT_NE(inst->from, nullptr); ASSERT_NE(inst->From(), nullptr);
ASSERT_EQ(inst->from->Usage().Length(), 1u); ASSERT_EQ(inst->From()->Usage().Length(), 1u);
EXPECT_EQ(inst->from->Usage()[0], inst); EXPECT_EQ(inst->From()->Usage()[0], inst);
} }
} // namespace } // namespace

View File

@ -18,7 +18,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Switch);
namespace tint::ir { namespace tint::ir {
Switch::Switch(Value* cond) : Base(), condition(cond) {} Switch::Switch(Value* cond) : Base(), condition_(cond) {}
Switch::~Switch() = default; Switch::~Switch() = default;

View File

@ -41,21 +41,40 @@ class Switch : public utils::Castable<Switch, FlowNode> {
utils::Vector<CaseSelector, 4> selectors; utils::Vector<CaseSelector, 4> selectors;
/// The start block for the case block. /// The start block for the case block.
Branch start = {}; Branch start = {};
/// @returns the case start target
const Branch& Start() const { return start; }
/// @returns the case start target
Branch& Start() { return start; }
}; };
/// Constructor /// Constructor
/// @param cond the condition /// @param cond the condition
explicit Switch(Value* cond); explicit Switch(Value* cond);
Switch(const Switch&) = delete;
Switch(Switch&&) = delete;
~Switch() override; ~Switch() override;
/// The switch merge target Switch& operator=(const Switch&) = delete;
Branch merge = {}; Switch& operator=(Switch&&) = delete;
/// The switch case statements /// @returns the switch merge branch
utils::Vector<Case, 4> cases; const Branch& Merge() const { return merge_; }
/// @returns the switch merge branch
Branch& Merge() { return merge_; }
/// Value holding the condition result /// @returns the switch cases
const Value* condition = nullptr; utils::VectorRef<Case> Cases() const { return cases_; }
/// @returns the switch cases
utils::Vector<Case, 4>& Cases() { return cases_; }
/// @returns the condition
const Value* Condition() const { return condition_; }
private:
Branch merge_ = {};
utils::Vector<Case, 4> cases_;
Value* condition_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -33,7 +33,6 @@ template <typename BASE>
class TestHelperBase : public BASE, public ProgramBuilder { class TestHelperBase : public BASE, public ProgramBuilder {
public: public:
TestHelperBase() = default; TestHelperBase() = default;
~TestHelperBase() override = default; ~TestHelperBase() override = default;
/// Build the module, cleaning up the program before returning. /// Build the module, cleaning up the program before returning.

View File

@ -91,14 +91,14 @@ class State {
const ast::Function* Fn(const Function* fn) { const ast::Function* Fn(const Function* fn) {
SCOPED_NESTING(); SCOPED_NESTING();
auto name = Sym(fn->name); auto name = Sym(fn->Name());
// TODO(crbug.com/tint/1915): Properly implement this when we've fleshed out Function // TODO(crbug.com/tint/1915): Properly implement this when we've fleshed out Function
utils::Vector<const ast::Parameter*, 1> params{}; utils::Vector<const ast::Parameter*, 1> params{};
auto ret_ty = Type(fn->return_type); auto ret_ty = Type(fn->ReturnType());
if (!ret_ty) { if (!ret_ty) {
return nullptr; return nullptr;
} }
auto* body = FlowNodeGraph(fn->start_target); auto* body = FlowNodeGraph(fn->StartTarget());
if (!body) { if (!body) {
return nullptr; return nullptr;
} }
@ -126,7 +126,7 @@ class State {
branch->target, branch->target,
[&](const ir::Block* block) { [&](const ir::Block* block) {
for (auto* inst : block->instructions) { for (const auto* inst : block->Instructions()) {
auto stmt = Stmt(inst); auto stmt = Stmt(inst);
if (TINT_UNLIKELY(!stmt)) { if (TINT_UNLIKELY(!stmt)) {
return kError; return kError;
@ -135,7 +135,7 @@ class State {
stmts.Push(s); stmts.Push(s);
} }
} }
branch = &block->branch; branch = &block->Branch();
return kContinue; return kContinue;
}, },
@ -145,8 +145,8 @@ class State {
return kError; return kError;
} }
stmts.Push(stmt); stmts.Push(stmt);
branch = &if_->merge; branch = &if_->Merge();
return branch->target->inbound_branches.IsEmpty() ? kStop : kContinue; return branch->target->InboundBranches().IsEmpty() ? kStop : kContinue;
}, },
[&](const ir::Switch* switch_) { [&](const ir::Switch* switch_) {
@ -155,8 +155,8 @@ class State {
return kError; return kError;
} }
stmts.Push(stmt); stmts.Push(stmt);
branch = &switch_->merge; branch = &switch_->Merge();
return branch->target->inbound_branches.IsEmpty() ? kStop : kContinue; return branch->target->InboundBranches().IsEmpty() ? kStop : kContinue;
}, },
[&](const ir::FunctionTerminator*) { [&](const ir::FunctionTerminator*) {
@ -189,25 +189,25 @@ class State {
const ast::IfStatement* If(const ir::If* i) { const ast::IfStatement* If(const ir::If* i) {
SCOPED_NESTING(); SCOPED_NESTING();
auto* cond = Expr(i->condition); auto* cond = Expr(i->Condition());
auto* t = FlowNodeGraph(i->true_.target, i->merge.target); auto* t = FlowNodeGraph(i->True().target, i->Merge().target);
if (TINT_UNLIKELY(!t)) { if (TINT_UNLIKELY(!t)) {
return nullptr; return nullptr;
} }
if (!IsEmpty(i->false_.target, i->merge.target)) { if (!IsEmpty(i->False().target, i->Merge().target)) {
// If the else target is an if flow node with the same merge target as this if, then // If the else target is an if flow node with the same Merge().target as this if, then
// emit an 'else if' instead of a block statement for the else. // emit an 'else if' instead of a block statement for the else.
if (auto* else_if = As<ir::If>(NextNonEmptyNode(i->false_.target)); if (auto* else_if = As<ir::If>(NextNonEmptyNode(i->False().target));
else_if && else_if &&
NextNonEmptyNode(i->merge.target) == NextNonEmptyNode(else_if->merge.target)) { NextNonEmptyNode(i->Merge().target) == NextNonEmptyNode(else_if->Merge().target)) {
auto* f = If(else_if); auto* f = If(else_if);
if (!f) { if (!f) {
return nullptr; return nullptr;
} }
return b.If(cond, t, b.Else(f)); return b.If(cond, t, b.Else(f));
} else { } else {
auto* f = FlowNodeGraph(i->false_.target, i->merge.target); auto* f = FlowNodeGraph(i->False().target, i->Merge().target);
if (!f) { if (!f) {
return nullptr; return nullptr;
} }
@ -221,16 +221,16 @@ class State {
const ast::SwitchStatement* Switch(const ir::Switch* s) { const ast::SwitchStatement* Switch(const ir::Switch* s) {
SCOPED_NESTING(); SCOPED_NESTING();
auto* cond = Expr(s->condition); auto* cond = Expr(s->Condition());
if (!cond) { if (!cond) {
return nullptr; return nullptr;
} }
auto cases = utils::Transform( auto cases = utils::Transform<1>(
s->cases, // s->Cases(), //
[&](const ir::Switch::Case& c) -> const tint::ast::CaseStatement* { [&](const ir::Switch::Case& c) -> const tint::ast::CaseStatement* {
SCOPED_NESTING(); SCOPED_NESTING();
auto* body = FlowNodeGraph(c.start.target, s->merge.target); auto* body = FlowNodeGraph(c.start.target, s->Merge().target);
if (!body) { if (!body) {
return nullptr; return nullptr;
} }
@ -292,10 +292,10 @@ class State {
bool IsEmpty(const ir::FlowNode* node, const ir::FlowNode* stop_at) { bool IsEmpty(const ir::FlowNode* node, const ir::FlowNode* stop_at) {
while (node != stop_at) { while (node != stop_at) {
if (auto* block = node->As<ir::Block>()) { if (auto* block = node->As<ir::Block>()) {
if (block->instructions.Length() > 0) { if (!block->Instructions().IsEmpty()) {
return false; return false;
} }
node = block->branch.target; node = block->Branch().target;
} else { } else {
return false; return false;
} }
@ -307,13 +307,13 @@ class State {
const ir::FlowNode* NextNonEmptyNode(const ir::FlowNode* node) { const ir::FlowNode* NextNonEmptyNode(const ir::FlowNode* node) {
while (node) { while (node) {
if (auto* block = node->As<ir::Block>()) { if (auto* block = node->As<ir::Block>()) {
for (auto* inst : block->instructions) { for (const auto* inst : block->Instructions()) {
// Load instructions will be inlined, so ignore them. // Load instructions will be inlined, so ignore them.
if (!inst->Is<ir::Load>()) { if (!inst->Is<ir::Load>()) {
return node; return node;
} }
} }
node = block->branch.target; node = block->Branch().target;
} else { } else {
return node; return node;
} }
@ -351,8 +351,8 @@ class State {
} }
auto ty = Type(ptr->StoreType()); auto ty = Type(ptr->StoreType());
const ast::Expression* init = nullptr; const ast::Expression* init = nullptr;
if (var->initializer) { if (var->Initializer()) {
init = Expr(var->initializer); init = Expr(var->Initializer());
if (!init) { if (!init) {
return nullptr; return nullptr;
} }
@ -368,18 +368,19 @@ class State {
} }
const ast::AssignmentStatement* Store(const ir::Store* store) { const ast::AssignmentStatement* Store(const ir::Store* store) {
auto* expr = Expr(store->from); auto* expr = Expr(store->From());
return b.Assign(NameOf(store->to), expr); return b.Assign(NameOf(store->To()), expr);
} }
const ast::CallExpression* Call(const ir::Call* call) { const ast::CallExpression* Call(const ir::Call* call) {
auto args = utils::Transform(call->args, [&](const ir::Value* arg) { return Expr(arg); }); auto args =
utils::Transform<2>(call->Args(), [&](const ir::Value* arg) { return Expr(arg); });
if (args.Any(utils::IsNull)) { if (args.Any(utils::IsNull)) {
return nullptr; return nullptr;
} }
return tint::Switch( return tint::Switch(
call, // call, //
[&](const ir::UserCall* c) { return b.Call(Sym(c->name), std::move(args)); }, [&](const ir::UserCall* c) { return b.Call(Sym(c->Name()), std::move(args)); },
[&](Default) { [&](Default) {
UNHANDLED_CASE(call); UNHANDLED_CASE(call);
return nullptr; return nullptr;
@ -401,18 +402,18 @@ class State {
const ast::Expression* ConstExpr(const ir::Constant* c) { const ast::Expression* ConstExpr(const ir::Constant* c) {
return tint::Switch( return tint::Switch(
c->Type(), // c->Type(), //
[&](const type::I32*) { return b.Expr(c->value->ValueAs<i32>()); }, [&](const type::I32*) { return b.Expr(c->Value()->ValueAs<i32>()); },
[&](const type::U32*) { return b.Expr(c->value->ValueAs<u32>()); }, [&](const type::U32*) { return b.Expr(c->Value()->ValueAs<u32>()); },
[&](const type::F32*) { return b.Expr(c->value->ValueAs<f32>()); }, [&](const type::F32*) { return b.Expr(c->Value()->ValueAs<f32>()); },
[&](const type::F16*) { return b.Expr(c->value->ValueAs<f16>()); }, [&](const type::F16*) { return b.Expr(c->Value()->ValueAs<f16>()); },
[&](const type::Bool*) { return b.Expr(c->value->ValueAs<bool>()); }, [&](const type::Bool*) { return b.Expr(c->Value()->ValueAs<bool>()); },
[&](Default) { [&](Default) {
UNHANDLED_CASE(c); UNHANDLED_CASE(c);
return nullptr; return nullptr;
}); });
} }
const ast::Expression* LoadExpr(const ir::Load* l) { return Expr(l->from); } const ast::Expression* LoadExpr(const ir::Load* l) { return Expr(l->From()); }
const ast::Expression* VarExpr(const ir::Var* v) { return b.Expr(NameOf(v)); } const ast::Expression* VarExpr(const ir::Var* v) { return b.Expr(NameOf(v)); }

View File

@ -29,7 +29,7 @@ AddEmptyEntryPoint::~AddEmptyEntryPoint() = default;
void AddEmptyEntryPoint::Run(ir::Module* ir, const DataMap&, DataMap&) const { void AddEmptyEntryPoint::Run(ir::Module* ir, const DataMap&, DataMap&) const {
for (auto* func : ir->functions) { for (auto* func : ir->functions) {
if (func->pipeline_stage != Function::PipelineStage::kUndefined) { if (func->Stage() != Function::PipelineStage::kUndefined) {
return; return;
} }
} }
@ -38,7 +38,7 @@ void AddEmptyEntryPoint::Run(ir::Module* ir, const DataMap&, DataMap&) const {
auto* ep = auto* ep =
builder.CreateFunction(ir->symbols.New("unused_entry_point"), ir->types.Get<type::Void>(), builder.CreateFunction(ir->symbols.New("unused_entry_point"), ir->types.Get<type::Void>(),
Function::PipelineStage::kCompute, std::array{1u, 1u, 1u}); Function::PipelineStage::kCompute, std::array{1u, 1u, 1u});
builder.Branch(ep->start_target, ep->end_target); ep->StartTarget()->BranchTo(ep->EndTarget());
ir->functions.Push(ep); ir->functions.Push(ep);
} }

View File

@ -40,7 +40,7 @@ TEST_F(IR_AddEmptyEntryPointTest, EmptyModule) {
TEST_F(IR_AddEmptyEntryPointTest, ExistingEntryPoint) { TEST_F(IR_AddEmptyEntryPointTest, ExistingEntryPoint) {
auto* ep = b.CreateFunction(mod.symbols.New("main"), mod.types.Get<type::Void>(), auto* ep = b.CreateFunction(mod.symbols.New("main"), mod.types.Get<type::Void>(),
Function::PipelineStage::kFragment); Function::PipelineStage::kFragment);
b.Branch(ep->start_target, ep->end_target); ep->StartTarget()->BranchTo(ep->EndTarget());
mod.functions.Push(ep); mod.functions.Push(ep);
auto* expect = R"( auto* expect = R"(

View File

@ -19,8 +19,8 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Unary);
namespace tint::ir { namespace tint::ir {
Unary::Unary(Kind k, const type::Type* res_ty, Value* val) Unary::Unary(enum Kind k, const type::Type* res_ty, Value* val)
: kind(k), result_type(res_ty), val_(val) { : kind_(k), result_type_(res_ty), val_(val) {
TINT_ASSERT(IR, val_); TINT_ASSERT(IR, val_);
val_->AddUsage(this); val_->AddUsage(this);
} }

View File

@ -33,7 +33,7 @@ class Unary : public utils::Castable<Unary, Instruction> {
/// @param kind the kind of unary instruction /// @param kind the kind of unary instruction
/// @param result_type the result type /// @param result_type the result type
/// @param val the input value for the instruction /// @param val the input value for the instruction
Unary(Kind kind, const type::Type* result_type, Value* val); Unary(enum Kind kind, const type::Type* result_type, Value* val);
Unary(const Unary& inst) = delete; Unary(const Unary& inst) = delete;
Unary(Unary&& inst) = delete; Unary(Unary&& inst) = delete;
~Unary() override; ~Unary() override;
@ -42,19 +42,18 @@ class Unary : public utils::Castable<Unary, Instruction> {
Unary& operator=(Unary&& inst) = delete; Unary& operator=(Unary&& inst) = delete;
/// @returns the type of the value /// @returns the type of the value
const type::Type* Type() const override { return result_type; } const type::Type* Type() const override { return result_type_; }
/// @returns the value for the instruction /// @returns the value for the instruction
const Value* Val() const { return val_; } const Value* Val() const { return val_; }
/// the kind of unary instruction /// @returns the kind of unary instruction
Kind kind = Kind::kNegation; enum Kind Kind() const { return kind_; }
/// the result type of the instruction
const type::Type* result_type = nullptr;
private: private:
Value* val_ = nullptr; enum Kind kind_;
const type::Type* result_type_;
Value* val_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -29,10 +29,10 @@ TEST_F(IR_InstructionTest, CreateComplement) {
const auto* inst = b.Complement(b.ir.types.Get<type::I32>(), b.Constant(4_i)); const auto* inst = b.Complement(b.ir.types.Get<type::I32>(), b.Constant(4_i));
ASSERT_TRUE(inst->Is<Unary>()); ASSERT_TRUE(inst->Is<Unary>());
EXPECT_EQ(inst->kind, Unary::Kind::kComplement); EXPECT_EQ(inst->Kind(), Unary::Kind::kComplement);
ASSERT_TRUE(inst->Val()->Is<Constant>()); ASSERT_TRUE(inst->Val()->Is<Constant>());
auto lhs = inst->Val()->As<Constant>()->value; auto lhs = inst->Val()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -43,10 +43,10 @@ TEST_F(IR_InstructionTest, CreateNegation) {
const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i)); const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i));
ASSERT_TRUE(inst->Is<Unary>()); ASSERT_TRUE(inst->Is<Unary>());
EXPECT_EQ(inst->kind, Unary::Kind::kNegation); EXPECT_EQ(inst->Kind(), Unary::Kind::kNegation);
ASSERT_TRUE(inst->Val()->Is<Constant>()); ASSERT_TRUE(inst->Val()->Is<Constant>());
auto lhs = inst->Val()->As<Constant>()->value; auto lhs = inst->Val()->As<Constant>()->Value();
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>()); ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>()); EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
} }
@ -56,7 +56,7 @@ TEST_F(IR_InstructionTest, Unary_Usage) {
Builder b{mod}; Builder b{mod};
const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i)); const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i));
EXPECT_EQ(inst->kind, Unary::Kind::kNegation); EXPECT_EQ(inst->Kind(), Unary::Kind::kNegation);
ASSERT_NE(inst->Val(), nullptr); ASSERT_NE(inst->Val(), nullptr);
ASSERT_EQ(inst->Val()->Usage().Length(), 1u); ASSERT_EQ(inst->Val()->Usage().Length(), 1u);

View File

@ -23,7 +23,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::UserCall);
namespace tint::ir { namespace tint::ir {
UserCall::UserCall(const type::Type* ty, Symbol n, utils::VectorRef<Value*> arguments) UserCall::UserCall(const type::Type* ty, Symbol n, utils::VectorRef<Value*> arguments)
: Base(ty, std::move(arguments)), name(n) {} : Base(ty, std::move(arguments)), name_(n) {}
UserCall::~UserCall() = default; UserCall::~UserCall() = default;

View File

@ -36,8 +36,11 @@ class UserCall : public utils::Castable<UserCall, Call> {
UserCall& operator=(const UserCall& inst) = delete; UserCall& operator=(const UserCall& inst) = delete;
UserCall& operator=(UserCall&& inst) = delete; UserCall& operator=(UserCall&& inst) = delete;
/// The function name /// @returns the called function name
Symbol name; Symbol Name() const { return name_; }
private:
Symbol name_;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -19,8 +19,14 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Var);
namespace tint::ir { namespace tint::ir {
Var::Var(const type::Type* ty) : type(ty) {} Var::Var(const type::Type* ty) : type_(ty) {}
Var::~Var() = default; Var::~Var() = default;
void Var::SetInitializer(Value* initializer) {
initializer_ = initializer;
initializer_->AddUsage(this);
// TODO(dsinclair): Probably should do a RemoveUsage on an existing initializer if set
}
} // namespace tint::ir } // namespace tint::ir

View File

@ -36,13 +36,17 @@ class Var : public utils::Castable<Var, Instruction> {
Var& operator=(Var&& inst) = delete; Var& operator=(Var&& inst) = delete;
/// @returns the type of the var /// @returns the type of the var
const type::Type* Type() const override { return type; } const type::Type* Type() const override { return type_; }
/// the result type of the instruction /// Sets the var initializer
const type::Type* type = nullptr; /// @param initializer the initializer
void SetInitializer(Value* initializer);
/// @returns the initializer
const Value* Initializer() const { return initializer_; }
/// The optional initializer private:
Value* initializer = nullptr; const type::Type* type_;
Value* initializer_ = nullptr;
}; };
} // namespace tint::ir } // namespace tint::ir

View File

@ -52,7 +52,7 @@ class IR_AddFunction final : public ir::transform::Transform {
ir::Builder builder(*mod); ir::Builder builder(*mod);
auto* func = auto* func =
builder.CreateFunction(mod->symbols.New("ir_func"), mod->types.Get<type::Void>()); builder.CreateFunction(mod->symbols.New("ir_func"), mod->types.Get<type::Void>());
builder.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
mod->functions.Push(func); mod->functions.Push(func);
} }
}; };
@ -70,7 +70,7 @@ ir::Module MakeIR() {
ir::Builder builder(mod); ir::Builder builder(mod);
auto* func = auto* func =
builder.CreateFunction(builder.ir.symbols.New("main"), builder.ir.types.Get<type::Void>()); builder.CreateFunction(builder.ir.symbols.New("main"), builder.ir.types.Get<type::Void>());
builder.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
builder.ir.functions.Push(func); builder.ir.functions.Push(func);
return mod; return mod;
} }
@ -102,9 +102,10 @@ TEST_F(TransformManagerTest, IR_MutateInPlace) {
manager.Add<IR_AddFunction>(); manager.Add<IR_AddFunction>();
manager.Run(&ir, {}, outputs); manager.Run(&ir, {}, outputs);
ASSERT_EQ(ir.functions.Length(), 2u); ASSERT_EQ(ir.functions.Length(), 2u);
EXPECT_EQ(ir.functions[0]->name.Name(), "main"); EXPECT_EQ(ir.functions[0]->Name().Name(), "main");
EXPECT_EQ(ir.functions[1]->name.Name(), "ir_func"); EXPECT_EQ(ir.functions[1]->Name().Name(), "ir_func");
} }
TEST_F(TransformManagerTest, AST_MixedTransforms_AST_Before_IR) { TEST_F(TransformManagerTest, AST_MixedTransforms_AST_Before_IR) {
@ -149,9 +150,9 @@ TEST_F(TransformManagerTest, IR_MixedTransforms_AST_Before_IR) {
manager.Run(&ir, {}, outputs); manager.Run(&ir, {}, outputs);
ASSERT_EQ(ir.functions.Length(), 3u); ASSERT_EQ(ir.functions.Length(), 3u);
EXPECT_EQ(ir.functions[0]->name.Name(), "ast_func"); EXPECT_EQ(ir.functions[0]->Name().Name(), "ast_func");
EXPECT_EQ(ir.functions[1]->name.Name(), "main"); EXPECT_EQ(ir.functions[1]->Name().Name(), "main");
EXPECT_EQ(ir.functions[2]->name.Name(), "ir_func"); EXPECT_EQ(ir.functions[2]->Name().Name(), "ir_func");
} }
TEST_F(TransformManagerTest, IR_MixedTransforms_IR_Before_AST) { TEST_F(TransformManagerTest, IR_MixedTransforms_IR_Before_AST) {
@ -164,9 +165,9 @@ TEST_F(TransformManagerTest, IR_MixedTransforms_IR_Before_AST) {
manager.Run(&ir, {}, outputs); manager.Run(&ir, {}, outputs);
ASSERT_EQ(ir.functions.Length(), 3u); ASSERT_EQ(ir.functions.Length(), 3u);
EXPECT_EQ(ir.functions[0]->name.Name(), "ast_func"); EXPECT_EQ(ir.functions[0]->Name().Name(), "ast_func");
EXPECT_EQ(ir.functions[1]->name.Name(), "main"); EXPECT_EQ(ir.functions[1]->Name().Name(), "main");
EXPECT_EQ(ir.functions[2]->name.Name(), "ir_func"); EXPECT_EQ(ir.functions[2]->Name().Name(), "ir_func");
} }
#endif // TINT_BUILD_IR #endif // TINT_BUILD_IR

View File

@ -110,7 +110,7 @@ bool GeneratorImplIr::Generate() {
} }
uint32_t GeneratorImplIr::Constant(const ir::Constant* constant) { uint32_t GeneratorImplIr::Constant(const ir::Constant* constant) {
return Constant(constant->value); return Constant(constant->Value());
} }
uint32_t GeneratorImplIr::Constant(const constant::Value* constant) { uint32_t GeneratorImplIr::Constant(const constant::Value* constant) {
@ -214,15 +214,15 @@ void GeneratorImplIr::EmitFunction(const ir::Function* func) {
auto id = module_.NextId(); auto id = module_.NextId();
// Emit the function name. // Emit the function name.
module_.PushDebug(spv::Op::OpName, {id, Operand(func->name.Name())}); module_.PushDebug(spv::Op::OpName, {id, Operand(func->Name().Name())});
// Emit OpEntryPoint and OpExecutionMode declarations if needed. // Emit OpEntryPoint and OpExecutionMode declarations if needed.
if (func->pipeline_stage != ir::Function::PipelineStage::kUndefined) { if (func->Stage() != ir::Function::PipelineStage::kUndefined) {
EmitEntryPoint(func, id); EmitEntryPoint(func, id);
} }
// Get the ID for the return type. // Get the ID for the return type.
auto return_type_id = Type(func->return_type); auto return_type_id = Type(func->ReturnType());
// Get the ID for the function type (creating it if needed). // Get the ID for the function type (creating it if needed).
// TODO(jrprice): Add the parameter types when they are supported in the IR. // TODO(jrprice): Add the parameter types when they are supported in the IR.
@ -248,7 +248,7 @@ void GeneratorImplIr::EmitFunction(const ir::Function* func) {
TINT_DEFER(current_function_ = Function()); TINT_DEFER(current_function_ = Function());
// Emit the body of the function. // Emit the body of the function.
EmitBlock(func->start_target); EmitBlock(func->StartTarget());
// Add the function to the module. // Add the function to the module.
module_.PushFunction(current_function_); module_.PushFunction(current_function_);
@ -256,13 +256,13 @@ void GeneratorImplIr::EmitFunction(const ir::Function* func) {
void GeneratorImplIr::EmitEntryPoint(const ir::Function* func, uint32_t id) { void GeneratorImplIr::EmitEntryPoint(const ir::Function* func, uint32_t id) {
SpvExecutionModel stage = SpvExecutionModelMax; SpvExecutionModel stage = SpvExecutionModelMax;
switch (func->pipeline_stage) { switch (func->Stage()) {
case ir::Function::PipelineStage::kCompute: { case ir::Function::PipelineStage::kCompute: {
stage = SpvExecutionModelGLCompute; stage = SpvExecutionModelGLCompute;
module_.PushExecutionMode( module_.PushExecutionMode(
spv::Op::OpExecutionMode, spv::Op::OpExecutionMode,
{id, U32Operand(SpvExecutionModeLocalSize), func->workgroup_size->at(0), {id, U32Operand(SpvExecutionModeLocalSize), func->WorkgroupSize()->at(0),
func->workgroup_size->at(1), func->workgroup_size->at(2)}); func->WorkgroupSize()->at(1), func->WorkgroupSize()->at(2)});
break; break;
} }
case ir::Function::PipelineStage::kFragment: { case ir::Function::PipelineStage::kFragment: {
@ -282,7 +282,7 @@ void GeneratorImplIr::EmitEntryPoint(const ir::Function* func, uint32_t id) {
} }
// TODO(jrprice): Add the interface list of all referenced global variables. // TODO(jrprice): Add the interface list of all referenced global variables.
module_.PushEntryPoint(spv::Op::OpEntryPoint, {U32Operand(stage), id, func->name.Name()}); module_.PushEntryPoint(spv::Op::OpEntryPoint, {U32Operand(stage), id, func->Name().Name()});
} }
void GeneratorImplIr::EmitBlock(const ir::Block* block) { void GeneratorImplIr::EmitBlock(const ir::Block* block) {
@ -293,7 +293,7 @@ void GeneratorImplIr::EmitBlock(const ir::Block* block) {
} }
// Emit the instructions. // Emit the instructions.
for (auto* inst : block->instructions) { for (const auto* inst : block->Instructions()) {
auto result = Switch( auto result = Switch(
inst, // inst, //
[&](const ir::Binary* b) { return EmitBinary(b); }, [&](const ir::Binary* b) { return EmitBinary(b); },
@ -313,43 +313,43 @@ void GeneratorImplIr::EmitBlock(const ir::Block* block) {
// Handle the branch at the end of the block. // Handle the branch at the end of the block.
Switch( Switch(
block->branch.target, block->Branch().target,
[&](const ir::Block* b) { current_function_.push_inst(spv::Op::OpBranch, {Label(b)}); }, [&](const ir::Block* b) { current_function_.push_inst(spv::Op::OpBranch, {Label(b)}); },
[&](const ir::If* i) { EmitIf(i); }, [&](const ir::If* i) { EmitIf(i); },
[&](const ir::FunctionTerminator*) { [&](const ir::FunctionTerminator*) {
// TODO(jrprice): Handle the return value, which will be a branch argument. // TODO(jrprice): Handle the return value, which will be a branch argument.
if (!block->branch.args.IsEmpty()) { if (!block->Branch().args.IsEmpty()) {
TINT_ICE(Writer, diagnostics_) << "unimplemented return value"; TINT_ICE(Writer, diagnostics_) << "unimplemented return value";
} }
current_function_.push_inst(spv::Op::OpReturn, {}); current_function_.push_inst(spv::Op::OpReturn, {});
}, },
[&](Default) { [&](Default) {
if (!block->branch.target) { if (!block->Branch().target) {
// A block may not have an outward branch (e.g. an unreachable merge block). // A block may not have an outward branch (e.g. an unreachable merge block).
current_function_.push_inst(spv::Op::OpUnreachable, {}); current_function_.push_inst(spv::Op::OpUnreachable, {});
} else { } else {
TINT_ICE(Writer, diagnostics_) TINT_ICE(Writer, diagnostics_)
<< "unimplemented branch target: " << block->branch.target->TypeInfo().name; << "unimplemented branch target: " << block->Branch().target->TypeInfo().name;
} }
}); });
} }
void GeneratorImplIr::EmitIf(const ir::If* i) { void GeneratorImplIr::EmitIf(const ir::If* i) {
auto* merge_block = i->merge.target->As<ir::Block>(); auto* merge_block = i->Merge().target->As<ir::Block>();
auto* true_block = i->true_.target->As<ir::Block>(); auto* true_block = i->True().target->As<ir::Block>();
auto* false_block = i->false_.target->As<ir::Block>(); auto* false_block = i->False().target->As<ir::Block>();
// Generate labels for the blocks. We emit the true or false block if it: // Generate labels for the blocks. We emit the true or false block if it:
// 1. contains instructions, or // 1. contains instructions, or
// 2. branches somewhere other then the merge target. // 2. branches somewhere other then the Merge().target.
// Otherwise we skip them and branch straight to the merge block. // Otherwise we skip them and branch straight to the merge block.
uint32_t merge_label = Label(merge_block); uint32_t merge_label = Label(merge_block);
uint32_t true_label = merge_label; uint32_t true_label = merge_label;
uint32_t false_label = merge_label; uint32_t false_label = merge_label;
if (!true_block->instructions.IsEmpty() || true_block->branch.target != merge_block) { if (!true_block->Instructions().IsEmpty() || true_block->Branch().target != merge_block) {
true_label = Label(true_block); true_label = Label(true_block);
} }
if (!false_block->instructions.IsEmpty() || false_block->branch.target != merge_block) { if (!false_block->Instructions().IsEmpty() || false_block->Branch().target != merge_block) {
false_label = Label(false_block); false_label = Label(false_block);
} }
@ -357,7 +357,7 @@ void GeneratorImplIr::EmitIf(const ir::If* i) {
current_function_.push_inst(spv::Op::OpSelectionMerge, current_function_.push_inst(spv::Op::OpSelectionMerge,
{merge_label, U32Operand(SpvSelectionControlMaskNone)}); {merge_label, U32Operand(SpvSelectionControlMaskNone)});
current_function_.push_inst(spv::Op::OpBranchConditional, current_function_.push_inst(spv::Op::OpBranchConditional,
{Value(i->condition), true_label, false_label}); {Value(i->Condition()), true_label, false_label});
// Emit the `true` and `false` blocks, if they're not being skipped. // Emit the `true` and `false` blocks, if they're not being skipped.
if (true_label != merge_label) { if (true_label != merge_label) {
@ -376,7 +376,7 @@ uint32_t GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
// Determine the opcode. // Determine the opcode.
spv::Op op = spv::Op::Max; spv::Op op = spv::Op::Max;
switch (binary->kind) { switch (binary->Kind()) {
case ir::Binary::Kind::kAdd: { case ir::Binary::Kind::kAdd: {
op = binary->Type()->is_integer_scalar_or_vector() ? spv::Op::OpIAdd : spv::Op::OpFAdd; op = binary->Type()->is_integer_scalar_or_vector() ? spv::Op::OpIAdd : spv::Op::OpFAdd;
break; break;
@ -387,7 +387,7 @@ uint32_t GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
} }
default: { default: {
TINT_ICE(Writer, diagnostics_) TINT_ICE(Writer, diagnostics_)
<< "unimplemented binary instruction: " << static_cast<uint32_t>(binary->kind); << "unimplemented binary instruction: " << static_cast<uint32_t>(binary->Kind());
} }
} }
@ -400,12 +400,12 @@ uint32_t GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
uint32_t GeneratorImplIr::EmitLoad(const ir::Load* load) { uint32_t GeneratorImplIr::EmitLoad(const ir::Load* load) {
auto id = module_.NextId(); auto id = module_.NextId();
current_function_.push_inst(spv::Op::OpLoad, {Type(load->Type()), id, Value(load->from)}); current_function_.push_inst(spv::Op::OpLoad, {Type(load->Type()), id, Value(load->From())});
return id; return id;
} }
void GeneratorImplIr::EmitStore(const ir::Store* store) { void GeneratorImplIr::EmitStore(const ir::Store* store) {
current_function_.push_inst(spv::Op::OpStore, {Value(store->to), Value(store->from)}); current_function_.push_inst(spv::Op::OpStore, {Value(store->To()), Value(store->From())});
} }
uint32_t GeneratorImplIr::EmitVar(const ir::Var* var) { uint32_t GeneratorImplIr::EmitVar(const ir::Var* var) {
@ -417,8 +417,8 @@ uint32_t GeneratorImplIr::EmitVar(const ir::Var* var) {
if (ptr->AddressSpace() == builtin::AddressSpace::kFunction) { if (ptr->AddressSpace() == builtin::AddressSpace::kFunction) {
TINT_ASSERT(Writer, current_function_); TINT_ASSERT(Writer, current_function_);
current_function_.push_var({ty, id, U32Operand(SpvStorageClassFunction)}); current_function_.push_var({ty, id, U32Operand(SpvStorageClassFunction)});
if (var->initializer) { if (var->Initializer()) {
current_function_.push_inst(spv::Op::OpStore, {id, Value(var->initializer)}); current_function_.push_inst(spv::Op::OpStore, {id, Value(var->Initializer())});
} }
} else { } else {
TINT_ICE(Writer, diagnostics_) TINT_ICE(Writer, diagnostics_)

View File

@ -21,10 +21,10 @@ namespace {
TEST_F(SpvGeneratorImplTest, Binary_Add_I32) { TEST_F(SpvGeneratorImplTest, Binary_Add_I32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i))); utils::Vector{b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -43,10 +43,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Add_U32) { TEST_F(SpvGeneratorImplTest, Binary_Add_U32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Add(mod.types.Get<type::U32>(), b.Constant(1_u), b.Constant(2_u))); utils::Vector{b.Add(mod.types.Get<type::U32>(), b.Constant(1_u), b.Constant(2_u))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -65,10 +65,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Add_F32) { TEST_F(SpvGeneratorImplTest, Binary_Add_F32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Add(mod.types.Get<type::F32>(), b.Constant(1_f), b.Constant(2_f))); utils::Vector{b.Add(mod.types.Get<type::F32>(), b.Constant(1_f), b.Constant(2_f))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -87,10 +87,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Sub_I32) { TEST_F(SpvGeneratorImplTest, Binary_Sub_I32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Subtract(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i))); utils::Vector{b.Subtract(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -109,10 +109,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Sub_U32) { TEST_F(SpvGeneratorImplTest, Binary_Sub_U32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Subtract(mod.types.Get<type::U32>(), b.Constant(1_u), b.Constant(2_u))); utils::Vector{b.Subtract(mod.types.Get<type::U32>(), b.Constant(1_u), b.Constant(2_u))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -131,10 +131,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Sub_F32) { TEST_F(SpvGeneratorImplTest, Binary_Sub_F32) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Subtract(mod.types.Get<type::F32>(), b.Constant(1_f), b.Constant(2_f))); utils::Vector{b.Subtract(mod.types.Get<type::F32>(), b.Constant(1_f), b.Constant(2_f))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -153,17 +153,17 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Sub_Vec2i) { TEST_F(SpvGeneratorImplTest, Binary_Sub_Vec2i) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* lhs = mod.constants_arena.Create<constant::Composite>( auto* lhs = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u), mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u),
utils::Vector{b.Constant(42_i)->value, b.Constant(-1_i)->value}, false, false); utils::Vector{b.Constant(42_i)->Value(), b.Constant(-1_i)->Value()}, false, false);
auto* rhs = mod.constants_arena.Create<constant::Composite>( auto* rhs = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u), mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u),
utils::Vector{b.Constant(0_i)->value, b.Constant(-43_i)->value}, false, false); utils::Vector{b.Constant(0_i)->Value(), b.Constant(-43_i)->Value()}, false, false);
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Subtract(mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u), b.Constant(lhs), utils::Vector{b.Subtract(mod.types.Get<type::Vector>(mod.types.Get<type::I32>(), 2u),
b.Constant(rhs))); b.Constant(lhs), b.Constant(rhs))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -187,21 +187,21 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Sub_Vec4f) { TEST_F(SpvGeneratorImplTest, Binary_Sub_Vec4f) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* lhs = mod.constants_arena.Create<constant::Composite>( auto* lhs = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u), mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u),
utils::Vector{b.Constant(42_f)->value, b.Constant(-1_f)->value, b.Constant(0_f)->value, utils::Vector{b.Constant(42_f)->Value(), b.Constant(-1_f)->Value(),
b.Constant(1.25_f)->value}, b.Constant(0_f)->Value(), b.Constant(1.25_f)->Value()},
false, false); false, false);
auto* rhs = mod.constants_arena.Create<constant::Composite>( auto* rhs = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u), mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u),
utils::Vector{b.Constant(0_f)->value, b.Constant(1.25_f)->value, b.Constant(-42_f)->value, utils::Vector{b.Constant(0_f)->Value(), b.Constant(1.25_f)->Value(),
b.Constant(1_f)->value}, b.Constant(-42_f)->Value(), b.Constant(1_f)->Value()},
false, false); false, false);
func->start_target->instructions.Push( func->StartTarget()->SetInstructions(
b.Subtract(mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u), b.Constant(lhs), utils::Vector{b.Subtract(mod.types.Get<type::Vector>(mod.types.Get<type::F32>(), 4u),
b.Constant(rhs))); b.Constant(lhs), b.Constant(rhs))});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -227,11 +227,10 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Binary_Chain) { TEST_F(SpvGeneratorImplTest, Binary_Chain) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* a = b.Subtract(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i)); auto* a = b.Subtract(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(2_i));
func->start_target->instructions.Push(a); func->StartTarget()->SetInstructions(utils::Vector{a, b.Add(mod.types.Get<type::I32>(), a, a)});
func->start_target->instructions.Push(b.Add(mod.types.Get<type::I32>(), a, a));
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"

View File

@ -67,7 +67,7 @@ TEST_F(SpvGeneratorImplTest, Constant_Vec4Bool) {
auto* f = b.Constant(false); auto* f = b.Constant(false);
auto* v = mod.constants_arena.Create<constant::Composite>( auto* v = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(mod.types.Get<type::Bool>(), 4u), mod.types.Get<type::Vector>(mod.types.Get<type::Bool>(), 4u),
utils::Vector{t->value, f->value, f->value, t->value}, false, true); utils::Vector{t->Value(), f->Value(), f->Value(), t->Value()}, false, true);
generator_.Constant(b.Constant(v)); generator_.Constant(b.Constant(v));
EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeBool EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeBool
%2 = OpTypeVector %3 4 %2 = OpTypeVector %3 4
@ -82,7 +82,8 @@ TEST_F(SpvGeneratorImplTest, Constant_Vec2i) {
auto* i_42 = b.Constant(i32(42)); auto* i_42 = b.Constant(i32(42));
auto* i_n1 = b.Constant(i32(-1)); auto* i_n1 = b.Constant(i32(-1));
auto* v = mod.constants_arena.Create<constant::Composite>( auto* v = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(i, 2u), utils::Vector{i_42->value, i_n1->value}, false, false); mod.types.Get<type::Vector>(i, 2u), utils::Vector{i_42->Value(), i_n1->Value()}, false,
false);
generator_.Constant(b.Constant(v)); generator_.Constant(b.Constant(v));
EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeInt 32 1
%2 = OpTypeVector %3 2 %2 = OpTypeVector %3 2
@ -98,8 +99,8 @@ TEST_F(SpvGeneratorImplTest, Constant_Vec3u) {
auto* u_0 = b.Constant(u32(0)); auto* u_0 = b.Constant(u32(0));
auto* u_4b = b.Constant(u32(4000000000)); auto* u_4b = b.Constant(u32(4000000000));
auto* v = mod.constants_arena.Create<constant::Composite>( auto* v = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(u, 3u), utils::Vector{u_42->value, u_0->value, u_4b->value}, mod.types.Get<type::Vector>(u, 3u),
false, true); utils::Vector{u_42->Value(), u_0->Value(), u_4b->Value()}, false, true);
generator_.Constant(b.Constant(v)); generator_.Constant(b.Constant(v));
EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeInt 32 0 EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeInt 32 0
%2 = OpTypeVector %3 3 %2 = OpTypeVector %3 3
@ -118,7 +119,7 @@ TEST_F(SpvGeneratorImplTest, Constant_Vec4f) {
auto* f_n1 = b.Constant(f32(-1)); auto* f_n1 = b.Constant(f32(-1));
auto* v = mod.constants_arena.Create<constant::Composite>( auto* v = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(f, 4u), mod.types.Get<type::Vector>(f, 4u),
utils::Vector{f_42->value, f_0->value, f_q->value, f_n1->value}, false, true); utils::Vector{f_42->Value(), f_0->Value(), f_q->Value(), f_n1->Value()}, false, true);
generator_.Constant(b.Constant(v)); generator_.Constant(b.Constant(v));
EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeFloat 32
%2 = OpTypeVector %3 4 %2 = OpTypeVector %3 4
@ -135,7 +136,8 @@ TEST_F(SpvGeneratorImplTest, Constant_Vec2h) {
auto* h_42 = b.Constant(f16(42)); auto* h_42 = b.Constant(f16(42));
auto* h_q = b.Constant(f16(0.25)); auto* h_q = b.Constant(f16(0.25));
auto* v = mod.constants_arena.Create<constant::Composite>( auto* v = mod.constants_arena.Create<constant::Composite>(
mod.types.Get<type::Vector>(h, 2u), utils::Vector{h_42->value, h_q->value}, false, false); mod.types.Get<type::Vector>(h, 2u), utils::Vector{h_42->Value(), h_q->Value()}, false,
false);
generator_.Constant(b.Constant(v)); generator_.Constant(b.Constant(v));
EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeFloat 16 EXPECT_EQ(DumpTypes(), R"(%3 = OpTypeFloat 16
%2 = OpTypeVector %3 2 %2 = OpTypeVector %3 2

View File

@ -19,7 +19,7 @@ namespace {
TEST_F(SpvGeneratorImplTest, Function_Empty) { TEST_F(SpvGeneratorImplTest, Function_Empty) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -35,7 +35,7 @@ OpFunctionEnd
// Test that we do not emit the same function type more than once. // Test that we do not emit the same function type more than once.
TEST_F(SpvGeneratorImplTest, Function_DeduplicateType) { TEST_F(SpvGeneratorImplTest, Function_DeduplicateType) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
generator_.EmitFunction(func); generator_.EmitFunction(func);
generator_.EmitFunction(func); generator_.EmitFunction(func);
@ -48,7 +48,7 @@ TEST_F(SpvGeneratorImplTest, Function_DeduplicateType) {
TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Compute) { TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Compute) {
auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(), auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kCompute, {{32, 4, 1}}); ir::Function::PipelineStage::kCompute, {{32, 4, 1}});
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint GLCompute %1 "main" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint GLCompute %1 "main"
@ -66,7 +66,7 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Fragment) { TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Fragment) {
auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(), auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kFragment); ir::Function::PipelineStage::kFragment);
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint Fragment %1 "main" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint Fragment %1 "main"
@ -84,7 +84,7 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Vertex) { TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Vertex) {
auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(), auto* func = b.CreateFunction(mod.symbols.Register("main"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kVertex); ir::Function::PipelineStage::kVertex);
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint Vertex %1 "main" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpEntryPoint Vertex %1 "main"
@ -101,15 +101,15 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Multiple) { TEST_F(SpvGeneratorImplTest, Function_EntryPoint_Multiple) {
auto* f1 = b.CreateFunction(mod.symbols.Register("main1"), mod.types.Get<type::Void>(), auto* f1 = b.CreateFunction(mod.symbols.Register("main1"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kCompute, {{32, 4, 1}}); ir::Function::PipelineStage::kCompute, {{32, 4, 1}});
b.Branch(f1->start_target, f1->end_target); f1->StartTarget()->BranchTo(f1->EndTarget());
auto* f2 = b.CreateFunction(mod.symbols.Register("main2"), mod.types.Get<type::Void>(), auto* f2 = b.CreateFunction(mod.symbols.Register("main2"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kCompute, {{8, 2, 16}}); ir::Function::PipelineStage::kCompute, {{8, 2, 16}});
b.Branch(f2->start_target, f2->end_target); f2->StartTarget()->BranchTo(f2->EndTarget());
auto* f3 = b.CreateFunction(mod.symbols.Register("main3"), mod.types.Get<type::Void>(), auto* f3 = b.CreateFunction(mod.symbols.Register("main3"), mod.types.Get<type::Void>(),
ir::Function::PipelineStage::kFragment); ir::Function::PipelineStage::kFragment);
b.Branch(f3->start_target, f3->end_target); f3->StartTarget()->BranchTo(f3->EndTarget());
generator_.EmitFunction(f1); generator_.EmitFunction(f1);
generator_.EmitFunction(f2); generator_.EmitFunction(f2);

View File

@ -23,11 +23,11 @@ TEST_F(SpvGeneratorImplTest, If_TrueEmpty_FalseEmpty) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
auto* i = b.CreateIf(b.Constant(true)); auto* i = b.CreateIf(b.Constant(true));
b.Branch(i->true_.target->As<ir::Block>(), i->merge.target); i->True().target->As<ir::Block>()->BranchTo(i->Merge().target);
b.Branch(i->false_.target->As<ir::Block>(), i->merge.target); i->False().target->As<ir::Block>()->BranchTo(i->Merge().target);
b.Branch(i->merge.target->As<ir::Block>(), func->end_target); i->Merge().target->As<ir::Block>()->BranchTo(func->EndTarget());
b.Branch(func->start_target, i); func->StartTarget()->BranchTo(i);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -49,15 +49,15 @@ TEST_F(SpvGeneratorImplTest, If_FalseEmpty) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
auto* i = b.CreateIf(b.Constant(true)); auto* i = b.CreateIf(b.Constant(true));
b.Branch(i->false_.target->As<ir::Block>(), i->merge.target); i->False().target->As<ir::Block>()->BranchTo(i->Merge().target);
b.Branch(i->merge.target->As<ir::Block>(), func->end_target); i->Merge().target->As<ir::Block>()->BranchTo(func->EndTarget());
auto* true_block = i->true_.target->As<ir::Block>(); auto* true_block = i->True().target->As<ir::Block>();
true_block->instructions.Push( true_block->SetInstructions(
b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(1_i))); utils::Vector{b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(1_i))});
b.Branch(true_block, i->merge.target); true_block->BranchTo(i->Merge().target);
b.Branch(func->start_target, i); func->StartTarget()->BranchTo(i);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -84,15 +84,15 @@ TEST_F(SpvGeneratorImplTest, If_TrueEmpty) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
auto* i = b.CreateIf(b.Constant(true)); auto* i = b.CreateIf(b.Constant(true));
b.Branch(i->true_.target->As<ir::Block>(), i->merge.target); i->True().target->As<ir::Block>()->BranchTo(i->Merge().target);
b.Branch(i->merge.target->As<ir::Block>(), func->end_target); i->Merge().target->As<ir::Block>()->BranchTo(func->EndTarget());
auto* false_block = i->false_.target->As<ir::Block>(); auto* false_block = i->False().target->As<ir::Block>();
false_block->instructions.Push( false_block->SetInstructions(
b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(1_i))); utils::Vector{b.Add(mod.types.Get<type::I32>(), b.Constant(1_i), b.Constant(1_i))});
b.Branch(false_block, i->merge.target); false_block->BranchTo(i->Merge().target);
b.Branch(func->start_target, i); func->StartTarget()->BranchTo(i);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -119,11 +119,11 @@ TEST_F(SpvGeneratorImplTest, If_BothBranchesReturn) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
auto* i = b.CreateIf(b.Constant(true)); auto* i = b.CreateIf(b.Constant(true));
b.Branch(i->true_.target->As<ir::Block>(), func->end_target); i->True().target->As<ir::Block>()->BranchTo(func->EndTarget());
b.Branch(i->false_.target->As<ir::Block>(), func->end_target); i->False().target->As<ir::Block>()->BranchTo(func->EndTarget());
i->merge.target->As<ir::Block>()->branch.target = nullptr; i->Merge().target->As<ir::Block>()->BranchTo(nullptr);
b.Branch(func->start_target, i); func->StartTarget()->BranchTo(i);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"

View File

@ -22,12 +22,11 @@ namespace {
TEST_F(SpvGeneratorImplTest, FunctionVar_NoInit) { TEST_F(SpvGeneratorImplTest, FunctionVar_NoInit) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* ty = mod.types.Get<type::Pointer>( auto* ty = mod.types.Get<type::Pointer>(
mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite); mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite);
auto* v = b.Declare(ty); func->StartTarget()->SetInstructions(utils::Vector{b.Declare(ty)});
func->start_target->instructions.Push(v);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -45,13 +44,14 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, FunctionVar_WithInit) { TEST_F(SpvGeneratorImplTest, FunctionVar_WithInit) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* ty = mod.types.Get<type::Pointer>( auto* ty = mod.types.Get<type::Pointer>(
mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite); mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite);
auto* v = b.Declare(ty); auto* v = b.Declare(ty);
func->start_target->instructions.Push(v); v->SetInitializer(b.Constant(42_i));
v->initializer = b.Constant(42_i);
func->StartTarget()->SetInstructions(utils::Vector{v});
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -71,12 +71,12 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, FunctionVar_Name) { TEST_F(SpvGeneratorImplTest, FunctionVar_Name) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* ty = mod.types.Get<type::Pointer>( auto* ty = mod.types.Get<type::Pointer>(
mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite); mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite);
auto* v = b.Declare(ty); auto* v = b.Declare(ty);
func->start_target->instructions.Push(v); func->StartTarget()->SetInstructions(utils::Vector{v});
mod.SetName(v, "myvar"); mod.SetName(v, "myvar");
generator_.EmitFunction(func); generator_.EmitFunction(func);
@ -96,22 +96,22 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, FunctionVar_DeclInsideBlock) { TEST_F(SpvGeneratorImplTest, FunctionVar_DeclInsideBlock) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* ty = mod.types.Get<type::Pointer>( auto* ty = mod.types.Get<type::Pointer>(
mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite); mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite);
auto* v = b.Declare(ty); auto* v = b.Declare(ty);
v->initializer = b.Constant(42_i); v->SetInitializer(b.Constant(42_i));
auto* i = b.CreateIf(b.Constant(true)); auto* i = b.CreateIf(b.Constant(true));
b.Branch(i->false_.target->As<ir::Block>(), func->end_target); i->False().target->As<ir::Block>()->BranchTo(func->EndTarget());
b.Branch(i->merge.target->As<ir::Block>(), func->end_target); i->Merge().target->As<ir::Block>()->BranchTo(func->EndTarget());
auto* true_block = i->true_.target->As<ir::Block>(); auto* true_block = i->True().target->As<ir::Block>();
true_block->instructions.Push(v); true_block->SetInstructions(utils::Vector{v});
b.Branch(true_block, i->merge.target); true_block->BranchTo(i->Merge().target);
b.Branch(func->start_target, i); func->StartTarget()->BranchTo(i);
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -140,14 +140,13 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, FunctionVar_Load) { TEST_F(SpvGeneratorImplTest, FunctionVar_Load) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* store_ty = mod.types.Get<type::I32>(); auto* store_ty = mod.types.Get<type::I32>();
auto* ty = mod.types.Get<type::Pointer>(store_ty, builtin::AddressSpace::kFunction, auto* ty = mod.types.Get<type::Pointer>(store_ty, builtin::AddressSpace::kFunction,
builtin::Access::kReadWrite); builtin::Access::kReadWrite);
auto* v = b.Declare(ty); auto* v = b.Declare(ty);
func->start_target->instructions.Push(v); func->StartTarget()->SetInstructions(utils::Vector{v, b.Load(v)});
func->start_target->instructions.Push(b.Load(v));
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
@ -166,13 +165,12 @@ OpFunctionEnd
TEST_F(SpvGeneratorImplTest, FunctionVar_Store) { TEST_F(SpvGeneratorImplTest, FunctionVar_Store) {
auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>()); auto* func = b.CreateFunction(mod.symbols.Register("foo"), mod.types.Get<type::Void>());
b.Branch(func->start_target, func->end_target); func->StartTarget()->BranchTo(func->EndTarget());
auto* ty = mod.types.Get<type::Pointer>( auto* ty = mod.types.Get<type::Pointer>(
mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite); mod.types.Get<type::I32>(), builtin::AddressSpace::kFunction, builtin::Access::kReadWrite);
auto* v = b.Declare(ty); auto* v = b.Declare(ty);
func->start_target->instructions.Push(v); func->StartTarget()->SetInstructions(utils::Vector{v, b.Store(v, b.Constant(42_i))});
func->start_target->instructions.Push(b.Store(v, b.Constant(42_i)));
generator_.EmitFunction(func); generator_.EmitFunction(func);
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"