[spirv-writer] Start emitting types

This CL starts the emitting of SPIR-V types. This CL adds code to emit
the `AliasType`, `BoolType`, `F32Type`, `I32Type`, `MatrixType`,
`U32Type`, `VectorType` and `VoidType`.

Bug: tint:5
Change-Id: Ic13026ca9006fc0a253d019b1a6dd984ae992fba
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17561
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-03-24 15:03:29 +00:00 committed by dan sinclair
parent 7c6e8ded7d
commit 7a480f4313
11 changed files with 349 additions and 18 deletions

View File

@ -404,6 +404,7 @@ if(${TINT_BUILD_SPV_WRITER})
list(APPEND TINT_TEST_SRCS list(APPEND TINT_TEST_SRCS
writer/spirv/binary_writer_test.cc writer/spirv/binary_writer_test.cc
writer/spirv/builder_test.cc writer/spirv/builder_test.cc
writer/spirv/builder_type_test.cc
writer/spirv/builder_entry_point_test.cc writer/spirv/builder_entry_point_test.cc
writer/spirv/instruction_test.cc writer/spirv/instruction_test.cc
writer/spirv/operand_test.cc writer/spirv/operand_test.cc

View File

@ -20,7 +20,7 @@ namespace tint {
namespace ast { namespace ast {
namespace type { namespace type {
MatrixType::MatrixType(Type* subtype, size_t rows, size_t columns) MatrixType::MatrixType(Type* subtype, uint32_t rows, uint32_t columns)
: subtype_(subtype), rows_(rows), columns_(columns) { : subtype_(subtype), rows_(rows), columns_(columns) {
assert(rows > 1); assert(rows > 1);
assert(rows < 5); assert(rows < 5);

View File

@ -30,7 +30,7 @@ class MatrixType : public Type {
/// @param subtype type matrix type /// @param subtype type matrix type
/// @param rows the number of rows in the matrix /// @param rows the number of rows in the matrix
/// @param columns the number of columns in the matrix /// @param columns the number of columns in the matrix
MatrixType(Type* subtype, size_t rows, size_t columns); MatrixType(Type* subtype, uint32_t rows, uint32_t columns);
/// Move constructor /// Move constructor
MatrixType(MatrixType&&) = default; MatrixType(MatrixType&&) = default;
~MatrixType() override; ~MatrixType() override;
@ -41,9 +41,9 @@ class MatrixType : public Type {
/// @returns the type of the matrix /// @returns the type of the matrix
Type* type() const { return subtype_; } Type* type() const { return subtype_; }
/// @returns the number of rows in the matrix /// @returns the number of rows in the matrix
size_t rows() const { return rows_; } uint32_t rows() const { return rows_; }
/// @returns the number of columns in the matrix /// @returns the number of columns in the matrix
size_t columns() const { return columns_; } uint32_t columns() const { return columns_; }
/// @returns the name for this type /// @returns the name for this type
std::string type_name() const override { std::string type_name() const override {
@ -53,8 +53,8 @@ class MatrixType : public Type {
private: private:
Type* subtype_ = nullptr; Type* subtype_ = nullptr;
size_t rows_ = 2; uint32_t rows_ = 2;
size_t columns_ = 2; uint32_t columns_ = 2;
}; };
} // namespace type } // namespace type

View File

@ -63,7 +63,7 @@ class Type {
/// @returns true if the type is a void type /// @returns true if the type is a void type
virtual bool IsVoid() const { return false; } virtual bool IsVoid() const { return false; }
/// @returns the name for this type /// @returns the name for this type. The |type_name| is unique over all types.
virtual std::string type_name() const = 0; virtual std::string type_name() const = 0;
/// @returns the type as an alias type /// @returns the type as an alias type

View File

@ -20,7 +20,7 @@ namespace tint {
namespace ast { namespace ast {
namespace type { namespace type {
VectorType::VectorType(Type* subtype, size_t size) VectorType::VectorType(Type* subtype, uint32_t size)
: subtype_(subtype), size_(size) { : subtype_(subtype), size_(size) {
assert(size_ > 1); assert(size_ > 1);
assert(size_ < 5); assert(size_ < 5);

View File

@ -29,7 +29,7 @@ class VectorType : public Type {
/// Constructor /// Constructor
/// @param subtype the vector element type /// @param subtype the vector element type
/// @param size the number of elements in the vector /// @param size the number of elements in the vector
VectorType(Type* subtype, size_t size); VectorType(Type* subtype, uint32_t size);
/// Move constructor /// Move constructor
VectorType(VectorType&&) = default; VectorType(VectorType&&) = default;
~VectorType() override; ~VectorType() override;
@ -40,7 +40,7 @@ class VectorType : public Type {
/// @returns the type of the vector elements /// @returns the type of the vector elements
Type* type() const { return subtype_; } Type* type() const { return subtype_; }
/// @returns the size of the vector /// @returns the size of the vector
size_t size() const { return size_; } uint32_t size() const { return size_; }
/// @returns the name for th type /// @returns the name for th type
std::string type_name() const override { std::string type_name() const override {
@ -49,7 +49,7 @@ class VectorType : public Type {
private: private:
Type* subtype_ = nullptr; Type* subtype_ = nullptr;
size_t size_ = 2; uint32_t size_ = 2;
}; };
} // namespace type } // namespace type

View File

@ -15,6 +15,8 @@
#include "src/writer/spirv/builder.h" #include "src/writer/spirv/builder.h"
#include "spirv/unified1/spirv.h" #include "spirv/unified1/spirv.h"
#include "src/ast/type/matrix_type.h"
#include "src/ast/type/vector_type.h"
namespace tint { namespace tint {
namespace writer { namespace writer {
@ -137,14 +139,65 @@ bool Builder::GenerateEntryPoint(ast::EntryPoint* ep) {
} }
void Builder::GenerateImport(ast::Import* imp) { void Builder::GenerateImport(ast::Import* imp) {
auto op = result_op(); auto result = result_op();
auto id = op.to_i(); auto id = result.to_i();
push_preamble(spv::Op::OpExtInstImport, {op, Operand::String(imp->path())}); push_preamble(spv::Op::OpExtInstImport,
{result, Operand::String(imp->path())});
import_name_to_id_[imp->name()] = id; import_name_to_id_[imp->name()] = id;
} }
uint32_t Builder::GenerateTypeIfNeeded(ast::type::Type* type) {
if (type->IsAlias()) {
return GenerateTypeIfNeeded(type->AsAlias()->type());
}
auto val = type_name_to_id_.find(type->type_name());
if (val != type_name_to_id_.end()) {
return val->second;
}
auto result = result_op();
auto id = result.to_i();
if (type->IsBool()) {
push_type(spv::Op::OpTypeBool, {result});
} else if (type->IsF32()) {
push_type(spv::Op::OpTypeFloat, {result, Operand::Int(32)});
} else if (type->IsI32()) {
push_type(spv::Op::OpTypeInt, {result, Operand::Int(32), Operand::Int(1)});
} else if (type->IsMatrix()) {
auto mat = type->AsMatrix();
ast::type::VectorType col_type(mat->type(), mat->rows());
auto type_id = GenerateTypeIfNeeded(&col_type);
if (has_error()) {
return 0;
}
push_type(spv::Op::OpTypeMatrix,
{result, Operand::Int(type_id), Operand::Int(mat->columns())});
} else if (type->IsU32()) {
push_type(spv::Op::OpTypeInt, {result, Operand::Int(32), Operand::Int(0)});
} else if (type->IsVector()) {
auto vec = type->AsVector();
auto col_type_id = GenerateTypeIfNeeded(vec->type());
if (has_error()) {
return 0;
}
push_type(spv::Op::OpTypeVector,
{result, Operand::Int(col_type_id), Operand::Int(vec->size())});
} else if (type->IsVoid()) {
push_type(spv::Op::OpTypeVoid, {result});
} else {
error_ = "unable to convert type: " + type->type_name();
return 0;
}
type_name_to_id_[type->type_name()] = id;
return id;
}
} // namespace spirv } // namespace spirv
} // namespace writer } // namespace writer
} // namespace tint } // namespace tint

View File

@ -41,6 +41,8 @@ class Builder {
/// @returns the error string or blank if no error was reported. /// @returns the error string or blank if no error was reported.
const std::string& error() const { return error_; } const std::string& error() const { return error_; }
/// @returns true if the builder encountered an error
bool has_error() const { return !error_.empty(); }
/// @returns the number of uint32_t's needed to make up the results /// @returns the number of uint32_t's needed to make up the results
uint32_t total_size() const; uint32_t total_size() const;
@ -99,7 +101,7 @@ class Builder {
types_.push_back(Instruction{op, operands}); types_.push_back(Instruction{op, operands});
} }
/// @returns the type instructions /// @returns the type instructions
const std::vector<Instruction>& type() const { return types_; } const std::vector<Instruction>& types() const { return types_; }
/// Adds an instruction to the instruction list /// Adds an instruction to the instruction list
/// @param op the op to set /// @param op the op to set
/// @param operands the operands for the instruction /// @param operands the operands for the instruction
@ -124,8 +126,14 @@ class Builder {
/// Generates an import instruction /// Generates an import instruction
/// @param imp the import /// @param imp the import
void GenerateImport(ast::Import* imp); void GenerateImport(ast::Import* imp);
/// Generates a type if not already created
/// @param type the type to create
/// @returns the ID to use for the given type. Returns 0 on unknown type.
uint32_t GenerateTypeIfNeeded(ast::type::Type* type);
private: private:
/// @returns an Operand with a new result ID in it. Increments the next_id_
/// automatically.
Operand result_op(); Operand result_op();
std::string error_; std::string error_;
@ -138,6 +146,7 @@ class Builder {
std::unordered_map<std::string, uint32_t> import_name_to_id_; std::unordered_map<std::string, uint32_t> import_name_to_id_;
std::unordered_map<std::string, uint32_t> func_name_to_id_; std::unordered_map<std::string, uint32_t> func_name_to_id_;
std::unordered_map<std::string, uint32_t> type_name_to_id_;
}; };
} // namespace spirv } // namespace spirv

View File

@ -0,0 +1,256 @@
// Copyright 2020 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 <memory>
#include "gtest/gtest.h"
#include "src/ast/type/alias_type.h"
#include "src/ast/type/array_type.h"
#include "src/ast/type/bool_type.h"
#include "src/ast/type/f32_type.h"
#include "src/ast/type/i32_type.h"
#include "src/ast/type/matrix_type.h"
#include "src/ast/type/pointer_type.h"
#include "src/ast/type/u32_type.h"
#include "src/ast/type/vector_type.h"
#include "src/ast/type/void_type.h"
#include "src/writer/spirv/builder.h"
#include "src/writer/spirv/spv_dump.h"
namespace tint {
namespace writer {
namespace spirv {
using BuilderTest_Type = testing::Test;
TEST_F(BuilderTest_Type, GenerateAlias) {
ast::type::F32Type f32;
ast::type::AliasType alias_type("my_type", &f32);
Builder b;
auto id = b.GenerateTypeIfNeeded(&alias_type);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
EXPECT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedAlias) {
ast::type::I32Type i32;
ast::type::F32Type f32;
ast::type::AliasType alias_type("my_type", &f32);
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&alias_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&alias_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&f32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateBool) {
ast::type::BoolType bool_type;
Builder b;
auto id = b.GenerateTypeIfNeeded(&bool_type);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
ASSERT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeBool
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedBool) {
ast::type::I32Type i32;
ast::type::BoolType bool_type;
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&bool_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&bool_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateF32) {
ast::type::F32Type f32;
Builder b;
auto id = b.GenerateTypeIfNeeded(&f32);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
ASSERT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeFloat 32
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedF32) {
ast::type::I32Type i32;
ast::type::F32Type f32;
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&f32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&f32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateI32) {
ast::type::I32Type i32;
Builder b;
auto id = b.GenerateTypeIfNeeded(&i32);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
ASSERT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeInt 32 1
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedI32) {
ast::type::I32Type i32;
ast::type::F32Type f32;
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&f32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateMatrix) {
ast::type::F32Type f32;
ast::type::MatrixType mat_type(&f32, 3, 2);
Builder b;
auto id = b.GenerateTypeIfNeeded(&mat_type);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
EXPECT_EQ(b.types().size(), 3);
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypeVector %3 3
%1 = OpTypeMatrix %2 2
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedMatrix) {
ast::type::I32Type i32;
ast::type::MatrixType mat_type(&i32, 3, 4);
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&mat_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 3);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&mat_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateU32) {
ast::type::U32Type u32;
Builder b;
auto id = b.GenerateTypeIfNeeded(&u32);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
ASSERT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeInt 32 0
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedU32) {
ast::type::U32Type u32;
ast::type::F32Type f32;
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&u32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&f32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&u32), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateVector) {
ast::type::F32Type f32;
ast::type::VectorType vec_type(&f32, 3);
Builder b;
auto id = b.GenerateTypeIfNeeded(&vec_type);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
EXPECT_EQ(b.types().size(), 2);
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
%1 = OpTypeVector %2 3
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedVector) {
ast::type::I32Type i32;
ast::type::VectorType vec_type(&i32, 3);
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&vec_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&vec_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
TEST_F(BuilderTest_Type, GenerateVoid) {
ast::type::VoidType void_type;
Builder b;
auto id = b.GenerateTypeIfNeeded(&void_type);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(id, 1);
ASSERT_EQ(b.types().size(), 1);
EXPECT_EQ(DumpInstruction(b.types()[0]), R"(%1 = OpTypeVoid
)");
}
TEST_F(BuilderTest_Type, ReturnsGeneratedVoid) {
ast::type::I32Type i32;
ast::type::VoidType void_type;
Builder b;
EXPECT_EQ(b.GenerateTypeIfNeeded(&void_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&i32), 2);
ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(b.GenerateTypeIfNeeded(&void_type), 1);
ASSERT_FALSE(b.has_error()) << b.error();
}
} // namespace spirv
} // namespace writer
} // namespace tint

View File

@ -53,9 +53,7 @@ std::string Disassemble(const std::vector<uint32_t>& data) {
tools.SetMessageConsumer(msg_consumer); tools.SetMessageConsumer(msg_consumer);
std::string result; std::string result;
if (!tools.Disassemble(data, &result, if (!tools.Disassemble(data, &result, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) {
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) {
printf("%s\n", spv_errors.c_str()); printf("%s\n", spv_errors.c_str());
} }
return result; return result;
@ -77,6 +75,15 @@ std::string DumpInstruction(const Instruction& inst) {
return Disassemble(writer.result()); return Disassemble(writer.result());
} }
std::string DumpInstructions(const std::vector<Instruction>& insts) {
BinaryWriter writer;
writer.WriteHeader(kDefaultMaxIdBound);
for (const auto& inst : insts) {
writer.WriteInstruction(inst);
}
return Disassemble(writer.result());
}
} // namespace spirv } // namespace spirv
} // namespace writer } // namespace writer
} // namespace tint } // namespace tint

View File

@ -34,6 +34,11 @@ std::string DumpBuilder(const Builder& builder);
/// @returns the instruction as a SPIR-V disassembly string /// @returns the instruction as a SPIR-V disassembly string
std::string DumpInstruction(const Instruction& inst); std::string DumpInstruction(const Instruction& inst);
/// Dumps the given instructions to a SPIR-V disassembly string
/// @param insts the instructions to dump
/// @returns the instruction as a SPIR-V disassembly string
std::string DumpInstructions(const std::vector<Instruction>& insts);
} // namespace spirv } // namespace spirv
} // namespace writer } // namespace writer
} // namespace tint } // namespace tint