• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SRC_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_
16 #define SRC_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_
17 
18 #include <memory>
19 #include <string>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23 
24 #if TINT_BUILD_SPV_READER
25 #include "source/opt/ir_context.h"
26 #endif
27 
28 #include "gtest/gtest.h"
29 #include "src/demangler.h"
30 #include "src/reader/spirv/fail_stream.h"
31 #include "src/reader/spirv/function.h"
32 #include "src/reader/spirv/namer.h"
33 #include "src/reader/spirv/parser_impl.h"
34 #include "src/reader/spirv/spirv_tools_helpers_test.h"
35 #include "src/reader/spirv/usage.h"
36 
37 namespace tint {
38 namespace reader {
39 namespace spirv {
40 namespace test {
41 
42 /// A test class that wraps ParseImpl
43 class ParserImplWrapperForTest {
44  public:
45   /// Constructor
46   /// @param input the input data to parse
47   explicit ParserImplWrapperForTest(const std::vector<uint32_t>& input);
48   /// Dumps SPIR-V if the conversion succeeded, then destroys the wrapper.
49   ~ParserImplWrapperForTest();
50 
51   /// Sets global state to force dumping of the assembly text of succesfully
52   /// SPIR-V.
DumpSuccessfullyConvertedSpirv()53   static void DumpSuccessfullyConvertedSpirv() {
54     dump_successfully_converted_spirv_ = true;
55   }
56   /// Marks the test has having deliberately invalid SPIR-V
DeliberatelyInvalidSpirv()57   void DeliberatelyInvalidSpirv() { skip_dumping_spirv_ = true; }
58   /// Marks the test's SPIR-V as not being suitable for dumping, for a stated
59   /// reason.
SkipDumpingPending(std::string)60   void SkipDumpingPending(std::string) { skip_dumping_spirv_ = true; }
61 
62   /// @returns a new function emitter for the given function ID.
63   /// Assumes ParserImpl::BuildInternalRepresentation has been run and
64   /// succeeded.
65   /// @param function_id the SPIR-V identifier of the function
function_emitter(uint32_t function_id)66   FunctionEmitter function_emitter(uint32_t function_id) {
67     auto* spirv_function = impl_.ir_context()->GetFunction(function_id);
68     return FunctionEmitter(&impl_, *spirv_function);
69   }
70 
71   /// Run the parser
72   /// @returns true if the parse was successful, false otherwise.
Parse()73   bool Parse() { return impl_.Parse(); }
74 
75   /// @returns the program. The program builder in the parser will be reset
76   /// after this.
program()77   Program program() { return impl_.program(); }
78 
79   /// @returns the namer object
namer()80   Namer& namer() { return impl_.namer(); }
81 
82   /// @returns a reference to the internal builder, without building the
83   /// program. To be used only for testing.
builder()84   ProgramBuilder& builder() { return impl_.builder(); }
85 
86   /// @returns the accumulated error string
error()87   const std::string error() { return impl_.error(); }
88 
89   /// @return true if failure has not yet occurred
success()90   bool success() { return impl_.success(); }
91 
92   /// Logs failure, ands return a failure stream to accumulate diagnostic
93   /// messages. By convention, a failure should only be logged along with
94   /// a non-empty string diagnostic.
95   /// @returns the failure stream
Fail()96   FailStream& Fail() { return impl_.Fail(); }
97 
98   /// @returns a borrowed pointer to the internal representation of the module.
99   /// This is null until BuildInternalModule has been called.
ir_context()100   spvtools::opt::IRContext* ir_context() { return impl_.ir_context(); }
101 
102   /// Builds the internal representation of the SPIR-V module.
103   /// Assumes the module is somewhat well-formed.  Normally you
104   /// would want to validate the SPIR-V module before attempting
105   /// to build this internal representation. Also computes a topological
106   /// ordering of the functions.
107   /// This is a no-op if the parser has already failed.
108   /// @returns true if the parser is still successful.
BuildInternalModule()109   bool BuildInternalModule() { return impl_.BuildInternalModule(); }
110 
111   /// Builds an internal representation of the SPIR-V binary,
112   /// and parses the module, except functions, into a Tint AST module.
113   /// Diagnostics are emitted to the error stream.
114   /// @returns true if it was successful.
BuildAndParseInternalModuleExceptFunctions()115   bool BuildAndParseInternalModuleExceptFunctions() {
116     return impl_.BuildAndParseInternalModuleExceptFunctions();
117   }
118 
119   /// Builds an internal representation of the SPIR-V binary,
120   /// and parses it into a Tint AST module.  Diagnostics are emitted
121   /// to the error stream.
122   /// @returns true if it was successful.
BuildAndParseInternalModule()123   bool BuildAndParseInternalModule() {
124     return impl_.BuildAndParseInternalModule();
125   }
126 
127   /// Registers user names for SPIR-V objects, from OpName, and OpMemberName.
128   /// Also synthesizes struct field names.  Ensures uniqueness for names for
129   /// SPIR-V IDs, and uniqueness of names of fields within any single struct.
130   /// This is a no-op if the parser has already failed.
131   /// @returns true if parser is still successful.
RegisterUserAndStructMemberNames()132   bool RegisterUserAndStructMemberNames() {
133     return impl_.RegisterUserAndStructMemberNames();
134   }
135 
136   /// Register Tint AST types for SPIR-V types, including type aliases as
137   /// needed.  This is a no-op if the parser has already failed.
138   /// @returns true if parser is still successful.
RegisterTypes()139   bool RegisterTypes() { return impl_.RegisterTypes(); }
140 
141   /// Register sampler and texture usage for memory object declarations.
142   /// This must be called after we've registered line numbers for all
143   /// instructions. This is a no-op if the parser has already failed.
144   /// @returns true if parser is still successful.
RegisterHandleUsage()145   bool RegisterHandleUsage() { return impl_.RegisterHandleUsage(); }
146 
147   /// Emits module-scope variables.
148   /// This is a no-op if the parser has already failed.
149   /// @returns true if parser is still successful.
EmitModuleScopeVariables()150   bool EmitModuleScopeVariables() { return impl_.EmitModuleScopeVariables(); }
151 
152   /// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
153   /// extended instruction set.
glsl_std_450_imports()154   const std::unordered_set<uint32_t>& glsl_std_450_imports() const {
155     return impl_.glsl_std_450_imports();
156   }
157 
158   /// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
159   /// If the type is only used for builtins, then register that specially,
160   /// and return null.  If the type is a sampler, image, or sampled image, then
161   /// return the Void type, because those opaque types are handled in a
162   /// different way.
163   /// On failure, logs an error and returns null.  This should only be called
164   /// after the internal representation of the module has been built.
165   /// @param id the SPIR-V ID of a type.
166   /// @returns a Tint type, or nullptr
ConvertType(uint32_t id)167   const Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); }
168 
169   /// Gets the list of decorations for a SPIR-V result ID.  Returns an empty
170   /// vector if the ID is not a result ID, or if no decorations target that ID.
171   /// The internal representation must have already been built.
172   /// @param id SPIR-V ID
173   /// @returns the list of decorations on the given ID
GetDecorationsFor(uint32_t id)174   DecorationList GetDecorationsFor(uint32_t id) const {
175     return impl_.GetDecorationsFor(id);
176   }
177 
178   /// Gets the list of decorations for the member of a struct.  Returns an empty
179   /// list if the `id` is not the ID of a struct, or if the member index is out
180   /// of range, or if the target member has no decorations.
181   /// The internal representation must have already been built.
182   /// @param id SPIR-V ID of a struct
183   /// @param member_index the member within the struct
184   /// @returns the list of decorations on the member
GetDecorationsForMember(uint32_t id,uint32_t member_index)185   DecorationList GetDecorationsForMember(uint32_t id,
186                                          uint32_t member_index) const {
187     return impl_.GetDecorationsForMember(id, member_index);
188   }
189 
190   /// Converts a SPIR-V struct member decoration into a number of AST
191   /// decorations. If the decoration is recognized but deliberately dropped,
192   /// then returns an empty list without a diagnostic. On failure, emits a
193   /// diagnostic and returns an empty list.
194   /// @param struct_type_id the ID of the struct type
195   /// @param member_index the index of the member
196   /// @param member_ty the type of the member
197   /// @param decoration an encoded SPIR-V Decoration
198   /// @returns the AST decorations
ConvertMemberDecoration(uint32_t struct_type_id,uint32_t member_index,const Type * member_ty,const Decoration & decoration)199   ast::DecorationList ConvertMemberDecoration(uint32_t struct_type_id,
200                                               uint32_t member_index,
201                                               const Type* member_ty,
202                                               const Decoration& decoration) {
203     return impl_.ConvertMemberDecoration(struct_type_id, member_index,
204                                          member_ty, decoration);
205   }
206 
207   /// For a SPIR-V ID that might define a sampler, image, or sampled image
208   /// value, return the SPIR-V instruction that represents the memory object
209   /// declaration for the object.  If we encounter an OpSampledImage along the
210   /// way, follow the image operand when follow_image is true; otherwise follow
211   /// the sampler operand. Returns nullptr if we can't trace back to a memory
212   /// object declaration.  Emits an error and returns nullptr when the scan
213   /// fails due to a malformed module. This method can be used any time after
214   /// BuildInternalModule has been invoked.
215   /// @param id the SPIR-V ID of the sampler, image, or sampled image
216   /// @param follow_image indicates whether to follow the image operand of
217   /// OpSampledImage
218   /// @returns the memory object declaration for the handle, or nullptr
GetMemoryObjectDeclarationForHandle(uint32_t id,bool follow_image)219   const spvtools::opt::Instruction* GetMemoryObjectDeclarationForHandle(
220       uint32_t id,
221       bool follow_image) {
222     return impl_.GetMemoryObjectDeclarationForHandle(id, follow_image);
223   }
224 
225   /// @param entry_point the SPIR-V ID of an entry point.
226   /// @returns the entry point info for the given ID
GetEntryPointInfo(uint32_t entry_point)227   const std::vector<EntryPointInfo>& GetEntryPointInfo(uint32_t entry_point) {
228     return impl_.GetEntryPointInfo(entry_point);
229   }
230 
231   /// Returns the handle usage for a memory object declaration.
232   /// @param id SPIR-V ID of a sampler or image OpVariable or
233   /// OpFunctionParameter
234   /// @returns the handle usage, or an empty usage object.
GetHandleUsage(uint32_t id)235   Usage GetHandleUsage(uint32_t id) const { return impl_.GetHandleUsage(id); }
236 
237   /// Returns the SPIR-V instruction with the given ID, or nullptr.
238   /// @param id the SPIR-V result ID
239   /// @returns the instruction, or nullptr on error
GetInstructionForTest(uint32_t id)240   const spvtools::opt::Instruction* GetInstructionForTest(uint32_t id) const {
241     return impl_.GetInstructionForTest(id);
242   }
243 
244   /// @returns info about the gl_Position builtin variable.
GetBuiltInPositionInfo()245   const ParserImpl::BuiltInPositionInfo& GetBuiltInPositionInfo() {
246     return impl_.GetBuiltInPositionInfo();
247   }
248 
249   /// Returns the source record for the SPIR-V instruction with the given
250   /// result ID.
251   /// @param id the SPIR-V result id.
252   /// @return the Source record, or a default one
GetSourceForResultIdForTest(uint32_t id)253   Source GetSourceForResultIdForTest(uint32_t id) const {
254     return impl_.GetSourceForResultIdForTest(id);
255   }
256 
257  private:
258   ParserImpl impl_;
259   /// When true, indicates the input SPIR-V module should not be emitted.
260   /// It's either deliberately invalid, or not supported for some pending
261   /// reason.
262   bool skip_dumping_spirv_ = false;
263   static bool dump_successfully_converted_spirv_;
264 };
265 
266 // Sets global state to force dumping of the assembly text of succesfully
267 // SPIR-V.
DumpSuccessfullyConvertedSpirv()268 inline void DumpSuccessfullyConvertedSpirv() {
269   ParserImplWrapperForTest::DumpSuccessfullyConvertedSpirv();
270 }
271 
272 /// Returns the WGSL printed string of a program.
273 /// @param program the Program
274 /// @returns the WGSL printed string the program.
275 std::string ToString(const Program& program);
276 
277 /// Returns the WGSL printed string of a statement list.
278 /// @param program the Program
279 /// @param stmts the statement list
280 /// @returns the WGSL printed string of a statement list.
281 std::string ToString(const Program& program, const ast::StatementList& stmts);
282 
283 /// Returns the WGSL printed string of an AST node.
284 /// @param program the Program
285 /// @param node the AST node
286 /// @returns the WGSL printed string of the AST node.
287 std::string ToString(const Program& program, const ast::Node* node);
288 
289 }  // namespace test
290 
291 /// SPIR-V Parser test class
292 template <typename T>
293 class SpvParserTestBase : public T {
294  public:
295   SpvParserTestBase() = default;
296   ~SpvParserTestBase() override = default;
297 
298   /// Retrieves the parser from the helper
299   /// @param input the SPIR-V binary to parse
300   /// @returns a parser for the given binary
parser(const std::vector<uint32_t> & input)301   std::unique_ptr<test::ParserImplWrapperForTest> parser(
302       const std::vector<uint32_t>& input) {
303     auto parser = std::make_unique<test::ParserImplWrapperForTest>(input);
304 
305     // Don't run the Resolver when building the program.
306     // We're not interested in type information with these tests.
307     parser->builder().SetResolveOnBuild(false);
308     return parser;
309   }
310 };
311 
312 // Use this form when you don't need to template any further.
313 using SpvParserTest = SpvParserTestBase<::testing::Test>;
314 
315 }  // namespace spirv
316 }  // namespace reader
317 }  // namespace tint
318 
319 #endif  // SRC_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_
320