• 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_H_
16 #define SRC_READER_SPIRV_PARSER_IMPL_H_
17 
18 #include <memory>
19 #include <string>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <utility>
23 #include <vector>
24 
25 #if TINT_BUILD_SPV_READER
26 #include "source/opt/ir_context.h"
27 #endif
28 
29 #include "src/program_builder.h"
30 #include "src/reader/reader.h"
31 #include "src/reader/spirv/entry_point_info.h"
32 #include "src/reader/spirv/enum_converter.h"
33 #include "src/reader/spirv/namer.h"
34 #include "src/reader/spirv/parser_type.h"
35 #include "src/reader/spirv/usage.h"
36 
37 /// This is the implementation of the SPIR-V parser for Tint.
38 
39 /// Notes on terminology:
40 ///
41 /// A WGSL "handle" is an opaque object used for accessing a resource via
42 /// special builtins.  In SPIR-V, a handle is stored a variable in the
43 /// UniformConstant storage class.  The handles supported by SPIR-V are:
44 ///   - images, both sampled texture and storage image
45 ///   - samplers
46 ///   - combined image+sampler
47 ///   - acceleration structures for raytracing.
48 ///
49 /// WGSL only supports samplers and images, but calls images "textures".
50 /// When emitting errors, we aim to use terminology most likely to be
51 /// familiar to Vulkan SPIR-V developers.  We will tend to use "image"
52 /// and "sampler" instead of "handle".
53 
54 namespace tint {
55 namespace reader {
56 namespace spirv {
57 
58 /// The binary representation of a SPIR-V decoration enum followed by its
59 /// operands, if any.
60 /// Example:   { SpvDecorationBlock }
61 /// Example:   { SpvDecorationArrayStride, 16 }
62 using Decoration = std::vector<uint32_t>;
63 using DecorationList = std::vector<Decoration>;
64 
65 /// An AST expression with its type.
66 struct TypedExpression {
67   /// Constructor
68   TypedExpression();
69 
70   /// Copy constructor
71   TypedExpression(const TypedExpression&);
72 
73   /// Constructor
74   /// @param type_in the type of the expression
75   /// @param expr_in the expression
76   TypedExpression(const Type* type_in, const ast::Expression* expr_in);
77 
78   /// Assignment operator
79   /// @returns this TypedExpression
80   TypedExpression& operator=(const TypedExpression&);
81 
82   /// @returns true if both type and expr are not nullptr
83   operator bool() const { return type && expr; }
84 
85   /// The type
86   const Type* type = nullptr;
87   /// The expression
88   const ast::Expression* expr = nullptr;
89 };
90 
91 /// Info about the WorkgroupSize builtin.
92 struct WorkgroupSizeInfo {
93   /// Constructor
94   WorkgroupSizeInfo();
95   /// Destructor
96   ~WorkgroupSizeInfo();
97   /// The SPIR-V ID of the WorkgroupSize builtin, if any.
98   uint32_t id = 0u;
99   /// The SPIR-V type ID of the WorkgroupSize builtin, if any.
100   uint32_t type_id = 0u;
101   /// The SPIR-V type IDs of the x, y, and z components.
102   uint32_t component_type_id = 0u;
103   /// The SPIR-V IDs of the X, Y, and Z components of the workgroup size
104   /// builtin.
105   uint32_t x_id = 0u;  /// X component ID
106   uint32_t y_id = 0u;  /// Y component ID
107   uint32_t z_id = 0u;  /// Z component ID
108   /// The effective workgroup size, if this is a compute shader.
109   uint32_t x_value = 0u;  /// X workgroup size
110   uint32_t y_value = 0u;  /// Y workgroup size
111   uint32_t z_value = 0u;  /// Z workgroup size
112 };
113 
114 /// Parser implementation for SPIR-V.
115 class ParserImpl : Reader {
116  public:
117   /// Creates a new parser
118   /// @param input the input data to parse
119   explicit ParserImpl(const std::vector<uint32_t>& input);
120   /// Destructor
121   ~ParserImpl() override;
122 
123   /// Run the parser
124   /// @returns true if the parse was successful, false otherwise.
125   bool Parse() override;
126 
127   /// @returns the program. The program builder in the parser will be reset
128   /// after this.
129   Program program() override;
130 
131   /// @returns a reference to the internal builder, without building the
132   /// program. To be used only for testing.
builder()133   ProgramBuilder& builder() { return builder_; }
134 
135   /// @returns the type manager
type_manager()136   TypeManager& type_manager() { return ty_; }
137 
138   /// Logs failure, ands return a failure stream to accumulate diagnostic
139   /// messages. By convention, a failure should only be logged along with
140   /// a non-empty string diagnostic.
141   /// @returns the failure stream
Fail()142   FailStream& Fail() {
143     success_ = false;
144     return fail_stream_;
145   }
146 
147   /// @return true if failure has not yet occurred
success()148   bool success() const { return success_; }
149 
150   /// @returns the accumulated error string
error()151   const std::string error() { return errors_.str(); }
152 
153   /// Builds an internal representation of the SPIR-V binary,
154   /// and parses it into a Tint AST module.  Diagnostics are emitted
155   /// to the error stream.
156   /// @returns true if it was successful.
BuildAndParseInternalModule()157   bool BuildAndParseInternalModule() {
158     return BuildInternalModule() && ParseInternalModule();
159   }
160   /// Builds an internal representation of the SPIR-V binary,
161   /// and parses the module, except functions, into a Tint AST module.
162   /// Diagnostics are emitted to the error stream.
163   /// @returns true if it was successful.
BuildAndParseInternalModuleExceptFunctions()164   bool BuildAndParseInternalModuleExceptFunctions() {
165     return BuildInternalModule() && ParseInternalModuleExceptFunctions();
166   }
167 
168   /// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
169   /// extended instruction set.
glsl_std_450_imports()170   const std::unordered_set<uint32_t>& glsl_std_450_imports() const {
171     return glsl_std_450_imports_;
172   }
173 
174   /// Desired handling of SPIR-V pointers by ConvertType()
175   enum class PtrAs {
176     // SPIR-V pointer is converted to a spirv::Pointer
177     Ptr,
178     // SPIR-V pointer is converted to a spirv::Reference
179     Ref
180   };
181 
182   /// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
183   /// If the type is only used for builtins, then register that specially,
184   /// and return null.  If the type is a sampler, image, or sampled image, then
185   /// return the Void type, because those opaque types are handled in a
186   /// different way.
187   /// On failure, logs an error and returns null.  This should only be called
188   /// after the internal representation of the module has been built.
189   /// @param type_id the SPIR-V ID of a type.
190   /// @param ptr_as if the SPIR-V type is a pointer and ptr_as is equal to
191   /// PtrAs::Ref then a Reference will be returned, otherwise a Pointer will be
192   /// returned for a SPIR-V pointer
193   /// @returns a Tint type, or nullptr
194   const Type* ConvertType(uint32_t type_id, PtrAs ptr_as = PtrAs::Ptr);
195 
196   /// Emits an alias type declaration for array or runtime-sized array type,
197   /// when needed to distinguish between differently-decorated underlying types.
198   /// Updates the mapping of the SPIR-V type ID to the alias type.
199   /// This is a no-op if the parser has already failed.
200   /// @param type_id the SPIR-V ID for the type
201   /// @param type the type that might get an alias
202   /// @param ast_type the ast type that might get an alias
203   /// @returns an alias type or `ast_type` if no alias was created
204   const Type* MaybeGenerateAlias(uint32_t type_id,
205                                  const spvtools::opt::analysis::Type* type,
206                                  const Type* ast_type);
207 
208   /// Adds `decl` as a declared type if it hasn't been added yet.
209   /// @param name the type's unique name
210   /// @param decl the type declaration to add
211   void AddTypeDecl(Symbol name, const ast::TypeDecl* decl);
212 
213   /// @returns the fail stream object
fail_stream()214   FailStream& fail_stream() { return fail_stream_; }
215   /// @returns the namer object
namer()216   Namer& namer() { return namer_; }
217   /// @returns a borrowed pointer to the internal representation of the module.
218   /// This is null until BuildInternalModule has been called.
ir_context()219   spvtools::opt::IRContext* ir_context() { return ir_context_.get(); }
220 
221   /// Gets the list of decorations for a SPIR-V result ID.  Returns an empty
222   /// vector if the ID is not a result ID, or if no decorations target that ID.
223   /// The internal representation must have already been built.
224   /// @param id SPIR-V ID
225   /// @returns the list of decorations on the given ID
226   DecorationList GetDecorationsFor(uint32_t id) const;
227   /// Gets the list of decorations for the member of a struct.  Returns an empty
228   /// list if the `id` is not the ID of a struct, or if the member index is out
229   /// of range, or if the target member has no decorations.
230   /// The internal representation must have already been built.
231   /// @param id SPIR-V ID of a struct
232   /// @param member_index the member within the struct
233   /// @returns the list of decorations on the member
234   DecorationList GetDecorationsForMember(uint32_t id,
235                                          uint32_t member_index) const;
236 
237   /// Converts SPIR-V decorations for the variable with the given ID.
238   /// Registers the IDs of variables that require special handling by code
239   /// generation.  If the WGSL type differs from the store type for SPIR-V,
240   /// then the `type` parameter is updated.  Returns false on failure (with
241   /// a diagnostic), or when the variable should not be emitted, e.g. for a
242   /// PointSize builtin.
243   /// @param id the ID of the SPIR-V variable
244   /// @param store_type the WGSL store type for the variable, which should be
245   /// prepopulatd
246   /// @param ast_decos the decoration list to populate
247   /// @param transfer_pipeline_io true if pipeline IO decorations (builtins,
248   /// or locations) will update the store type and the decorations list
249   /// @returns false when the variable should not be emitted as a variable
250   bool ConvertDecorationsForVariable(uint32_t id,
251                                      const Type** store_type,
252                                      ast::DecorationList* ast_decos,
253                                      bool transfer_pipeline_io);
254 
255   /// Converts SPIR-V decorations for pipeline IO into AST decorations.
256   /// @param store_type the store type for the variable or member
257   /// @param decorations the SPIR-V interpolation decorations
258   /// @param ast_decos the decoration list to populate.
259   /// @returns false if conversion fails
260   bool ConvertPipelineDecorations(const Type* store_type,
261                                   const DecorationList& decorations,
262                                   ast::DecorationList* ast_decos);
263 
264   /// Updates the decoration list, placing a non-null location decoration into
265   /// the list, replacing an existing one if it exists. Does nothing if the
266   /// replacement is nullptr.
267   /// Assumes the list contains at most one Location decoration.
268   /// @param decos the decoration list to modify
269   /// @param replacement the location decoration to place into the list
270   /// @returns the location decoration that was replaced, if one was replaced,
271   /// or null otherwise.
272   const ast::Decoration* SetLocation(ast::DecorationList* decos,
273                                      const ast::Decoration* replacement);
274 
275   /// Converts a SPIR-V struct member decoration into a number of AST
276   /// decorations. If the decoration is recognized but deliberately dropped,
277   /// then returns an empty list without a diagnostic. On failure, emits a
278   /// diagnostic and returns an empty list.
279   /// @param struct_type_id the ID of the struct type
280   /// @param member_index the index of the member
281   /// @param member_ty the type of the member
282   /// @param decoration an encoded SPIR-V Decoration
283   /// @returns the AST decorations
284   ast::DecorationList ConvertMemberDecoration(uint32_t struct_type_id,
285                                               uint32_t member_index,
286                                               const Type* member_ty,
287                                               const Decoration& decoration);
288 
289   /// Returns a string for the given type.  If the type ID is invalid,
290   /// then the resulting string only names the type ID.
291   /// @param type_id the SPIR-V ID for the type
292   /// @returns a string description of the type.
293   std::string ShowType(uint32_t type_id);
294 
295   /// Builds the internal representation of the SPIR-V module.
296   /// Assumes the module is somewhat well-formed.  Normally you
297   /// would want to validate the SPIR-V module before attempting
298   /// to build this internal representation. Also computes a topological
299   /// ordering of the functions.
300   /// This is a no-op if the parser has already failed.
301   /// @returns true if the parser is still successful.
302   bool BuildInternalModule();
303 
304   /// Walks the internal representation of the module to populate
305   /// the AST form of the module.
306   /// This is a no-op if the parser has already failed.
307   /// @returns true if the parser is still successful.
308   bool ParseInternalModule();
309 
310   /// Records line numbers for each instruction.
311   void RegisterLineNumbers();
312 
313   /// Walks the internal representation of the module, except for function
314   /// definitions, to populate the AST form of the module.
315   /// This is a no-op if the parser has already failed.
316   /// @returns true if the parser is still successful.
317   bool ParseInternalModuleExceptFunctions();
318 
319   /// Destroys the internal representation of the SPIR-V module.
320   void ResetInternalModule();
321 
322   /// Registers extended instruction imports.  Only "GLSL.std.450" is supported.
323   /// This is a no-op if the parser has already failed.
324   /// @returns true if parser is still successful.
325   bool RegisterExtendedInstructionImports();
326 
327   /// Returns true when the given instruction is an extended instruction
328   /// for GLSL.std.450.
329   /// @param inst a SPIR-V instruction
330   /// @returns true if its an SpvOpExtInst for GLSL.std.450
331   bool IsGlslExtendedInstruction(const spvtools::opt::Instruction& inst) const;
332 
333   /// Returns true when the given instruction is an extended instruction
334   /// from an ignored extended instruction set.
335   /// @param inst a SPIR-V instruction
336   /// @returns true if its an SpvOpExtInst for an ignored extended instruction
337   bool IsIgnoredExtendedInstruction(
338       const spvtools::opt::Instruction& inst) const;
339 
340   /// Registers user names for SPIR-V objects, from OpName, and OpMemberName.
341   /// Also synthesizes struct field names.  Ensures uniqueness for names for
342   /// SPIR-V IDs, and uniqueness of names of fields within any single struct.
343   /// This is a no-op if the parser has already failed.
344   /// @returns true if parser is still successful.
345   bool RegisterUserAndStructMemberNames();
346 
347   /// Register the WorkgroupSize builtin and its associated constant value.
348   /// @returns true if parser is still successful.
349   bool RegisterWorkgroupSizeBuiltin();
350 
351   /// @returns the workgroup size builtin
workgroup_size_builtin()352   const WorkgroupSizeInfo& workgroup_size_builtin() {
353     return workgroup_size_builtin_;
354   }
355 
356   /// Register entry point information.
357   /// This is a no-op if the parser has already failed.
358   /// @returns true if parser is still successful.
359   bool RegisterEntryPoints();
360 
361   /// Register Tint AST types for SPIR-V types, including type aliases as
362   /// needed.  This is a no-op if the parser has already failed.
363   /// @returns true if parser is still successful.
364   bool RegisterTypes();
365 
366   /// Fail if there are any module-scope pointer values other than those
367   /// declared by OpVariable.
368   /// @returns true if parser is still successful.
369   bool RejectInvalidPointerRoots();
370 
371   /// Register sampler and texture usage for memory object declarations.
372   /// This must be called after we've registered line numbers for all
373   /// instructions. This is a no-op if the parser has already failed.
374   /// @returns true if parser is still successful.
375   bool RegisterHandleUsage();
376 
377   /// Emit const definitions for scalar specialization constants generated
378   /// by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant.
379   /// This is a no-op if the parser has already failed.
380   /// @returns true if parser is still successful.
381   bool EmitScalarSpecConstants();
382 
383   /// Emits module-scope variables.
384   /// This is a no-op if the parser has already failed.
385   /// @returns true if parser is still successful.
386   bool EmitModuleScopeVariables();
387 
388   /// Emits functions, with callees preceding their callers.
389   /// This is a no-op if the parser has already failed.
390   /// @returns true if parser is still successful.
391   bool EmitFunctions();
392 
393   /// Emits a single function, if it has a body.
394   /// This is a no-op if the parser has already failed.
395   /// @param f the function to emit
396   /// @returns true if parser is still successful.
397   bool EmitFunction(const spvtools::opt::Function& f);
398 
399   /// Returns the integer constant for the array size of the given variable.
400   /// @param var_id SPIR-V ID for an array variable
401   /// @returns the integer constant for its array size, or nullptr.
402   const spvtools::opt::analysis::IntConstant* GetArraySize(uint32_t var_id);
403 
404   /// Returns the member name for the struct member.
405   /// @param struct_type the parser's structure type.
406   /// @param member_index the member index
407   /// @returns the field name
408   std::string GetMemberName(const Struct& struct_type, int member_index);
409 
410   /// Returns the SPIR-V decorations for pipeline IO, if any, on a struct
411   /// member.
412   /// @param struct_type the parser's structure type.
413   /// @param member_index the member index
414   /// @returns a list of SPIR-V decorations.
415   DecorationList GetMemberPipelineDecorations(const Struct& struct_type,
416                                               int member_index);
417 
418   /// Creates an AST Variable node for a SPIR-V ID, including any attached
419   /// decorations, unless it's an ignorable builtin variable.
420   /// @param id the SPIR-V result ID
421   /// @param sc the storage class, which cannot be ast::StorageClass::kNone
422   /// @param storage_type the storage type of the variable
423   /// @param is_const if true, the variable is const
424   /// @param constructor the variable constructor
425   /// @param decorations the variable decorations
426   /// @returns a new Variable node, or null in the ignorable variable case and
427   /// in the error case
428   ast::Variable* MakeVariable(uint32_t id,
429                               ast::StorageClass sc,
430                               const Type* storage_type,
431                               bool is_const,
432                               const ast::Expression* constructor,
433                               ast::DecorationList decorations);
434 
435   /// Returns true if a constant expression can be generated.
436   /// @param id the SPIR-V ID of the value
437   /// @returns true if a constant expression can be generated
438   bool CanMakeConstantExpression(uint32_t id);
439 
440   /// Creates an AST expression node for a SPIR-V ID.  This is valid to call
441   /// when `CanMakeConstantExpression` returns true.
442   /// @param id the SPIR-V ID of the constant
443   /// @returns a new expression
444   TypedExpression MakeConstantExpression(uint32_t id);
445 
446   /// Creates an AST expression node for a scalar SPIR-V constant.
447   /// @param source the source location
448   /// @param ast_type the AST type for the value
449   /// @param spirv_const the internal representation of the SPIR-V constant.
450   /// @returns a new expression
451   TypedExpression MakeConstantExpressionForScalarSpirvConstant(
452       Source source,
453       const Type* ast_type,
454       const spvtools::opt::analysis::Constant* spirv_const);
455 
456   /// Creates an AST expression node for the null value for the given type.
457   /// @param type the AST type
458   /// @returns a new expression
459   const ast::Expression* MakeNullValue(const Type* type);
460 
461   /// Make a typed expression for the null value for the given type.
462   /// @param type the AST type
463   /// @returns a new typed expression
464   TypedExpression MakeNullExpression(const Type* type);
465 
466   /// Converts a given expression to the signedness demanded for an operand
467   /// of the given SPIR-V instruction, if required.  If the instruction assumes
468   /// signed integer operands, and `expr` is unsigned, then return an
469   /// as-cast expression converting it to signed. Otherwise, return
470   /// `expr` itself.  Similarly, convert as required from unsigned
471   /// to signed. Assumes all SPIR-V types have been mapped to AST types.
472   /// @param inst the SPIR-V instruction
473   /// @param expr an expression
474   /// @returns expr, or a cast of expr
475   TypedExpression RectifyOperandSignedness(
476       const spvtools::opt::Instruction& inst,
477       TypedExpression&& expr);
478 
479   /// Converts a second operand to the signedness of the first operand
480   /// of a binary operator, if the WGSL operator requires they be the same.
481   /// Returns the converted expression, or the original expression if the
482   /// conversion is not needed.
483   /// @param inst the SPIR-V instruction
484   /// @param first_operand_type the type of the first operand to the instruction
485   /// @param second_operand_expr the second operand of the instruction
486   /// @returns second_operand_expr, or a cast of it
487   TypedExpression RectifySecondOperandSignedness(
488       const spvtools::opt::Instruction& inst,
489       const Type* first_operand_type,
490       TypedExpression&& second_operand_expr);
491 
492   /// Returns the "forced" result type for the given SPIR-V instruction.
493   /// If the WGSL result type for an operation has a more strict rule than
494   /// requried by SPIR-V, then we say the result type is "forced".  This occurs
495   /// for signed integer division (OpSDiv), for example, where the result type
496   /// in WGSL must match the operand types.
497   /// @param inst the SPIR-V instruction
498   /// @param first_operand_type the AST type for the first operand.
499   /// @returns the forced AST result type, or nullptr if no forcing is required.
500   const Type* ForcedResultType(const spvtools::opt::Instruction& inst,
501                                const Type* first_operand_type);
502 
503   /// Returns a signed integer scalar or vector type matching the shape (scalar,
504   /// vector, and component bit width) of another type, which itself is a
505   /// numeric scalar or vector. Returns null if the other type does not meet the
506   /// requirement.
507   /// @param other the type whose shape must be matched
508   /// @returns the signed scalar or vector type
509   const Type* GetSignedIntMatchingShape(const Type* other);
510 
511   /// Returns a signed integer scalar or vector type matching the shape (scalar,
512   /// vector, and component bit width) of another type, which itself is a
513   /// numeric scalar or vector. Returns null if the other type does not meet the
514   /// requirement.
515   /// @param other the type whose shape must be matched
516   /// @returns the unsigned scalar or vector type
517   const Type* GetUnsignedIntMatchingShape(const Type* other);
518 
519   /// Wraps the given expression in an as-cast to the given expression's type,
520   /// when the underlying operation produces a forced result type different
521   /// from the expression's result type. Otherwise, returns the given expression
522   /// unchanged.
523   /// @param expr the expression to pass through or to wrap
524   /// @param inst the SPIR-V instruction
525   /// @param first_operand_type the AST type for the first operand.
526   /// @returns the forced AST result type, or nullptr if no forcing is required.
527   TypedExpression RectifyForcedResultType(
528       TypedExpression expr,
529       const spvtools::opt::Instruction& inst,
530       const Type* first_operand_type);
531 
532   /// Returns the given expression, but ensuring it's an unsigned type of the
533   /// same shape as the operand. Wraps the expression with a bitcast if needed.
534   /// Assumes the given expresion is a integer scalar or vector.
535   /// @param expr an integer scalar or integer vector expression.
536   /// @return the potentially cast TypedExpression
537   TypedExpression AsUnsigned(TypedExpression expr);
538 
539   /// Returns the given expression, but ensuring it's a signed type of the
540   /// same shape as the operand. Wraps the expression with a bitcast if needed.
541   /// Assumes the given expresion is a integer scalar or vector.
542   /// @param expr an integer scalar or integer vector expression.
543   /// @return the potentially cast TypedExpression
544   TypedExpression AsSigned(TypedExpression expr);
545 
546   /// Bookkeeping used for tracking the "position" builtin variable.
547   struct BuiltInPositionInfo {
548     /// The ID for the gl_PerVertex struct containing the Position builtin.
549     uint32_t struct_type_id = 0;
550     /// The member index for the Position builtin within the struct.
551     uint32_t position_member_index = 0;
552     /// The member index for the PointSize builtin within the struct.
553     uint32_t pointsize_member_index = 0;
554     /// The ID for the member type, which should map to vec4<f32>.
555     uint32_t position_member_type_id = 0;
556     /// The ID of the type of a pointer to the struct in the Output storage
557     /// class class.
558     uint32_t pointer_type_id = 0;
559     /// The SPIR-V storage class.
560     SpvStorageClass storage_class = SpvStorageClassOutput;
561     /// The ID of the type of a pointer to the Position member.
562     uint32_t position_member_pointer_type_id = 0;
563     /// The ID of the gl_PerVertex variable, if it was declared.
564     /// We'll use this for the gl_Position variable instead.
565     uint32_t per_vertex_var_id = 0;
566     /// The ID of the initializer to gl_PerVertex, if any.
567     uint32_t per_vertex_var_init_id = 0;
568   };
569   /// @returns info about the gl_Position builtin variable.
GetBuiltInPositionInfo()570   const BuiltInPositionInfo& GetBuiltInPositionInfo() {
571     return builtin_position_;
572   }
573 
574   /// Returns the source record for the SPIR-V instruction with the given
575   /// result ID.
576   /// @param id the SPIR-V result id.
577   /// @return the Source record, or a default one
578   Source GetSourceForResultIdForTest(uint32_t id) const;
579   /// Returns the source record for the given instruction.
580   /// @param inst the SPIR-V instruction
581   /// @return the Source record, or a default one
582   Source GetSourceForInst(const spvtools::opt::Instruction* inst) const;
583 
584   /// @param str a candidate identifier
585   /// @returns true if the given string is a valid WGSL identifier.
586   static bool IsValidIdentifier(const std::string& str);
587 
588   /// Returns true if the given SPIR-V ID is a declared specialization constant,
589   /// generated by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant
590   /// @param id a SPIR-V result ID
591   /// @returns true if the ID is a scalar spec constant.
IsScalarSpecConstant(uint32_t id)592   bool IsScalarSpecConstant(uint32_t id) {
593     return scalar_spec_constants_.find(id) != scalar_spec_constants_.end();
594   }
595 
596   /// For a SPIR-V ID that might define a sampler, image, or sampled image
597   /// value, return the SPIR-V instruction that represents the memory object
598   /// declaration for the object.  If we encounter an OpSampledImage along the
599   /// way, follow the image operand when follow_image is true; otherwise follow
600   /// the sampler operand. Returns nullptr if we can't trace back to a memory
601   /// object declaration.  Emits an error and returns nullptr when the scan
602   /// fails due to a malformed module. This method can be used any time after
603   /// BuildInternalModule has been invoked.
604   /// @param id the SPIR-V ID of the sampler, image, or sampled image
605   /// @param follow_image indicates whether to follow the image operand of
606   /// OpSampledImage
607   /// @returns the memory object declaration for the handle, or nullptr
608   const spvtools::opt::Instruction* GetMemoryObjectDeclarationForHandle(
609       uint32_t id,
610       bool follow_image);
611 
612   /// Returns the handle usage for a memory object declaration.
613   /// @param id SPIR-V ID of a sampler or image OpVariable or
614   /// OpFunctionParameter
615   /// @returns the handle usage, or an empty usage object.
616   Usage GetHandleUsage(uint32_t id) const;
617 
618   /// Returns the SPIR-V type for the sampler or image type for the given
619   /// variable in UniformConstant storage class, or function parameter pointing
620   /// into the UniformConstant storage class .  Returns null and emits an
621   /// error on failure.
622   /// @param var the OpVariable instruction or OpFunctionParameter
623   /// @returns the Tint AST type for the sampler or texture, or null on error
624   const spvtools::opt::Instruction*
625   GetSpirvTypeForHandleMemoryObjectDeclaration(
626       const spvtools::opt::Instruction& var);
627 
628   /// Returns the AST type for the pointer-to-sampler or pointer-to-texture type
629   /// for the given variable in UniformConstant storage class.  Returns null and
630   /// emits an error on failure.
631   /// @param var the OpVariable instruction
632   /// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
633   /// error
634   const Pointer* GetTypeForHandleVar(const spvtools::opt::Instruction& var);
635 
636   /// Returns the channel component type corresponding to the given image
637   /// format.
638   /// @param format image texel format
639   /// @returns the component type, one of f32, i32, u32
640   const Type* GetComponentTypeForFormat(ast::ImageFormat format);
641 
642   /// Returns the number of channels in the given image format.
643   /// @param format image texel format
644   /// @returns the number of channels in the format
645   unsigned GetChannelCountForFormat(ast::ImageFormat format);
646 
647   /// Returns the texel type corresponding to the given image format.
648   /// This the WGSL type used for the texel parameter to textureStore.
649   /// It's always a 4-element vector.
650   /// @param format image texel format
651   /// @returns the texel format
652   const Type* GetTexelTypeForFormat(ast::ImageFormat format);
653 
654   /// Returns the SPIR-V instruction with the given ID, or nullptr.
655   /// @param id the SPIR-V result ID
656   /// @returns the instruction, or nullptr on error
657   const spvtools::opt::Instruction* GetInstructionForTest(uint32_t id) const;
658 
659   /// A map of SPIR-V identifiers to builtins
660   using BuiltInsMap = std::unordered_map<uint32_t, SpvBuiltIn>;
661 
662   /// @returns a map of builtins that should be handled specially by code
663   /// generation. Either the builtin does not exist in WGSL, or a type
664   /// conversion must be implemented on load and store.
special_builtins()665   const BuiltInsMap& special_builtins() const { return special_builtins_; }
666 
667   /// @param builtin the SPIR-V builtin variable kind
668   /// @returns the SPIR-V ID for the variable defining the given builtin, or 0
IdForSpecialBuiltIn(SpvBuiltIn builtin)669   uint32_t IdForSpecialBuiltIn(SpvBuiltIn builtin) const {
670     // Do a linear search.
671     for (const auto& entry : special_builtins_) {
672       if (entry.second == builtin) {
673         return entry.first;
674       }
675     }
676     return 0;
677   }
678 
679   /// @param entry_point the SPIR-V ID of an entry point.
680   /// @returns the entry point info for the given ID
GetEntryPointInfo(uint32_t entry_point)681   const std::vector<EntryPointInfo>& GetEntryPointInfo(uint32_t entry_point) {
682     return function_to_ep_info_[entry_point];
683   }
684 
685   /// @returns the SPIR-V binary.
spv_binary()686   const std::vector<uint32_t>& spv_binary() { return spv_binary_; }
687 
688  private:
689   /// Converts a specific SPIR-V type to a Tint type. Integer case
690   const Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
691   /// Converts a specific SPIR-V type to a Tint type. Float case
692   const Type* ConvertType(const spvtools::opt::analysis::Float* float_ty);
693   /// Converts a specific SPIR-V type to a Tint type. Vector case
694   const Type* ConvertType(const spvtools::opt::analysis::Vector* vec_ty);
695   /// Converts a specific SPIR-V type to a Tint type. Matrix case
696   const Type* ConvertType(const spvtools::opt::analysis::Matrix* mat_ty);
697   /// Converts a specific SPIR-V type to a Tint type. RuntimeArray case
698   /// Distinct SPIR-V array types map to distinct Tint array types.
699   /// @param rtarr_ty the Tint type
700   const Type* ConvertType(
701       uint32_t type_id,
702       const spvtools::opt::analysis::RuntimeArray* rtarr_ty);
703   /// Converts a specific SPIR-V type to a Tint type. Array case
704   /// Distinct SPIR-V array types map to distinct Tint array types.
705   /// @param arr_ty the Tint type
706   const Type* ConvertType(uint32_t type_id,
707                           const spvtools::opt::analysis::Array* arr_ty);
708   /// Converts a specific SPIR-V type to a Tint type. Struct case.
709   /// SPIR-V allows distinct struct type definitions for two OpTypeStruct
710   /// that otherwise have the same set of members (and struct and member
711   /// decorations).  However, the SPIRV-Tools always produces a unique
712   /// `spvtools::opt::analysis::Struct` object in these cases. For this type
713   /// conversion, we need to have the original SPIR-V ID because we can't always
714   /// recover it from the optimizer's struct type object. This also lets us
715   /// preserve member names, which are given by OpMemberName which is normally
716   /// not significant to the optimizer's module representation.
717   /// @param type_id the SPIR-V ID for the type.
718   /// @param struct_ty the Tint type
719   const Type* ConvertType(uint32_t type_id,
720                           const spvtools::opt::analysis::Struct* struct_ty);
721   /// Converts a specific SPIR-V type to a Tint type. Pointer / Reference case
722   /// The pointer to gl_PerVertex maps to nullptr, and instead is recorded
723   /// in member #builtin_position_.
724   /// @param type_id the SPIR-V ID for the type.
725   /// @param ptr_as if PtrAs::Ref then a Reference will be returned, otherwise
726   /// Pointer
727   /// @param ptr_ty the Tint type
728   const Type* ConvertType(uint32_t type_id,
729                           PtrAs ptr_as,
730                           const spvtools::opt::analysis::Pointer* ptr_ty);
731 
732   /// If `type` is a signed integral, or vector of signed integral,
733   /// returns the unsigned type, otherwise returns `type`.
734   /// @param type the possibly signed type
735   /// @returns the unsigned type
736   const Type* UnsignedTypeFor(const Type* type);
737 
738   /// If `type` is a unsigned integral, or vector of unsigned integral,
739   /// returns the signed type, otherwise returns `type`.
740   /// @param type the possibly unsigned type
741   /// @returns the signed type
742   const Type* SignedTypeFor(const Type* type);
743 
744   /// Parses the array or runtime-array decorations. Sets 0 if no explicit
745   /// stride was found, and therefore the implicit stride should be used.
746   /// @param spv_type the SPIR-V array or runtime-array type.
747   /// @param array_stride pointer to the array stride
748   /// @returns true on success.
749   bool ParseArrayDecorations(const spvtools::opt::analysis::Type* spv_type,
750                              uint32_t* array_stride);
751 
752   /// Creates a new `ast::Node` owned by the ProgramBuilder.
753   /// @param args the arguments to pass to the type constructor
754   /// @returns the node pointer
755   template <typename T, typename... ARGS>
create(ARGS &&...args)756   T* create(ARGS&&... args) {
757     return builder_.create<T>(std::forward<ARGS>(args)...);
758   }
759 
760   // The SPIR-V binary we're parsing
761   std::vector<uint32_t> spv_binary_;
762 
763   // The program builder.
764   ProgramBuilder builder_;
765 
766   // The type manager.
767   TypeManager ty_;
768 
769   // Is the parse successful?
770   bool success_ = true;
771   // Collector for diagnostic messages.
772   std::stringstream errors_;
773   FailStream fail_stream_;
774   spvtools::MessageConsumer message_consumer_;
775 
776   // An object used to store and generate names for SPIR-V objects.
777   Namer namer_;
778   // An object used to convert SPIR-V enums to Tint enums
779   EnumConverter enum_converter_;
780 
781   // The internal representation of the SPIR-V module and its context.
782   spvtools::Context tools_context_;
783   // All the state is owned by ir_context_.
784   std::unique_ptr<spvtools::opt::IRContext> ir_context_;
785   // The following are borrowed pointers to the internal state of ir_context_.
786   spvtools::opt::Module* module_ = nullptr;
787   spvtools::opt::analysis::DefUseManager* def_use_mgr_ = nullptr;
788   spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr;
789   spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
790   spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
791 
792   // The functions ordered so that callees precede their callers.
793   std::vector<const spvtools::opt::Function*> topologically_ordered_functions_;
794 
795   // Maps an instruction to its source location. If no OpLine information
796   // is in effect for the instruction, map the instruction to its position
797   // in the SPIR-V module, counting by instructions, where the first
798   // instruction is line 1.
799   std::unordered_map<const spvtools::opt::Instruction*, Source::Location>
800       inst_source_;
801 
802   // The set of IDs that are imports of the GLSL.std.450 extended instruction
803   // sets.
804   std::unordered_set<uint32_t> glsl_std_450_imports_;
805   // The set of IDs of imports that are ignored. For example, any
806   // "NonSemanticInfo." import is ignored.
807   std::unordered_set<uint32_t> ignored_imports_;
808 
809   // The SPIR-V IDs of structure types that are the store type for buffer
810   // variables, either UBO or SSBO.
811   std::unordered_set<uint32_t> struct_types_for_buffers_;
812 
813   // Bookkeeping for the gl_Position builtin.
814   // In Vulkan SPIR-V, it's the 0 member of the gl_PerVertex structure.
815   // But in WGSL we make a module-scope variable:
816   //    [[position]] var<in> gl_Position : vec4<f32>;
817   // The builtin variable was detected if and only if the struct_id is non-zero.
818   BuiltInPositionInfo builtin_position_;
819 
820   // SPIR-V type IDs that are either:
821   // - a struct type decorated by BufferBlock
822   // - an array, runtime array containing one of these
823   // - a pointer type to one of these
824   // These are the types "enclosing" a buffer block with the old style
825   // representation: using Uniform storage class and BufferBlock decoration
826   // on the struct.  The new style is to use the StorageBuffer storage class
827   // and Block decoration.
828   std::unordered_set<uint32_t> remap_buffer_block_type_;
829 
830   // The ast::Struct type names with only read-only members.
831   std::unordered_set<Symbol> read_only_struct_types_;
832 
833   // The IDs of scalar spec constants
834   std::unordered_set<uint32_t> scalar_spec_constants_;
835 
836   // Maps function_id to a list of entrypoint information
837   std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
838       function_to_ep_info_;
839 
840   // Maps from a SPIR-V ID to its underlying memory object declaration,
841   // following image paths. This a memoization table for
842   // GetMemoryObjectDeclarationForHandle. (A SPIR-V memory object declaration is
843   // an OpVariable or an OpFunctinParameter with pointer type).
844   std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
845       mem_obj_decl_image_;
846   // Maps from a SPIR-V ID to its underlying memory object declaration,
847   // following sampler paths. This a memoization table for
848   // GetMemoryObjectDeclarationForHandle.
849   std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
850       mem_obj_decl_sampler_;
851 
852   // Maps a memory-object-declaration instruction to any sampler or texture
853   // usages implied by usages of the memory-object-declaration.
854   std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
855   // The inferred pointer type for the given handle variable.
856   std::unordered_map<const spvtools::opt::Instruction*, const Pointer*>
857       handle_type_;
858 
859   // Set of symbols of declared type that have been added, used to avoid
860   // adding duplicates.
861   std::unordered_set<Symbol> declared_types_;
862 
863   // Maps a struct type name to the SPIR-V ID for the structure type.
864   std::unordered_map<Symbol, uint32_t> struct_id_for_symbol_;
865 
866   /// Maps the SPIR-V ID of a module-scope builtin variable that should be
867   /// ignored or type-converted, to its builtin kind.
868   /// See also BuiltInPositionInfo which is a separate mechanism for a more
869   /// complex case of replacing an entire structure.
870   BuiltInsMap special_builtins_;
871 
872   /// Info about the WorkgroupSize builtin. If it's not present, then the 'id'
873   /// field will be 0. Sadly, in SPIR-V right now, there's only one workgroup
874   /// size object in the module.
875   WorkgroupSizeInfo workgroup_size_builtin_;
876 };
877 
878 }  // namespace spirv
879 }  // namespace reader
880 }  // namespace tint
881 
882 #endif  // SRC_READER_SPIRV_PARSER_IMPL_H_
883