[spirv-writer] Generate struct types
This CL adds generation of Struct types to the SPIR-V writer.
Bug: tint:5
Change-Id: Ibcabf7b1a688026297de682f4825d5195d8007d2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17701
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/ast/struct_member_offset_decoration.cc b/src/ast/struct_member_offset_decoration.cc
index 0a70a95..ec49b19 100644
--- a/src/ast/struct_member_offset_decoration.cc
+++ b/src/ast/struct_member_offset_decoration.cc
@@ -17,7 +17,7 @@
namespace tint {
namespace ast {
-StructMemberOffsetDecoration::StructMemberOffsetDecoration(size_t offset)
+StructMemberOffsetDecoration::StructMemberOffsetDecoration(uint32_t offset)
: offset_(offset) {}
StructMemberOffsetDecoration::~StructMemberOffsetDecoration() = default;
diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h
index 8e15a63..4e59b2a 100644
--- a/src/ast/struct_member_offset_decoration.h
+++ b/src/ast/struct_member_offset_decoration.h
@@ -29,20 +29,20 @@
public:
/// constructor
/// @param offset the offset value
- explicit StructMemberOffsetDecoration(size_t offset);
+ explicit StructMemberOffsetDecoration(uint32_t offset);
~StructMemberOffsetDecoration() override;
/// @returns true if this is an offset decoration
bool IsOffset() const override { return true; }
/// @returns the offset value
- size_t offset() const { return offset_; }
+ uint32_t offset() const { return offset_; }
/// @returns the decoration as a string
std::string to_str() const override;
private:
- size_t offset_;
+ uint32_t offset_;
};
} // namespace ast
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 1a386db..6a9063d 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -15,7 +15,11 @@
#include "src/writer/spirv/builder.h"
#include "spirv/unified1/spirv.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/struct_type.h"
#include "src/ast/type/vector_type.h"
namespace tint {
@@ -168,25 +172,19 @@
} 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()) {
+ if (!GenerateMatrixType(type->AsMatrix(), result)) {
return 0;
}
-
- push_type(spv::Op::OpTypeMatrix,
- {result, Operand::Int(type_id), Operand::Int(mat->columns())});
+ } else if (type->IsStruct()) {
+ if (!GenerateStructType(type->AsStruct(), result)) {
+ return 0;
+ }
} 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()) {
+ if (!GenerateVectorType(type->AsVector(), result)) {
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 {
@@ -198,6 +196,84 @@
return id;
}
+bool Builder::GenerateMatrixType(ast::type::MatrixType* mat,
+ const Operand& result) {
+ ast::type::VectorType col_type(mat->type(), mat->rows());
+ auto col_type_id = GenerateTypeIfNeeded(&col_type);
+ if (has_error()) {
+ return false;
+ }
+
+ push_type(spv::Op::OpTypeMatrix,
+ {result, Operand::Int(col_type_id), Operand::Int(mat->columns())});
+ return true;
+}
+
+bool Builder::GenerateStructType(ast::type::StructType* struct_type,
+ const Operand& result) {
+ auto struct_id = result.to_i();
+ auto impl = struct_type->impl();
+
+ std::vector<Operand> ops;
+ ops.push_back(result);
+
+ if (impl->decoration() == ast::StructDecoration::kBlock) {
+ push_annot(spv::Op::OpDecorate,
+ {Operand::Int(struct_id), Operand::Int(SpvDecorationBlock)});
+ } else {
+ if (impl->decoration() != ast::StructDecoration::kNone) {
+ error_ = "unknown struct decoration";
+ return false;
+ }
+ }
+
+ auto& members = impl->members();
+ for (uint32_t i = 0; i < members.size(); ++i) {
+ auto mem_id = GenerateStructMember(struct_id, i, members[i].get());
+ if (mem_id == 0) {
+ return false;
+ }
+
+ ops.push_back(Operand::Int(mem_id));
+ }
+
+ push_type(spv::Op::OpTypeStruct, std::move(ops));
+ return true;
+}
+
+uint32_t Builder::GenerateStructMember(uint32_t struct_id,
+ uint32_t idx,
+ ast::StructMember* member) {
+ push_debug(spv::Op::OpMemberName, {Operand::Int(struct_id), Operand::Int(idx),
+ Operand::String(member->name())});
+
+ for (const auto& deco : member->decorations()) {
+ if (deco->IsOffset()) {
+ push_annot(spv::Op::OpMemberDecorate,
+ {Operand::Int(struct_id), Operand::Int(idx),
+ Operand::Int(SpvDecorationOffset),
+ Operand::Int(deco->AsOffset()->offset())});
+ } else {
+ error_ = "unknown struct member decoration";
+ return 0;
+ }
+ }
+
+ return GenerateTypeIfNeeded(member->type());
+}
+
+bool Builder::GenerateVectorType(ast::type::VectorType* vec,
+ const Operand& result) {
+ auto type_id = GenerateTypeIfNeeded(vec->type());
+ if (has_error()) {
+ return false;
+ }
+
+ push_type(spv::Op::OpTypeVector,
+ {result, Operand::Int(type_id), Operand::Int(vec->size())});
+ return true;
+}
+
} // namespace spirv
} // namespace writer
} // namespace tint
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 7a42e3e..31efbde 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -21,6 +21,7 @@
#include <vector>
#include "src/ast/module.h"
+#include "src/ast/struct_member.h"
#include "src/writer/spirv/instruction.h"
namespace tint {
@@ -117,7 +118,7 @@
annotations_.push_back(Instruction{op, operands});
}
/// @returns the annotations
- const std::vector<Instruction>& annot() const { return annotations_; }
+ const std::vector<Instruction>& annots() const { return annotations_; }
/// Generates an entry point instruction
/// @param ep the entry point
@@ -130,6 +131,30 @@
/// @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);
+ /// Generates a matrix type declaration
+ /// @param mat the matrix to generate
+ /// @param result the result operand
+ /// @returns true if the matrix was successfully generated
+ bool GenerateMatrixType(ast::type::MatrixType* mat, const Operand& result);
+ /// Generates a vector type declaration
+ /// @param struct_type the vector to generate
+ /// @param result the result operand
+ /// @returns true if the vector was successfully generated
+ bool GenerateStructType(ast::type::StructType* struct_type,
+ const Operand& result);
+ /// Generates a vector type declaration
+ /// @param vec the vector to generate
+ /// @param result the result operand
+ /// @returns true if the vector was successfully generated
+ bool GenerateVectorType(ast::type::VectorType* vec, const Operand& result);
+ /// Generates a struct member
+ /// @param struct_id the id of the parent structure
+ /// @param idx the index of the member
+ /// @param member the member to generate
+ /// @returns the id of the struct member or 0 on error.
+ uint32_t GenerateStructMember(uint32_t struct_id,
+ uint32_t idx,
+ ast::StructMember* member);
private:
/// @returns an Operand with a new result ID in it. Increments the next_id_
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index 014d8c1..7dca01c 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -15,6 +15,9 @@
#include <memory>
#include "gtest/gtest.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/type/alias_type.h"
#include "src/ast/type/array_type.h"
#include "src/ast/type/bool_type.h"
@@ -22,6 +25,7 @@
#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/struct_type.h"
#include "src/ast/type/u32_type.h"
#include "src/ast/type/vector_type.h"
#include "src/ast/type/void_type.h"
@@ -171,6 +175,104 @@
ASSERT_FALSE(b.has_error()) << b.error();
}
+TEST_F(BuilderTest_Type, GenerateStruct_Empty) {
+ auto s = std::make_unique<ast::Struct>();
+ ast::type::StructType s_type(std::move(s));
+
+ Builder b;
+ auto id = b.GenerateTypeIfNeeded(&s_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 = OpTypeStruct
+)");
+}
+
+TEST_F(BuilderTest_Type, GenerateStruct) {
+ ast::type::F32Type f32;
+
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>> decos;
+ std::vector<std::unique_ptr<ast::StructMember>> members;
+ members.push_back(
+ std::make_unique<ast::StructMember>("a", &f32, std::move(decos)));
+
+ auto s = std::make_unique<ast::Struct>(ast::StructDecoration::kNone,
+ std::move(members));
+ ast::type::StructType s_type(std::move(s));
+
+ Builder b;
+ auto id = b.GenerateTypeIfNeeded(&s_type);
+ ASSERT_FALSE(b.has_error()) << b.error();
+ EXPECT_EQ(id, 1);
+
+ EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeStruct %2
+)");
+ EXPECT_EQ(DumpInstructions(b.debug()), R"(OpMemberName %1 0 "a"
+)");
+}
+
+TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
+ ast::type::F32Type f32;
+
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>> decos;
+ std::vector<std::unique_ptr<ast::StructMember>> members;
+ members.push_back(
+ std::make_unique<ast::StructMember>("a", &f32, std::move(decos)));
+
+ auto s = std::make_unique<ast::Struct>(ast::StructDecoration::kBlock,
+ std::move(members));
+ ast::type::StructType s_type(std::move(s));
+
+ Builder b;
+ auto id = b.GenerateTypeIfNeeded(&s_type);
+ ASSERT_FALSE(b.has_error()) << b.error();
+ EXPECT_EQ(id, 1);
+
+ EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeStruct %2
+)");
+ EXPECT_EQ(DumpInstructions(b.debug()), R"(OpMemberName %1 0 "a"
+)");
+ EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 Block
+)");
+}
+
+TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
+ ast::type::F32Type f32;
+
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>> a_decos;
+ a_decos.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(0));
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>> b_decos;
+ b_decos.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(8));
+
+ std::vector<std::unique_ptr<ast::StructMember>> members;
+ members.push_back(
+ std::make_unique<ast::StructMember>("a", &f32, std::move(a_decos)));
+ members.push_back(
+ std::make_unique<ast::StructMember>("b", &f32, std::move(b_decos)));
+
+ auto s = std::make_unique<ast::Struct>(ast::StructDecoration::kNone,
+ std::move(members));
+ ast::type::StructType s_type(std::move(s));
+
+ Builder b;
+ auto id = b.GenerateTypeIfNeeded(&s_type);
+ ASSERT_FALSE(b.has_error()) << b.error();
+ EXPECT_EQ(id, 1);
+
+ EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeStruct %2 %2
+)");
+ EXPECT_EQ(DumpInstructions(b.debug()), R"(OpMemberName %1 0 "a"
+OpMemberName %1 1 "b"
+)");
+ EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %1 0 Offset 0
+OpMemberDecorate %1 1 Offset 8
+)");
+}
+
TEST_F(BuilderTest_Type, GenerateU32) {
ast::type::U32Type u32;