[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>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b459584..72eb55e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -404,6 +404,7 @@
list(APPEND TINT_TEST_SRCS
writer/spirv/binary_writer_test.cc
writer/spirv/builder_test.cc
+ writer/spirv/builder_type_test.cc
writer/spirv/builder_entry_point_test.cc
writer/spirv/instruction_test.cc
writer/spirv/operand_test.cc
diff --git a/src/ast/type/matrix_type.cc b/src/ast/type/matrix_type.cc
index 64aef3e..08937d9 100644
--- a/src/ast/type/matrix_type.cc
+++ b/src/ast/type/matrix_type.cc
@@ -20,7 +20,7 @@
namespace ast {
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) {
assert(rows > 1);
assert(rows < 5);
diff --git a/src/ast/type/matrix_type.h b/src/ast/type/matrix_type.h
index f37d12a..a3f95d3 100644
--- a/src/ast/type/matrix_type.h
+++ b/src/ast/type/matrix_type.h
@@ -30,7 +30,7 @@
/// @param subtype type matrix type
/// @param rows the number of rows 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
MatrixType(MatrixType&&) = default;
~MatrixType() override;
@@ -41,9 +41,9 @@
/// @returns the type of the matrix
Type* type() const { return subtype_; }
/// @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
- size_t columns() const { return columns_; }
+ uint32_t columns() const { return columns_; }
/// @returns the name for this type
std::string type_name() const override {
@@ -53,8 +53,8 @@
private:
Type* subtype_ = nullptr;
- size_t rows_ = 2;
- size_t columns_ = 2;
+ uint32_t rows_ = 2;
+ uint32_t columns_ = 2;
};
} // namespace type
diff --git a/src/ast/type/type.h b/src/ast/type/type.h
index cf66f3b..d2e171d 100644
--- a/src/ast/type/type.h
+++ b/src/ast/type/type.h
@@ -63,7 +63,7 @@
/// @returns true if the type is a void type
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;
/// @returns the type as an alias type
diff --git a/src/ast/type/vector_type.cc b/src/ast/type/vector_type.cc
index 8530214..de97e04 100644
--- a/src/ast/type/vector_type.cc
+++ b/src/ast/type/vector_type.cc
@@ -20,7 +20,7 @@
namespace ast {
namespace type {
-VectorType::VectorType(Type* subtype, size_t size)
+VectorType::VectorType(Type* subtype, uint32_t size)
: subtype_(subtype), size_(size) {
assert(size_ > 1);
assert(size_ < 5);
diff --git a/src/ast/type/vector_type.h b/src/ast/type/vector_type.h
index a773275..d2704c3 100644
--- a/src/ast/type/vector_type.h
+++ b/src/ast/type/vector_type.h
@@ -29,7 +29,7 @@
/// Constructor
/// @param subtype the vector element type
/// @param size the number of elements in the vector
- VectorType(Type* subtype, size_t size);
+ VectorType(Type* subtype, uint32_t size);
/// Move constructor
VectorType(VectorType&&) = default;
~VectorType() override;
@@ -40,7 +40,7 @@
/// @returns the type of the vector elements
Type* type() const { return subtype_; }
/// @returns the size of the vector
- size_t size() const { return size_; }
+ uint32_t size() const { return size_; }
/// @returns the name for th type
std::string type_name() const override {
@@ -49,7 +49,7 @@
private:
Type* subtype_ = nullptr;
- size_t size_ = 2;
+ uint32_t size_ = 2;
};
} // namespace type
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index b1a5c8b..1a386db 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -15,6 +15,8 @@
#include "src/writer/spirv/builder.h"
#include "spirv/unified1/spirv.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/vector_type.h"
namespace tint {
namespace writer {
@@ -137,14 +139,65 @@
}
void Builder::GenerateImport(ast::Import* imp) {
- auto op = result_op();
- auto id = op.to_i();
+ auto result = result_op();
+ 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;
}
+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 writer
} // namespace tint
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 91d67dc..7a42e3e 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -41,6 +41,8 @@
/// @returns the error string or blank if no error was reported.
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
uint32_t total_size() const;
@@ -99,7 +101,7 @@
types_.push_back(Instruction{op, operands});
}
/// @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
/// @param op the op to set
/// @param operands the operands for the instruction
@@ -124,8 +126,14 @@
/// Generates an import instruction
/// @param imp the import
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:
+ /// @returns an Operand with a new result ID in it. Increments the next_id_
+ /// automatically.
Operand result_op();
std::string error_;
@@ -138,6 +146,7 @@
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> type_name_to_id_;
};
} // namespace spirv
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
new file mode 100644
index 0000000..014d8c1
--- /dev/null
+++ b/src/writer/spirv/builder_type_test.cc
@@ -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
diff --git a/src/writer/spirv/spv_dump.cc b/src/writer/spirv/spv_dump.cc
index e001a3a..c649acc 100644
--- a/src/writer/spirv/spv_dump.cc
+++ b/src/writer/spirv/spv_dump.cc
@@ -53,9 +53,7 @@
tools.SetMessageConsumer(msg_consumer);
std::string result;
- if (!tools.Disassemble(data, &result,
- SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
- SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) {
+ if (!tools.Disassemble(data, &result, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) {
printf("%s\n", spv_errors.c_str());
}
return result;
@@ -77,6 +75,15 @@
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 writer
} // namespace tint
diff --git a/src/writer/spirv/spv_dump.h b/src/writer/spirv/spv_dump.h
index 295861c..34a023e 100644
--- a/src/writer/spirv/spv_dump.h
+++ b/src/writer/spirv/spv_dump.h
@@ -34,6 +34,11 @@
/// @returns the instruction as a SPIR-V disassembly string
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 writer
} // namespace tint