[ir] Add assignment statements

This CL adds Assignment and CompoundAssignment statements to the IR
builder.

Bug: tint:1718
Change-Id: I3037da0115c7f4fe68941565b7e48866d421bbbf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129201
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2023-04-26 00:15:31 +00:00 committed by Dawn LUCI CQ
parent 5b541ff3c2
commit aa97bb5327
8 changed files with 276 additions and 7 deletions

View File

@ -736,6 +736,8 @@ if(${TINT_BUILD_IR})
ir/module.h
ir/runtime.cc
ir/runtime.h
ir/store.cc
ir/store.h
ir/switch.cc
ir/switch.h
ir/terminator.cc
@ -1419,6 +1421,7 @@ if(TINT_BUILD_TESTS)
ir/constant_test.cc
ir/discard_test.cc
ir/runtime_test.cc
ir/store_test.cc
ir/test_helper.h
ir/unary_test.cc
)

View File

@ -227,4 +227,8 @@ ir::Builtin* Builder::Builtin(const type::Type* type,
return ir.instructions.Create<ir::Builtin>(Runtime(type), func, args);
}
ir::Store* Builder::Store(Value* to, Value* from) {
return ir.instructions.Create<ir::Store>(to, from);
}
} // namespace tint::ir

View File

@ -30,6 +30,7 @@
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
#include "src/tint/ir/runtime.h"
#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
#include "src/tint/ir/unary.h"
@ -359,6 +360,12 @@ class Builder {
builtin::Function func,
utils::VectorRef<Value*> args);
/// Creates an store instruction
/// @param to the expression being stored too
/// @param from the expression being stored
/// @returns the instruction
ir::Store* Store(Value* to, Value* from);
/// @returns a unique runtime id
Runtime::Id AllocateRuntimeId();

View File

@ -17,6 +17,7 @@
#include <iostream>
#include "src/tint/ast/alias.h"
#include "src/tint/ast/assignment_statement.h"
#include "src/tint/ast/binary_expression.h"
#include "src/tint/ast/bitcast_expression.h"
#include "src/tint/ast/block_statement.h"
@ -25,6 +26,7 @@
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/call_expression.h"
#include "src/tint/ast/call_statement.h"
#include "src/tint/ast/compound_assignment_statement.h"
#include "src/tint/ast/const.h"
#include "src/tint/ast/const_assert.h"
#include "src/tint/ast/continue_statement.h"
@ -54,8 +56,10 @@
#include "src/tint/ir/if.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
#include "src/tint/ir/value.h"
#include "src/tint/program.h"
#include "src/tint/sem/builtin.h"
#include "src/tint/sem/call.h"
@ -236,17 +240,13 @@ void BuilderImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts)
void BuilderImpl::EmitStatement(const ast::Statement* stmt) {
tint::Switch(
stmt,
// [&](const ast::AssignmentStatement* a) {
// TODO(dsinclair): Implement
// },
stmt, //
[&](const ast::AssignmentStatement* a) { EmitAssignment(a); },
[&](const ast::BlockStatement* b) { EmitBlock(b); },
[&](const ast::BreakStatement* b) { EmitBreak(b); },
[&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
[&](const ast::CallStatement* c) { EmitCall(c); },
// [&](const ast::CompoundAssignmentStatement* c) {
// TODO(dsinclair): Implement
// },
[&](const ast::CompoundAssignmentStatement* c) { EmitCompoundAssignment(c); },
[&](const ast::ContinueStatement* c) { EmitContinue(c); },
[&](const ast::DiscardStatement* d) { EmitDiscard(d); },
[&](const ast::IfStatement* i) { EmitIf(i); },
@ -265,6 +265,98 @@ void BuilderImpl::EmitStatement(const ast::Statement* stmt) {
});
}
void BuilderImpl::EmitAssignment(const ast::AssignmentStatement* stmt) {
auto lhs = EmitExpression(stmt->lhs);
if (!lhs) {
return;
}
auto rhs = EmitExpression(stmt->rhs);
if (!rhs) {
return;
}
auto store = builder.Store(lhs.Get(), rhs.Get());
current_flow_block->instructions.Push(store);
}
void BuilderImpl::EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt) {
auto lhs = EmitExpression(stmt->lhs);
if (!lhs) {
return;
}
auto rhs = EmitExpression(stmt->rhs);
if (!rhs) {
return;
}
auto* ty = lhs.Get()->Type();
Binary* instr = nullptr;
switch (stmt->op) {
case ast::BinaryOp::kAnd:
instr = builder.And(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kOr:
instr = builder.Or(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kXor:
instr = builder.Xor(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kLogicalAnd:
instr = builder.LogicalAnd(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kLogicalOr:
instr = builder.LogicalOr(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kEqual:
instr = builder.Equal(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kNotEqual:
instr = builder.NotEqual(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kLessThan:
instr = builder.LessThan(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kGreaterThan:
instr = builder.GreaterThan(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kLessThanEqual:
instr = builder.LessThanEqual(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kGreaterThanEqual:
instr = builder.GreaterThanEqual(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kShiftLeft:
instr = builder.ShiftLeft(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kShiftRight:
instr = builder.ShiftRight(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kAdd:
instr = builder.Add(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kSubtract:
instr = builder.Subtract(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kMultiply:
instr = builder.Multiply(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kDivide:
instr = builder.Divide(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kModulo:
instr = builder.Modulo(ty, lhs.Get(), rhs.Get());
break;
case ast::BinaryOp::kNone:
TINT_ICE(IR, diagnostics_) << "missing binary operand type";
return;
}
current_flow_block->instructions.Push(instr);
auto store = builder.Store(lhs.Get(), instr->Result());
current_flow_block->instructions.Push(store);
}
void BuilderImpl::EmitBlock(const ast::BlockStatement* block) {
// Note, this doesn't need to emit a Block as the current block flow node should be
// sufficient as the blocks all get flattened. Each flow control node will inject the basic

View File

@ -34,6 +34,7 @@ class Program;
} // namespace tint
namespace tint::ast {
class Attribute;
class AssignmentStatement;
class BinaryExpression;
class BitcastExpression;
class BlockStatement;
@ -41,6 +42,7 @@ class BreakIfStatement;
class BreakStatement;
class CallExpression;
class CallStatement;
class CompoundAssignmentStatement;
class ContinueStatement;
class DiscardStatement;
class Expression;
@ -142,6 +144,14 @@ class BuilderImpl {
/// @param stmt the break-if statement
void EmitBreakIf(const ast::BreakIfStatement* stmt);
/// Emits an assignment statement
/// @param stmt the statement
void EmitAssignment(const ast::AssignmentStatement* stmt);
/// Emits a compound assignment statement
/// @param stmt the statement
void EmitCompoundAssignment(const ast::CompoundAssignmentStatement* stmt);
/// Emits an expression
/// @param expr the expression to emit
/// @returns true if successful, false otherwise

36
src/tint/ir/store.cc Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ir/store.h"
#include "src/tint/debug.h"
TINT_INSTANTIATE_TYPEINFO(tint::ir::Store);
namespace tint::ir {
Store::Store(Value* to, Value* from) : Base(to), from_(from) {
TINT_ASSERT(IR, from_);
from_->AddUsage(this);
}
Store::~Store() = default;
utils::StringStream& Store::ToString(utils::StringStream& out) const {
Result()->ToString(out);
out << " = ";
from_->ToString(out);
return out;
}
} // namespace tint::ir

52
src/tint/ir/store.h Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_IR_STORE_H_
#define SRC_TINT_IR_STORE_H_
#include "src/tint/ir/instruction.h"
#include "src/tint/utils/castable.h"
#include "src/tint/utils/string_stream.h"
namespace tint::ir {
/// An instruction in the IR.
class Store : public utils::Castable<Store, Instruction> {
public:
/// Constructor
/// @param to the value to store too
/// @param from the value being stored from
Store(Value* to, Value* from);
Store(const Store& instr) = delete;
Store(Store&& instr) = delete;
~Store() override;
Store& operator=(const Store& instr) = delete;
Store& operator=(Store&& instr) = delete;
/// @returns the value being stored
const Value* from() const { return from_; }
/// Write the instruction to the given stream
/// @param out the stream to write to
/// @returns the stream
utils::StringStream& ToString(utils::StringStream& out) const override;
private:
Value* from_ = nullptr;
};
} // namespace tint::ir
#endif // SRC_TINT_IR_STORE_H_

65
src/tint/ir/store_test.cc Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ir/instruction.h"
#include "src/tint/ir/test_helper.h"
#include "src/tint/utils/string_stream.h"
namespace tint::ir {
namespace {
using namespace tint::number_suffixes; // NOLINT
using IR_InstructionTest = TestHelper;
TEST_F(IR_InstructionTest, CreateStore) {
auto& b = CreateEmptyBuilder();
b.builder.next_runtime_id = Runtime::Id(42);
auto* rt = b.builder.Runtime(b.builder.ir.types.Get<type::I32>());
const auto* instr = b.builder.Store(rt, b.builder.Constant(4_i));
ASSERT_TRUE(instr->Result()->Is<Runtime>());
ASSERT_NE(instr->Result()->Type(), nullptr);
EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
ASSERT_TRUE(instr->from()->Is<Constant>());
auto lhs = instr->from()->As<Constant>()->value;
ASSERT_TRUE(lhs->Is<constant::Scalar<i32>>());
EXPECT_EQ(4_i, lhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
utils::StringStream str;
instr->ToString(str);
EXPECT_EQ(str.str(), "%42 (i32) = 4");
}
TEST_F(IR_InstructionTest, Store_Usage) {
auto& b = CreateEmptyBuilder();
b.builder.next_runtime_id = Runtime::Id(42);
auto* rt = b.builder.Runtime(b.builder.ir.types.Get<type::I32>());
const auto* instr = b.builder.Store(rt, b.builder.Constant(4_i));
ASSERT_NE(instr->Result(), nullptr);
ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_NE(instr->from(), nullptr);
ASSERT_EQ(instr->from()->Usage().Length(), 1u);
EXPECT_EQ(instr->from()->Usage()[0], instr);
}
} // namespace
} // namespace tint::ir