[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:
parent
5b541ff3c2
commit
aa97bb5327
|
@ -736,6 +736,8 @@ if(${TINT_BUILD_IR})
|
||||||
ir/module.h
|
ir/module.h
|
||||||
ir/runtime.cc
|
ir/runtime.cc
|
||||||
ir/runtime.h
|
ir/runtime.h
|
||||||
|
ir/store.cc
|
||||||
|
ir/store.h
|
||||||
ir/switch.cc
|
ir/switch.cc
|
||||||
ir/switch.h
|
ir/switch.h
|
||||||
ir/terminator.cc
|
ir/terminator.cc
|
||||||
|
@ -1419,6 +1421,7 @@ if(TINT_BUILD_TESTS)
|
||||||
ir/constant_test.cc
|
ir/constant_test.cc
|
||||||
ir/discard_test.cc
|
ir/discard_test.cc
|
||||||
ir/runtime_test.cc
|
ir/runtime_test.cc
|
||||||
|
ir/store_test.cc
|
||||||
ir/test_helper.h
|
ir/test_helper.h
|
||||||
ir/unary_test.cc
|
ir/unary_test.cc
|
||||||
)
|
)
|
||||||
|
|
|
@ -227,4 +227,8 @@ ir::Builtin* Builder::Builtin(const type::Type* type,
|
||||||
return ir.instructions.Create<ir::Builtin>(Runtime(type), func, args);
|
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
|
} // namespace tint::ir
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "src/tint/ir/loop.h"
|
#include "src/tint/ir/loop.h"
|
||||||
#include "src/tint/ir/module.h"
|
#include "src/tint/ir/module.h"
|
||||||
#include "src/tint/ir/runtime.h"
|
#include "src/tint/ir/runtime.h"
|
||||||
|
#include "src/tint/ir/store.h"
|
||||||
#include "src/tint/ir/switch.h"
|
#include "src/tint/ir/switch.h"
|
||||||
#include "src/tint/ir/terminator.h"
|
#include "src/tint/ir/terminator.h"
|
||||||
#include "src/tint/ir/unary.h"
|
#include "src/tint/ir/unary.h"
|
||||||
|
@ -359,6 +360,12 @@ class Builder {
|
||||||
builtin::Function func,
|
builtin::Function func,
|
||||||
utils::VectorRef<Value*> args);
|
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
|
/// @returns a unique runtime id
|
||||||
Runtime::Id AllocateRuntimeId();
|
Runtime::Id AllocateRuntimeId();
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "src/tint/ast/alias.h"
|
#include "src/tint/ast/alias.h"
|
||||||
|
#include "src/tint/ast/assignment_statement.h"
|
||||||
#include "src/tint/ast/binary_expression.h"
|
#include "src/tint/ast/binary_expression.h"
|
||||||
#include "src/tint/ast/bitcast_expression.h"
|
#include "src/tint/ast/bitcast_expression.h"
|
||||||
#include "src/tint/ast/block_statement.h"
|
#include "src/tint/ast/block_statement.h"
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include "src/tint/ast/break_statement.h"
|
#include "src/tint/ast/break_statement.h"
|
||||||
#include "src/tint/ast/call_expression.h"
|
#include "src/tint/ast/call_expression.h"
|
||||||
#include "src/tint/ast/call_statement.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.h"
|
||||||
#include "src/tint/ast/const_assert.h"
|
#include "src/tint/ast/const_assert.h"
|
||||||
#include "src/tint/ast/continue_statement.h"
|
#include "src/tint/ast/continue_statement.h"
|
||||||
|
@ -54,8 +56,10 @@
|
||||||
#include "src/tint/ir/if.h"
|
#include "src/tint/ir/if.h"
|
||||||
#include "src/tint/ir/loop.h"
|
#include "src/tint/ir/loop.h"
|
||||||
#include "src/tint/ir/module.h"
|
#include "src/tint/ir/module.h"
|
||||||
|
#include "src/tint/ir/store.h"
|
||||||
#include "src/tint/ir/switch.h"
|
#include "src/tint/ir/switch.h"
|
||||||
#include "src/tint/ir/terminator.h"
|
#include "src/tint/ir/terminator.h"
|
||||||
|
#include "src/tint/ir/value.h"
|
||||||
#include "src/tint/program.h"
|
#include "src/tint/program.h"
|
||||||
#include "src/tint/sem/builtin.h"
|
#include "src/tint/sem/builtin.h"
|
||||||
#include "src/tint/sem/call.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) {
|
void BuilderImpl::EmitStatement(const ast::Statement* stmt) {
|
||||||
tint::Switch(
|
tint::Switch(
|
||||||
stmt,
|
stmt, //
|
||||||
// [&](const ast::AssignmentStatement* a) {
|
[&](const ast::AssignmentStatement* a) { EmitAssignment(a); },
|
||||||
// TODO(dsinclair): Implement
|
|
||||||
// },
|
|
||||||
[&](const ast::BlockStatement* b) { EmitBlock(b); },
|
[&](const ast::BlockStatement* b) { EmitBlock(b); },
|
||||||
[&](const ast::BreakStatement* b) { EmitBreak(b); },
|
[&](const ast::BreakStatement* b) { EmitBreak(b); },
|
||||||
[&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
|
[&](const ast::BreakIfStatement* b) { EmitBreakIf(b); },
|
||||||
[&](const ast::CallStatement* c) { EmitCall(c); },
|
[&](const ast::CallStatement* c) { EmitCall(c); },
|
||||||
// [&](const ast::CompoundAssignmentStatement* c) {
|
[&](const ast::CompoundAssignmentStatement* c) { EmitCompoundAssignment(c); },
|
||||||
// TODO(dsinclair): Implement
|
|
||||||
// },
|
|
||||||
[&](const ast::ContinueStatement* c) { EmitContinue(c); },
|
[&](const ast::ContinueStatement* c) { EmitContinue(c); },
|
||||||
[&](const ast::DiscardStatement* d) { EmitDiscard(d); },
|
[&](const ast::DiscardStatement* d) { EmitDiscard(d); },
|
||||||
[&](const ast::IfStatement* i) { EmitIf(i); },
|
[&](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) {
|
void BuilderImpl::EmitBlock(const ast::BlockStatement* block) {
|
||||||
// Note, this doesn't need to emit a Block as the current block flow node should be
|
// 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
|
// sufficient as the blocks all get flattened. Each flow control node will inject the basic
|
||||||
|
|
|
@ -34,6 +34,7 @@ class Program;
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
class Attribute;
|
class Attribute;
|
||||||
|
class AssignmentStatement;
|
||||||
class BinaryExpression;
|
class BinaryExpression;
|
||||||
class BitcastExpression;
|
class BitcastExpression;
|
||||||
class BlockStatement;
|
class BlockStatement;
|
||||||
|
@ -41,6 +42,7 @@ class BreakIfStatement;
|
||||||
class BreakStatement;
|
class BreakStatement;
|
||||||
class CallExpression;
|
class CallExpression;
|
||||||
class CallStatement;
|
class CallStatement;
|
||||||
|
class CompoundAssignmentStatement;
|
||||||
class ContinueStatement;
|
class ContinueStatement;
|
||||||
class DiscardStatement;
|
class DiscardStatement;
|
||||||
class Expression;
|
class Expression;
|
||||||
|
@ -142,6 +144,14 @@ class BuilderImpl {
|
||||||
/// @param stmt the break-if statement
|
/// @param stmt the break-if statement
|
||||||
void EmitBreakIf(const ast::BreakIfStatement* stmt);
|
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
|
/// Emits an expression
|
||||||
/// @param expr the expression to emit
|
/// @param expr the expression to emit
|
||||||
/// @returns true if successful, false otherwise
|
/// @returns true if successful, false otherwise
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
Loading…
Reference in New Issue