| // 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_TINT_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_ |
| #define SRC_TINT_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #if TINT_BUILD_SPV_READER |
| #include "source/opt/ir_context.h" |
| #endif |
| |
| #include "gtest/gtest.h" |
| #include "src/tint/demangler.h" |
| #include "src/tint/reader/spirv/fail_stream.h" |
| #include "src/tint/reader/spirv/function.h" |
| #include "src/tint/reader/spirv/namer.h" |
| #include "src/tint/reader/spirv/parser_impl.h" |
| #include "src/tint/reader/spirv/spirv_tools_helpers_test.h" |
| #include "src/tint/reader/spirv/usage.h" |
| |
| namespace tint::reader::spirv { |
| namespace test { |
| |
| /// A test class that wraps ParseImpl |
| class ParserImplWrapperForTest { |
| public: |
| /// Constructor |
| /// @param input the input data to parse |
| explicit ParserImplWrapperForTest(const std::vector<uint32_t>& input); |
| /// Dumps SPIR-V if the conversion succeeded, then destroys the wrapper. |
| ~ParserImplWrapperForTest(); |
| |
| /// Sets global state to force dumping of the assembly text of succesfully |
| /// SPIR-V. |
| static void DumpSuccessfullyConvertedSpirv() { dump_successfully_converted_spirv_ = true; } |
| /// Marks the test has having deliberately invalid SPIR-V |
| void DeliberatelyInvalidSpirv() { skip_dumping_spirv_ = true; } |
| /// Marks the test's SPIR-V as not being suitable for dumping, for a stated |
| /// reason. |
| void SkipDumpingPending(std::string) { skip_dumping_spirv_ = true; } |
| |
| /// @returns a new function emitter for the given function ID. |
| /// Assumes ParserImpl::BuildInternalRepresentation has been run and |
| /// succeeded. |
| /// @param function_id the SPIR-V identifier of the function |
| FunctionEmitter function_emitter(uint32_t function_id) { |
| auto* spirv_function = impl_.ir_context()->GetFunction(function_id); |
| return FunctionEmitter(&impl_, *spirv_function); |
| } |
| |
| /// Run the parser |
| /// @returns true if the parse was successful, false otherwise. |
| bool Parse() { return impl_.Parse(); } |
| |
| /// @returns the program. The program builder in the parser will be reset |
| /// after this. |
| Program program() { return impl_.program(); } |
| |
| /// @returns the namer object |
| Namer& namer() { return impl_.namer(); } |
| |
| /// @returns a reference to the internal builder, without building the |
| /// program. To be used only for testing. |
| ProgramBuilder& builder() { return impl_.builder(); } |
| |
| /// @returns the accumulated error string |
| const std::string error() { return impl_.error(); } |
| |
| /// @return true if failure has not yet occurred |
| bool success() { return impl_.success(); } |
| |
| /// Logs failure, ands return a failure stream to accumulate diagnostic |
| /// messages. By convention, a failure should only be logged along with |
| /// a non-empty string diagnostic. |
| /// @returns the failure stream |
| FailStream& Fail() { return impl_.Fail(); } |
| |
| /// @returns a borrowed pointer to the internal representation of the module. |
| /// This is null until BuildInternalModule has been called. |
| spvtools::opt::IRContext* ir_context() { return impl_.ir_context(); } |
| |
| /// Builds the internal representation of the SPIR-V module. |
| /// Assumes the module is somewhat well-formed. Normally you |
| /// would want to validate the SPIR-V module before attempting |
| /// to build this internal representation. Also computes a topological |
| /// ordering of the functions. |
| /// This is a no-op if the parser has already failed. |
| /// @returns true if the parser is still successful. |
| bool BuildInternalModule() { return impl_.BuildInternalModule(); } |
| |
| /// Builds an internal representation of the SPIR-V binary, |
| /// and parses the module, except functions, into a Tint AST module. |
| /// Diagnostics are emitted to the error stream. |
| /// @returns true if it was successful. |
| bool BuildAndParseInternalModuleExceptFunctions() { |
| return impl_.BuildAndParseInternalModuleExceptFunctions(); |
| } |
| |
| /// 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 impl_.BuildAndParseInternalModule(); } |
| |
| /// Registers user names for SPIR-V objects, from OpName, and OpMemberName. |
| /// Also synthesizes struct field names. Ensures uniqueness for names for |
| /// SPIR-V IDs, and uniqueness of names of fields within any single struct. |
| /// This is a no-op if the parser has already failed. |
| /// @returns true if parser is still successful. |
| bool RegisterUserAndStructMemberNames() { return impl_.RegisterUserAndStructMemberNames(); } |
| |
| /// Register Tint AST types for SPIR-V types, including type aliases as |
| /// needed. This is a no-op if the parser has already failed. |
| /// @returns true if parser is still successful. |
| bool RegisterTypes() { return impl_.RegisterTypes(); } |
| |
| /// Register sampler and texture usage for memory object declarations. |
| /// This must be called after we've registered line numbers for all |
| /// instructions. This is a no-op if the parser has already failed. |
| /// @returns true if parser is still successful. |
| bool RegisterHandleUsage() { return impl_.RegisterHandleUsage(); } |
| |
| /// Emits module-scope variables. |
| /// This is a no-op if the parser has already failed. |
| /// @returns true if parser is still successful. |
| bool EmitModuleScopeVariables() { return impl_.EmitModuleScopeVariables(); } |
| |
| /// @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 impl_.glsl_std_450_imports(); |
| } |
| |
| /// Converts a SPIR-V type to a Tint type, and saves it for fast lookup. |
| /// If the type is only used for builtins, then register that specially, |
| /// and return null. If the type is a sampler, image, or sampled image, then |
| /// return the Void type, because those opaque types are handled in a |
| /// different way. |
| /// On failure, logs an error and returns null. This should only be called |
| /// after the internal representation of the module has been built. |
| /// @param id the SPIR-V ID of a type. |
| /// @returns a Tint type, or nullptr |
| const Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); } |
| |
| /// Gets the list of decorations for a SPIR-V result ID. Returns an empty |
| /// vector if the ID is not a result ID, or if no decorations target that ID. |
| /// The internal representation must have already been built. |
| /// @param id SPIR-V ID |
| /// @returns the list of decorations on the given ID |
| DecorationList GetDecorationsFor(uint32_t id) const { return impl_.GetDecorationsFor(id); } |
| |
| /// Gets the list of decorations for the member of a struct. Returns an empty |
| /// list if the `id` is not the ID of a struct, or if the member index is out |
| /// of range, or if the target member has no decorations. |
| /// The internal representation must have already been built. |
| /// @param id SPIR-V ID of a struct |
| /// @param member_index the member within the struct |
| /// @returns the list of decorations on the member |
| DecorationList GetDecorationsForMember(uint32_t id, uint32_t member_index) const { |
| return impl_.GetDecorationsForMember(id, member_index); |
| } |
| |
| /// Converts a SPIR-V struct member decoration into a number of AST |
| /// decorations. If the decoration is recognized but deliberately dropped, |
| /// then returns an empty list without a diagnostic. On failure, emits a |
| /// diagnostic and returns an empty list. |
| /// @param struct_type_id the ID of the struct type |
| /// @param member_index the index of the member |
| /// @param member_ty the type of the member |
| /// @param decoration an encoded SPIR-V Decoration |
| /// @returns the AST decorations |
| ast::AttributeList ConvertMemberDecoration(uint32_t struct_type_id, |
| uint32_t member_index, |
| const Type* member_ty, |
| const Decoration& decoration) { |
| return impl_.ConvertMemberDecoration(struct_type_id, member_index, member_ty, decoration); |
| } |
| |
| /// For a SPIR-V ID that might define a sampler, image, or sampled image |
| /// value, return the SPIR-V instruction that represents the memory object |
| /// declaration for the object. If we encounter an OpSampledImage along the |
| /// way, follow the image operand when follow_image is true; otherwise follow |
| /// the sampler operand. Returns nullptr if we can't trace back to a memory |
| /// object declaration. Emits an error and returns nullptr when the scan |
| /// fails due to a malformed module. This method can be used any time after |
| /// BuildInternalModule has been invoked. |
| /// @param id the SPIR-V ID of the sampler, image, or sampled image |
| /// @param follow_image indicates whether to follow the image operand of |
| /// OpSampledImage |
| /// @returns the memory object declaration for the handle, or nullptr |
| const spvtools::opt::Instruction* GetMemoryObjectDeclarationForHandle(uint32_t id, |
| bool follow_image) { |
| return impl_.GetMemoryObjectDeclarationForHandle(id, follow_image); |
| } |
| |
| /// @param entry_point the SPIR-V ID of an entry point. |
| /// @returns the entry point info for the given ID |
| const std::vector<EntryPointInfo>& GetEntryPointInfo(uint32_t entry_point) { |
| return impl_.GetEntryPointInfo(entry_point); |
| } |
| |
| /// Returns the handle usage for a memory object declaration. |
| /// @param id SPIR-V ID of a sampler or image OpVariable or |
| /// OpFunctionParameter |
| /// @returns the handle usage, or an empty usage object. |
| Usage GetHandleUsage(uint32_t id) const { return impl_.GetHandleUsage(id); } |
| |
| /// Returns the SPIR-V instruction with the given ID, or nullptr. |
| /// @param id the SPIR-V result ID |
| /// @returns the instruction, or nullptr on error |
| const spvtools::opt::Instruction* GetInstructionForTest(uint32_t id) const { |
| return impl_.GetInstructionForTest(id); |
| } |
| |
| /// @returns info about the gl_Position builtin variable. |
| const ParserImpl::BuiltInPositionInfo& GetBuiltInPositionInfo() { |
| return impl_.GetBuiltInPositionInfo(); |
| } |
| |
| /// Returns the source record for the SPIR-V instruction with the given |
| /// result ID. |
| /// @param id the SPIR-V result id. |
| /// @return the Source record, or a default one |
| Source GetSourceForResultIdForTest(uint32_t id) const { |
| return impl_.GetSourceForResultIdForTest(id); |
| } |
| |
| private: |
| ParserImpl impl_; |
| /// When true, indicates the input SPIR-V module should not be emitted. |
| /// It's either deliberately invalid, or not supported for some pending |
| /// reason. |
| bool skip_dumping_spirv_ = false; |
| static bool dump_successfully_converted_spirv_; |
| }; |
| |
| // Sets global state to force dumping of the assembly text of succesfully |
| // SPIR-V. |
| inline void DumpSuccessfullyConvertedSpirv() { |
| ParserImplWrapperForTest::DumpSuccessfullyConvertedSpirv(); |
| } |
| |
| /// Returns the WGSL printed string of a program. |
| /// @param program the Program |
| /// @returns the WGSL printed string the program. |
| std::string ToString(const Program& program); |
| |
| /// Returns the WGSL printed string of a statement list. |
| /// @param program the Program |
| /// @param stmts the statement list |
| /// @returns the WGSL printed string of a statement list. |
| std::string ToString(const Program& program, const ast::StatementList& stmts); |
| |
| /// Returns the WGSL printed string of an AST node. |
| /// @param program the Program |
| /// @param node the AST node |
| /// @returns the WGSL printed string of the AST node. |
| std::string ToString(const Program& program, const ast::Node* node); |
| |
| } // namespace test |
| |
| /// SPIR-V Parser test class |
| template <typename T> |
| class SpvParserTestBase : public T { |
| public: |
| SpvParserTestBase() = default; |
| ~SpvParserTestBase() override = default; |
| |
| /// Retrieves the parser from the helper |
| /// @param input the SPIR-V binary to parse |
| /// @returns a parser for the given binary |
| std::unique_ptr<test::ParserImplWrapperForTest> parser(const std::vector<uint32_t>& input) { |
| auto parser = std::make_unique<test::ParserImplWrapperForTest>(input); |
| |
| // Don't run the Resolver when building the program. |
| // We're not interested in type information with these tests. |
| parser->builder().SetResolveOnBuild(false); |
| return parser; |
| } |
| }; |
| |
| /// SpvParserTest the the base class for SPIR-V reader unit tests. |
| /// Use this form when you don't need to template any further. |
| using SpvParserTest = SpvParserTestBase<::testing::Test>; |
| |
| } // namespace tint::reader::spirv |
| |
| #endif // SRC_TINT_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_ |