[ir] Add EmitBinary
This CL adds the machinery to emit binary operations to the IR. The debug helper is split into Debug and Disassembler. The Disassembler is used to help test the IR output. Bug: tint:1718 Change-Id: Iffdd3be92e69a87828655ac41be91b34d5618174 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/110841 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
f6fcf0a3ef
commit
669e15e139
|
@ -658,6 +658,8 @@ if(${TINT_BUILD_IR})
|
|||
ir/builder_impl.h
|
||||
ir/debug.cc
|
||||
ir/debug.h
|
||||
ir/disassembler.cc
|
||||
ir/disassembler.h
|
||||
ir/flow_node.cc
|
||||
ir/flow_node.h
|
||||
ir/function.cc
|
||||
|
@ -668,6 +670,8 @@ if(${TINT_BUILD_IR})
|
|||
ir/loop.h
|
||||
ir/module.cc
|
||||
ir/module.h
|
||||
ir/op.cc
|
||||
ir/op.h
|
||||
ir/register.cc
|
||||
ir/register.h
|
||||
ir/switch.cc
|
||||
|
@ -1335,6 +1339,7 @@ if(TINT_BUILD_TESTS)
|
|||
if (${TINT_BUILD_IR})
|
||||
list(APPEND TINT_TEST_SRCS
|
||||
ir/builder_impl_test.cc
|
||||
ir/op_test.cc
|
||||
ir/register_test.cc
|
||||
ir/test_helper.h
|
||||
)
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#if TINT_BUILD_IR
|
||||
#include "src/tint/ir/debug.h"
|
||||
#include "src/tint/ir/disassembler.h"
|
||||
#include "src/tint/ir/module.h"
|
||||
#endif // TINT_BUILD_IR
|
||||
|
||||
|
@ -1343,7 +1344,8 @@ int main(int argc, const char** argv) {
|
|||
} else {
|
||||
auto mod = result.Move();
|
||||
if (options.dump_ir) {
|
||||
std::cout << tint::ir::Debug::AsString(&mod) << std::endl;
|
||||
tint::ir::Disassembler d;
|
||||
std::cout << d.Disassemble(mod) << std::endl;
|
||||
}
|
||||
if (options.dump_ir_graph) {
|
||||
auto graph = tint::ir::Debug::AsDotGraph(&mod);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#define SRC_TINT_IR_BLOCK_H_
|
||||
|
||||
#include "src/tint/ir/flow_node.h"
|
||||
#include "src/tint/ir/op.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
||||
|
@ -30,6 +32,9 @@ class Block : public Castable<Block, FlowNode> {
|
|||
|
||||
/// The node this block branches too.
|
||||
const FlowNode* branch_target = nullptr;
|
||||
|
||||
/// The operations in the block
|
||||
utils::Vector<Op, 16> ops;
|
||||
};
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -93,4 +93,84 @@ void Builder::Branch(Block* from, FlowNode* to) {
|
|||
to->inbound_branches.Push(from);
|
||||
}
|
||||
|
||||
Register::Id Builder::AllocateRegister() {
|
||||
return next_register_id++;
|
||||
}
|
||||
|
||||
Op Builder::CreateOp(Op::Kind kind, Register lhs, Register rhs) {
|
||||
return Op(kind, Register(AllocateRegister()), lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::And(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kAnd, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Or(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kOr, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Xor(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kXor, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::LogicalAnd(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kLogicalAnd, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::LogicalOr(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kLogicalOr, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Equal(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kEqual, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::NotEqual(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kNotEqual, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::LessThan(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kLessThan, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::GreaterThan(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kGreaterThan, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::LessThanEqual(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kLessThanEqual, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::GreaterThanEqual(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kGreaterThanEqual, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::ShiftLeft(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kShiftLeft, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::ShiftRight(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kShiftRight, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Add(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kAdd, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Subtract(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kSubtract, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Multiply(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kMultiply, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Divide(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kDivide, lhs, rhs);
|
||||
}
|
||||
|
||||
Op Builder::Modulo(Register lhs, Register rhs) {
|
||||
return CreateOp(Op::Kind::kModulo, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "src/tint/ir/if.h"
|
||||
#include "src/tint/ir/loop.h"
|
||||
#include "src/tint/ir/module.h"
|
||||
#include "src/tint/ir/op.h"
|
||||
#include "src/tint/ir/register.h"
|
||||
#include "src/tint/ir/switch.h"
|
||||
#include "src/tint/ir/terminator.h"
|
||||
|
||||
|
@ -81,8 +83,129 @@ class Builder {
|
|||
/// @param to the node to branch too
|
||||
void Branch(Block* from, FlowNode* to);
|
||||
|
||||
/// Creates an op for `lhs kind rhs`
|
||||
/// @param kind the kind of operation
|
||||
/// @param lhs the left-hand-side of the operation
|
||||
/// @param rhs the right-hand-side of the operation
|
||||
/// @returns the operation
|
||||
Op CreateOp(Op::Kind kind, Register lhs, Register rhs);
|
||||
|
||||
/// Creates an And operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op And(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Or operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Or(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Xor operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Xor(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an LogicalAnd operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op LogicalAnd(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an LogicalOr operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op LogicalOr(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Equal operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Equal(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an NotEqual operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op NotEqual(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an LessThan operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op LessThan(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an GreaterThan operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op GreaterThan(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an LessThanEqual operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op LessThanEqual(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an GreaterThanEqual operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op GreaterThanEqual(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an ShiftLeft operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op ShiftLeft(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an ShiftRight operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op ShiftRight(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Add operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Add(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Subtract operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Subtract(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Multiply operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Multiply(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Divide operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Divide(Register lhs, Register rhs);
|
||||
|
||||
/// Creates an Modulo operation
|
||||
/// @param lhs the lhs of the add
|
||||
/// @param rhs the rhs of the add
|
||||
/// @returns the operation
|
||||
Op Modulo(Register lhs, Register rhs);
|
||||
|
||||
/// @returns a unique register id
|
||||
Register::Id AllocateRegister();
|
||||
|
||||
/// The IR module.
|
||||
Module ir;
|
||||
|
||||
/// The next register number to allocate
|
||||
Register::Id next_register_id = 1;
|
||||
};
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "src/tint/ir/builder_impl.h"
|
||||
|
||||
#include "src/tint/ast/alias.h"
|
||||
#include "src/tint/ast/binary_expression.h"
|
||||
#include "src/tint/ast/block_statement.h"
|
||||
#include "src/tint/ast/bool_literal_expression.h"
|
||||
#include "src/tint/ast/break_if_statement.h"
|
||||
|
@ -519,7 +520,7 @@ utils::Result<Register> BuilderImpl::EmitExpression(const ast::Expression* expr)
|
|||
return tint::Switch(
|
||||
expr,
|
||||
// [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
|
||||
// [&](const ast::BinaryExpression* b) { return EmitBinary(b); },
|
||||
[&](const ast::BinaryExpression* b) { return EmitBinary(b); },
|
||||
// [&](const ast::BitcastExpression* b) { return EmitBitcast(b); },
|
||||
// [&](const ast::CallExpression* c) { return EmitCall(c); },
|
||||
// [&](const ast::IdentifierExpression* i) { return EmitIdentifier(i); },
|
||||
|
@ -550,6 +551,83 @@ bool BuilderImpl::EmitVariable(const ast::Variable* var) {
|
|||
});
|
||||
}
|
||||
|
||||
utils::Result<Register> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
|
||||
auto lhs = EmitExpression(expr->lhs);
|
||||
if (!lhs) {
|
||||
return utils::Failure;
|
||||
}
|
||||
|
||||
auto rhs = EmitExpression(expr->rhs);
|
||||
if (!rhs) {
|
||||
return utils::Failure;
|
||||
}
|
||||
|
||||
Op op;
|
||||
switch (expr->op) {
|
||||
case ast::BinaryOp::kAnd:
|
||||
op = builder.And(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kOr:
|
||||
op = builder.Or(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kXor:
|
||||
op = builder.Xor(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kLogicalAnd:
|
||||
op = builder.LogicalAnd(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kLogicalOr:
|
||||
op = builder.LogicalOr(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kEqual:
|
||||
op = builder.Equal(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kNotEqual:
|
||||
op = builder.NotEqual(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kLessThan:
|
||||
op = builder.LessThan(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kGreaterThan:
|
||||
op = builder.GreaterThan(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kLessThanEqual:
|
||||
op = builder.LessThanEqual(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kGreaterThanEqual:
|
||||
op = builder.GreaterThanEqual(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kShiftLeft:
|
||||
op = builder.ShiftLeft(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kShiftRight:
|
||||
op = builder.ShiftRight(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kAdd:
|
||||
op = builder.Add(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kSubtract:
|
||||
op = builder.Subtract(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kMultiply:
|
||||
op = builder.Multiply(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kDivide:
|
||||
op = builder.Divide(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kModulo:
|
||||
op = builder.Modulo(lhs.Get(), rhs.Get());
|
||||
break;
|
||||
case ast::BinaryOp::kNone:
|
||||
TINT_ICE(IR, diagnostics_) << "missing binary operand type";
|
||||
return utils::Failure;
|
||||
}
|
||||
|
||||
auto result = op.Result();
|
||||
current_flow_block->ops.Push(op);
|
||||
return result;
|
||||
}
|
||||
|
||||
utils::Result<Register> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
|
||||
return tint::Switch( //
|
||||
lit,
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace tint {
|
|||
class Program;
|
||||
} // namespace tint
|
||||
namespace tint::ast {
|
||||
class BinaryExpression;
|
||||
class BlockStatement;
|
||||
class BreakIfStatement;
|
||||
class BreakStatement;
|
||||
|
@ -146,6 +147,11 @@ class BuilderImpl {
|
|||
/// @returns true if successful, false otherwise
|
||||
bool EmitVariable(const ast::Variable* var);
|
||||
|
||||
/// Emits a binary expression
|
||||
/// @param expr the binary expression
|
||||
/// @returns the register storing the result if successful, utils::Failure otherwise
|
||||
utils::Result<Register> EmitBinary(const ast::BinaryExpression* expr);
|
||||
|
||||
/// Emits a literal expression
|
||||
/// @param lit the literal to emit
|
||||
/// @returns true if successful, false otherwise
|
||||
|
|
|
@ -1396,5 +1396,221 @@ TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
|
|||
EXPECT_EQ(2_u, reg.AsU32());
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Add(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 + 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Subtract) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Sub(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 - 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Multiply) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Mul(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 * 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Div) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Div(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 / 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Modulo) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Mod(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 % 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_And) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(And(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 & 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Or) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Or(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 | 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Xor) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Xor(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 ^ 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalAnd) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(LogicalAnd(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 && 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LogicalOr) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(LogicalOr(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 || 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Eqaul) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Equal(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 == 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_NotEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(NotEqual(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 != 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThan) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(LessThan(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 < 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThan) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(GreaterThan(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 > 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_LessThanEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(LessThanEqual(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 <= 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_GreaterThanEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(GreaterThanEqual(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 >= 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftLeft) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Shl(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 << 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_ShiftRight) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(Shr(3_u, 4_u));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 >> 4
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Compound) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
auto r = b.EmitExpression(LogicalOr( //
|
||||
LessThan(1_u, Add(Shr(3_u, 4_u), 9_u)), GreaterThan(2.5_f, Div(6.7_f, Mul(2.3_f, 5.5_f)))));
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d;
|
||||
d.EmitBlockOps(b.current_flow_block);
|
||||
EXPECT_EQ(d.AsString(), R"(%1 = 3 >> 4
|
||||
%2 = %1 + 9
|
||||
%3 = 1 < %2
|
||||
%4 = 2.300000 * 5.500000
|
||||
%5 = 6.700000 / %4
|
||||
%6 = 2.500000 > %5
|
||||
%7 = %3 || %6
|
||||
)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -26,33 +26,6 @@
|
|||
#include "src/tint/program.h"
|
||||
|
||||
namespace tint::ir {
|
||||
namespace {
|
||||
|
||||
class ScopedStopNode {
|
||||
public:
|
||||
ScopedStopNode(std::unordered_set<const FlowNode*>* stop_nodes, const FlowNode* node)
|
||||
: stop_nodes_(stop_nodes), node_(node) {
|
||||
stop_nodes_->insert(node_);
|
||||
}
|
||||
|
||||
~ScopedStopNode() { stop_nodes_->erase(node_); }
|
||||
|
||||
private:
|
||||
std::unordered_set<const FlowNode*>* stop_nodes_;
|
||||
const FlowNode* node_;
|
||||
};
|
||||
|
||||
class ScopedIndent {
|
||||
public:
|
||||
explicit ScopedIndent(uint32_t* indent) : indent_(indent) { (*indent_) += 2; }
|
||||
|
||||
~ScopedIndent() { (*indent_) -= 2; }
|
||||
|
||||
private:
|
||||
uint32_t* indent_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::string Debug::AsDotGraph(const Module* mod) {
|
||||
|
@ -183,102 +156,4 @@ std::string Debug::AsDotGraph(const Module* mod) {
|
|||
return out.str();
|
||||
}
|
||||
|
||||
// static
|
||||
std::string Debug::AsString(const Module* mod) {
|
||||
std::stringstream out;
|
||||
|
||||
std::unordered_set<const FlowNode*> visited;
|
||||
std::unordered_set<const FlowNode*> stop_nodes;
|
||||
uint32_t indent_size = 0;
|
||||
|
||||
std::function<std::ostream&(void)> indent = [&]() -> std::ostream& {
|
||||
for (uint32_t i = 0; i < indent_size; i++) {
|
||||
out << " ";
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
std::function<void(const FlowNode*)> Walk = [&](const FlowNode* node) {
|
||||
if ((visited.count(node) > 0) || (stop_nodes.count(node) > 0)) {
|
||||
return;
|
||||
}
|
||||
visited.insert(node);
|
||||
|
||||
tint::Switch(
|
||||
node,
|
||||
[&](const ir::Function* f) {
|
||||
out << "Function" << std::endl;
|
||||
|
||||
{
|
||||
ScopedIndent func_indent(&indent_size);
|
||||
ScopedStopNode scope(&stop_nodes, f->end_target);
|
||||
Walk(f->start_target);
|
||||
}
|
||||
Walk(f->end_target);
|
||||
},
|
||||
[&](const ir::Block* b) {
|
||||
indent() << "Block" << std::endl;
|
||||
Walk(b->branch_target);
|
||||
},
|
||||
[&](const ir::Switch* s) {
|
||||
indent() << "Switch (" << s->condition.AsString() << ")" << std::endl;
|
||||
|
||||
{
|
||||
ScopedIndent switch_indent(&indent_size);
|
||||
ScopedStopNode scope(&stop_nodes, s->merge_target);
|
||||
for (const auto& c : s->cases) {
|
||||
indent() << "Case" << std::endl;
|
||||
ScopedIndent case_indent(&indent_size);
|
||||
Walk(c.start_target);
|
||||
}
|
||||
}
|
||||
|
||||
indent() << "Switch Merge" << std::endl;
|
||||
Walk(s->merge_target);
|
||||
},
|
||||
[&](const ir::If* i) {
|
||||
indent() << "if (" << i->condition.AsString() << ")" << std::endl;
|
||||
{
|
||||
ScopedIndent if_indent(&indent_size);
|
||||
ScopedStopNode scope(&stop_nodes, i->merge_target);
|
||||
|
||||
indent() << "true branch" << std::endl;
|
||||
Walk(i->true_target);
|
||||
|
||||
indent() << "false branch" << std::endl;
|
||||
Walk(i->false_target);
|
||||
}
|
||||
|
||||
indent() << "if merge" << std::endl;
|
||||
Walk(i->merge_target);
|
||||
},
|
||||
[&](const ir::Loop* l) {
|
||||
indent() << "loop" << std::endl;
|
||||
{
|
||||
ScopedStopNode loop_scope(&stop_nodes, l->merge_target);
|
||||
ScopedIndent loop_indent(&indent_size);
|
||||
{
|
||||
ScopedStopNode inner_scope(&stop_nodes, l->continuing_target);
|
||||
indent() << "loop start" << std::endl;
|
||||
Walk(l->start_target);
|
||||
}
|
||||
|
||||
indent() << "loop continuing" << std::endl;
|
||||
ScopedIndent continuing_indent(&indent_size);
|
||||
Walk(l->continuing_target);
|
||||
}
|
||||
|
||||
indent() << "loop merge" << std::endl;
|
||||
Walk(l->merge_target);
|
||||
},
|
||||
[&](const ir::Terminator*) { indent() << "Function end" << std::endl; });
|
||||
};
|
||||
|
||||
for (const auto* func : mod->functions) {
|
||||
Walk(func);
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright 2022 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/tint/ir/disassembler.h"
|
||||
|
||||
#include "src/tint/ir/block.h"
|
||||
#include "src/tint/ir/if.h"
|
||||
#include "src/tint/ir/loop.h"
|
||||
#include "src/tint/ir/switch.h"
|
||||
#include "src/tint/ir/terminator.h"
|
||||
#include "src/tint/program.h"
|
||||
|
||||
namespace tint::ir {
|
||||
namespace {
|
||||
|
||||
class ScopedStopNode {
|
||||
public:
|
||||
ScopedStopNode(std::unordered_set<const FlowNode*>* stop_nodes_, const FlowNode* node)
|
||||
: stop_nodes__(stop_nodes_), node_(node) {
|
||||
stop_nodes__->insert(node_);
|
||||
}
|
||||
|
||||
~ScopedStopNode() { stop_nodes__->erase(node_); }
|
||||
|
||||
private:
|
||||
std::unordered_set<const FlowNode*>* stop_nodes__;
|
||||
const FlowNode* node_;
|
||||
};
|
||||
|
||||
class ScopedIndent {
|
||||
public:
|
||||
explicit ScopedIndent(uint32_t* indent) : indent_(indent) { (*indent_) += 2; }
|
||||
|
||||
~ScopedIndent() { (*indent_) -= 2; }
|
||||
|
||||
private:
|
||||
uint32_t* indent_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Disassembler::Disassembler() = default;
|
||||
|
||||
Disassembler::~Disassembler() = default;
|
||||
|
||||
std::ostream& Disassembler::Indent() {
|
||||
for (uint32_t i = 0; i < indent_size_; i++) {
|
||||
out_ << " ";
|
||||
}
|
||||
return out_;
|
||||
}
|
||||
|
||||
void Disassembler::EmitBlockOps(const Block* b) {
|
||||
for (const auto& op : b->ops) {
|
||||
out_ << op << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::Walk(const FlowNode* node) {
|
||||
if ((visited_.count(node) > 0) || (stop_nodes_.count(node) > 0)) {
|
||||
return;
|
||||
}
|
||||
visited_.insert(node);
|
||||
|
||||
tint::Switch(
|
||||
node,
|
||||
[&](const ir::Function* f) {
|
||||
Indent() << "Function" << std::endl;
|
||||
|
||||
{
|
||||
ScopedIndent func_indent(&indent_size_);
|
||||
ScopedStopNode scope(&stop_nodes_, f->end_target);
|
||||
Walk(f->start_target);
|
||||
}
|
||||
Walk(f->end_target);
|
||||
},
|
||||
[&](const ir::Block* b) {
|
||||
Indent() << "Block" << std::endl;
|
||||
EmitBlockOps(b);
|
||||
Walk(b->branch_target);
|
||||
},
|
||||
[&](const ir::Switch* s) {
|
||||
Indent() << "Switch (" << s->condition << ")" << std::endl;
|
||||
|
||||
{
|
||||
ScopedIndent switch_indent(&indent_size_);
|
||||
ScopedStopNode scope(&stop_nodes_, s->merge_target);
|
||||
for (const auto& c : s->cases) {
|
||||
Indent() << "Case" << std::endl;
|
||||
ScopedIndent case_indent(&indent_size_);
|
||||
Walk(c.start_target);
|
||||
}
|
||||
}
|
||||
|
||||
Indent() << "Switch Merge" << std::endl;
|
||||
Walk(s->merge_target);
|
||||
},
|
||||
[&](const ir::If* i) {
|
||||
Indent() << "if (" << i->condition << ")" << std::endl;
|
||||
{
|
||||
ScopedIndent if_indent(&indent_size_);
|
||||
ScopedStopNode scope(&stop_nodes_, i->merge_target);
|
||||
|
||||
Indent() << "true branch" << std::endl;
|
||||
Walk(i->true_target);
|
||||
|
||||
Indent() << "false branch" << std::endl;
|
||||
Walk(i->false_target);
|
||||
}
|
||||
|
||||
Indent() << "if merge" << std::endl;
|
||||
Walk(i->merge_target);
|
||||
},
|
||||
[&](const ir::Loop* l) {
|
||||
Indent() << "loop" << std::endl;
|
||||
{
|
||||
ScopedStopNode loop_scope(&stop_nodes_, l->merge_target);
|
||||
ScopedIndent loop_indent(&indent_size_);
|
||||
{
|
||||
ScopedStopNode inner_scope(&stop_nodes_, l->continuing_target);
|
||||
Indent() << "loop start" << std::endl;
|
||||
Walk(l->start_target);
|
||||
}
|
||||
|
||||
Indent() << "loop continuing" << std::endl;
|
||||
ScopedIndent continuing_indent(&indent_size_);
|
||||
Walk(l->continuing_target);
|
||||
}
|
||||
|
||||
Indent() << "loop merge" << std::endl;
|
||||
Walk(l->merge_target);
|
||||
},
|
||||
[&](const ir::Terminator*) { Indent() << "Function end" << std::endl; });
|
||||
}
|
||||
|
||||
std::string Disassembler::Disassemble(const Module& mod) {
|
||||
for (const auto* func : mod.functions) {
|
||||
Walk(func);
|
||||
}
|
||||
return out_.str();
|
||||
}
|
||||
|
||||
} // namespace tint::ir
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2022 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_TINT_IR_DISASSEMBLER_H_
|
||||
#define SRC_TINT_IR_DISASSEMBLER_H_
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/tint/ir/flow_node.h"
|
||||
#include "src/tint/ir/module.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
||||
/// Helper class to disassemble the IR
|
||||
class Disassembler {
|
||||
public:
|
||||
/// Constructor
|
||||
Disassembler();
|
||||
~Disassembler();
|
||||
|
||||
/// Returns the module as a string
|
||||
/// @param mod the module to emit
|
||||
/// @returns the string representation of the module
|
||||
std::string Disassemble(const Module& mod);
|
||||
|
||||
/// Writes the block ops to the stream
|
||||
/// @param b the block containing the ops
|
||||
void EmitBlockOps(const Block* b);
|
||||
|
||||
/// @returns the string representation
|
||||
std::string AsString() const { return out_.str(); }
|
||||
|
||||
private:
|
||||
std::ostream& Indent();
|
||||
void Walk(const FlowNode* node);
|
||||
|
||||
std::stringstream out_;
|
||||
std::unordered_set<const FlowNode*> visited_;
|
||||
std::unordered_set<const FlowNode*> stop_nodes_;
|
||||
uint32_t indent_size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace tint::ir
|
||||
|
||||
#endif // SRC_TINT_IR_DISASSEMBLER_H_
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2022 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/tint/ir/op.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
||||
Op::Op() {}
|
||||
|
||||
Op::Op(Kind kind, Register result, Register lhs, Register rhs)
|
||||
: kind_(kind), result_(result), args_({lhs, rhs}) {}
|
||||
|
||||
Op::Op(const Op&) = default;
|
||||
|
||||
Op::Op(Op&& o) = default;
|
||||
|
||||
Op::~Op() = default;
|
||||
|
||||
Op& Op::operator=(const Op& o) = default;
|
||||
|
||||
Op& Op::operator=(Op&& o) = default;
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Op& op) {
|
||||
out << op.Result() << " = ";
|
||||
if (op.HasLHS()) {
|
||||
out << op.LHS();
|
||||
}
|
||||
out << " ";
|
||||
|
||||
switch (op.GetKind()) {
|
||||
case Op::Kind::kAdd:
|
||||
out << "+";
|
||||
break;
|
||||
case Op::Kind::kSubtract:
|
||||
out << "-";
|
||||
break;
|
||||
case Op::Kind::kMultiply:
|
||||
out << "*";
|
||||
break;
|
||||
case Op::Kind::kDivide:
|
||||
out << "/";
|
||||
break;
|
||||
case Op::Kind::kModulo:
|
||||
out << "%";
|
||||
break;
|
||||
case Op::Kind::kAnd:
|
||||
out << "&";
|
||||
break;
|
||||
case Op::Kind::kOr:
|
||||
out << "|";
|
||||
break;
|
||||
case Op::Kind::kXor:
|
||||
out << "^";
|
||||
break;
|
||||
case Op::Kind::kLogicalAnd:
|
||||
out << "&&";
|
||||
break;
|
||||
case Op::Kind::kLogicalOr:
|
||||
out << "||";
|
||||
break;
|
||||
case Op::Kind::kEqual:
|
||||
out << "==";
|
||||
break;
|
||||
case Op::Kind::kNotEqual:
|
||||
out << "!=";
|
||||
break;
|
||||
case Op::Kind::kLessThan:
|
||||
out << "<";
|
||||
break;
|
||||
case Op::Kind::kGreaterThan:
|
||||
out << ">";
|
||||
break;
|
||||
case Op::Kind::kLessThanEqual:
|
||||
out << "<=";
|
||||
break;
|
||||
case Op::Kind::kGreaterThanEqual:
|
||||
out << ">=";
|
||||
break;
|
||||
case Op::Kind::kShiftLeft:
|
||||
out << "<<";
|
||||
break;
|
||||
case Op::Kind::kShiftRight:
|
||||
out << ">>";
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.HasRHS()) {
|
||||
out << " " << op.RHS();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace tint::ir
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2022 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_TINT_IR_OP_H_
|
||||
#define SRC_TINT_IR_OP_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "src/tint/ir/register.h"
|
||||
#include "src/tint/utils/vector.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
||||
/// An operation in the IR.
|
||||
class Op {
|
||||
public:
|
||||
/// The kind of operation.
|
||||
enum class Kind {
|
||||
kAdd,
|
||||
kSubtract,
|
||||
kMultiply,
|
||||
kDivide,
|
||||
kModulo,
|
||||
|
||||
kAnd,
|
||||
kOr,
|
||||
kXor,
|
||||
|
||||
kLogicalAnd,
|
||||
kLogicalOr,
|
||||
|
||||
kEqual,
|
||||
kNotEqual,
|
||||
kLessThan,
|
||||
kGreaterThan,
|
||||
kLessThanEqual,
|
||||
kGreaterThanEqual,
|
||||
|
||||
kShiftLeft,
|
||||
kShiftRight
|
||||
};
|
||||
|
||||
/// Constructor
|
||||
Op();
|
||||
/// Constructor
|
||||
/// @param kind the kind of operation
|
||||
/// @param result the result register
|
||||
/// @param lhs the lhs of the operation
|
||||
/// @param rhs the rhs of the operation
|
||||
Op(Kind kind, Register result, Register lhs, Register rhs);
|
||||
/// Copy constructor
|
||||
/// @param o the op to copy from
|
||||
Op(const Op& o);
|
||||
/// Move constructor
|
||||
/// @param o the op to move from
|
||||
Op(Op&& o);
|
||||
/// Destructor
|
||||
~Op();
|
||||
|
||||
/// Copy assign
|
||||
/// @param o the op to copy from
|
||||
/// @returns a reference to this
|
||||
Op& operator=(const Op& o);
|
||||
/// Move assign
|
||||
/// @param o the op to move from
|
||||
/// @returns a reference to this
|
||||
Op& operator=(Op&& o);
|
||||
|
||||
/// @returns the kind of operation
|
||||
Kind GetKind() const { return kind_; }
|
||||
|
||||
/// @returns the result register for the operation
|
||||
const Register& Result() const { return result_; }
|
||||
|
||||
/// @returns true if the op has a LHS
|
||||
bool HasLHS() const { return args_.Length() >= 1; }
|
||||
/// @returns the left-hand-side register for the operation
|
||||
const Register& LHS() const {
|
||||
TINT_ASSERT(IR, HasLHS());
|
||||
return args_[0];
|
||||
}
|
||||
|
||||
/// @returns true if the op has a RHS
|
||||
bool HasRHS() const { return args_.Length() >= 2; }
|
||||
/// @returns the right-hand-side register for the operation
|
||||
const Register& RHS() const {
|
||||
TINT_ASSERT(IR, HasRHS());
|
||||
return args_[1];
|
||||
}
|
||||
|
||||
private:
|
||||
Kind kind_;
|
||||
|
||||
Register result_;
|
||||
utils::Vector<Register, 2> args_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Op&);
|
||||
|
||||
} // namespace tint::ir
|
||||
|
||||
#endif // SRC_TINT_IR_OP_H_
|
|
@ -0,0 +1,494 @@
|
|||
// Copyright 2022 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "src/tint/ir/op.h"
|
||||
#include "src/tint/ir/test_helper.h"
|
||||
|
||||
namespace tint::ir {
|
||||
namespace {
|
||||
|
||||
using IR_OpTest = TestHelper;
|
||||
|
||||
TEST_F(IR_OpTest, CreateAnd) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.And(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kAnd);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 & 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateOr) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Or(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kOr);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 | 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateXor) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Xor(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kXor);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 ^ 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateLogicalAnd) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.LogicalAnd(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kLogicalAnd);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 && 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateLogicalOr) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.LogicalOr(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kLogicalOr);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 || 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Equal(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kEqual);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 == 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateNotEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.NotEqual(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kNotEqual);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 != 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateLessThan) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.LessThan(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kLessThan);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 < 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateGreaterThan) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.GreaterThan(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kGreaterThan);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 > 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateLessThanEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.LessThanEqual(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kLessThanEqual);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 <= 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateGreaterThanEqual) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.GreaterThanEqual(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kGreaterThanEqual);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 >= 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateShiftLeft) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.ShiftLeft(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kShiftLeft);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 << 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateShiftRight) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.ShiftRight(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kShiftRight);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 >> 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateAdd) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Add(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kAdd);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 + 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateSubtract) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Subtract(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kSubtract);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 - 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateMultiply) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Multiply(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kMultiply);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 * 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateDivide) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Divide(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kDivide);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 / 2");
|
||||
}
|
||||
|
||||
TEST_F(IR_OpTest, CreateModulo) {
|
||||
auto& b = CreateEmptyBuilder();
|
||||
|
||||
b.builder.next_register_id = Register::Id(42);
|
||||
auto o = b.builder.Modulo(Register(i32(4)), Register(i32(2)));
|
||||
|
||||
EXPECT_EQ(o.GetKind(), Op::Kind::kModulo);
|
||||
|
||||
ASSERT_TRUE(o.Result().IsTemp());
|
||||
EXPECT_EQ(Register::Id(42), o.Result().AsId());
|
||||
|
||||
ASSERT_TRUE(o.HasLHS());
|
||||
auto& lhs = o.LHS();
|
||||
ASSERT_TRUE(lhs.IsI32());
|
||||
EXPECT_EQ(i32(4), lhs.AsI32());
|
||||
|
||||
ASSERT_TRUE(o.HasRHS());
|
||||
auto& rhs = o.RHS();
|
||||
ASSERT_TRUE(rhs.IsI32());
|
||||
EXPECT_EQ(i32(2), rhs.AsI32());
|
||||
|
||||
std::stringstream str;
|
||||
str << o;
|
||||
EXPECT_EQ(str.str(), "%42 = 4 % 2");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::ir
|
|
@ -42,27 +42,35 @@ Register& Register::operator=(const Register& o) = default;
|
|||
|
||||
Register& Register::operator=(Register&& o) = default;
|
||||
|
||||
std::string Register::AsString() const {
|
||||
switch (kind_) {
|
||||
case Kind::kTemp:
|
||||
return "%" + std::to_string(AsId());
|
||||
case Kind::kF32:
|
||||
return std::to_string(AsF32().value);
|
||||
case Kind::kF16:
|
||||
return std::to_string(AsF16().value);
|
||||
case Kind::kI32:
|
||||
return std::to_string(AsI32().value);
|
||||
case Kind::kU32:
|
||||
return std::to_string(AsU32().value);
|
||||
// TODO(dsinclair): Emit the symbol instead of v
|
||||
case Kind::kVar:
|
||||
return "%v" + std::to_string(AsVarData().id);
|
||||
case Kind::kBool:
|
||||
return AsBool() ? "true" : "false";
|
||||
case Kind::kUninitialized:
|
||||
std::ostream& operator<<(std::ostream& out, const Register& r) {
|
||||
switch (r.GetKind()) {
|
||||
case Register::Kind::kTemp:
|
||||
out << "%" << std::to_string(r.AsId());
|
||||
break;
|
||||
case Register::Kind::kF32:
|
||||
out << std::to_string(r.AsF32().value);
|
||||
break;
|
||||
case Register::Kind::kF16:
|
||||
out << std::to_string(r.AsF16().value);
|
||||
break;
|
||||
case Register::Kind::kI32:
|
||||
out << std::to_string(r.AsI32().value);
|
||||
break;
|
||||
case Register::Kind::kU32:
|
||||
out << std::to_string(r.AsU32().value);
|
||||
break;
|
||||
// TODO(dsinclair): Emit the symbol instead of v
|
||||
case Register::Kind::kVar:
|
||||
out << "%v" << std::to_string(r.AsVarData().id);
|
||||
break;
|
||||
case Register::Kind::kBool:
|
||||
out << (r.AsBool() ? "true" : "false");
|
||||
break;
|
||||
case Register::Kind::kUninitialized:
|
||||
out << "unknown register";
|
||||
break;
|
||||
}
|
||||
return "unknown register";
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef SRC_TINT_IR_REGISTER_H_
|
||||
#define SRC_TINT_IR_REGISTER_H_
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <variant>
|
||||
|
||||
#include "src/tint/number.h"
|
||||
|
@ -31,6 +31,26 @@ class Register {
|
|||
/// A register id.
|
||||
using Id = uint32_t;
|
||||
|
||||
/// The type of the register
|
||||
enum class Kind {
|
||||
/// A uninitialized register
|
||||
kUninitialized,
|
||||
/// A temporary allocated register
|
||||
kTemp,
|
||||
/// A f32 register
|
||||
kF32,
|
||||
/// A f16 register
|
||||
kF16,
|
||||
/// An i32 register
|
||||
kI32,
|
||||
/// A u32 register
|
||||
kU32,
|
||||
/// A variable register
|
||||
kVar,
|
||||
/// A boolean register
|
||||
kBool,
|
||||
};
|
||||
|
||||
/// Stores data for a given variable. There will be multiple `VarData` entries for a given `id`.
|
||||
/// The `id` acts like a generation number (although they aren't sequential, they are
|
||||
/// increasing). As the variable is stored too a new register will be created and the the `id`
|
||||
|
@ -110,6 +130,9 @@ class Register {
|
|||
/// @returns true if this is a bool register
|
||||
bool IsBool() const { return kind_ == Kind::kBool; }
|
||||
|
||||
/// @returns the kind of register
|
||||
Kind GetKind() const { return kind_; }
|
||||
|
||||
/// @returns the register data as a `f32`.
|
||||
/// @note, must only be called if `IsF32()` is true
|
||||
f32 AsF32() const { return std::get<f32>(data_); }
|
||||
|
@ -132,36 +155,15 @@ class Register {
|
|||
/// @note, must only be called if `IsBool()` is true
|
||||
bool AsBool() const { return std::get<bool>(data_); }
|
||||
|
||||
/// @returns the string representation of the register
|
||||
std::string AsString() const;
|
||||
|
||||
private:
|
||||
/// The type of the register
|
||||
enum class Kind {
|
||||
/// A uninitialized register
|
||||
kUninitialized,
|
||||
/// A temporary allocated register
|
||||
kTemp,
|
||||
/// A f32 register
|
||||
kF32,
|
||||
/// A f16 register
|
||||
kF16,
|
||||
/// An i32 register
|
||||
kI32,
|
||||
/// A u32 register
|
||||
kU32,
|
||||
/// A variable register
|
||||
kVar,
|
||||
/// A boolean register
|
||||
kBool,
|
||||
};
|
||||
|
||||
/// The type of data stored in this register
|
||||
Kind kind_;
|
||||
/// The data stored in the register
|
||||
std::variant<Id, f32, f16, u32, i32, VarData, bool> data_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Register& r);
|
||||
|
||||
} // namespace tint::ir
|
||||
|
||||
#endif // SRC_TINT_IR_REGISTER_H_
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/tint/ir/test_helper.h"
|
||||
#include <sstream>
|
||||
|
||||
#include "src/tint/ir/register.h"
|
||||
#include "src/tint/ir/test_helper.h"
|
||||
|
||||
namespace tint::ir {
|
||||
namespace {
|
||||
|
@ -24,9 +25,13 @@ using namespace tint::number_suffixes; // NOLINT
|
|||
using IR_RegisterTest = TestHelper;
|
||||
|
||||
TEST_F(IR_RegisterTest, f32) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(1.2_f);
|
||||
EXPECT_EQ(1.2_f, r.AsF32());
|
||||
EXPECT_EQ("1.200000", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("1.200000", str.str());
|
||||
|
||||
EXPECT_TRUE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
@ -38,9 +43,13 @@ TEST_F(IR_RegisterTest, f32) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, f16) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(1.1_h);
|
||||
EXPECT_EQ(1.1_h, r.AsF16());
|
||||
EXPECT_EQ("1.099609", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("1.099609", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_TRUE(r.IsF16());
|
||||
|
@ -52,9 +61,13 @@ TEST_F(IR_RegisterTest, f16) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, i32) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(1_i);
|
||||
EXPECT_EQ(1_i, r.AsI32());
|
||||
EXPECT_EQ("1", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("1", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
@ -66,9 +79,13 @@ TEST_F(IR_RegisterTest, i32) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, u32) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(2_u);
|
||||
EXPECT_EQ(2_u, r.AsU32());
|
||||
EXPECT_EQ("2", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("2", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
@ -80,9 +97,13 @@ TEST_F(IR_RegisterTest, u32) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, id) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(Register::Id(4));
|
||||
EXPECT_EQ(4u, r.AsId());
|
||||
EXPECT_EQ("%4", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("%4", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
@ -94,13 +115,20 @@ TEST_F(IR_RegisterTest, id) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, bool) {
|
||||
std::stringstream str;
|
||||
|
||||
Register r(false);
|
||||
EXPECT_FALSE(r.AsBool());
|
||||
EXPECT_EQ("false", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("false", str.str());
|
||||
|
||||
str.str("");
|
||||
r = Register(true);
|
||||
EXPECT_TRUE(r.AsBool());
|
||||
EXPECT_EQ("true", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("true", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
@ -112,16 +140,23 @@ TEST_F(IR_RegisterTest, bool) {
|
|||
}
|
||||
|
||||
TEST_F(IR_RegisterTest, var) {
|
||||
std::stringstream str;
|
||||
|
||||
Symbol s;
|
||||
Register r(s, 2);
|
||||
EXPECT_EQ(2u, r.AsVarData().id);
|
||||
EXPECT_EQ(s, r.AsVarData().sym);
|
||||
EXPECT_EQ("%v2", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("%v2", str.str());
|
||||
str.str("");
|
||||
|
||||
r = Register(s, 4);
|
||||
EXPECT_EQ(4u, r.AsVarData().id);
|
||||
EXPECT_EQ(s, r.AsVarData().sym);
|
||||
EXPECT_EQ("%v4", r.AsString());
|
||||
|
||||
str << r;
|
||||
EXPECT_EQ("%v4", str.str());
|
||||
|
||||
EXPECT_FALSE(r.IsF32());
|
||||
EXPECT_FALSE(r.IsF16());
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
#include "src/tint/ir/builder_impl.h"
|
||||
#include "src/tint/ir/disassembler.h"
|
||||
#include "src/tint/program_builder.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
|
Loading…
Reference in New Issue