[tint][ir] Clean up tests

Remove the direct use of BuilderImpl from TestHelperBase to cut down the amount
of internal state management required by the tests, and removing confusing
conflation between the BuilderImpl and Builder.

This change removes the following methods from TestHelperBase:
* CreateBuilder()
* InjectFlowBlock()
* CreateEmptyBuilder()
* FlowNodeForAstNode()

Tests now just use FromProgram() function for testing AST -> IR.

The downside to the black-box testing is that the per-method granularity of the
unit testing increases to whole FromProgram() granularity. However, my personal
opinion is that this is more than offset by the lack of state leakage from the
implementation to the tests.

Bug tint:1718

Change-Id: Iba2560e0fbcbd3dfb936694e50997d716f09fbd8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/132960
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2023-05-15 20:26:43 +00:00 committed by Dawn LUCI CQ
parent 0aa34f27b2
commit 36aa48ce36
17 changed files with 733 additions and 820 deletions

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/instruction.h" #include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
@ -23,10 +24,9 @@ using namespace tint::number_suffixes; // NOLINT
using IR_InstructionTest = TestHelper; using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, CreateAnd) { TEST_F(IR_InstructionTest, CreateAnd) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.And(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i), const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.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);
@ -45,10 +45,9 @@ TEST_F(IR_InstructionTest, CreateAnd) {
} }
TEST_F(IR_InstructionTest, CreateOr) { TEST_F(IR_InstructionTest, CreateOr) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Or(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i), const auto* inst = b.Or(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.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);
@ -65,10 +64,9 @@ TEST_F(IR_InstructionTest, CreateOr) {
} }
TEST_F(IR_InstructionTest, CreateXor) { TEST_F(IR_InstructionTest, CreateXor) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Xor(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i), const auto* inst = b.Xor(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.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);
@ -85,10 +83,9 @@ TEST_F(IR_InstructionTest, CreateXor) {
} }
TEST_F(IR_InstructionTest, CreateEqual) { TEST_F(IR_InstructionTest, CreateEqual) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Equal(b.builder.ir.types.Get<type::Bool>(), const auto* inst = b.Equal(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -105,10 +102,9 @@ TEST_F(IR_InstructionTest, CreateEqual) {
} }
TEST_F(IR_InstructionTest, CreateNotEqual) { TEST_F(IR_InstructionTest, CreateNotEqual) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.NotEqual(b.builder.ir.types.Get<type::Bool>(), const auto* inst = b.NotEqual(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -125,10 +121,9 @@ TEST_F(IR_InstructionTest, CreateNotEqual) {
} }
TEST_F(IR_InstructionTest, CreateLessThan) { TEST_F(IR_InstructionTest, CreateLessThan) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.LessThan(b.builder.ir.types.Get<type::Bool>(), const auto* inst = b.LessThan(b.ir.types.Get<type::Bool>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -145,10 +140,10 @@ TEST_F(IR_InstructionTest, CreateLessThan) {
} }
TEST_F(IR_InstructionTest, CreateGreaterThan) { TEST_F(IR_InstructionTest, CreateGreaterThan) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.GreaterThan(b.builder.ir.types.Get<type::Bool>(), const auto* inst =
b.builder.Constant(4_i), b.builder.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);
@ -165,10 +160,10 @@ TEST_F(IR_InstructionTest, CreateGreaterThan) {
} }
TEST_F(IR_InstructionTest, CreateLessThanEqual) { TEST_F(IR_InstructionTest, CreateLessThanEqual) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.LessThanEqual(b.builder.ir.types.Get<type::Bool>(), const auto* inst =
b.builder.Constant(4_i), b.builder.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);
@ -185,10 +180,10 @@ TEST_F(IR_InstructionTest, CreateLessThanEqual) {
} }
TEST_F(IR_InstructionTest, CreateGreaterThanEqual) { TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.GreaterThanEqual(b.builder.ir.types.Get<type::Bool>(), const auto* inst =
b.builder.Constant(4_i), b.builder.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);
@ -205,9 +200,8 @@ TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
} }
TEST_F(IR_InstructionTest, CreateNot) { TEST_F(IR_InstructionTest, CreateNot) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Not(b.ir.types.Get<type::Bool>(), b.Constant(true));
b.builder.Not(b.builder.ir.types.Get<type::Bool>(), b.builder.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);
@ -224,10 +218,9 @@ TEST_F(IR_InstructionTest, CreateNot) {
} }
TEST_F(IR_InstructionTest, CreateShiftLeft) { TEST_F(IR_InstructionTest, CreateShiftLeft) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.ShiftLeft(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.ShiftLeft(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -244,10 +237,9 @@ TEST_F(IR_InstructionTest, CreateShiftLeft) {
} }
TEST_F(IR_InstructionTest, CreateShiftRight) { TEST_F(IR_InstructionTest, CreateShiftRight) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.ShiftRight(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.ShiftRight(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -264,10 +256,9 @@ TEST_F(IR_InstructionTest, CreateShiftRight) {
} }
TEST_F(IR_InstructionTest, CreateAdd) { TEST_F(IR_InstructionTest, CreateAdd) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Add(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i), const auto* inst = b.Add(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.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);
@ -284,10 +275,9 @@ TEST_F(IR_InstructionTest, CreateAdd) {
} }
TEST_F(IR_InstructionTest, CreateSubtract) { TEST_F(IR_InstructionTest, CreateSubtract) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Subtract(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.Subtract(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -304,10 +294,9 @@ TEST_F(IR_InstructionTest, CreateSubtract) {
} }
TEST_F(IR_InstructionTest, CreateMultiply) { TEST_F(IR_InstructionTest, CreateMultiply) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Multiply(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.Multiply(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -324,10 +313,9 @@ TEST_F(IR_InstructionTest, CreateMultiply) {
} }
TEST_F(IR_InstructionTest, CreateDivide) { TEST_F(IR_InstructionTest, CreateDivide) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Divide(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.Divide(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -344,10 +332,9 @@ TEST_F(IR_InstructionTest, CreateDivide) {
} }
TEST_F(IR_InstructionTest, CreateModulo) { TEST_F(IR_InstructionTest, CreateModulo) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Modulo(b.builder.ir.types.Get<type::I32>(), const auto* inst = b.Modulo(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(4_i), b.builder.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);
@ -364,9 +351,8 @@ TEST_F(IR_InstructionTest, CreateModulo) {
} }
TEST_F(IR_InstructionTest, Binary_Usage) { TEST_F(IR_InstructionTest, Binary_Usage) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.And(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i), const auto* inst = b.And(b.ir.types.Get<type::I32>(), b.Constant(4_i), b.Constant(2_i));
b.builder.Constant(2_i));
EXPECT_EQ(inst->kind, Binary::Kind::kAnd); EXPECT_EQ(inst->kind, Binary::Kind::kAnd);
@ -380,9 +366,9 @@ TEST_F(IR_InstructionTest, Binary_Usage) {
} }
TEST_F(IR_InstructionTest, Binary_Usage_DuplicateValue) { TEST_F(IR_InstructionTest, Binary_Usage_DuplicateValue) {
auto& b = CreateEmptyBuilder(); Builder b;
auto val = b.builder.Constant(4_i); auto val = b.Constant(4_i);
const auto* inst = b.builder.And(b.builder.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());

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/constant.h"
#include "src/tint/ir/instruction.h" #include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
@ -23,9 +25,8 @@ using namespace tint::number_suffixes; // NOLINT
using IR_InstructionTest = TestHelper; using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, Bitcast) { TEST_F(IR_InstructionTest, Bitcast) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Bitcast(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
ASSERT_TRUE(inst->Is<ir::Bitcast>()); ASSERT_TRUE(inst->Is<ir::Bitcast>());
ASSERT_NE(inst->Type(), nullptr); ASSERT_NE(inst->Type(), nullptr);
@ -38,9 +39,8 @@ TEST_F(IR_InstructionTest, Bitcast) {
} }
TEST_F(IR_InstructionTest, Bitcast_Usage) { TEST_F(IR_InstructionTest, Bitcast_Usage) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Bitcast(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
ASSERT_EQ(inst->args.Length(), 1u); ASSERT_EQ(inst->args.Length(), 1u);
ASSERT_NE(inst->args[0], nullptr); ASSERT_NE(inst->args[0], nullptr);

View File

@ -31,6 +31,7 @@
#include "src/tint/ast/const_assert.h" #include "src/tint/ast/const_assert.h"
#include "src/tint/ast/continue_statement.h" #include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/discard_statement.h" #include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/enable.h"
#include "src/tint/ast/float_literal_expression.h" #include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/for_loop_statement.h" #include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/function.h" #include "src/tint/ast/function.h"
@ -182,10 +183,10 @@ ResultType BuilderImpl::Build() {
EmitVariable(var); EmitVariable(var);
}, },
[&](const ast::Function* func) { EmitFunction(func); }, [&](const ast::Function* func) { EmitFunction(func); },
// [&](const ast::Enable*) { [&](const ast::Enable*) {
// TODO(dsinclair): Implement? I think these need to be passed along so further stages // TODO(dsinclair): Implement? I think these need to be passed along so further
// know what is enabled. // stages know what is enabled.
// }, },
[&](const ast::ConstAssert*) { [&](const ast::ConstAssert*) {
// Evaluated by the resolver, drop from the IR. // Evaluated by the resolver, drop from the IR.
}, },

View File

@ -31,16 +31,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
auto* expr = Add(Call("my_func"), 4_u); auto* expr = Add(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = add %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = add %1:u32, 4u
)"); )");
} }
@ -49,11 +54,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundAdd) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kAdd); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kAdd);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -73,16 +77,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Subtract) {
auto* expr = Sub(Call("my_func"), 4_u); auto* expr = Sub(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = sub %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = sub %1:u32, 4u
)"); )");
} }
@ -91,11 +100,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundSubtract) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kSubtract); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kSubtract);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -115,16 +123,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Multiply) {
auto* expr = Mul(Call("my_func"), 4_u); auto* expr = Mul(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = mul %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = mul %1:u32, 4u
)"); )");
} }
@ -133,11 +146,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundMultiply) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kMultiply); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kMultiply);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -157,16 +169,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Div) {
auto* expr = Div(Call("my_func"), 4_u); auto* expr = Div(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = div %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = div %1:u32, 4u
)"); )");
} }
@ -175,11 +192,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundDiv) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kDivide); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kDivide);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -199,16 +215,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Modulo) {
auto* expr = Mod(Call("my_func"), 4_u); auto* expr = Mod(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = mod %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = mod %1:u32, 4u
)"); )");
} }
@ -217,11 +238,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundModulo) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kModulo); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kModulo);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -241,16 +261,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_And) {
auto* expr = And(Call("my_func"), 4_u); auto* expr = And(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = and %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = and %1:u32, 4u
)"); )");
} }
@ -259,11 +284,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundAnd) {
auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kAnd); auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kAnd);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, bool, read_write> = var private, read_write %v1:ref<private, bool, read_write> = var private, read_write
@ -283,16 +307,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Or) {
auto* expr = Or(Call("my_func"), 4_u); auto* expr = Or(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = or %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = or %1:u32, 4u
)"); )");
} }
@ -301,11 +330,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundOr) {
auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kOr); auto* expr = CompoundAssign("v1", false, ast::BinaryOp::kOr);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, bool, read_write> = var private, read_write %v1:ref<private, bool, read_write> = var private, read_write
@ -325,16 +353,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Xor) {
auto* expr = Xor(Call("my_func"), 4_u); auto* expr = Xor(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = xor %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = xor %1:u32, 4u
)"); )");
} }
@ -343,11 +376,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundXor) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kXor); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kXor);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -367,11 +399,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalAnd) {
auto* expr = LogicalAnd(Call("my_func"), false); auto* expr = LogicalAnd(Call("my_func"), false);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = func my_func():bool EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():bool
%fn2 = block %fn2 = block
ret true ret true
func_end func_end
@ -402,11 +433,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalOr) {
auto* expr = LogicalOr(Call("my_func"), true); auto* expr = LogicalOr(Call("my_func"), true);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = func my_func():bool EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():bool
%fn2 = block %fn2 = block
ret true ret true
func_end func_end
@ -438,16 +468,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Equal) {
auto* expr = Equal(Call("my_func"), 4_u); auto* expr = Equal(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = eq %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = eq %1:u32, 4u
)"); )");
} }
@ -456,16 +491,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_NotEqual) {
auto* expr = NotEqual(Call("my_func"), 4_u); auto* expr = NotEqual(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = neq %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = neq %1:u32, 4u
)"); )");
} }
@ -474,16 +514,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThan) {
auto* expr = LessThan(Call("my_func"), 4_u); auto* expr = LessThan(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = lt %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = lt %1:u32, 4u
)"); )");
} }
@ -492,16 +537,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThan) {
auto* expr = GreaterThan(Call("my_func"), 4_u); auto* expr = GreaterThan(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = gt %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = gt %1:u32, 4u
)"); )");
} }
@ -510,16 +560,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThanEqual) {
auto* expr = LessThanEqual(Call("my_func"), 4_u); auto* expr = LessThanEqual(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = lte %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = lte %1:u32, 4u
)"); )");
} }
@ -528,16 +583,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThanEqual) {
auto* expr = GreaterThanEqual(Call("my_func"), 4_u); auto* expr = GreaterThanEqual(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:bool = gte %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:bool = gte %1:u32, 4u
)"); )");
} }
@ -546,16 +606,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftLeft) {
auto* expr = Shl(Call("my_func"), 4_u); auto* expr = Shl(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = shiftl %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = shiftl %1:u32, 4u
)"); )");
} }
@ -564,11 +629,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundShiftLeft) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftLeft); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftLeft);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -588,16 +652,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftRight) {
auto* expr = Shr(Call("my_func"), 4_u); auto* expr = Shr(Call("my_func"), 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 0u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = shiftr %1:u32, 4u
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = shiftr %1:u32, 4u
)"); )");
} }
@ -606,11 +675,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_CompoundShiftRight) {
auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftRight); auto* expr = CompoundAssign("v1", 1_u, ast::BinaryOp::kShiftRight);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, u32, read_write> = var private, read_write %v1:ref<private, u32, read_write> = var private, read_write
@ -632,11 +700,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound) {
GreaterThan(2.5_f, Div(Call("my_func"), Mul(2.3_f, Call("my_func"))))); GreaterThan(2.5_f, Div(Call("my_func"), Mul(2.3_f, Call("my_func")))));
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = func my_func():f32 EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():f32
%fn2 = block %fn2 = block
ret 0.0f ret 0.0f
func_end func_end
@ -674,11 +741,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound_WithConstEval) {
GreaterThan(2.5_f, Div(10_f, Mul(2.3_f, 9.4_f))))); GreaterThan(2.5_f, Div(10_f, Mul(2.3_f, 9.4_f)))));
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = func my_func():bool EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():bool
%fn2 = block %fn2 = block
ret true ret true
func_end func_end

View File

@ -32,16 +32,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Bitcast) {
auto* expr = Bitcast<f32>(Call("my_func")); auto* expr = Bitcast<f32>(Call("my_func"));
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():f32
ASSERT_TRUE(r); %fn2 = block
ret 0.0f
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:f32 = call my_func
%tint_symbol:f32 = bitcast %1:f32
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:f32 = call my_func
%2:f32 = bitcast %1:f32
)"); )");
} }
@ -52,14 +57,15 @@ TEST_F(IR_BuilderImplTest, EmitStatement_Discard) {
create<ast::StageAttribute>(ast::PipelineStage::kFragment), create<ast::StageAttribute>(ast::PipelineStage::kFragment),
}); });
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
b.EmitStatement(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func test_function():void [@fragment]
%fn2 = block
discard
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(discard
)"); )");
} }
@ -68,16 +74,20 @@ TEST_F(IR_BuilderImplTest, EmitStatement_UserFunction) {
auto* stmt = CallStmt(Call("my_func", Mul(2_a, 3_a))); auto* stmt = CallStmt(Call("my_func", Mul(2_a, 3_a)));
WrapInFunction(stmt); WrapInFunction(stmt);
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto& b = CreateBuilder(); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():void
%fn2 = block
ret
func_end
InjectFlowBlock(); %fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
b.EmitStatement(stmt); %fn4 = block
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); %1:void = call my_func, 6.0f
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:void = call my_func, 6.0f
)"); )");
} }
@ -86,12 +96,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Convert) {
auto* expr = Call(ty.f32(), i); auto* expr = Call(ty.f32(), i);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
ASSERT_TRUE(r);
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%i:ref<private, i32, read_write> = var private, read_write, 1i %i:ref<private, i32, read_write> = var private, read_write, 1i
@ -109,12 +117,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_ConstructEmpty) {
auto* expr = vec3(ty.f32()); auto* expr = vec3(ty.f32());
GlobalVar("i", builtin::AddressSpace::kPrivate, expr); GlobalVar("i", builtin::AddressSpace::kPrivate, expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
ASSERT_TRUE(r);
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%i:ref<private, vec3<f32>, read_write> = var private, read_write, vec3<f32> 0.0f %i:ref<private, vec3<f32>, read_write> = var private, read_write, vec3<f32> 0.0f
@ -127,12 +133,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Construct) {
auto* expr = vec3(ty.f32(), 2_f, 3_f, i); auto* expr = vec3(ty.f32(), 2_f, 3_f, i);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
ASSERT_TRUE(r);
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%i:ref<private, f32, read_write> = var private, read_write, 1.0f %i:ref<private, f32, read_write> = var private, read_write, 1.0f

View File

@ -18,10 +18,26 @@
#include "src/tint/ast/case_selector.h" #include "src/tint/ast/case_selector.h"
#include "src/tint/ast/int_literal_expression.h" #include "src/tint/ast/int_literal_expression.h"
#include "src/tint/constant/scalar.h" #include "src/tint/constant/scalar.h"
#include "src/tint/ir/block.h"
#include "src/tint/ir/constant.h"
#include "src/tint/ir/var.h"
namespace tint::ir { namespace tint::ir {
namespace { namespace {
Value* GlobalVarInitializer(const Module& m) {
if (m.root_block->instructions.Length() == 0u) {
ADD_FAILURE() << "m.root_block has no instruction";
return nullptr;
}
auto* var = m.root_block->instructions[0]->As<ir::Var>();
if (!var) {
ADD_FAILURE() << "m.root_block.instructions[0] was not a var";
return nullptr;
}
return var->initializer;
}
using namespace tint::number_suffixes; // NOLINT using namespace tint::number_suffixes; // NOLINT
using IR_BuilderImplTest = TestHelper; using IR_BuilderImplTest = TestHelper;
@ -30,12 +46,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
auto* expr = Expr(true); auto* expr = Expr(true);
GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }
@ -44,12 +60,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
auto* expr = Expr(false); auto* expr = Expr(false);
GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.bool_(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }
@ -58,12 +74,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
auto* expr = Expr(1.2_f); auto* expr = Expr(1.2_f);
GlobalVar("a", ty.f32(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.f32(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }
@ -73,12 +89,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
auto* expr = Expr(1.2_h); auto* expr = Expr(1.2_h);
GlobalVar("a", ty.f16(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.f16(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }
@ -87,12 +103,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
auto* expr = Expr(-2_i); auto* expr = Expr(-2_i);
GlobalVar("a", ty.i32(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.i32(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }
@ -101,12 +117,12 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
auto* expr = Expr(2_u); auto* expr = Expr(2_u);
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr);
auto& b = CreateBuilder(); auto m = Build();
auto r = b.EmitLiteral(expr); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
ASSERT_TRUE(r.Get()->Is<Constant>()); auto* init = GlobalVarInitializer(m.Get());
auto* val = r.Get()->As<Constant>()->value; ASSERT_TRUE(Is<Constant>(init));
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>());
} }

View File

@ -31,11 +31,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_MaterializedCall) {
Func("test_function", {}, ty.f32(), expr, utils::Empty); Func("test_function", {}, ty.f32(), expr, utils::Empty);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = func test_function():f32 EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func test_function():f32
%fn2 = block %fn2 = block
ret 2.0f ret 2.0f
func_end func_end

View File

@ -32,11 +32,10 @@ TEST_F(IR_BuilderImplTest, EmitStatement_Assign) {
auto* expr = Assign("a", 4_u); auto* expr = Assign("a", 4_u);
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%a:ref<private, u32, read_write> = var private, read_write %a:ref<private, u32, read_write> = var private, read_write

View File

@ -1,4 +1,4 @@
// Copyright 2022 The Tint Authors. // Copyright 2023 The Tint Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -18,31 +18,59 @@
#include "src/tint/ast/case_selector.h" #include "src/tint/ast/case_selector.h"
#include "src/tint/ast/int_literal_expression.h" #include "src/tint/ast/int_literal_expression.h"
#include "src/tint/constant/scalar.h" #include "src/tint/constant/scalar.h"
#include "src/tint/ir/block.h"
#include "src/tint/ir/function_terminator.h"
#include "src/tint/ir/if.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/switch.h"
namespace tint::ir { namespace tint::ir {
namespace { namespace {
/// Looks for the flow node with the given type T.
/// If no flow node is found, then nullptr is returned.
/// If multiple flow nodes are found with the type T, then an error is raised and the first is
/// returned.
template <typename T>
const T* FindSingleFlowNode(const Module& mod) {
const T* found = nullptr;
size_t count = 0;
for (auto* node : mod.flow_nodes.Objects()) {
if (auto* as = node->As<T>()) {
count++;
if (!found) {
found = as;
}
}
}
if (count > 1) {
ADD_FAILURE() << "FindSingleFlowNode() found " << count << " nodes of type "
<< utils::TypeInfo::Of<T>().name;
}
return found;
}
using namespace tint::number_suffixes; // NOLINT using namespace tint::number_suffixes; // NOLINT
using IR_BuilderImplTest = TestHelper; using IR_BuilderImplTest = TestHelper;
TEST_F(IR_BuilderImplTest, Func) { TEST_F(IR_BuilderImplTest, Func) {
Func("f", utils::Empty, ty.void_(), utils::Empty); Func("f", utils::Empty, ty.void_(), utils::Empty);
auto r = Build();
ASSERT_TRUE(r) << Error();
auto m = r.Move();
ASSERT_EQ(0u, m.entry_points.Length()); auto m = Build();
ASSERT_EQ(1u, m.functions.Length()); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto* f = m.functions[0]; ASSERT_EQ(0u, m->entry_points.Length());
ASSERT_EQ(1u, m->functions.Length());
auto* f = m->functions[0];
ASSERT_NE(f->start_target, nullptr); ASSERT_NE(f->start_target, nullptr);
ASSERT_NE(f->end_target, nullptr); ASSERT_NE(f->end_target, nullptr);
EXPECT_EQ(1u, f->start_target->inbound_branches.Length()); EXPECT_EQ(1u, f->start_target->inbound_branches.Length());
EXPECT_EQ(1u, f->end_target->inbound_branches.Length()); EXPECT_EQ(1u, f->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), R"(%fn1 = func f():void EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func f():void
%fn2 = block %fn2 = block
ret ret
func_end func_end
@ -53,33 +81,28 @@ func_end
TEST_F(IR_BuilderImplTest, EntryPoint) { TEST_F(IR_BuilderImplTest, EntryPoint) {
Func("f", utils::Empty, ty.void_(), utils::Empty, Func("f", utils::Empty, ty.void_(), utils::Empty,
utils::Vector{Stage(ast::PipelineStage::kFragment)}); utils::Vector{Stage(ast::PipelineStage::kFragment)});
auto r = Build();
ASSERT_TRUE(r) << Error();
auto m = r.Move();
ASSERT_EQ(1u, m.entry_points.Length()); auto m = Build();
EXPECT_EQ(m.functions[0], m.entry_points[0]); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
ASSERT_EQ(1u, m->entry_points.Length());
EXPECT_EQ(m->functions[0], m->entry_points[0]);
} }
TEST_F(IR_BuilderImplTest, IfStatement) { TEST_F(IR_BuilderImplTest, IfStatement) {
auto* ast_if = If(true, Block(), Else(Block())); auto* ast_if = If(true, Block(), Else(Block()));
WrapInFunction(ast_if); WrapInFunction(ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_if = FlowNodeForAstNode(ast_if); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
@ -88,7 +111,7 @@ TEST_F(IR_BuilderImplTest, IfStatement) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -114,21 +137,16 @@ TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
auto* ast_if = If(true, Block(Return())); auto* ast_if = If(true, Block(Return()));
WrapInFunction(ast_if); WrapInFunction(ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_if = FlowNodeForAstNode(ast_if); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
@ -137,7 +155,7 @@ TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -162,21 +180,16 @@ TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
auto* ast_if = If(true, Block(), Else(Block(Return()))); auto* ast_if = If(true, Block(), Else(Block(Return())));
WrapInFunction(ast_if); WrapInFunction(ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_if = FlowNodeForAstNode(ast_if); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
@ -185,7 +198,7 @@ TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -210,21 +223,16 @@ TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
auto* ast_if = If(true, Block(Return()), Else(Block(Return()))); auto* ast_if = If(true, Block(Return()), Else(Block(Return())));
WrapInFunction(ast_if); WrapInFunction(ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_if = FlowNodeForAstNode(ast_if); auto* flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
@ -233,7 +241,7 @@ TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -255,29 +263,21 @@ TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
auto* ast_if = If(true, Block(ast_loop)); auto* ast_if = If(true, Block(ast_loop));
WrapInFunction(ast_if); WrapInFunction(ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_if = FlowNodeForAstNode(ast_if); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->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);
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr); ASSERT_NE(loop_flow, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -312,21 +312,16 @@ TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
auto* ast_loop = Loop(Block(Break())); auto* ast_loop = Loop(Block(Break()));
WrapInFunction(ast_loop); WrapInFunction(ast_loop);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* flow = ir_loop->As<ir::Loop>();
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->inbound_branches.Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
@ -335,7 +330,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -358,30 +353,21 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
auto* ast_loop = Loop(Block(ast_if, Continue())); auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop); WrapInFunction(ast_loop);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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* ir_if = FlowNodeForAstNode(ast_if); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->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, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -394,7 +380,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -434,30 +420,21 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
auto* ast_loop = Loop(Block(), Block(ast_break_if)); auto* ast_loop = Loop(Block(), Block(ast_break_if));
WrapInFunction(ast_loop); WrapInFunction(ast_loop);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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* ir_break_if = FlowNodeForAstNode(ast_break_if); auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_break_if, nullptr);
ASSERT_TRUE(ir_break_if->Is<ir::If>());
auto* break_if_flow = ir_break_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -470,7 +447,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -510,30 +487,21 @@ TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
auto* ast_loop = Loop(Block(ast_if, Continue())); auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop); WrapInFunction(ast_loop);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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* ir_if = FlowNodeForAstNode(ast_if); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->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, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -546,7 +514,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -581,21 +549,16 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
auto* ast_loop = Loop(Block(Return(), Continue())); auto* ast_loop = Loop(Block(Return(), Continue()));
WrapInFunction(ast_loop, If(true, Block(Return()))); WrapInFunction(ast_loop, If(true, Block(Return())));
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -604,7 +567,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -631,33 +594,21 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
auto* ast_if = If(true, Block(Return())); auto* ast_if = If(true, Block(Return()));
WrapInFunction(Block(ast_loop, ast_if)); WrapInFunction(Block(ast_loop, ast_if));
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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* ir_if = FlowNodeForAstNode(ast_if); auto* break_if_flow = FindSingleFlowNode<ir::If>(m.Get());
EXPECT_EQ(ir_if, nullptr);
auto* ir_break_if = FlowNodeForAstNode(ast_break_if);
ASSERT_NE(ir_break_if, nullptr);
EXPECT_TRUE(ir_break_if->Is<ir::If>());
auto* break_if_flow = ir_break_if->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -667,7 +618,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
// 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->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -686,30 +637,21 @@ TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
auto* ast_loop = Loop(Block(ast_if, Continue())); auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop); WrapInFunction(ast_loop);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop = FlowNodeForAstNode(ast_loop); auto* loop_flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
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* ir_if = FlowNodeForAstNode(ast_if); auto* if_flow = FindSingleFlowNode<ir::If>(m.Get());
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->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, loop_flow->inbound_branches.Length()); EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
@ -722,7 +664,7 @@ TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -763,76 +705,68 @@ TEST_F(IR_BuilderImplTest, Loop_Nested) {
WrapInFunction(ast_loop_a); WrapInFunction(ast_loop_a);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_loop_a = FlowNodeForAstNode(ast_loop_a); ASSERT_EQ(1u, m->functions.Length());
ASSERT_NE(ir_loop_a, nullptr);
EXPECT_TRUE(ir_loop_a->Is<ir::Loop>()); auto block_exit = [&](const ir::FlowNode* node) -> const ir::FlowNode* {
auto* loop_flow_a = ir_loop_a->As<ir::Loop>(); if (auto* block = As<ir::Block>(node)) {
return block->branch.target;
}
return nullptr;
};
auto* loop_flow_a = As<ir::Loop>(m->functions[0]->start_target->branch.target);
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* ir_loop_b = FlowNodeForAstNode(ast_loop_b); auto* loop_flow_b = As<ir::Loop>(block_exit(loop_flow_a->start.target));
ASSERT_NE(ir_loop_b, nullptr); ASSERT_NE(loop_flow_b, nullptr);
EXPECT_TRUE(ir_loop_b->Is<ir::Loop>());
auto* loop_flow_b = ir_loop_b->As<ir::Loop>();
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* ir_loop_c = FlowNodeForAstNode(ast_loop_c); auto* if_flow_a = As<ir::If>(block_exit(loop_flow_b->start.target));
ASSERT_NE(ir_loop_c, nullptr); ASSERT_NE(if_flow_a, nullptr);
EXPECT_TRUE(ir_loop_c->Is<ir::Loop>());
auto* loop_flow_c = ir_loop_c->As<ir::Loop>();
ASSERT_NE(loop_flow_c->start.target, nullptr);
ASSERT_NE(loop_flow_c->continuing.target, nullptr);
ASSERT_NE(loop_flow_c->merge.target, nullptr);
auto* ir_loop_d = FlowNodeForAstNode(ast_loop_d);
ASSERT_NE(ir_loop_d, nullptr);
EXPECT_TRUE(ir_loop_d->Is<ir::Loop>());
auto* loop_flow_d = ir_loop_d->As<ir::Loop>();
ASSERT_NE(loop_flow_d->start.target, nullptr);
ASSERT_NE(loop_flow_d->continuing.target, nullptr);
ASSERT_NE(loop_flow_d->merge.target, nullptr);
auto* ir_if_a = FlowNodeForAstNode(ast_if_a);
ASSERT_NE(ir_if_a, nullptr);
EXPECT_TRUE(ir_if_a->Is<ir::If>());
auto* if_flow_a = ir_if_a->As<ir::If>();
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* ir_if_b = FlowNodeForAstNode(ast_if_b); auto* if_flow_b = As<ir::If>(block_exit(if_flow_a->merge.target));
ASSERT_NE(ir_if_b, nullptr); ASSERT_NE(if_flow_b, nullptr);
EXPECT_TRUE(ir_if_b->Is<ir::If>());
auto* if_flow_b = ir_if_b->As<ir::If>();
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* ir_if_c = FlowNodeForAstNode(ast_if_c); auto* loop_flow_c = As<ir::Loop>(block_exit(loop_flow_b->continuing.target));
ASSERT_NE(ir_if_c, nullptr); ASSERT_NE(loop_flow_c, nullptr);
EXPECT_TRUE(ir_if_c->Is<ir::If>()); ASSERT_NE(loop_flow_c->start.target, nullptr);
auto* if_flow_c = ir_if_c->As<ir::If>(); ASSERT_NE(loop_flow_c->continuing.target, nullptr);
ASSERT_NE(loop_flow_c->merge.target, nullptr);
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->start.target, nullptr);
ASSERT_NE(loop_flow_d->continuing.target, nullptr);
ASSERT_NE(loop_flow_d->merge.target, nullptr);
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->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* ir_if_d = FlowNodeForAstNode(ast_if_d); auto* if_flow_d = As<ir::If>(block_exit(loop_flow_b->merge.target));
ASSERT_NE(ir_if_d, nullptr); ASSERT_NE(if_flow_d, nullptr);
EXPECT_TRUE(ir_if_d->Is<ir::If>());
auto* if_flow_d = ir_if_d->As<ir::If>();
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->inbound_branches.Length());
EXPECT_EQ(2u, loop_flow_a->start.target->inbound_branches.Length()); EXPECT_EQ(2u, loop_flow_a->start.target->inbound_branches.Length());
@ -869,7 +803,7 @@ TEST_F(IR_BuilderImplTest, Loop_Nested) {
EXPECT_EQ(1u, func->start_target->inbound_branches.Length()); EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -982,15 +916,10 @@ TEST_F(IR_BuilderImplTest, While) {
auto* ast_while = While(false, Block()); auto* ast_while = While(false, Block());
WrapInFunction(ast_while); WrapInFunction(ast_while);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_while = FlowNodeForAstNode(ast_while); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_while, nullptr);
ASSERT_TRUE(ir_while->Is<ir::Loop>());
auto* flow = ir_while->As<ir::Loop>();
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);
@ -1002,8 +931,8 @@ TEST_F(IR_BuilderImplTest, While) {
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->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->inbound_branches.Length());
@ -1014,7 +943,7 @@ TEST_F(IR_BuilderImplTest, While) {
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1053,15 +982,10 @@ TEST_F(IR_BuilderImplTest, While_Return) {
auto* ast_while = While(true, Block(Return())); auto* ast_while = While(true, Block(Return()));
WrapInFunction(ast_while); WrapInFunction(ast_while);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_while = FlowNodeForAstNode(ast_while); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_while, nullptr);
ASSERT_TRUE(ir_while->Is<ir::Loop>());
auto* flow = ir_while->As<ir::Loop>();
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);
@ -1073,8 +997,8 @@ TEST_F(IR_BuilderImplTest, While_Return) {
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->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->inbound_branches.Length());
@ -1085,7 +1009,7 @@ TEST_F(IR_BuilderImplTest, While_Return) {
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1132,15 +1056,10 @@ TEST_F(IR_BuilderImplTest, DISABLED_For) {
auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block()); auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
WrapInFunction(ast_for); WrapInFunction(ast_for);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_for = FlowNodeForAstNode(ast_for); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_for, nullptr);
ASSERT_TRUE(ir_for->Is<ir::Loop>());
auto* flow = ir_for->As<ir::Loop>();
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);
@ -1152,8 +1071,8 @@ TEST_F(IR_BuilderImplTest, DISABLED_For) {
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->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length()); EXPECT_EQ(1u, flow->inbound_branches.Length());
@ -1164,28 +1083,23 @@ TEST_F(IR_BuilderImplTest, DISABLED_For) {
EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), R"()"); EXPECT_EQ(Disassemble(m.Get()), R"()");
} }
TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) { TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break())); auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
WrapInFunction(ast_for); WrapInFunction(ast_for);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_for = FlowNodeForAstNode(ast_for); auto* flow = FindSingleFlowNode<ir::Loop>(m.Get());
ASSERT_NE(ir_for, nullptr);
ASSERT_TRUE(ir_for->Is<ir::Loop>());
auto* flow = ir_for->As<ir::Loop>();
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->inbound_branches.Length());
EXPECT_EQ(2u, flow->start.target->inbound_branches.Length()); EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
@ -1193,7 +1107,7 @@ TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1218,20 +1132,15 @@ TEST_F(IR_BuilderImplTest, Switch) {
WrapInFunction(ast_switch); WrapInFunction(ast_switch);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_switch = FlowNodeForAstNode(ast_switch); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(3u, flow->cases.Length()); 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()); ASSERT_EQ(1u, flow->cases[0].selectors.Length());
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
@ -1253,7 +1162,7 @@ TEST_F(IR_BuilderImplTest, Switch) {
EXPECT_EQ(3u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(3u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1287,20 +1196,15 @@ TEST_F(IR_BuilderImplTest, Switch_MultiSelector) {
WrapInFunction(ast_switch); WrapInFunction(ast_switch);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_switch = FlowNodeForAstNode(ast_switch); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, flow->cases.Length()); 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()); ASSERT_EQ(3u, flow->cases[0].selectors.Length());
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
@ -1318,7 +1222,7 @@ TEST_F(IR_BuilderImplTest, Switch_MultiSelector) {
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1340,20 +1244,15 @@ TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())}); auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
WrapInFunction(ast_switch); WrapInFunction(ast_switch);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_switch = FlowNodeForAstNode(ast_switch); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, flow->cases.Length()); 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()); ASSERT_EQ(1u, flow->cases[0].selectors.Length());
EXPECT_TRUE(flow->cases[0].selectors[0].IsDefault()); EXPECT_TRUE(flow->cases[0].selectors[0].IsDefault());
@ -1363,7 +1262,7 @@ TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length()); EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1387,20 +1286,15 @@ TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
DefaultCase(Block())}); DefaultCase(Block())});
WrapInFunction(ast_switch); WrapInFunction(ast_switch);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
auto* ir_switch = FlowNodeForAstNode(ast_switch); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(2u, flow->cases.Length()); 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()); ASSERT_EQ(1u, flow->cases[0].selectors.Length());
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
@ -1417,7 +1311,7 @@ TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
// 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->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3
@ -1446,22 +1340,17 @@ TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
auto* ast_if = If(true, Block(Return())); auto* ast_if = If(true, Block(Return()));
WrapInFunction(ast_switch, ast_if); WrapInFunction(ast_switch, ast_if);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
ASSERT_EQ(FlowNodeForAstNode(ast_if), nullptr); ASSERT_EQ(FindSingleFlowNode<ir::If>(m.Get()), nullptr);
auto* ir_switch = FlowNodeForAstNode(ast_switch); auto* flow = FindSingleFlowNode<ir::Switch>(m.Get());
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
ASSERT_NE(flow->merge.target, nullptr); ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(2u, flow->cases.Length()); 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()); ASSERT_EQ(1u, flow->cases[0].selectors.Length());
ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>()); ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
@ -1477,7 +1366,7 @@ TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length()); EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length()); EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
branch %fn3 branch %fn3

View File

@ -31,16 +31,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Not) {
auto* expr = Not(Call("my_func")); auto* expr = Not(Call("my_func"));
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():bool
ASSERT_TRUE(r); %fn2 = block
ret false
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:bool = call my_func
%tint_symbol:bool = eq %1:bool, false
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:bool = call my_func
%2:bool = eq %1:bool, false
)"); )");
} }
@ -49,16 +54,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Complement) {
auto* expr = Complement(Call("my_func")); auto* expr = Complement(Call("my_func"));
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():u32
ASSERT_TRUE(r); %fn2 = block
ret 1u
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:u32 = call my_func
%tint_symbol:u32 = complement %1:u32
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:u32 = call my_func
%2:u32 = complement %1:u32
)"); )");
} }
@ -67,16 +77,21 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Negation) {
auto* expr = Negation(Call("my_func")); auto* expr = Negation(Call("my_func"));
WrapInFunction(expr); WrapInFunction(expr);
auto& b = CreateBuilder(); auto m = Build();
InjectFlowBlock(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto r = b.EmitExpression(expr);
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty()); EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = func my_func():i32
ASSERT_TRUE(r); %fn2 = block
ret 1i
func_end
%fn3 = func test_function():void [@compute @workgroup_size(1, 1, 1)]
%fn4 = block
%1:i32 = call my_func
%tint_symbol:i32 = negation %1:i32
ret
func_end
Disassembler d(b.builder.ir);
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1:i32 = call my_func
%2:i32 = negation %1:i32
)"); )");
} }
@ -86,11 +101,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Unary_AddressOf) {
auto* expr = Decl(Let("v2", AddressOf("v1"))); auto* expr = Decl(Let("v2", AddressOf("v1")));
WrapInFunction(expr); WrapInFunction(expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, i32, read_write> = var private, read_write %v1:ref<private, i32, read_write> = var private, read_write
@ -112,11 +126,10 @@ TEST_F(IR_BuilderImplTest, EmitExpression_Unary_Indirection) {
}; };
WrapInFunction(stmts); WrapInFunction(stmts);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%v1:ref<private, i32, read_write> = var private, read_write %v1:ref<private, i32, read_write> = var private, read_write

View File

@ -29,11 +29,10 @@ using IR_BuilderImplTest = TestHelper;
TEST_F(IR_BuilderImplTest, Emit_GlobalVar_NoInit) { TEST_F(IR_BuilderImplTest, Emit_GlobalVar_NoInit) {
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate); GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%a:ref<private, u32, read_write> = var private, read_write %a:ref<private, u32, read_write> = var private, read_write
@ -45,11 +44,10 @@ TEST_F(IR_BuilderImplTest, Emit_GlobalVar_Init) {
auto* expr = Expr(2_u); auto* expr = Expr(2_u);
GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr); GlobalVar("a", ty.u32(), builtin::AddressSpace::kPrivate, expr);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), R"(%fn1 = block EXPECT_EQ(Disassemble(m.Get()), R"(%fn1 = block
%a:ref<private, u32, read_write> = var private, read_write, 2u %a:ref<private, u32, read_write> = var private, read_write, 2u
@ -61,11 +59,10 @@ TEST_F(IR_BuilderImplTest, Emit_Var_NoInit) {
auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction); auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction);
WrapInFunction(a); WrapInFunction(a);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
%a:ref<function, u32, read_write> = var function, read_write %a:ref<function, u32, read_write> = var function, read_write
@ -80,11 +77,10 @@ TEST_F(IR_BuilderImplTest, Emit_Var_Init) {
auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction, expr); auto* a = Var("a", ty.u32(), builtin::AddressSpace::kFunction, expr);
WrapInFunction(a); WrapInFunction(a);
auto r = Build(); auto m = Build();
ASSERT_TRUE(r) << Error(); ASSERT_TRUE(m) << (!m ? m.Failure() : "");
auto m = r.Move();
EXPECT_EQ(Disassemble(m), 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)]
%fn2 = block %fn2 = block
%a:ref<function, u32, read_write> = var function, read_write, 2u %a:ref<function, u32, read_write> = var function, read_write, 2u

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
#include "src/tint/ir/value.h" #include "src/tint/ir/value.h"
@ -23,11 +24,11 @@ using namespace tint::number_suffixes; // NOLINT
using IR_ConstantTest = TestHelper; using IR_ConstantTest = TestHelper;
TEST_F(IR_ConstantTest, f32) { TEST_F(IR_ConstantTest, f32) {
auto& b = CreateEmptyBuilder(); Builder b;
utils::StringStream str; utils::StringStream str;
auto* c = b.builder.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>>());
@ -38,11 +39,11 @@ TEST_F(IR_ConstantTest, f32) {
} }
TEST_F(IR_ConstantTest, f16) { TEST_F(IR_ConstantTest, f16) {
auto& b = CreateEmptyBuilder(); Builder b;
utils::StringStream str; utils::StringStream str;
auto* c = b.builder.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>>());
@ -53,11 +54,11 @@ TEST_F(IR_ConstantTest, f16) {
} }
TEST_F(IR_ConstantTest, i32) { TEST_F(IR_ConstantTest, i32) {
auto& b = CreateEmptyBuilder(); Builder b;
utils::StringStream str; utils::StringStream str;
auto* c = b.builder.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>>());
@ -68,11 +69,11 @@ TEST_F(IR_ConstantTest, i32) {
} }
TEST_F(IR_ConstantTest, u32) { TEST_F(IR_ConstantTest, u32) {
auto& b = CreateEmptyBuilder(); Builder b;
utils::StringStream str; utils::StringStream str;
auto* c = b.builder.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>>());
@ -83,18 +84,18 @@ TEST_F(IR_ConstantTest, u32) {
} }
TEST_F(IR_ConstantTest, bool) { TEST_F(IR_ConstantTest, bool) {
auto& b = CreateEmptyBuilder(); Builder b;
{ {
utils::StringStream str; utils::StringStream str;
auto* c = b.builder.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.builder.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>>());

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/instruction.h" #include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
@ -21,9 +22,9 @@ namespace {
using IR_InstructionTest = TestHelper; using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, Discard) { TEST_F(IR_InstructionTest, Discard) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = b.builder.Discard(); const auto* inst = b.Discard();
ASSERT_TRUE(inst->Is<ir::Discard>()); ASSERT_TRUE(inst->Is<ir::Discard>());
} }

View File

@ -14,6 +14,7 @@
#include "src/tint/ir/module.h" #include "src/tint/ir/module.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
#include "src/tint/ir/var.h"
namespace tint::ir { namespace tint::ir {
namespace { namespace {

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/instruction.h" #include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
@ -23,12 +24,12 @@ using namespace tint::number_suffixes; // NOLINT
using IR_InstructionTest = TestHelper; using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, CreateStore) { TEST_F(IR_InstructionTest, CreateStore) {
auto& b = CreateEmptyBuilder(); Builder b;
// TODO(dsinclair): This is wrong, but we don't have anything correct to store too at the // TODO(dsinclair): This is wrong, but we don't have anything correct to store too at the
// moment. // moment.
auto* to = b.builder.Discard(); auto* to = b.Discard();
const auto* inst = b.builder.Store(to, b.builder.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);
@ -40,10 +41,10 @@ TEST_F(IR_InstructionTest, CreateStore) {
} }
TEST_F(IR_InstructionTest, Store_Usage) { TEST_F(IR_InstructionTest, Store_Usage) {
auto& b = CreateEmptyBuilder(); Builder b;
auto* to = b.builder.Discard(); auto* to = b.Discard();
const auto* inst = b.builder.Store(to, b.builder.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);

View File

@ -20,8 +20,8 @@
#include <utility> #include <utility>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "src/tint/ir/builder_impl.h"
#include "src/tint/ir/disassembler.h" #include "src/tint/ir/disassembler.h"
#include "src/tint/ir/from_program.h"
#include "src/tint/number.h" #include "src/tint/number.h"
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/utils/string_stream.h" #include "src/tint/utils/string_stream.h"
@ -36,84 +36,28 @@ class TestHelperBase : public BASE, public ProgramBuilder {
~TestHelperBase() override = default; ~TestHelperBase() override = default;
/// Builds and returns a BuilderImpl from the program.
/// @note The builder is only created once. Multiple calls to Build() will
/// return the same builder without rebuilding.
/// @return the builder
BuilderImpl& CreateBuilder() {
SetResolveOnBuild(true);
if (gen_) {
return *gen_;
}
diag::Formatter formatter;
program_ = std::make_unique<Program>(std::move(*this));
[&]() { ASSERT_TRUE(program_->IsValid()) << formatter.format(program_->Diagnostics()); }();
gen_ = std::make_unique<BuilderImpl>(program_.get());
return *gen_;
}
/// Injects a flow block into the builder
/// @returns the injected block
ir::Block* InjectFlowBlock() {
auto* block = gen_->builder.CreateBlock();
gen_->current_flow_block = block;
return block;
}
/// Creates a BuilderImpl without an originating program. This is used for testing the
/// expressions which don't require the full builder implementation. The current flow block
/// is initialized with an empty block.
/// @returns the BuilderImpl for testing.
BuilderImpl& CreateEmptyBuilder() {
program_ = std::make_unique<Program>();
gen_ = std::make_unique<BuilderImpl>(program_.get());
gen_->current_flow_block = gen_->builder.CreateBlock();
return *gen_;
}
/// Build the module, cleaning up the program before returning. /// Build the module, cleaning up the program before returning.
/// @returns the generated module /// @returns the generated module
utils::Result<Module> Build() { utils::Result<Module, std::string> Build() {
auto& b = CreateBuilder(); SetResolveOnBuild(true);
auto m = b.Build();
// Store the error away in case we need it auto program = std::make_unique<Program>(std::move(*this));
error_ = b.Diagnostics().str(); [&]() {
diag::Formatter formatter;
ASSERT_TRUE(program->IsValid()) << formatter.format(program->Diagnostics());
}();
// Explicitly remove program to guard against pointers back to ast. Note, this does mean the return FromProgram(program.get());
// BuilderImpl is pointing to an invalid program. We keep the BuilderImpl around because we
// need to be able to map from ast pointers to flow nodes in tests.
program_ = nullptr;
return m;
}
/// @param node the ast node to lookup
/// @returns the IR flow node for the given ast node.
const ir::FlowNode* FlowNodeForAstNode(const ast::Node* node) const {
return gen_->FlowNodeForAstNode(node);
} }
/// @param mod the module /// @param mod the module
/// @returns the disassembly string of the module /// @returns the disassembly string of the module
std::string Disassemble(Module& mod) const { std::string Disassemble(const Module& mod) const {
Disassembler d(mod); Disassembler d(mod);
return d.Disassemble(); return d.Disassemble();
} }
/// @returns the error generated during build, if any
std::string Error() const { return error_; }
private:
std::unique_ptr<BuilderImpl> gen_;
/// The program built with a call to Build()
std::unique_ptr<Program> program_;
/// Error generated when calling `Build`
std::string error_;
}; };
using TestHelper = TestHelperBase<testing::Test>; using TestHelper = TestHelperBase<testing::Test>;
template <typename T> template <typename T>

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ir/builder.h"
#include "src/tint/ir/instruction.h" #include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h" #include "src/tint/ir/test_helper.h"
@ -23,14 +24,13 @@ using namespace tint::number_suffixes; // NOLINT
using IR_InstructionTest = TestHelper; using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, CreateAddressOf) { TEST_F(IR_InstructionTest, CreateAddressOf) {
auto& b = CreateEmptyBuilder(); Builder b;
// TODO(dsinclair): This would be better as an identifier, but works for now. // TODO(dsinclair): This would be better as an identifier, but works for now.
const auto* inst = const auto* inst = b.AddressOf(
b.builder.AddressOf(b.builder.ir.types.Get<type::Pointer>( b.ir.types.Get<type::Pointer>(b.ir.types.Get<type::I32>(), builtin::AddressSpace::kPrivate,
b.builder.ir.types.Get<type::I32>(), builtin::Access::kReadWrite),
builtin::AddressSpace::kPrivate, builtin::Access::kReadWrite), b.Constant(4_i));
b.builder.Constant(4_i));
ASSERT_TRUE(inst->Is<Unary>()); ASSERT_TRUE(inst->Is<Unary>());
EXPECT_EQ(inst->kind, Unary::Kind::kAddressOf); EXPECT_EQ(inst->kind, Unary::Kind::kAddressOf);
@ -44,9 +44,8 @@ TEST_F(IR_InstructionTest, CreateAddressOf) {
} }
TEST_F(IR_InstructionTest, CreateComplement) { TEST_F(IR_InstructionTest, CreateComplement) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Complement(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Complement(b.builder.ir.types.Get<type::I32>(), b.builder.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);
@ -58,11 +57,10 @@ TEST_F(IR_InstructionTest, CreateComplement) {
} }
TEST_F(IR_InstructionTest, CreateIndirection) { TEST_F(IR_InstructionTest, CreateIndirection) {
auto& b = CreateEmptyBuilder(); Builder b;
// TODO(dsinclair): This would be better as an identifier, but works for now. // TODO(dsinclair): This would be better as an identifier, but works for now.
const auto* inst = const auto* inst = b.Indirection(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Indirection(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
ASSERT_TRUE(inst->Is<Unary>()); ASSERT_TRUE(inst->Is<Unary>());
EXPECT_EQ(inst->kind, Unary::Kind::kIndirection); EXPECT_EQ(inst->kind, Unary::Kind::kIndirection);
@ -74,9 +72,8 @@ TEST_F(IR_InstructionTest, CreateIndirection) {
} }
TEST_F(IR_InstructionTest, CreateNegation) { TEST_F(IR_InstructionTest, CreateNegation) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.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);
@ -88,9 +85,8 @@ TEST_F(IR_InstructionTest, CreateNegation) {
} }
TEST_F(IR_InstructionTest, Unary_Usage) { TEST_F(IR_InstructionTest, Unary_Usage) {
auto& b = CreateEmptyBuilder(); Builder b;
const auto* inst = const auto* inst = b.Negation(b.ir.types.Get<type::I32>(), b.Constant(4_i));
b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
EXPECT_EQ(inst->kind, Unary::Kind::kNegation); EXPECT_EQ(inst->kind, Unary::Kind::kNegation);