spv: Parse OpExtInst for GLSL.std.450

It will always use the "std::glsl" import name.

Remember all the IDs of such imports.

Never add more than one GLSL.std.450 import to the AST.

Also refactor the Assemble test helper into its own file.

Bug: tint:3
Change-Id: I5b2b70ea0f00d44aacf553aa009756dff2a4cecf
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16662
Reviewed-by: dan sinclair <dsinclair@google.com>
This commit is contained in:
David Neto 2020-03-17 16:14:10 +00:00 committed by dan sinclair
parent c83578038a
commit 3ac99398b5
8 changed files with 245 additions and 26 deletions

View File

@ -353,8 +353,11 @@ endif()
if(${TINT_BUILD_SPV_PARSER}) if(${TINT_BUILD_SPV_PARSER})
list (APPEND TINT_TEST_SRCS list (APPEND TINT_TEST_SRCS
reader/spv/fail_stream_test.cc reader/spv/fail_stream_test.cc
reader/spv/parser_impl_import_test.cc
reader/spv/parser_impl_test.cc reader/spv/parser_impl_test.cc
reader/spv/parser_test.cc reader/spv/parser_test.cc
reader/spv/spirv_tools_helpers_test.cc
reader/spv/spirv_tools_helpers_test.h
) )
endif() endif()

View File

@ -1,4 +1,4 @@
// Copyright 2020 Google LLC // Copyright 2020 The Tint Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.

View File

@ -104,6 +104,45 @@ bool ParserImpl::BuildInternalModule() {
return true; return true;
} }
void ParserImpl::ResetInternalModule() {
ir_context_.reset(nullptr);
module_ = nullptr;
def_use_mgr_ = nullptr;
constant_mgr_ = nullptr;
type_mgr_ = nullptr;
deco_mgr_ = nullptr;
import_map_.clear();
glsl_std_450_imports_.clear();
}
bool ParserImpl::ParseInternalModule() {
return ParseExtendedInstructionImports();
// TODO(dneto): fill in the rest
}
bool ParserImpl::ParseExtendedInstructionImports() {
for (const spvtools::opt::Instruction& import : module_->ext_inst_imports()) {
std::string name(
reinterpret_cast<const char*>(import.GetInOperand(0).words.data()));
// TODO(dneto): Handle other extended instruction sets when needed.
if (name == "GLSL.std.450") {
// Only create the AST import once, so we can use import name 'std::glsl'.
// This is a canonicalization.
if (glsl_std_450_imports_.empty()) {
auto ast_import =
std::make_unique<tint::ast::Import>(name, "std::glsl");
import_map_[import.result_id()] = ast_import.get();
ast_module_.AddImport(std::move(ast_import));
}
glsl_std_450_imports_.insert(import.result_id());
} else {
return Fail() << "Unrecognized extended instruction set: " << name;
}
}
return true;
}
} // namespace spv } // namespace spv
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -18,6 +18,8 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
#include "source/opt/constants.h" #include "source/opt/constants.h"
@ -26,6 +28,8 @@
#include "source/opt/module.h" #include "source/opt/module.h"
#include "source/opt/type_manager.h" #include "source/opt/type_manager.h"
#include "spirv-tools/libspirv.hpp" #include "spirv-tools/libspirv.hpp"
#include "src/ast/import.h"
#include "src/ast/module.h"
#include "src/reader/reader.h" #include "src/reader/reader.h"
#include "src/reader/spv/fail_stream.h" #include "src/reader/spv/fail_stream.h"
@ -61,6 +65,20 @@ class ParserImpl : Reader {
/// @returns the accumulated error string /// @returns the accumulated error string
const std::string error() { return errors_.str(); } const std::string error() { return errors_.str(); }
/// Builds an internal representation of the SPIR-V binary,
/// and parses it into a Tint AST module. Diagnostics are emitted
/// to the error stream.
/// @returns true if it was successful.
bool BuildAndParseInternalModule() {
return BuildInternalModule() && ParseInternalModule();
}
/// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
/// extended instruction set.
const std::unordered_set<uint32_t>& glsl_std_450_imports() const {
return glsl_std_450_imports_;
}
private: private:
/// Builds the internal representation of the SPIR-V module. /// Builds the internal representation of the SPIR-V module.
/// Assumes the module is somewhat well-formed. Normally you /// Assumes the module is somewhat well-formed. Normally you
@ -69,6 +87,17 @@ class ParserImpl : Reader {
/// @returns true if successful. /// @returns true if successful.
bool BuildInternalModule(); bool BuildInternalModule();
/// Walks the internal representation of the module to populate
/// the AST form of the module.
/// @returns true on success
bool ParseInternalModule();
/// Destroys the internal representation of the SPIR-V module.
void ResetInternalModule();
/// Parses OpExtInstImport instructions.
bool ParseExtendedInstructionImports();
// The SPIR-V binary we're parsing // The SPIR-V binary we're parsing
std::vector<uint32_t> spv_binary_; std::vector<uint32_t> spv_binary_;
@ -93,6 +122,12 @@ class ParserImpl : Reader {
spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr; spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr;
spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr; spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr; spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
/// Maps a SPIR-V ID for an external instruction import to an AST import
std::unordered_map<uint32_t, ast::Import*> import_map_;
// The set of IDs that are imports of the GLSL.std.450 extended instruction
// sets.
std::unordered_set<uint32_t> glsl_std_450_imports_;
}; };
} // namespace spv } // namespace spv

View File

@ -0,0 +1,74 @@
// 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/spv/parser_impl.h"
#include <memory>
#include <sstream>
#include "gmock/gmock.h"
#include "src/reader/spv/spirv_tools_helpers_test.h"
namespace tint {
namespace reader {
namespace spv {
namespace {
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
using ::testing::UnorderedElementsAre;
using SpvParseImport = ::testing::Test;
TEST_F(SpvParseImport, NoImport) {
ParserImpl p(test::Assemble("%1 = OpTypeVoid"));
EXPECT_TRUE(p.BuildAndParseInternalModule());
EXPECT_TRUE(p.error().empty());
const auto module_ast = p.module().to_str();
EXPECT_THAT(module_ast, Not(HasSubstr("Import")));
}
TEST_F(SpvParseImport, ImportGlslStd450) {
ParserImpl p(test::Assemble(R"(%1 = OpExtInstImport "GLSL.std.450")"));
EXPECT_TRUE(p.BuildAndParseInternalModule());
EXPECT_TRUE(p.error().empty());
EXPECT_THAT(p.glsl_std_450_imports(), ElementsAre(1));
const auto module_ast = p.module().to_str();
EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
}
TEST_F(SpvParseImport, ImportGlslStd450Twice) {
ParserImpl p(test::Assemble(R"(
%1 = OpExtInstImport "GLSL.std.450"
%42 = OpExtInstImport "GLSL.std.450"
)"));
EXPECT_TRUE(p.BuildAndParseInternalModule());
EXPECT_TRUE(p.error().empty());
EXPECT_THAT(p.glsl_std_450_imports(), UnorderedElementsAre(1, 42));
const auto module = p.module();
EXPECT_EQ(module.imports().size(), 1);
const auto module_ast = module.to_str();
// TODO(dneto): Use a matcher to show there is only one import.
EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
}
// TODO(dneto): We don't currently support other kinds of extended instruction
// imports.
} // namespace
} // namespace spv
} // namespace reader
} // namespace tint

View File

@ -18,7 +18,7 @@
#include <vector> #include <vector>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "spirv-tools/libspirv.hpp" #include "src/reader/spv/spirv_tools_helpers_test.h"
namespace tint { namespace tint {
namespace reader { namespace reader {
@ -37,31 +37,8 @@ TEST_F(SpvParserImplTest, Uint32VecEmpty) {
// TODO(dneto): What message? // TODO(dneto): What message?
} }
/// @returns the SPIR-V module assembled from the given text. Numeric IDs
/// are preserved.
std::vector<uint32_t> Assemble(const std::string& spirv_assembly) {
// TODO(dneto): Use ScopedTrace?
// (The target environment doesn't affect assembly.
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::stringstream errors;
std::vector<uint32_t> result;
tools.SetMessageConsumer([&errors](spv_message_level_t, const char*,
const spv_position_t& position,
const char* message) {
errors << "assembly error:" << position.line << ":" << position.column
<< ": " << message;
});
const auto success = tools.Assemble(
spirv_assembly, &result, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
EXPECT_TRUE(success) << errors.str();
return result;
}
TEST_F(SpvParserImplTest, InvalidModuleFails) { TEST_F(SpvParserImplTest, InvalidModuleFails) {
auto invalid_spv = Assemble("%ty = OpTypeInt 3 0"); auto invalid_spv = test::Assemble("%ty = OpTypeInt 3 0");
ParserImpl p{invalid_spv}; ParserImpl p{invalid_spv};
EXPECT_FALSE(p.Parse()); EXPECT_FALSE(p.Parse());
EXPECT_THAT( EXPECT_THAT(

View File

@ -0,0 +1,55 @@
// 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/spv/spirv_tools_helpers_test.h"
#include <cstdint>
#include <sstream>
#include <vector>
#include "gtest/gtest.h"
#include "spirv-tools/libspirv.hpp"
namespace tint {
namespace reader {
namespace spv {
namespace test {
/// @returns the SPIR-V module assembled from the given text. Numeric IDs
/// are preserved.
std::vector<uint32_t> Assemble(const std::string& spirv_assembly) {
// TODO(dneto): Use ScopedTrace?
// (The target environment doesn't affect assembly.
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::stringstream errors;
std::vector<uint32_t> result;
tools.SetMessageConsumer([&errors](spv_message_level_t, const char*,
const spv_position_t& position,
const char* message) {
errors << "assembly error:" << position.line << ":" << position.column
<< ": " << message;
});
const auto success = tools.Assemble(
spirv_assembly, &result, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
EXPECT_TRUE(success) << errors.str();
return result;
}
} // namespace test
} // namespace spv
} // namespace reader
} // namespace tint

View File

@ -0,0 +1,36 @@
// 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.
#ifndef SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_
#define SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_
#include <cstdint>
#include <string>
#include <vector>
namespace tint {
namespace reader {
namespace spv {
namespace test {
/// @returns the SPIR-V module assembled from the given text. Numeric IDs
/// are preserved.
std::vector<uint32_t> Assemble(const std::string& spirv_assembly);
} // namespace test
} // namespace spv
} // namespace reader
} // namespace tint
#endif // SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_