| // Copyright 2020 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef SRC_TINT_LANG_SPIRV_READER_AST_PARSER_HELPER_TEST_H_ |
| #define SRC_TINT_LANG_SPIRV_READER_AST_PARSER_HELPER_TEST_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "src/tint/utils/macros/compiler.h" |
| |
| TINT_BEGIN_DISABLE_WARNING(NEWLINE_EOF); |
| TINT_BEGIN_DISABLE_WARNING(OLD_STYLE_CAST); |
| TINT_BEGIN_DISABLE_WARNING(SIGN_CONVERSION); |
| TINT_BEGIN_DISABLE_WARNING(WEAK_VTABLES); |
| #include "source/opt/ir_context.h" |
| TINT_END_DISABLE_WARNING(WEAK_VTABLES); |
| TINT_END_DISABLE_WARNING(SIGN_CONVERSION); |
| TINT_END_DISABLE_WARNING(OLD_STYLE_CAST); |
| TINT_END_DISABLE_WARNING(NEWLINE_EOF); |
| |
| #include "gtest/gtest.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/ast_parser.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/fail_stream.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/function.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/namer.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/spirv_tools_helpers_test.h" |
| #include "src/tint/lang/spirv/reader/ast_parser/usage.h" |
| |
| namespace tint::spirv::reader::ast_parser::test { |
| |
| /// A test class that wraps ParseImpl |
| class ASTParserWrapperForTest { |
| public: |
| /// Constructor |
| /// @param input the input data to parse |
| explicit ASTParserWrapperForTest(const std::vector<uint32_t>& input); |
| /// Dumps SPIR-V if the conversion succeeded, then destroys the wrapper. |
| ~ASTParserWrapperForTest(); |
| |
| /// 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 ASTParser::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(resolve_); } |
| |
| /// @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 |
| auto 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 ASTParser::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); |
| } |
| |
| /// @param resolve if true, the resolver should be run on the program when its build |
| void SetResolveOnBuild(bool resolve) { resolve_ = resolve; } |
| |
| private: |
| ASTParser 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_; |
| bool resolve_ = true; |
| }; |
| |
| // Sets global state to force dumping of the assembly text of succesfully |
| // SPIR-V. |
| inline void DumpSuccessfullyConvertedSpirv() { |
| ASTParserWrapperForTest::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, VectorRef<const ast::Statement*> 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 tint::spirv::reader::ast_parser::test |
| |
| namespace tint::spirv::reader::ast_parser { |
| |
| /// SPIR-V Parser test class |
| template <typename T> |
| class SpirvASTParserTestBase : public T { |
| public: |
| SpirvASTParserTestBase() = default; |
| ~SpirvASTParserTestBase() 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::ASTParserWrapperForTest> parser(const std::vector<uint32_t>& input) { |
| auto parser = std::make_unique<test::ASTParserWrapperForTest>(input); |
| // Don't run the Resolver when building the program. |
| // We're not interested in type information with these tests. |
| parser->SetResolveOnBuild(false); |
| return parser; |
| } |
| }; |
| |
| /// SpirvASTParserTest the the base class for SPIR-V reader unit tests. |
| /// Use this form when you don't need to template any further. |
| using SpirvASTParserTest = SpirvASTParserTestBase<::testing::Test>; |
| |
| } // namespace tint::spirv::reader::ast_parser |
| |
| #endif // SRC_TINT_LANG_SPIRV_READER_AST_PARSER_HELPER_TEST_H_ |