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