From 15cbc98a8cb7cbef92605ec23bc4c63df0b298ad Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Mon, 30 Mar 2020 19:49:01 +0000 Subject: [PATCH] [spirv-writer] Start global variable output This CL starts adding global variable output to the SPIR-V writer. This does not handle constants or initializers at this point. Bug: tint:5 Change-Id: Id06533b2ec1f61feadf66f3e43a484f6f3765546 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17922 Reviewed-by: David Neto --- src/CMakeLists.txt | 3 +- src/ast/binding_decoration.cc | 2 +- src/ast/binding_decoration.h | 6 +- src/ast/location_decoration.cc | 2 +- src/ast/location_decoration.h | 6 +- src/ast/set_decoration.cc | 2 +- src/ast/set_decoration.h | 6 +- src/writer/spirv/builder.cc | 96 ++++++++++ src/writer/spirv/builder.h | 9 + .../spirv/builder_global_variable_test.cc | 177 ++++++++++++++++++ 10 files changed, 296 insertions(+), 13 deletions(-) create mode 100644 src/writer/spirv/builder_global_variable_test.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f4523696f..d0b678cb97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -408,10 +408,11 @@ endif() if(${TINT_BUILD_SPV_WRITER}) list(APPEND TINT_TEST_SRCS writer/spirv/binary_writer_test.cc - writer/spirv/builder_test.cc writer/spirv/builder_entry_point_test.cc writer/spirv/builder_function_test.cc + writer/spirv/builder_global_variable_test.cc writer/spirv/builder_literal_test.cc + writer/spirv/builder_test.cc writer/spirv/builder_type_test.cc writer/spirv/instruction_test.cc writer/spirv/operand_test.cc diff --git a/src/ast/binding_decoration.cc b/src/ast/binding_decoration.cc index d9532fe4aa..a2fbc0cff8 100644 --- a/src/ast/binding_decoration.cc +++ b/src/ast/binding_decoration.cc @@ -17,7 +17,7 @@ namespace tint { namespace ast { -BindingDecoration::BindingDecoration(size_t val) : value_(val) {} +BindingDecoration::BindingDecoration(uint32_t val) : value_(val) {} BindingDecoration::~BindingDecoration() = default; diff --git a/src/ast/binding_decoration.h b/src/ast/binding_decoration.h index 1a7e573fff..b9484a50e9 100644 --- a/src/ast/binding_decoration.h +++ b/src/ast/binding_decoration.h @@ -27,21 +27,21 @@ class BindingDecoration : public VariableDecoration { public: /// constructor /// @param value the binding value - explicit BindingDecoration(size_t value); + explicit BindingDecoration(uint32_t value); ~BindingDecoration() override; /// @returns true if this is a binding decoration bool IsBinding() const override { return true; } /// @returns the binding value - size_t value() const { return value_; } + uint32_t value() const { return value_; } /// Outputs the decoration to the given stream /// @param out the stream to output too void to_str(std::ostream& out) const override; private: - size_t value_; + uint32_t value_; }; } // namespace ast diff --git a/src/ast/location_decoration.cc b/src/ast/location_decoration.cc index 9fb8d5f2ed..f261b3a533 100644 --- a/src/ast/location_decoration.cc +++ b/src/ast/location_decoration.cc @@ -17,7 +17,7 @@ namespace tint { namespace ast { -LocationDecoration::LocationDecoration(size_t val) : value_(val) {} +LocationDecoration::LocationDecoration(uint32_t val) : value_(val) {} LocationDecoration::~LocationDecoration() = default; diff --git a/src/ast/location_decoration.h b/src/ast/location_decoration.h index 69b61dcb06..287e891d99 100644 --- a/src/ast/location_decoration.h +++ b/src/ast/location_decoration.h @@ -27,21 +27,21 @@ class LocationDecoration : public VariableDecoration { public: /// constructor /// @param value the location value - explicit LocationDecoration(size_t value); + explicit LocationDecoration(uint32_t value); ~LocationDecoration() override; /// @returns true if this is a location decoration bool IsLocation() const override { return true; } /// @returns the location value - size_t value() const { return value_; } + uint32_t value() const { return value_; } /// Outputs the decoration to the given stream /// @param out the stream to output too void to_str(std::ostream& out) const override; private: - size_t value_; + uint32_t value_; }; } // namespace ast diff --git a/src/ast/set_decoration.cc b/src/ast/set_decoration.cc index 7d54d98a6d..6d965a274f 100644 --- a/src/ast/set_decoration.cc +++ b/src/ast/set_decoration.cc @@ -17,7 +17,7 @@ namespace tint { namespace ast { -SetDecoration::SetDecoration(size_t val) : value_(val) {} +SetDecoration::SetDecoration(uint32_t val) : value_(val) {} SetDecoration::~SetDecoration() = default; diff --git a/src/ast/set_decoration.h b/src/ast/set_decoration.h index 253e940211..dd0e2d92d0 100644 --- a/src/ast/set_decoration.h +++ b/src/ast/set_decoration.h @@ -27,21 +27,21 @@ class SetDecoration : public VariableDecoration { public: /// constructor /// @param value the set value - explicit SetDecoration(size_t value); + explicit SetDecoration(uint32_t value); ~SetDecoration() override; /// @returns true if this is a set decoration bool IsSet() const override { return true; } /// @returns the set value - size_t value() const { return value_; } + uint32_t value() const { return value_; } /// Outputs the decoration to the given stream /// @param out the stream to output too void to_str(std::ostream& out) const override; private: - size_t value_; + uint32_t value_; }; } // namespace ast diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index 5820bf2fc3..7896ae007b 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc @@ -16,9 +16,14 @@ #include #include "spirv/unified1/spirv.h" +#include "src/ast/binding_decoration.h" #include "src/ast/bool_literal.h" +#include "src/ast/builtin_decoration.h" +#include "src/ast/decorated_variable.h" #include "src/ast/float_literal.h" #include "src/ast/int_literal.h" +#include "src/ast/location_decoration.h" +#include "src/ast/set_decoration.h" #include "src/ast/struct.h" #include "src/ast/struct_member.h" #include "src/ast/struct_member_offset_decoration.h" @@ -82,6 +87,12 @@ bool Builder::Build(const ast::Module& m) { {Operand::Int(SpvAddressingModelLogical), Operand::Int(SpvMemoryModelVulkanKHR)}); + for (const auto& var : m.global_variables()) { + if (!GenerateGlobalVariable(var.get())) { + return false; + } + } + for (const auto& func : m.functions()) { if (!GenerateFunction(func.get())) { return false; @@ -208,6 +219,60 @@ uint32_t Builder::GenerateFunctionTypeIfNeeded(ast::Function* func) { return func_type_id; } +bool Builder::GenerateGlobalVariable(ast::Variable* var) { + auto result = result_op(); + auto var_id = result.to_i(); + + if (var->is_const()) { + // TODO(dsinclair): Handle const variables + return false; + } + + auto sc = var->storage_class() == ast::StorageClass::kNone + ? ast::StorageClass::kPrivate + : var->storage_class(); + + ast::type::PointerType pt(var->type(), sc); + auto type_id = GenerateTypeIfNeeded(&pt); + if (type_id == 0) { + return false; + } + + // TODO(dsinclair): Handle variable initializer + push_debug(spv::Op::OpName, + {Operand::Int(var_id), Operand::String(var->name())}); + push_type(spv::Op::OpVariable, {Operand::Int(type_id), result, + Operand::Int(ConvertStorageClass(sc))}); + + if (var->IsDecorated()) { + for (const auto& deco : var->AsDecorated()->decorations()) { + if (deco->IsBuiltin()) { + push_debug(spv::Op::OpDecorate, + {Operand::Int(var_id), Operand::Int(SpvDecorationBuiltIn), + Operand::Int(ConvertBuiltin(deco->AsBuiltin()->value()))}); + } else if (deco->IsLocation()) { + push_debug(spv::Op::OpDecorate, + {Operand::Int(var_id), Operand::Int(SpvDecorationLocation), + Operand::Int(deco->AsLocation()->value())}); + } else if (deco->IsBinding()) { + push_debug(spv::Op::OpDecorate, + {Operand::Int(var_id), Operand::Int(SpvDecorationBinding), + Operand::Int(deco->AsBinding()->value())}); + } else if (deco->IsSet()) { + push_debug( + spv::Op::OpDecorate, + {Operand::Int(var_id), Operand::Int(SpvDecorationDescriptorSet), + Operand::Int(deco->AsSet()->value())}); + } else { + error_ = "unknown decoration"; + return false; + } + } + } + + return true; +} + void Builder::GenerateImport(ast::Import* imp) { auto result = result_op(); auto id = result.to_i(); @@ -353,6 +418,7 @@ bool Builder::GeneratePointerType(ast::type::PointerType* ptr, auto stg_class = ConvertStorageClass(ptr->storage_class()); if (stg_class == SpvStorageClassMax) { + error_ = "invalid storage class for pointer"; return false; } @@ -460,6 +526,36 @@ SpvStorageClass Builder::ConvertStorageClass(ast::StorageClass klass) const { return SpvStorageClassMax; } +SpvBuiltIn Builder::ConvertBuiltin(ast::Builtin builtin) const { + switch (builtin) { + case ast::Builtin::kPosition: + return SpvBuiltInPosition; + case ast::Builtin::kVertexIdx: + return SpvBuiltInVertexIndex; + case ast::Builtin::kInstanceIdx: + return SpvBuiltInInstanceIndex; + case ast::Builtin::kFrontFacing: + return SpvBuiltInFrontFacing; + case ast::Builtin::kFragCoord: + return SpvBuiltInFragCoord; + case ast::Builtin::kFragDepth: + return SpvBuiltInFragDepth; + case ast::Builtin::kNumWorkgroups: + return SpvBuiltInNumWorkgroups; + case ast::Builtin::kWorkgroupSize: + return SpvBuiltInWorkgroupSize; + case ast::Builtin::kLocalInvocationId: + return SpvBuiltInLocalInvocationId; + case ast::Builtin::kLocalInvocationIdx: + return SpvBuiltInLocalInvocationIndex; + case ast::Builtin::kGlobalInvocationId: + return SpvBuiltInGlobalInvocationId; + case ast::Builtin::kNone: + break; + } + return SpvBuiltInMax; +} + } // namespace spirv } // namespace writer } // namespace tint diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h index 8aefe564e5..6c4437e3f6 100644 --- a/src/writer/spirv/builder.h +++ b/src/writer/spirv/builder.h @@ -21,6 +21,7 @@ #include #include "spirv/unified1/spirv.h" +#include "src/ast/builtin.h" #include "src/ast/literal.h" #include "src/ast/module.h" #include "src/ast/struct_member.h" @@ -127,6 +128,10 @@ class Builder { /// @param klass the storage class to convert /// @returns the SPIR-V storage class or SpvStorageClassMax on error. SpvStorageClass ConvertStorageClass(ast::StorageClass klass) const; + /// Converts a builtin to a SPIR-V builtin + /// @param builtin the builtin to convert + /// @returns the SPIR-V builtin or SpvBuiltInMax on error. + SpvBuiltIn ConvertBuiltin(ast::Builtin builtin) const; /// Generates an entry point instruction /// @param ep the entry point @@ -140,6 +145,10 @@ class Builder { /// @param func the function to generate for /// @returns the ID to use for the function type. Returns 0 on failure. uint32_t GenerateFunctionTypeIfNeeded(ast::Function* func); + /// Generates a global variable + /// @param var the variable to generate + /// @returns true if the variable is emited. + bool GenerateGlobalVariable(ast::Variable* var); /// Generates an import instruction /// @param imp the import void GenerateImport(ast::Import* imp); diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc new file mode 100644 index 0000000000..693ea9330d --- /dev/null +++ b/src/writer/spirv/builder_global_variable_test.cc @@ -0,0 +1,177 @@ +// 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 + +#include "gtest/gtest.h" +#include "src/ast/binding_decoration.h" +#include "src/ast/builtin.h" +#include "src/ast/builtin_decoration.h" +#include "src/ast/decorated_variable.h" +#include "src/ast/location_decoration.h" +#include "src/ast/set_decoration.h" +#include "src/ast/storage_class.h" +#include "src/ast/type/f32_type.h" +#include "src/ast/variable.h" +#include "src/ast/variable_decoration.h" +#include "src/writer/spirv/builder.h" +#include "src/writer/spirv/spv_dump.h" + +namespace tint { +namespace writer { +namespace spirv { +namespace { + +using BuilderTest = testing::Test; + +TEST_F(BuilderTest, GlobalVar_NoStorageClass) { + ast::type::F32Type f32; + ast::Variable v("var", ast::StorageClass::kNone, &f32); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&v)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Private %3 +%1 = OpVariable %2 Private +)"); +} + +TEST_F(BuilderTest, GlobalVar_WithStorageClass) { + ast::type::F32Type f32; + ast::Variable v("var", ast::StorageClass::kOutput, &f32); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&v)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Output %3 +%1 = OpVariable %2 Output +)"); +} + +TEST_F(BuilderTest, DISABLED_GlobalVar_WithInitializer) {} + +TEST_F(BuilderTest, DISABLED_GlobalVar_Const) {} + +TEST_F(BuilderTest, GlobalVar_WithLocation) { + ast::type::F32Type f32; + auto v = + std::make_unique("var", ast::StorageClass::kOutput, &f32); + std::vector> decos; + decos.push_back(std::make_unique(5)); + + ast::DecoratedVariable dv(std::move(v)); + dv.set_decorations(std::move(decos)); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&dv)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +OpDecorate %1 Location 5 +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Output %3 +%1 = OpVariable %2 Output +)"); +} + +TEST_F(BuilderTest, GlobalVar_WithBindingAndSet) { + ast::type::F32Type f32; + auto v = + std::make_unique("var", ast::StorageClass::kOutput, &f32); + std::vector> decos; + decos.push_back(std::make_unique(2)); + decos.push_back(std::make_unique(3)); + + ast::DecoratedVariable dv(std::move(v)); + dv.set_decorations(std::move(decos)); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&dv)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +OpDecorate %1 Binding 2 +OpDecorate %1 DescriptorSet 3 +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Output %3 +%1 = OpVariable %2 Output +)"); +} + +TEST_F(BuilderTest, GlobalVar_WithBuiltin) { + ast::type::F32Type f32; + auto v = + std::make_unique("var", ast::StorageClass::kOutput, &f32); + std::vector> decos; + decos.push_back( + std::make_unique(ast::Builtin::kPosition)); + + ast::DecoratedVariable dv(std::move(v)); + dv.set_decorations(std::move(decos)); + + Builder b; + EXPECT_TRUE(b.GenerateGlobalVariable(&dv)) << b.error(); + EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var" +OpDecorate %1 BuiltIn Position +)"); + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Output %3 +%1 = OpVariable %2 Output +)"); +} + +struct BuiltinData { + ast::Builtin builtin; + SpvBuiltIn result; +}; +inline std::ostream& operator<<(std::ostream& out, BuiltinData data) { + out << data.builtin; + return out; +} +using BuiltinDataTest = testing::TestWithParam; +TEST_P(BuiltinDataTest, Convert) { + auto params = GetParam(); + + Builder b; + EXPECT_EQ(b.ConvertBuiltin(params.builtin), params.result); +} +INSTANTIATE_TEST_SUITE_P( + BuilderTest_Type, + BuiltinDataTest, + testing::Values( + BuiltinData{ast::Builtin::kNone, SpvBuiltInMax}, + BuiltinData{ast::Builtin::kPosition, SpvBuiltInPosition}, + BuiltinData{ + ast::Builtin::kVertexIdx, + SpvBuiltInVertexIndex, + }, + BuiltinData{ast::Builtin::kInstanceIdx, SpvBuiltInInstanceIndex}, + BuiltinData{ast::Builtin::kFrontFacing, SpvBuiltInFrontFacing}, + BuiltinData{ast::Builtin::kFragCoord, SpvBuiltInFragCoord}, + BuiltinData{ast::Builtin::kFragDepth, SpvBuiltInFragDepth}, + BuiltinData{ast::Builtin::kNumWorkgroups, SpvBuiltInNumWorkgroups}, + BuiltinData{ast::Builtin::kWorkgroupSize, SpvBuiltInWorkgroupSize}, + BuiltinData{ast::Builtin::kLocalInvocationId, + SpvBuiltInLocalInvocationId}, + BuiltinData{ast::Builtin::kLocalInvocationIdx, + SpvBuiltInLocalInvocationIndex}, + BuiltinData{ast::Builtin::kGlobalInvocationId, + SpvBuiltInGlobalInvocationId})); + +} // namespace +} // namespace spirv +} // namespace writer +} // namespace tint