[ir][spirv-writer] Implement binary add instructions
Adds a `Value()` method to the writer which gets the ID for a value, which could either be a constant or an instruction result. Bug: tint:1906 Change-Id: I57f5e0cfea1e3b8322702090714bac49d003ba75 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/132285 Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: James Price <jrprice@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
7b233269fe
commit
49bae34149
|
@ -1862,6 +1862,7 @@ if (tint_build_unittests) {
|
||||||
|
|
||||||
if (tint_build_ir) {
|
if (tint_build_ir) {
|
||||||
sources += [
|
sources += [
|
||||||
|
"writer/spirv/generator_impl_binary_test.cc",
|
||||||
"writer/spirv/generator_impl_constant_test.cc",
|
"writer/spirv/generator_impl_constant_test.cc",
|
||||||
"writer/spirv/generator_impl_function_test.cc",
|
"writer/spirv/generator_impl_function_test.cc",
|
||||||
"writer/spirv/generator_impl_ir_test.cc",
|
"writer/spirv/generator_impl_ir_test.cc",
|
||||||
|
|
|
@ -1229,6 +1229,7 @@ if(TINT_BUILD_TESTS)
|
||||||
|
|
||||||
if(${TINT_BUILD_IR})
|
if(${TINT_BUILD_IR})
|
||||||
list(APPEND TINT_TEST_SRCS
|
list(APPEND TINT_TEST_SRCS
|
||||||
|
writer/spirv/generator_impl_binary_test.cc
|
||||||
writer/spirv/generator_impl_constant_test.cc
|
writer/spirv/generator_impl_constant_test.cc
|
||||||
writer/spirv/generator_impl_function_test.cc
|
writer/spirv/generator_impl_function_test.cc
|
||||||
writer/spirv/generator_impl_ir_test.cc
|
writer/spirv/generator_impl_ir_test.cc
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
// 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/writer/spirv/test_helper_ir.h"
|
||||||
|
|
||||||
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
|
namespace tint::writer::spirv {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST_F(SpvGeneratorImplTest, Binary_Add_I32) {
|
||||||
|
auto* func = CreateFunction();
|
||||||
|
func->name = ir.symbols.Register("foo");
|
||||||
|
func->return_type = ir.types.Get<type::Void>();
|
||||||
|
func->start_target->branch.target = func->end_target;
|
||||||
|
|
||||||
|
func->start_target->instructions.Push(CreateBinary(
|
||||||
|
ir::Binary::Kind::kAdd, ir.types.Get<type::I32>(), Constant(1_i), Constant(2_i)));
|
||||||
|
|
||||||
|
generator_.EmitFunction(func);
|
||||||
|
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeInt 32 1
|
||||||
|
%7 = OpConstant %6 1
|
||||||
|
%8 = OpConstant %6 2
|
||||||
|
%1 = OpFunction %2 None %3
|
||||||
|
%4 = OpLabel
|
||||||
|
%5 = OpIAdd %6 %7 %8
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvGeneratorImplTest, Binary_Add_U32) {
|
||||||
|
auto* func = CreateFunction();
|
||||||
|
func->name = ir.symbols.Register("foo");
|
||||||
|
func->return_type = ir.types.Get<type::Void>();
|
||||||
|
func->start_target->branch.target = func->end_target;
|
||||||
|
|
||||||
|
func->start_target->instructions.Push(CreateBinary(
|
||||||
|
ir::Binary::Kind::kAdd, ir.types.Get<type::U32>(), Constant(1_u), Constant(2_u)));
|
||||||
|
|
||||||
|
generator_.EmitFunction(func);
|
||||||
|
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeInt 32 0
|
||||||
|
%7 = OpConstant %6 1
|
||||||
|
%8 = OpConstant %6 2
|
||||||
|
%1 = OpFunction %2 None %3
|
||||||
|
%4 = OpLabel
|
||||||
|
%5 = OpIAdd %6 %7 %8
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvGeneratorImplTest, Binary_Add_F32) {
|
||||||
|
auto* func = CreateFunction();
|
||||||
|
func->name = ir.symbols.Register("foo");
|
||||||
|
func->return_type = ir.types.Get<type::Void>();
|
||||||
|
func->start_target->branch.target = func->end_target;
|
||||||
|
|
||||||
|
func->start_target->instructions.Push(CreateBinary(
|
||||||
|
ir::Binary::Kind::kAdd, ir.types.Get<type::F32>(), Constant(1_f), Constant(2_f)));
|
||||||
|
|
||||||
|
generator_.EmitFunction(func);
|
||||||
|
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeFloat 32
|
||||||
|
%7 = OpConstant %6 1
|
||||||
|
%8 = OpConstant %6 2
|
||||||
|
%1 = OpFunction %2 None %3
|
||||||
|
%4 = OpLabel
|
||||||
|
%5 = OpFAdd %6 %7 %8
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvGeneratorImplTest, Binary_Add_Chain) {
|
||||||
|
auto* func = CreateFunction();
|
||||||
|
func->name = ir.symbols.Register("foo");
|
||||||
|
func->return_type = ir.types.Get<type::Void>();
|
||||||
|
func->start_target->branch.target = func->end_target;
|
||||||
|
|
||||||
|
auto* a = CreateBinary(ir::Binary::Kind::kAdd, ir.types.Get<type::I32>(), Constant(1_i),
|
||||||
|
Constant(2_i));
|
||||||
|
func->start_target->instructions.Push(a);
|
||||||
|
func->start_target->instructions.Push(
|
||||||
|
CreateBinary(ir::Binary::Kind::kAdd, ir.types.Get<type::I32>(), a, a));
|
||||||
|
|
||||||
|
generator_.EmitFunction(func);
|
||||||
|
EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo"
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeInt 32 1
|
||||||
|
%7 = OpConstant %6 1
|
||||||
|
%8 = OpConstant %6 2
|
||||||
|
%1 = OpFunction %2 None %3
|
||||||
|
%4 = OpLabel
|
||||||
|
%5 = OpIAdd %6 %7 %8
|
||||||
|
%9 = OpIAdd %6 %5 %5
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace tint::writer::spirv
|
|
@ -15,6 +15,7 @@
|
||||||
#include "src/tint/writer/spirv/generator_impl_ir.h"
|
#include "src/tint/writer/spirv/generator_impl_ir.h"
|
||||||
|
|
||||||
#include "spirv/unified1/spirv.h"
|
#include "spirv/unified1/spirv.h"
|
||||||
|
#include "src/tint/ir/binary.h"
|
||||||
#include "src/tint/ir/block.h"
|
#include "src/tint/ir/block.h"
|
||||||
#include "src/tint/ir/function_terminator.h"
|
#include "src/tint/ir/function_terminator.h"
|
||||||
#include "src/tint/ir/module.h"
|
#include "src/tint/ir/module.h"
|
||||||
|
@ -117,6 +118,24 @@ uint32_t GeneratorImplIr::Type(const type::Type* ty) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GeneratorImplIr::Value(const ir::Value* value) {
|
||||||
|
return Switch(
|
||||||
|
value, //
|
||||||
|
[&](const ir::Constant* constant) { return Constant(constant); },
|
||||||
|
[&](const ir::Instruction* inst) {
|
||||||
|
auto id = instructions_.Find(inst);
|
||||||
|
if (TINT_UNLIKELY(!id)) {
|
||||||
|
TINT_ICE(Writer, diagnostics_) << "missing instruction result";
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
return *id;
|
||||||
|
},
|
||||||
|
[&](Default) {
|
||||||
|
TINT_ICE(Writer, diagnostics_) << "unhandled value node: " << value->TypeInfo().name;
|
||||||
|
return 0u;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void GeneratorImplIr::EmitFunction(const ir::Function* func) {
|
void GeneratorImplIr::EmitFunction(const ir::Function* func) {
|
||||||
// Make an ID for the function.
|
// Make an ID for the function.
|
||||||
auto id = module_.NextId();
|
auto id = module_.NextId();
|
||||||
|
@ -196,12 +215,14 @@ void GeneratorImplIr::EmitEntryPoint(const ir::Function* func, uint32_t id) {
|
||||||
void GeneratorImplIr::EmitBlock(const ir::Block* block) {
|
void GeneratorImplIr::EmitBlock(const ir::Block* block) {
|
||||||
// Emit the instructions.
|
// Emit the instructions.
|
||||||
for (auto* inst : block->instructions) {
|
for (auto* inst : block->instructions) {
|
||||||
auto result = Switch(inst, //
|
auto result = Switch(
|
||||||
[&](Default) {
|
inst, //
|
||||||
TINT_ICE(Writer, diagnostics_)
|
[&](const ir::Binary* b) { return EmitBinary(b); },
|
||||||
<< "unimplemented instruction: " << inst->TypeInfo().name;
|
[&](Default) {
|
||||||
return 0u;
|
TINT_ICE(Writer, diagnostics_)
|
||||||
});
|
<< "unimplemented instruction: " << inst->TypeInfo().name;
|
||||||
|
return 0u;
|
||||||
|
});
|
||||||
instructions_.Add(inst, result);
|
instructions_.Add(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,4 +236,27 @@ void GeneratorImplIr::EmitBlock(const ir::Block* block) {
|
||||||
[&](Default) { TINT_ICE(Writer, diagnostics_) << "unimplemented branch target"; });
|
[&](Default) { TINT_ICE(Writer, diagnostics_) << "unimplemented branch target"; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GeneratorImplIr::EmitBinary(const ir::Binary* binary) {
|
||||||
|
auto id = module_.NextId();
|
||||||
|
|
||||||
|
// Determine the opcode.
|
||||||
|
spv::Op op = spv::Op::Max;
|
||||||
|
switch (binary->GetKind()) {
|
||||||
|
case ir::Binary::Kind::kAdd: {
|
||||||
|
op = binary->Type()->is_integer_scalar_or_vector() ? spv::Op::OpIAdd : spv::Op::OpFAdd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
TINT_ICE(Writer, diagnostics_)
|
||||||
|
<< "unimplemented binary instruction: " << static_cast<uint32_t>(binary->GetKind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the instruction.
|
||||||
|
current_function_.push_inst(
|
||||||
|
op, {Type(binary->Type()), id, Value(binary->LHS()), Value(binary->RHS())});
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tint::writer::spirv
|
} // namespace tint::writer::spirv
|
||||||
|
|
|
@ -28,9 +28,11 @@
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace tint::ir {
|
namespace tint::ir {
|
||||||
|
class Binary;
|
||||||
class Block;
|
class Block;
|
||||||
class Function;
|
class Function;
|
||||||
class Module;
|
class Module;
|
||||||
|
class Value;
|
||||||
} // namespace tint::ir
|
} // namespace tint::ir
|
||||||
namespace tint::type {
|
namespace tint::type {
|
||||||
class Type;
|
class Type;
|
||||||
|
@ -69,6 +71,11 @@ class GeneratorImplIr {
|
||||||
/// @returns the result ID of the type
|
/// @returns the result ID of the type
|
||||||
uint32_t Type(const type::Type* ty);
|
uint32_t Type(const type::Type* ty);
|
||||||
|
|
||||||
|
/// Get the result ID of the value `value`, emitting its instruction if necessary.
|
||||||
|
/// @param value the value to get the ID for
|
||||||
|
/// @returns the result ID of the value
|
||||||
|
uint32_t Value(const ir::Value* value);
|
||||||
|
|
||||||
/// Emit a function.
|
/// Emit a function.
|
||||||
/// @param func the function to emit
|
/// @param func the function to emit
|
||||||
void EmitFunction(const ir::Function* func);
|
void EmitFunction(const ir::Function* func);
|
||||||
|
@ -82,6 +89,11 @@ class GeneratorImplIr {
|
||||||
/// @param block the block to emit
|
/// @param block the block to emit
|
||||||
void EmitBlock(const ir::Block* block);
|
void EmitBlock(const ir::Block* block);
|
||||||
|
|
||||||
|
/// Emit a binary instruction.
|
||||||
|
/// @param binary the binary instruction to emit
|
||||||
|
/// @returns the result ID of the instruction
|
||||||
|
uint32_t EmitBinary(const ir::Binary* binary);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ir::Module* ir_;
|
const ir::Module* ir_;
|
||||||
spirv::Module module_;
|
spirv::Module module_;
|
||||||
|
|
Loading…
Reference in New Issue