[ir] Add usage tracking to ir::Value.

This CL updates ir:Value to track the instructions which use a given
value. The instructions add their usage upon construction. This
necessitates making the values non-const in a lot of places as they get
changed by the instruction.

The `result` value is moved up to the base instruction class as it
should exist in all instructions.

Bug: tint:1718
Change-Id: Id7ab6e43d48caea502756d274dd6be2e1e4240f1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/116141
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair 2023-01-03 21:55:36 +00:00 committed by Dawn LUCI CQ
parent b70d610bae
commit afd7f2aa21
14 changed files with 159 additions and 588 deletions

View File

@ -19,11 +19,12 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Binary);
namespace tint::ir {
Binary::Binary(Kind kind, const Value* result, const Value* lhs, const Value* rhs)
: kind_(kind), result_(result), lhs_(lhs), rhs_(rhs) {
TINT_ASSERT(IR, result_);
Binary::Binary(Kind kind, Value* result, Value* lhs, Value* rhs)
: Base(result), kind_(kind), lhs_(lhs), rhs_(rhs) {
TINT_ASSERT(IR, lhs_);
TINT_ASSERT(IR, rhs_);
lhs_->AddUsage(this);
rhs_->AddUsage(this);
}
Binary::~Binary() = default;

View File

@ -19,7 +19,6 @@
#include "src/tint/castable.h"
#include "src/tint/ir/instruction.h"
#include "src/tint/ir/value.h"
#include "src/tint/symbol_table.h"
#include "src/tint/type/type.h"
@ -59,7 +58,7 @@ class Binary : public Castable<Binary, Instruction> {
/// @param result the result value
/// @param lhs the lhs of the instruction
/// @param rhs the rhs of the instruction
Binary(Kind kind, const Value* result, const Value* lhs, const Value* rhs);
Binary(Kind kind, Value* result, Value* lhs, Value* rhs);
Binary(const Binary& instr) = delete;
Binary(Binary&& instr) = delete;
~Binary() override;
@ -70,9 +69,6 @@ class Binary : public Castable<Binary, Instruction> {
/// @returns the kind of instruction
Kind GetKind() const { return kind_; }
/// @returns the result value for the instruction
const Value* Result() const { return result_; }
/// @returns the left-hand-side value for the instruction
const Value* LHS() const { return lhs_; }
@ -87,9 +83,8 @@ class Binary : public Castable<Binary, Instruction> {
private:
Kind kind_;
const Value* result_ = nullptr;
const Value* lhs_ = nullptr;
const Value* rhs_ = nullptr;
Value* lhs_ = nullptr;
Value* rhs_ = nullptr;
};
std::ostream& operator<<(std::ostream& out, const Binary&);

View File

@ -511,5 +511,48 @@ TEST_F(IR_InstructionTest, CreateModulo) {
EXPECT_EQ(str.str(), "%42 (i32) = 4 % 2");
}
TEST_F(IR_InstructionTest, Binary_Usage) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.And(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i),
b.builder.Constant(2_i));
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
ASSERT_NE(instr->Result(), nullptr);
ASSERT_EQ(instr->Result()->Usage().Length(), 1);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_NE(instr->LHS(), nullptr);
ASSERT_EQ(instr->LHS()->Usage().Length(), 1);
EXPECT_EQ(instr->LHS()->Usage()[0], instr);
ASSERT_NE(instr->RHS(), nullptr);
ASSERT_EQ(instr->RHS()->Usage().Length(), 1);
EXPECT_EQ(instr->RHS()->Usage()[0], instr);
}
TEST_F(IR_InstructionTest, Binary_Usage_DuplicateValue) {
auto& b = CreateEmptyBuilder();
auto val = b.builder.Constant(4_i);
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.And(b.builder.ir.types.Get<type::I32>(), val, val);
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
ASSERT_NE(instr->Result(), nullptr);
ASSERT_EQ(instr->Result()->Usage().Length(), 1);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_EQ(instr->LHS(), instr->RHS());
ASSERT_NE(instr->LHS(), nullptr);
ASSERT_EQ(instr->LHS()->Usage().Length(), 1);
EXPECT_EQ(instr->LHS()->Usage()[0], instr);
}
} // namespace
} // namespace tint::ir

View File

@ -19,9 +19,9 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Bitcast);
namespace tint::ir {
Bitcast::Bitcast(const Value* result, const Value* val) : result_(result), val_(val) {
TINT_ASSERT(IR, result_);
Bitcast::Bitcast(Value* result, Value* val) : Base(result), val_(val) {
TINT_ASSERT(IR, val_);
val_->AddUsage(this);
}
Bitcast::~Bitcast() = default;

View File

@ -19,7 +19,6 @@
#include "src/tint/castable.h"
#include "src/tint/ir/instruction.h"
#include "src/tint/ir/value.h"
#include "src/tint/symbol_table.h"
#include "src/tint/type/type.h"
@ -31,7 +30,7 @@ class Bitcast : public Castable<Bitcast, Instruction> {
/// Constructor
/// @param result the result value
/// @param val the value being bitcast
Bitcast(const Value* result, const Value* val);
Bitcast(Value* result, Value* val);
Bitcast(const Bitcast& instr) = delete;
Bitcast(Bitcast&& instr) = delete;
~Bitcast() override;
@ -39,9 +38,6 @@ class Bitcast : public Castable<Bitcast, Instruction> {
Bitcast& operator=(const Bitcast& instr) = delete;
Bitcast& operator=(Bitcast&& instr) = delete;
/// @returns the result value for the instruction
const Value* Result() const { return result_; }
/// @returns the left-hand-side value for the instruction
const Value* Val() const { return val_; }
@ -52,8 +48,7 @@ class Bitcast : public Castable<Bitcast, Instruction> {
std::ostream& ToString(std::ostream& out, const SymbolTable& st) const override;
private:
const Value* result_ = nullptr;
const Value* val_ = nullptr;
Value* val_ = nullptr;
};
std::ostream& operator<<(std::ostream& out, const Bitcast&);

View File

@ -45,5 +45,21 @@ TEST_F(IR_InstructionTest, Bitcast) {
EXPECT_EQ(str.str(), "%42 (i32) = bitcast(4)");
}
TEST_F(IR_InstructionTest, Bitcast_Usage) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
ASSERT_NE(instr->Result(), nullptr);
ASSERT_EQ(instr->Result()->Usage().Length(), 1);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_NE(instr->Val(), nullptr);
ASSERT_EQ(instr->Val()->Usage().Length(), 1);
EXPECT_EQ(instr->Val()->Usage()[0], instr);
}
} // namespace
} // namespace tint::ir

View File

@ -97,88 +97,83 @@ Temp::Id Builder::AllocateTempId() {
return next_temp_id++;
}
const Binary* Builder::CreateBinary(Binary::Kind kind,
const type::Type* type,
const Value* lhs,
const Value* rhs) {
Binary* Builder::CreateBinary(Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs) {
return ir.instructions.Create<ir::Binary>(kind, Temp(type), lhs, rhs);
}
const Binary* Builder::And(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::And(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kAnd, type, lhs, rhs);
}
const Binary* Builder::Or(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Or(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kOr, type, lhs, rhs);
}
const Binary* Builder::Xor(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Xor(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kXor, type, lhs, rhs);
}
const Binary* Builder::LogicalAnd(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::LogicalAnd(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kLogicalAnd, type, lhs, rhs);
}
const Binary* Builder::LogicalOr(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::LogicalOr(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kLogicalOr, type, lhs, rhs);
}
const Binary* Builder::Equal(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Equal(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kEqual, type, lhs, rhs);
}
const Binary* Builder::NotEqual(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::NotEqual(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kNotEqual, type, lhs, rhs);
}
const Binary* Builder::LessThan(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::LessThan(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kLessThan, type, lhs, rhs);
}
const Binary* Builder::GreaterThan(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::GreaterThan(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kGreaterThan, type, lhs, rhs);
}
const Binary* Builder::LessThanEqual(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::LessThanEqual(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kLessThanEqual, type, lhs, rhs);
}
const Binary* Builder::GreaterThanEqual(const type::Type* type,
const Value* lhs,
const Value* rhs) {
Binary* Builder::GreaterThanEqual(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kGreaterThanEqual, type, lhs, rhs);
}
const Binary* Builder::ShiftLeft(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::ShiftLeft(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kShiftLeft, type, lhs, rhs);
}
const Binary* Builder::ShiftRight(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::ShiftRight(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kShiftRight, type, lhs, rhs);
}
const Binary* Builder::Add(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Add(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kAdd, type, lhs, rhs);
}
const Binary* Builder::Subtract(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Subtract(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kSubtract, type, lhs, rhs);
}
const Binary* Builder::Multiply(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Multiply(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kMultiply, type, lhs, rhs);
}
const Binary* Builder::Divide(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Divide(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kDivide, type, lhs, rhs);
}
const Binary* Builder::Modulo(const type::Type* type, const Value* lhs, const Value* rhs) {
Binary* Builder::Modulo(const type::Type* type, Value* lhs, Value* rhs) {
return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs);
}
const ir::Bitcast* Builder::Bitcast(const type::Type* type, const Value* val) {
ir::Bitcast* Builder::Bitcast(const type::Type* type, Value* val) {
return ir.instructions.Create<ir::Bitcast>(Temp(type), val);
}

View File

@ -105,49 +105,49 @@ class Builder {
/// Creates a new ir::Constant
/// @param val the constant value
/// @returns the new constant
const ir::Constant* Constant(const constant::Value* val) {
ir::Constant* Constant(const constant::Value* val) {
return ir.values.Create<ir::Constant>(val);
}
/// Creates a ir::Constant for an i32 Scalar
/// @param v the value
/// @returns the new constant
const ir::Constant* Constant(i32 v) {
ir::Constant* Constant(i32 v) {
return Constant(create<constant::Scalar<i32>>(ir.types.Get<type::I32>(), v));
}
/// Creates a ir::Constant for a u32 Scalar
/// @param v the value
/// @returns the new constant
const ir::Constant* Constant(u32 v) {
ir::Constant* Constant(u32 v) {
return Constant(create<constant::Scalar<u32>>(ir.types.Get<type::U32>(), v));
}
/// Creates a ir::Constant for a f32 Scalar
/// @param v the value
/// @returns the new constant
const ir::Constant* Constant(f32 v) {
ir::Constant* Constant(f32 v) {
return Constant(create<constant::Scalar<f32>>(ir.types.Get<type::F32>(), v));
}
/// Creates a ir::Constant for a f16 Scalar
/// @param v the value
/// @returns the new constant
const ir::Constant* Constant(f16 v) {
ir::Constant* Constant(f16 v) {
return Constant(create<constant::Scalar<f16>>(ir.types.Get<type::F16>(), v));
}
/// Creates a ir::Constant for a bool Scalar
/// @param v the value
/// @returns the new constant
const ir::Constant* Constant(bool v) {
ir::Constant* Constant(bool v) {
return Constant(create<constant::Scalar<bool>>(ir.types.Get<type::Bool>(), v));
}
/// Creates a new Temporary
/// @param type the type of the temporary
/// @returns the new temporary
const ir::Temp* Temp(const type::Type* type) {
ir::Temp* Temp(const type::Type* type) {
return ir.values.Create<ir::Temp>(type, AllocateTempId());
}
@ -157,142 +157,139 @@ class Builder {
/// @param lhs the left-hand-side of the operation
/// @param rhs the right-hand-side of the operation
/// @returns the operation
const Binary* CreateBinary(Binary::Kind kind,
const type::Type* type,
const Value* lhs,
const Value* rhs);
Binary* CreateBinary(Binary::Kind kind, const type::Type* type, Value* lhs, Value* rhs);
/// Creates an And operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* And(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* And(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Or operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Or(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Or(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Xor operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Xor(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Xor(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an LogicalAnd operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* LogicalAnd(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* LogicalAnd(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an LogicalOr operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* LogicalOr(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* LogicalOr(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Equal operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Equal(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Equal(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an NotEqual operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* NotEqual(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* NotEqual(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an LessThan operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* LessThan(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* LessThan(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an GreaterThan operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* GreaterThan(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* GreaterThan(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an LessThanEqual operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* LessThanEqual(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* LessThanEqual(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an GreaterThanEqual operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* GreaterThanEqual(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* GreaterThanEqual(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an ShiftLeft operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* ShiftLeft(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* ShiftLeft(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an ShiftRight operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* ShiftRight(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* ShiftRight(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Add operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Add(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Add(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Subtract operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Subtract(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Subtract(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Multiply operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Multiply(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Multiply(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Divide operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Divide(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Divide(const type::Type* type, Value* lhs, Value* rhs);
/// Creates an Modulo operation
/// @param type the result type of the expression
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
const Binary* Modulo(const type::Type* type, const Value* lhs, const Value* rhs);
Binary* Modulo(const type::Type* type, Value* lhs, Value* rhs);
/// Creates a bitcast instruction
/// @param type the result type of the bitcast
/// @param val the value being bitcast
/// @returns the instruction
const ir::Bitcast* Bitcast(const type::Type* type, const Value* val);
ir::Bitcast* Bitcast(const type::Type* type, Value* val);
/// @returns a unique temp id
Temp::Id AllocateTempId();

View File

@ -518,7 +518,7 @@ bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
return true;
}
utils::Result<const Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
utils::Result<Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
return tint::Switch(
expr,
// [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
@ -553,7 +553,7 @@ bool BuilderImpl::EmitVariable(const ast::Variable* var) {
});
}
utils::Result<const Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
utils::Result<Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
auto lhs = EmitExpression(expr->lhs);
if (!lhs) {
return utils::Failure;
@ -565,7 +565,7 @@ utils::Result<const Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression*
}
auto* sem = builder.ir.program->Sem().Get(expr);
const Binary* instr = nullptr;
Binary* instr = nullptr;
switch (expr->op) {
case ast::BinaryOp::kAnd:
instr = builder.And(sem->Type(), lhs.Get(), rhs.Get());
@ -627,10 +627,10 @@ utils::Result<const Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression*
}
current_flow_block->instructions.Push(instr);
return utils::Result<const Value*>(instr->Result());
return instr->Result();
}
utils::Result<const Value*> BuilderImpl::EmitBitcast(const ast::BitcastExpression* expr) {
utils::Result<Value*> BuilderImpl::EmitBitcast(const ast::BitcastExpression* expr) {
auto val = EmitExpression(expr->expr);
if (!val) {
return utils::Failure;
@ -640,10 +640,10 @@ utils::Result<const Value*> BuilderImpl::EmitBitcast(const ast::BitcastExpressio
auto* instr = builder.Bitcast(sem->Type(), val.Get());
current_flow_block->instructions.Push(instr);
return utils::Result<const Value*>(instr->Result());
return instr->Result();
}
utils::Result<const Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
utils::Result<Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
auto* sem = builder.ir.program->Sem().Get(lit);
if (!sem) {
diagnostics_.add_error(
@ -661,7 +661,7 @@ utils::Result<const Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpressio
lit->source);
return utils::Failure;
}
return utils::Result<const Value*>(builder.Constant(cv));
return builder.Constant(cv);
}
bool BuilderImpl::EmitType(const ast::Type* ty) {

View File

@ -141,7 +141,7 @@ class BuilderImpl {
/// Emits an expression
/// @param expr the expression to emit
/// @returns true if successful, false otherwise
utils::Result<const Value*> EmitExpression(const ast::Expression* expr);
utils::Result<Value*> EmitExpression(const ast::Expression* expr);
/// Emits a variable
/// @param var the variable to emit
@ -151,17 +151,17 @@ class BuilderImpl {
/// Emits a binary expression
/// @param expr the binary expression
/// @returns the value storing the result if successful, utils::Failure otherwise
utils::Result<const Value*> EmitBinary(const ast::BinaryExpression* expr);
utils::Result<Value*> EmitBinary(const ast::BinaryExpression* expr);
/// Emits a bitcast expression
/// @param expr the bitcast expression
/// @returns the value storing the result if successful, utils::Failure otherwise
utils::Result<const Value*> EmitBitcast(const ast::BitcastExpression* expr);
utils::Result<Value*> EmitBitcast(const ast::BitcastExpression* expr);
/// Emits a literal expression
/// @param lit the literal to emit
/// @returns true if successful, false otherwise
utils::Result<const Value*> EmitLiteral(const ast::LiteralExpression* lit);
utils::Result<Value*> EmitLiteral(const ast::LiteralExpression* lit);
/// Emits a type
/// @param ty the type to emit

View File

@ -18,7 +18,10 @@ TINT_INSTANTIATE_TYPEINFO(tint::ir::Instruction);
namespace tint::ir {
Instruction::Instruction() = default;
Instruction::Instruction(Value* result) : result_(result) {
TINT_ASSERT(IR, result_);
result_->AddUsage(this);
}
Instruction::~Instruction() = default;

View File

@ -18,6 +18,7 @@
#include <ostream>
#include "src/tint/castable.h"
#include "src/tint/ir/value.h"
#include "src/tint/symbol_table.h"
namespace tint::ir {
@ -33,6 +34,9 @@ class Instruction : public Castable<Instruction> {
Instruction& operator=(const Instruction& instr) = delete;
Instruction& operator=(Instruction&& instr) = delete;
/// @returns the result value for the instruction
Value* Result() const { return result_; }
/// Write the instruction to the given stream
/// @param out the stream to write to
/// @param st the symbol table
@ -41,7 +45,11 @@ class Instruction : public Castable<Instruction> {
protected:
/// Constructor
Instruction();
/// @param result the result value
explicit Instruction(Value* result);
private:
Value* result_ = nullptr;
};
} // namespace tint::ir

View File

@ -1,499 +0,0 @@
// 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/instruction.h"
#include "src/tint/ir/test_helper.h"
namespace tint::ir {
namespace {
using IR_BinaryTest = TestHelper;
TEST_F(IR_BinaryTest, CreateAnd) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.And(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kAnd);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 & 2");
}
TEST_F(IR_BinaryTest, CreateOr) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Or(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kOr);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 | 2");
}
TEST_F(IR_BinaryTest, CreateXor) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Xor(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kXor);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 ^ 2");
}
TEST_F(IR_BinaryTest, CreateLogicalAnd) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.LogicalAnd(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLogicalAnd);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 && 2");
}
TEST_F(IR_BinaryTest, CreateLogicalOr) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.LogicalOr(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLogicalOr);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 || 2");
}
TEST_F(IR_BinaryTest, CreateEqual) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Equal(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kEqual);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 == 2");
}
TEST_F(IR_BinaryTest, CreateNotEqual) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.NotEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kNotEqual);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 != 2");
}
TEST_F(IR_BinaryTest, CreateLessThan) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.LessThan(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLessThan);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 < 2");
}
TEST_F(IR_BinaryTest, CreateGreaterThan) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.GreaterThan(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kGreaterThan);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 > 2");
}
TEST_F(IR_BinaryTest, CreateLessThanEqual) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.LessThanEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLessThanEqual);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 <= 2");
}
TEST_F(IR_BinaryTest, CreateGreaterThanEqual) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.GreaterThanEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kGreaterThanEqual);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 >= 2");
}
TEST_F(IR_BinaryTest, CreateShiftLeft) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.ShiftLeft(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kShiftLeft);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 << 2");
}
TEST_F(IR_BinaryTest, CreateShiftRight) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr =
b.builder.ShiftRight(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kShiftRight);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 >> 2");
}
TEST_F(IR_BinaryTest, CreateAdd) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Add(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kAdd);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 + 2");
}
TEST_F(IR_BinaryTest, CreateSubtract) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Subtract(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kSubtract);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 - 2");
}
TEST_F(IR_BinaryTest, CreateMultiply) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Multiply(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kMultiply);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 * 2");
}
TEST_F(IR_BinaryTest, CreateDivide) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Divide(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kDivide);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 / 2");
}
TEST_F(IR_BinaryTest, CreateModulo) {
auto& b = CreateEmptyBuilder();
b.builder.next_temp_id = Temp::Id(42);
const auto* instr = b.builder.Modulo(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
EXPECT_EQ(instr->GetKind(), Instruction::Kind::kModulo);
ASSERT_TRUE(instr->Result()->Is<Temp>());
EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
ASSERT_TRUE(instr->LHS()->Is<Constant>());
auto lhs = instr->LHS()->As<Constant>();
ASSERT_TRUE(lhs->IsI32());
EXPECT_EQ(i32(4), lhs->AsI32());
ASSERT_TRUE(instr->RHS()->Is<Constant>());
auto rhs = instr->RHS()->As<Constant>();
ASSERT_TRUE(rhs->IsI32());
EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
instr->ToString(str, program->Symbols());
EXPECT_EQ(str.str(), "%42 = 4 % 2");
}
} // namespace
} // namespace tint::ir

View File

@ -20,6 +20,12 @@
#include "src/tint/castable.h"
#include "src/tint/symbol_table.h"
#include "src/tint/type/type.h"
#include "src/tint/utils/unique_vector.h"
// Forward declarations
namespace tint::ir {
class Instruction;
} // namespace tint::ir
namespace tint::ir {
@ -35,6 +41,14 @@ class Value : public Castable<Value> {
Value& operator=(const Value&) = delete;
Value& operator=(Value&&) = delete;
/// Adds an instruction which uses this value.
/// @param instr the instruction
void AddUsage(const Instruction* instr) { uses_.Add(instr); }
/// @returns the vector of instructions which use this value. An instruction will only be
/// returned once even if that instruction uses the given value multiple times.
utils::VectorRef<const Instruction*> Usage() const { return uses_; }
/// @returns the type of the value
virtual const type::Type* Type() const = 0;
@ -47,6 +61,9 @@ class Value : public Castable<Value> {
protected:
/// Constructor
Value();
private:
utils::UniqueVector<const Instruction*, 4> uses_;
};
} // namespace tint::ir