[ir] Add Unary expressions
This CL adds support for UnaryOpExpressions and converts them into Unary instructions in the IR. Bug: tint:1718 Change-Id: I736e29cec5e722b7c7f1b0f4f22ce55a3d3e4109 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129221 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
d2aaf76eb9
commit
5aa7ef2bc0
|
@ -740,6 +740,8 @@ if(${TINT_BUILD_IR})
|
||||||
ir/switch.h
|
ir/switch.h
|
||||||
ir/terminator.cc
|
ir/terminator.cc
|
||||||
ir/terminator.h
|
ir/terminator.h
|
||||||
|
ir/unary.cc
|
||||||
|
ir/unary.h
|
||||||
ir/user_call.cc
|
ir/user_call.cc
|
||||||
ir/user_call.h
|
ir/user_call.h
|
||||||
ir/value.cc
|
ir/value.cc
|
||||||
|
@ -1418,6 +1420,7 @@ if(TINT_BUILD_TESTS)
|
||||||
ir/discard_test.cc
|
ir/discard_test.cc
|
||||||
ir/runtime_test.cc
|
ir/runtime_test.cc
|
||||||
ir/test_helper.h
|
ir/test_helper.h
|
||||||
|
ir/unary_test.cc
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,30 @@ Binary* Builder::Modulo(const type::Type* type, Value* lhs, Value* rhs) {
|
||||||
return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs);
|
return CreateBinary(Binary::Kind::kModulo, type, lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Unary* Builder::CreateUnary(Unary::Kind kind, const type::Type* type, Value* val) {
|
||||||
|
return ir.instructions.Create<ir::Unary>(kind, Runtime(type), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary* Builder::AddressOf(const type::Type* type, Value* val) {
|
||||||
|
return CreateUnary(Unary::Kind::kAddressOf, type, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary* Builder::Complement(const type::Type* type, Value* val) {
|
||||||
|
return CreateUnary(Unary::Kind::kComplement, type, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary* Builder::Indirection(const type::Type* type, Value* val) {
|
||||||
|
return CreateUnary(Unary::Kind::kIndirection, type, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary* Builder::Negation(const type::Type* type, Value* val) {
|
||||||
|
return CreateUnary(Unary::Kind::kNegation, type, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary* Builder::Not(const type::Type* type, Value* val) {
|
||||||
|
return CreateUnary(Unary::Kind::kNot, type, val);
|
||||||
|
}
|
||||||
|
|
||||||
ir::Bitcast* Builder::Bitcast(const type::Type* type, Value* val) {
|
ir::Bitcast* Builder::Bitcast(const type::Type* type, Value* val) {
|
||||||
return ir.instructions.Create<ir::Bitcast>(Runtime(type), val);
|
return ir.instructions.Create<ir::Bitcast>(Runtime(type), val);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "src/tint/ir/runtime.h"
|
#include "src/tint/ir/runtime.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/user_call.h"
|
#include "src/tint/ir/user_call.h"
|
||||||
#include "src/tint/ir/value.h"
|
#include "src/tint/ir/value.h"
|
||||||
#include "src/tint/type/bool.h"
|
#include "src/tint/type/bool.h"
|
||||||
|
@ -280,6 +281,43 @@ class Builder {
|
||||||
/// @returns the operation
|
/// @returns the operation
|
||||||
Binary* Modulo(const type::Type* type, Value* lhs, Value* rhs);
|
Binary* Modulo(const type::Type* type, Value* lhs, Value* rhs);
|
||||||
|
|
||||||
|
/// Creates an op for `kind val`
|
||||||
|
/// @param kind the kind of operation
|
||||||
|
/// @param type the result type of the binary expression
|
||||||
|
/// @param val the value of the operation
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* CreateUnary(Unary::Kind kind, const type::Type* type, Value* val);
|
||||||
|
|
||||||
|
/// Creates an AddressOf operation
|
||||||
|
/// @param type the result type of the expression
|
||||||
|
/// @param val the value
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* AddressOf(const type::Type* type, Value* val);
|
||||||
|
|
||||||
|
/// Creates a Complement operation
|
||||||
|
/// @param type the result type of the expression
|
||||||
|
/// @param val the value
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* Complement(const type::Type* type, Value* val);
|
||||||
|
|
||||||
|
/// Creates an Indirection operation
|
||||||
|
/// @param type the result type of the expression
|
||||||
|
/// @param val the value
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* Indirection(const type::Type* type, Value* val);
|
||||||
|
|
||||||
|
/// Creates a Negation operation
|
||||||
|
/// @param type the result type of the expression
|
||||||
|
/// @param val the value
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* Negation(const type::Type* type, Value* val);
|
||||||
|
|
||||||
|
/// Creates a Not operation
|
||||||
|
/// @param type the result type of the expression
|
||||||
|
/// @param val the value
|
||||||
|
/// @returns the operation
|
||||||
|
Unary* Not(const type::Type* type, Value* val);
|
||||||
|
|
||||||
/// Creates a bitcast instruction
|
/// Creates a bitcast instruction
|
||||||
/// @param type the result type of the bitcast
|
/// @param type the result type of the bitcast
|
||||||
/// @param val the value being bitcast
|
/// @param val the value being bitcast
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "src/tint/ast/struct_member_size_attribute.h"
|
#include "src/tint/ast/struct_member_size_attribute.h"
|
||||||
#include "src/tint/ast/switch_statement.h"
|
#include "src/tint/ast/switch_statement.h"
|
||||||
#include "src/tint/ast/templated_identifier.h"
|
#include "src/tint/ast/templated_identifier.h"
|
||||||
|
#include "src/tint/ast/unary_op_expression.h"
|
||||||
#include "src/tint/ast/variable_decl_statement.h"
|
#include "src/tint/ast/variable_decl_statement.h"
|
||||||
#include "src/tint/ast/while_statement.h"
|
#include "src/tint/ast/while_statement.h"
|
||||||
#include "src/tint/ir/function.h"
|
#include "src/tint/ir/function.h"
|
||||||
|
@ -579,9 +580,7 @@ utils::Result<Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
|
||||||
// [&](const ast::PhonyExpression*) {
|
// [&](const ast::PhonyExpression*) {
|
||||||
// TODO(dsinclair): Implement. The call may have side effects so has to be made.
|
// TODO(dsinclair): Implement. The call may have side effects so has to be made.
|
||||||
// },
|
// },
|
||||||
// [&](const ast::UnaryOpExpression* u) {
|
[&](const ast::UnaryOpExpression* u) { return EmitUnary(u); },
|
||||||
// TODO(dsinclair): Implement
|
|
||||||
// },
|
|
||||||
[&](Default) {
|
[&](Default) {
|
||||||
add_error(expr->source,
|
add_error(expr->source,
|
||||||
"unknown expression type: " + std::string(expr->TypeInfo().name));
|
"unknown expression type: " + std::string(expr->TypeInfo().name));
|
||||||
|
@ -611,6 +610,38 @@ void BuilderImpl::EmitVariable(const ast::Variable* var) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::Result<Value*> BuilderImpl::EmitUnary(const ast::UnaryOpExpression* expr) {
|
||||||
|
auto val = EmitExpression(expr->expr);
|
||||||
|
if (!val) {
|
||||||
|
return utils::Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* sem = program_->Sem().Get(expr);
|
||||||
|
auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
|
||||||
|
|
||||||
|
Unary* instr = nullptr;
|
||||||
|
switch (expr->op) {
|
||||||
|
case ast::UnaryOp::kAddressOf:
|
||||||
|
instr = builder.AddressOf(ty, val.Get());
|
||||||
|
break;
|
||||||
|
case ast::UnaryOp::kComplement:
|
||||||
|
instr = builder.Complement(ty, val.Get());
|
||||||
|
break;
|
||||||
|
case ast::UnaryOp::kIndirection:
|
||||||
|
instr = builder.Indirection(ty, val.Get());
|
||||||
|
break;
|
||||||
|
case ast::UnaryOp::kNegation:
|
||||||
|
instr = builder.Negation(ty, val.Get());
|
||||||
|
break;
|
||||||
|
case ast::UnaryOp::kNot:
|
||||||
|
instr = builder.Not(ty, val.Get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_flow_block->instructions.Push(instr);
|
||||||
|
return instr->Result();
|
||||||
|
}
|
||||||
|
|
||||||
utils::Result<Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
|
utils::Result<Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
|
||||||
auto lhs = EmitExpression(expr->lhs);
|
auto lhs = EmitExpression(expr->lhs);
|
||||||
if (!lhs) {
|
if (!lhs) {
|
||||||
|
|
|
@ -53,6 +53,7 @@ class Node;
|
||||||
class ReturnStatement;
|
class ReturnStatement;
|
||||||
class Statement;
|
class Statement;
|
||||||
class SwitchStatement;
|
class SwitchStatement;
|
||||||
|
class UnaryOpExpression;
|
||||||
class WhileStatement;
|
class WhileStatement;
|
||||||
class Variable;
|
class Variable;
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
@ -150,6 +151,11 @@ class BuilderImpl {
|
||||||
/// @param var the variable to emit
|
/// @param var the variable to emit
|
||||||
void EmitVariable(const ast::Variable* var);
|
void EmitVariable(const ast::Variable* var);
|
||||||
|
|
||||||
|
/// Emits a Unary expression
|
||||||
|
/// @param expr the unary expression
|
||||||
|
/// @returns the value storing the result if successful, utils::Failure otherwise
|
||||||
|
utils::Result<Value*> EmitUnary(const ast::UnaryOpExpression* expr);
|
||||||
|
|
||||||
/// Emits a binary expression
|
/// Emits a binary expression
|
||||||
/// @param expr the binary expression
|
/// @param expr the binary expression
|
||||||
/// @returns the value storing the result if successful, utils::Failure otherwise
|
/// @returns the value storing the result if successful, utils::Failure otherwise
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// 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/unary.h"
|
||||||
|
#include "src/tint/debug.h"
|
||||||
|
|
||||||
|
TINT_INSTANTIATE_TYPEINFO(tint::ir::Unary);
|
||||||
|
|
||||||
|
namespace tint::ir {
|
||||||
|
|
||||||
|
Unary::Unary(Kind kind, Value* result, Value* val) : Base(result), kind_(kind), val_(val) {
|
||||||
|
TINT_ASSERT(IR, val_);
|
||||||
|
val_->AddUsage(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unary::~Unary() = default;
|
||||||
|
|
||||||
|
utils::StringStream& Unary::ToString(utils::StringStream& out) const {
|
||||||
|
Result()->ToString(out) << " = ";
|
||||||
|
switch (GetKind()) {
|
||||||
|
case Unary::Kind::kAddressOf:
|
||||||
|
out << "&";
|
||||||
|
break;
|
||||||
|
case Unary::Kind::kComplement:
|
||||||
|
out << "~";
|
||||||
|
break;
|
||||||
|
case Unary::Kind::kIndirection:
|
||||||
|
out << "*";
|
||||||
|
break;
|
||||||
|
case Unary::Kind::kNegation:
|
||||||
|
out << "-";
|
||||||
|
break;
|
||||||
|
case Unary::Kind::kNot:
|
||||||
|
out << "!";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val_->ToString(out);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tint::ir
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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_UNARY_H_
|
||||||
|
#define SRC_TINT_IR_UNARY_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 Unary : public utils::Castable<Unary, Instruction> {
|
||||||
|
public:
|
||||||
|
/// The kind of instruction.
|
||||||
|
enum class Kind {
|
||||||
|
kAddressOf,
|
||||||
|
kComplement,
|
||||||
|
kIndirection,
|
||||||
|
kNegation,
|
||||||
|
kNot,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
/// @param kind the kind of unary instruction
|
||||||
|
/// @param result the result value
|
||||||
|
/// @param val the lhs of the instruction
|
||||||
|
Unary(Kind kind, Value* result, Value* val);
|
||||||
|
Unary(const Unary& instr) = delete;
|
||||||
|
Unary(Unary&& instr) = delete;
|
||||||
|
~Unary() override;
|
||||||
|
|
||||||
|
Unary& operator=(const Unary& instr) = delete;
|
||||||
|
Unary& operator=(Unary&& instr) = delete;
|
||||||
|
|
||||||
|
/// @returns the kind of instruction
|
||||||
|
Kind GetKind() const { return kind_; }
|
||||||
|
|
||||||
|
/// @returns the value for the instruction
|
||||||
|
const Value* Val() const { return val_; }
|
||||||
|
|
||||||
|
/// 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:
|
||||||
|
Kind kind_;
|
||||||
|
Value* val_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tint::ir
|
||||||
|
|
||||||
|
#endif // SRC_TINT_IR_UNARY_H_
|
|
@ -0,0 +1,161 @@
|
||||||
|
// 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, CreateAddressOf) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
// TODO(dsinclair): This would be better as an identifier, but works for now.
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.AddressOf(b.builder.ir.types.Get<type::Pointer>(
|
||||||
|
b.builder.ir.types.Get<type::I32>(),
|
||||||
|
builtin::AddressSpace::kPrivate, builtin::Access::kReadWrite),
|
||||||
|
b.builder.Constant(4_i));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kAddressOf);
|
||||||
|
|
||||||
|
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->Val()->Is<Constant>());
|
||||||
|
auto lhs = instr->Val()->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 (ptr<private, i32, read_write>) = &4");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IR_InstructionTest, CreateComplement) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.Complement(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kComplement);
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Result()->Is<Runtime>());
|
||||||
|
EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Val()->Is<Constant>());
|
||||||
|
auto lhs = instr->Val()->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, CreateIndirection) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
// TODO(dsinclair): This would be better as an identifier, but works for now.
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.Indirection(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kIndirection);
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Result()->Is<Runtime>());
|
||||||
|
EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Val()->Is<Constant>());
|
||||||
|
auto lhs = instr->Val()->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, CreateNegation) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kNegation);
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Result()->Is<Runtime>());
|
||||||
|
EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Val()->Is<Constant>());
|
||||||
|
auto lhs = instr->Val()->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, CreateNot) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.Not(b.builder.ir.types.Get<type::Bool>(), b.builder.Constant(true));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kNot);
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Result()->Is<Runtime>());
|
||||||
|
EXPECT_EQ(Runtime::Id(42), instr->Result()->As<Runtime>()->AsId());
|
||||||
|
|
||||||
|
ASSERT_TRUE(instr->Val()->Is<Constant>());
|
||||||
|
auto lhs = instr->Val()->As<Constant>()->value;
|
||||||
|
ASSERT_TRUE(lhs->Is<constant::Scalar<bool>>());
|
||||||
|
EXPECT_TRUE(lhs->As<constant::Scalar<bool>>()->ValueAs<bool>());
|
||||||
|
|
||||||
|
utils::StringStream str;
|
||||||
|
instr->ToString(str);
|
||||||
|
EXPECT_EQ(str.str(), "%42 (bool) = !true");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IR_InstructionTest, Unary_Usage) {
|
||||||
|
auto& b = CreateEmptyBuilder();
|
||||||
|
|
||||||
|
b.builder.next_runtime_id = Runtime::Id(42);
|
||||||
|
const auto* instr =
|
||||||
|
b.builder.Negation(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
|
||||||
|
|
||||||
|
EXPECT_EQ(instr->GetKind(), Unary::Kind::kNegation);
|
||||||
|
|
||||||
|
ASSERT_NE(instr->Result(), nullptr);
|
||||||
|
ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
|
||||||
|
EXPECT_EQ(instr->Result()->Usage()[0], instr);
|
||||||
|
|
||||||
|
ASSERT_NE(instr->Val(), nullptr);
|
||||||
|
ASSERT_EQ(instr->Val()->Usage().Length(), 1u);
|
||||||
|
EXPECT_EQ(instr->Val()->Usage()[0], instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint::ir
|
Loading…
Reference in New Issue