From 4fa1ceb09487d5fb966e29bf60179f718ddc9e44 Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 7 Apr 2020 20:25:03 +0000 Subject: [PATCH] [spirv-reader] Emit function vars, no initializers Bug: tint:3 Change-Id: I64a4a2c675e5440766ffc814867e6cf247d14c13 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18706 Reviewed-by: dan sinclair --- src/CMakeLists.txt | 1 + src/reader/spirv/function.cc | 53 +++++++ src/reader/spirv/function.h | 14 ++ src/reader/spirv/function_var_test.cc | 173 +++++++++++++++++++++ src/reader/spirv/parser_impl_test_helper.h | 13 ++ 5 files changed, 254 insertions(+) create mode 100644 src/reader/spirv/function_var_test.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 456661e7c3..470df63a6f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -329,6 +329,7 @@ if(${TINT_BUILD_SPV_READER}) reader/spirv/enum_converter_test.cc reader/spirv/fail_stream_test.cc reader/spirv/function_decl_test.cc + reader/spirv/function_var_test.cc reader/spirv/namer_test.cc reader/spirv/parser_impl_convert_member_decoration_test.cc reader/spirv/parser_impl_convert_type_test.cc diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 9c841619ad..9950498147 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -32,6 +32,7 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi, : parser_impl_(*pi), ast_module_(pi->get_module()), ir_context_(*(pi->ir_context())), + type_mgr_(ir_context_.get_type_mgr()), fail_stream_(pi->fail_stream()), namer_(pi->namer()), function_(function) {} @@ -51,6 +52,15 @@ bool FunctionEmitter::Emit() { return false; } + // Start populating the body. + + if (!EmitFunctionVariables()) { + return false; + } + + // Set the body of the AST function node. + parser_impl_.get_module().functions().back()->set_body(std::move(ast_body_)); + return success(); } @@ -96,6 +106,49 @@ bool FunctionEmitter::EmitFunctionDeclaration() { return success(); } +ast::type::Type* FunctionEmitter::GetVariableStoreType( + const spvtools::opt::Instruction& var_decl_inst) { + const auto type_id = var_decl_inst.type_id(); + auto* var_ref_type = type_mgr_->GetType(type_id); + if (!var_ref_type) { + Fail() << "internal error: variable type id " << type_id + << " has no registered type"; + return nullptr; + } + auto* var_ref_ptr_type = var_ref_type->AsPointer(); + if (!var_ref_ptr_type) { + Fail() << "internal error: variable type id " << type_id + << " is not a pointer type"; + return nullptr; + } + auto var_store_type_id = type_mgr_->GetId(var_ref_ptr_type->pointee_type()); + return parser_impl_.ConvertType(var_store_type_id); +} + +bool FunctionEmitter::EmitFunctionVariables() { + if (failed()) { + return false; + } + for (auto& inst : *function_.entry()) { + if (inst.opcode() != SpvOpVariable) { + continue; + } + auto* var_store_type = GetVariableStoreType(inst); + if (failed()) { + return false; + } + // Use StorageClass::kNone because function variables should not explicitly mention + // their storage class. + auto var = + parser_impl_.MakeVariable(inst.result_id(), + ast::StorageClass::kNone, var_store_type); + // TODO(dneto): Add the initializer via Variable::set_constructor. + auto var_decl_stmt = std::make_unique(std::move(var)); + ast_body_.emplace_back(std::move(var_decl_stmt)); + } + return success(); +} + } // namespace spirv } // namespace reader } // namespace tint diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h index 4083575d2d..2a5c277e0a 100644 --- a/src/reader/spirv/function.h +++ b/src/reader/spirv/function.h @@ -17,6 +17,7 @@ #include "source/opt/function.h" #include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" #include "src/ast/module.h" #include "src/reader/spirv/fail_stream.h" #include "src/reader/spirv/namer.h" @@ -46,6 +47,9 @@ class FunctionEmitter { /// @returns true if emission has failed. bool failed() const { return !success(); } + /// @returns the body of the function. + const std::vector>& ast_body() { return ast_body_; } + /// Records failure. /// @returns a FailStream on which to emit diagnostics. FailStream& Fail() { return fail_stream_.Fail(); } @@ -56,10 +60,20 @@ class FunctionEmitter { /// @returns true if emission has not yet failed. bool EmitFunctionDeclaration(); + /// Emits declarations of function variables. + /// @returns false if emission failed. + bool EmitFunctionVariables(); + private: + /// @returns the store type for the OpVariable instruction, or + /// null on failure. + ast::type::Type* GetVariableStoreType( + const spvtools::opt::Instruction& var_decl_inst); + ParserImpl& parser_impl_; ast::Module& ast_module_; spvtools::opt::IRContext& ir_context_; + spvtools::opt::analysis::TypeManager* type_mgr_; FailStream& fail_stream_; Namer& namer_; const spvtools::opt::Function& function_; diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc new file mode 100644 index 0000000000..e93bbaa02e --- /dev/null +++ b/src/reader/spirv/function_var_test.cc @@ -0,0 +1,173 @@ +// 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 "src/reader/spirv/function.h" + +#include +#include + +#include "gmock/gmock.h" +#include "src/reader/spirv/parser_impl.h" +#include "src/reader/spirv/parser_impl_test_helper.h" +#include "src/reader/spirv/spirv_tools_helpers_test.h" + +namespace tint { +namespace reader { +namespace spirv { +namespace { + +using ::testing::HasSubstr; + +/// @returns a SPIR-V assembly segment which assigns debug names +/// to particular IDs. +std::string Names(std::vector ids) { + std::ostringstream outs; + for (auto& id : ids) { + outs << " OpName %" << id << " \"" << id << "\"\n"; + } + return outs.str(); +} + +std::string CommonTypes() { + return R"( + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float_0 = OpConstant %float 0.0 + %ptr_float = OpTypePointer Function %float + %ptr_uint = OpTypePointer Function %uint + %ptr_int = OpTypePointer Function %int + )"; +} + +TEST_F(SpvParserTest, EmitFunctionVariables_AnonymousVars) { + auto p = parser(test::Assemble(CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpVariable %ptr_uint Function + %2 = OpVariable %ptr_uint Function + %3 = OpVariable %ptr_uint Function + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error(); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_1 + none + __u32 + } +} +VariableDeclStatement{ + Variable{ + x_2 + none + __u32 + } +} +VariableDeclStatement{ + Variable{ + x_3 + none + __u32 + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_NamedVars) { + auto p = parser(test::Assemble(Names({"a", "b", "c"}) + CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %a = OpVariable %ptr_uint Function + %b = OpVariable %ptr_uint Function + %c = OpVariable %ptr_uint Function + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + a + none + __u32 + } +} +VariableDeclStatement{ + Variable{ + b + none + __u32 + } +} +VariableDeclStatement{ + Variable{ + c + none + __u32 + } +} +)")); +} + +TEST_F(SpvParserTest, EmitFunctionVariables_MixedTypes) { + auto p = parser(test::Assemble(Names({"a", "b", "c"}) + CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %a = OpVariable %ptr_uint Function + %b = OpVariable %ptr_int Function + %c = OpVariable %ptr_float Function + OpReturn + OpFunctionEnd + )")); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitFunctionVariables()); + + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{ + Variable{ + a + none + __u32 + } +} +VariableDeclStatement{ + Variable{ + b + none + __i32 + } +} +VariableDeclStatement{ + Variable{ + c + none + __f32 + } +} +)")); +} + +} // namespace +} // namespace spirv +} // namespace reader +} // namespace tint diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h index 4f52e0985f..95dc73d21e 100644 --- a/src/reader/spirv/parser_impl_test_helper.h +++ b/src/reader/spirv/parser_impl_test_helper.h @@ -17,6 +17,7 @@ #include #include +#include #include #include "gtest/gtest.h" @@ -51,6 +52,7 @@ class SpvParserTest : public testing::Test { /// Gets the internal representation of the function with the given ID. /// Assumes ParserImpl::BuildInternalRepresentation has been run and /// succeeded. + /// @param id the SPIR-V ID of the function /// @returns the internal representation of the function spvtools::opt::Function* spirv_function(uint32_t id) { return impl_->ir_context()->GetFunction(id); @@ -61,6 +63,17 @@ class SpvParserTest : public testing::Test { Context ctx_; }; +/// Returns the string dump of a function body. +/// @param body the statement in the body +/// @returnss the string dump of a function body. +inline std::string ToString(const std::vector>& body) { + std::ostringstream outs; + for (const auto& stmt : body) { + stmt->to_str(outs, 0); + } + return outs.str(); +} + } // namespace spirv } // namespace reader } // namespace tint