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