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_FUNCTION_H_ 16 #define SRC_READER_SPIRV_FUNCTION_H_ 17 18 #include <memory> 19 #include <string> 20 #include <unordered_map> 21 #include <unordered_set> 22 #include <utility> 23 #include <vector> 24 25 #include "src/program_builder.h" 26 #include "src/reader/spirv/construct.h" 27 #include "src/reader/spirv/parser_impl.h" 28 29 namespace tint { 30 namespace reader { 31 namespace spirv { 32 33 /// Kinds of CFG edges. 34 // 35 // The edge kinds are used in many ways. 36 // 37 // For example, consider the edges leaving a basic block and going to distinct 38 // targets. If the total number of kForward + kIfBreak + kCaseFallThrough edges 39 // is more than 1, then the block must be a structured header, i.e. it needs 40 // a merge instruction to declare the control flow divergence and associated 41 // reconvergence point. Those those edge kinds count toward divergence 42 // because SPIR-v is designed to easily map back to structured control flow 43 // in GLSL (and C). In GLSL and C, those forward-flow edges don't have a 44 // special statement to express them. The other forward edges: kSwitchBreak, 45 // kLoopBreak, and kLoopContinue directly map to 'break', 'break', and 46 // 'continue', respectively. 47 enum class EdgeKind { 48 // A back-edge: An edge from a node to one of its ancestors in a depth-first 49 // search from the entry block. 50 kBack, 51 // An edge from a node to the merge block of the nearest enclosing switch, 52 // where there is no intervening loop. 53 kSwitchBreak, 54 // An edge from a node to the merge block of the nearest enclosing loop, where 55 // there is no intervening switch. 56 // The source block is a "break block" as defined by SPIR-V. 57 kLoopBreak, 58 // An edge from a node in a loop body to the associated continue target, where 59 // there are no other intervening loops or switches. 60 // The source block is a "continue block" as defined by SPIR-V. 61 kLoopContinue, 62 // An edge from a node to the merge block of the nearest enclosing structured 63 // construct, but which is neither a kSwitchBreak or a kLoopBreak. 64 // This can only occur for an "if" selection, i.e. where the selection 65 // header ends in OpBranchConditional. 66 kIfBreak, 67 // An edge from one switch case to the next sibling switch case. 68 kCaseFallThrough, 69 // None of the above. 70 kForward 71 }; 72 73 enum : uint32_t { kInvalidBlockPos = ~(0u) }; 74 75 /// Bookkeeping info for a basic block. 76 struct BlockInfo { 77 /// Constructor 78 /// @param bb internal representation of the basic block 79 explicit BlockInfo(const spvtools::opt::BasicBlock& bb); 80 ~BlockInfo(); 81 82 /// The internal representation of the basic block. 83 const spvtools::opt::BasicBlock* basic_block; 84 85 /// The ID of the OpLabel instruction that starts this block. 86 uint32_t id = 0; 87 88 /// The position of this block in the reverse structured post-order. 89 /// If the block is not in that order, then this remains the invalid value. 90 uint32_t pos = kInvalidBlockPos; 91 92 /// If this block is a header, then this is the ID of the merge block. 93 uint32_t merge_for_header = 0; 94 /// If this block is a loop header, then this is the ID of the continue 95 /// target. 96 uint32_t continue_for_header = 0; 97 /// If this block is a merge, then this is the ID of the header. 98 uint32_t header_for_merge = 0; 99 /// If this block is a continue target, then this is the ID of the loop 100 /// header. 101 uint32_t header_for_continue = 0; 102 /// Is this block a continue target which is its own loop header block? 103 /// In this case the continue construct is the entire loop. The associated 104 /// "loop construct" is empty, and not represented. 105 bool is_continue_entire_loop = false; 106 107 /// The immediately enclosing structured construct. If this block is not 108 /// in the block order at all, then this is still nullptr. 109 const Construct* construct = nullptr; 110 111 /// Maps the ID of a successor block (in the CFG) to its edge classification. 112 std::unordered_map<uint32_t, EdgeKind> succ_edge; 113 114 /// The following fields record relationships among blocks in a selection 115 /// construct for an OpSwitch instruction. 116 117 /// If not null, then the pointed-at construct is a selection for an OpSwitch, 118 /// and this block is a case target for it. We say this block "heads" the 119 /// case construct. 120 const Construct* case_head_for = nullptr; 121 /// If not null, then the pointed-at construct is a selection for an OpSwitch, 122 /// and this block is the default target for it. We say this block "heads" 123 /// the default case construct. 124 const Construct* default_head_for = nullptr; 125 /// Is this a default target for a switch, and is it also the merge for its 126 /// switch? 127 bool default_is_merge = false; 128 /// The list of switch values that cause a branch to this block. 129 std::unique_ptr<std::vector<uint64_t>> case_values; 130 131 /// The following fields record relationships among blocks in a selection 132 /// construct for an OpBranchConditional instruction. 133 134 /// When this block is an if-selection header, this is the edge kind 135 /// for the true branch. 136 EdgeKind true_kind = EdgeKind::kForward; 137 /// When this block is an if-selection header, this is the edge kind 138 /// for the false branch. 139 EdgeKind false_kind = EdgeKind::kForward; 140 /// If not 0, then this block is an if-selection header, and `true_head` is 141 /// the target id of the true branch on the OpBranchConditional, and that 142 /// target is inside the if-selection. 143 uint32_t true_head = 0; 144 /// If not 0, then this block is an if-selection header, and `false_head` 145 /// is the target id of the false branch on the OpBranchConditional, and 146 /// that target is inside the if-selection. 147 uint32_t false_head = 0; 148 /// If not 0, then this block is an if-selection header, and when following 149 /// the flow via the true and false branches, control first reconverges at 150 /// the block with ID `premerge_head`, and `premerge_head` is still inside 151 /// the if-selection. 152 uint32_t premerge_head = 0; 153 /// If non-empty, then this block is an if-selection header, and control flow 154 /// in the body must be guarded by a boolean flow variable with this name. 155 /// This occurs when a block in this selection has both an if-break edge, and 156 /// also a different normal forward edge but without a merge instruction. 157 std::string flow_guard_name = ""; 158 159 /// The result IDs that this block is responsible for declaring as a 160 /// hoisted variable. 161 /// @see DefInfo#requires_hoisted_def 162 std::vector<uint32_t> hoisted_ids; 163 164 /// A PhiAssignment represents the assignment of a value to the state 165 /// variable associated with an OpPhi in a successor block. 166 struct PhiAssignment { 167 /// The ID of an OpPhi receiving a value from this basic block. 168 uint32_t phi_id; 169 /// The the value carried to the given OpPhi. 170 uint32_t value; 171 }; 172 /// If this basic block branches to a visited basic block containing phis, 173 /// then this is the list of writes to the variables associated those phis. 174 std::vector<PhiAssignment> phi_assignments; 175 /// The IDs of OpPhi instructions which require their associated state 176 /// variable to be declared in this basic block. 177 std::vector<uint32_t> phis_needing_state_vars; 178 }; 179 180 inline std::ostream& operator<<(std::ostream& o, const BlockInfo& bi) { 181 o << "BlockInfo{" 182 << " id: " << bi.id << " pos: " << bi.pos 183 << " merge_for_header: " << bi.merge_for_header 184 << " continue_for_header: " << bi.continue_for_header 185 << " header_for_merge: " << bi.header_for_merge 186 << " is_continue_entire_loop: " << int(bi.is_continue_entire_loop) << "}"; 187 return o; 188 } 189 190 /// Reasons for avoiding generating an intermediate value. 191 enum class SkipReason { 192 /// `kDontSkip`: The value should be generated. Used for most values. 193 kDontSkip, 194 195 /// For remaining cases, the value is not generated. 196 197 /// `kOpaqueObject`: used for any intermediate value which is an sampler, 198 /// image, 199 /// or sampled image, or any pointer to such object. Code is generated 200 /// for those objects only when emitting the image instructions that access 201 /// the image (read, write, sample, gather, fetch, or query). For example, 202 /// when encountering an OpImageSampleExplicitLod, a call to the 203 /// textureSampleLevel builtin function will be emitted, and the call will 204 /// directly reference the underlying texture and sampler (variable or 205 /// function parameter). 206 kOpaqueObject, 207 208 /// `kSinkPointerIntoUse`: used to avoid emitting certain pointer expressions, 209 /// by instead generating their reference expression directly at the point of 210 /// use. For example, we apply this to OpAccessChain when indexing into a 211 /// vector, to avoid generating address-of vector component expressions. 212 kSinkPointerIntoUse, 213 214 /// `kPointSizeBuiltinPointer`: the value is a pointer to the Position builtin 215 /// variable. Don't generate its address. Avoid generating stores to this 216 /// pointer. 217 kPointSizeBuiltinPointer, 218 /// `kPointSizeBuiltinValue`: the value is the value loaded from the 219 /// PointSize builtin. Use 1.0f instead, because that's the only value 220 /// supported by WebGPU. 221 kPointSizeBuiltinValue, 222 223 /// `kSampleMaskInBuiltinPointer`: the value is a pointer to the SampleMaskIn 224 /// builtin input variable. Don't generate its address. 225 kSampleMaskInBuiltinPointer, 226 227 /// `kSampleMaskOutBuiltinPointer`: the value is a pointer to the SampleMask 228 /// builtin output variable. 229 kSampleMaskOutBuiltinPointer, 230 }; 231 232 /// Bookkeeping info for a SPIR-V ID defined in the function, or some 233 /// module-scope variables. This will be valid for result IDs that are: 234 /// - defined in the function and: 235 /// - instructions that are not OpLabel, and not OpFunctionParameter 236 /// - are defined in a basic block visited in the block-order for the 237 /// function. 238 /// - certain module-scope builtin variables. 239 struct DefInfo { 240 /// Constructor. 241 /// @param def_inst the SPIR-V instruction defining the ID 242 /// @param block_pos the position of the basic block where the ID is defined. 243 /// @param index an ordering index for this local definition 244 DefInfo(const spvtools::opt::Instruction& def_inst, 245 uint32_t block_pos, 246 size_t index); 247 /// Destructor. 248 ~DefInfo(); 249 250 /// The SPIR-V instruction that defines the ID. 251 const spvtools::opt::Instruction& inst; 252 /// The position of the first block in which this ID is visible, in function 253 /// block order. For IDs defined outside of the function, it is 0. 254 /// For IDs defined in the function, it is the position of the block 255 /// containing the definition of the ID. 256 /// See method `FunctionEmitter::ComputeBlockOrderAndPositions` 257 const uint32_t block_pos = 0; 258 259 /// An index for uniquely and deterministically ordering all DefInfo records 260 /// in a function. 261 const size_t index = 0; 262 263 /// The number of uses of this ID. 264 uint32_t num_uses = 0; 265 266 /// The block position of the last use of this ID, or 0 if it is not used 267 /// at all. The "last" ordering is determined by the function block order. 268 uint32_t last_use_pos = 0; 269 270 /// Is this value used in a construct other than the one in which it was 271 /// defined? 272 bool used_in_another_construct = false; 273 274 /// True if this ID requires a WGSL 'const' definition, due to context. It 275 /// might get one anyway (so this is *not* an if-and-only-if condition). 276 bool requires_named_const_def = false; 277 278 /// True if this ID must map to a WGSL variable declaration before the 279 /// corresponding position of the ID definition in SPIR-V. This compensates 280 /// for the difference between dominance and scoping. An SSA definition can 281 /// dominate all its uses, but the construct where it is defined does not 282 /// enclose all the uses, and so if it were declared as a WGSL constant 283 /// definition at the point of its SPIR-V definition, then the WGSL name 284 /// would go out of scope too early. Fix that by creating a variable at the 285 /// top of the smallest construct that encloses both the definition and all 286 /// its uses. Then the original SPIR-V definition maps to a WGSL assignment 287 /// to that variable, and each SPIR-V use becomes a WGSL read from the 288 /// variable. 289 /// TODO(dneto): This works for constants of storable type, but not, for 290 /// example, pointers. crbug.com/tint/98 291 bool requires_hoisted_def = false; 292 293 /// If the definition is an OpPhi, then `phi_var` is the name of the 294 /// variable that stores the value carried from parent basic blocks into 295 /// the basic block containing the OpPhi. Otherwise this is the empty string. 296 std::string phi_var; 297 298 /// The storage class to use for this value, if it is of pointer type. 299 /// This is required to carry a storage class override from a storage 300 /// buffer expressed in the old style (with Uniform storage class) 301 /// that needs to be remapped to StorageBuffer storage class. 302 /// This is kInvalid for non-pointers. 303 ast::StorageClass storage_class = ast::StorageClass::kInvalid; 304 305 /// The expression to use when sinking pointers into their use. 306 /// When encountering a use of this instruction, we will emit this expression 307 /// instead. 308 TypedExpression sink_pointer_source_expr = {}; 309 310 /// The reason, if any, that this value should be ignored. 311 /// Normally no values are ignored. This field can be updated while 312 /// generating code because sometimes we only discover necessary facts 313 /// in the middle of generating code. 314 SkipReason skip = SkipReason::kDontSkip; 315 }; 316 317 inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) { 318 o << "DefInfo{" 319 << " inst.result_id: " << di.inst.result_id() 320 << " block_pos: " << di.block_pos << " num_uses: " << di.num_uses 321 << " last_use_pos: " << di.last_use_pos << " requires_named_const_def: " 322 << (di.requires_named_const_def ? "true" : "false") 323 << " requires_hoisted_def: " << (di.requires_hoisted_def ? "true" : "false") 324 << " phi_var: '" << di.phi_var << "'"; 325 if (di.storage_class != ast::StorageClass::kNone) { 326 o << " sc:" << int(di.storage_class); 327 } 328 switch (di.skip) { 329 case SkipReason::kDontSkip: 330 break; 331 case SkipReason::kOpaqueObject: 332 o << " skip:opaque"; 333 break; 334 case SkipReason::kSinkPointerIntoUse: 335 o << " skip:sink_pointer"; 336 break; 337 case SkipReason::kPointSizeBuiltinPointer: 338 o << " skip:pointsize_pointer"; 339 break; 340 case SkipReason::kPointSizeBuiltinValue: 341 o << " skip:pointsize_value"; 342 break; 343 case SkipReason::kSampleMaskInBuiltinPointer: 344 o << " skip:samplemaskin_pointer"; 345 break; 346 case SkipReason::kSampleMaskOutBuiltinPointer: 347 o << " skip:samplemaskout_pointer"; 348 break; 349 } 350 o << "}"; 351 return o; 352 } 353 354 /// A placeholder Statement that exists for the duration of building a 355 /// StatementBlock. Once the StatementBlock is built, Build() will be called to 356 /// construct the final AST node, which will be used in the place of this 357 /// StatementBuilder. 358 /// StatementBuilders are used to simplify construction of AST nodes that will 359 /// become immutable. The builders may hold mutable state while the 360 /// StatementBlock is being constructed, which becomes an immutable node on 361 /// StatementBlock::Finalize(). 362 class StatementBuilder : public Castable<StatementBuilder, ast::Statement> { 363 public: 364 /// Constructor StatementBuilder()365 StatementBuilder() : Base(ProgramID(), Source{}) {} 366 367 /// @param builder the program builder 368 /// @returns the build AST node 369 virtual const ast::Statement* Build(ProgramBuilder* builder) const = 0; 370 371 private: 372 Node* Clone(CloneContext*) const override; 373 }; 374 375 /// A FunctionEmitter emits a SPIR-V function onto a Tint AST module. 376 class FunctionEmitter { 377 public: 378 /// Creates a FunctionEmitter, and prepares to write to the AST module 379 /// in `pi` 380 /// @param pi a ParserImpl which has already executed BuildInternalModule 381 /// @param function the function to emit 382 FunctionEmitter(ParserImpl* pi, const spvtools::opt::Function& function); 383 /// Creates a FunctionEmitter, and prepares to write to the AST module 384 /// in `pi` 385 /// @param pi a ParserImpl which has already executed BuildInternalModule 386 /// @param function the function to emit 387 /// @param ep_info entry point information for this function, or nullptr 388 FunctionEmitter(ParserImpl* pi, 389 const spvtools::opt::Function& function, 390 const EntryPointInfo* ep_info); 391 /// Move constructor. Only valid when the other object was newly created. 392 /// @param other the emitter to clone 393 FunctionEmitter(FunctionEmitter&& other); 394 /// Destructor 395 ~FunctionEmitter(); 396 397 /// Emits the function to AST module. 398 /// @return whether emission succeeded 399 bool Emit(); 400 401 /// @returns true if emission has not yet failed. success()402 bool success() const { return fail_stream_.status(); } 403 /// @returns true if emission has failed. failed()404 bool failed() const { return !success(); } 405 406 /// Finalizes any StatementBuilders returns the body of the function. 407 /// Must only be called once, and to be used only for testing. 408 /// @returns the body of the function. 409 const ast::StatementList ast_body(); 410 411 /// Records failure. 412 /// @returns a FailStream on which to emit diagnostics. Fail()413 FailStream& Fail() { return fail_stream_.Fail(); } 414 415 /// @returns the parser implementation parser()416 ParserImpl* parser() { return &parser_impl_; } 417 418 /// Emits the entry point as a wrapper around its implementation function. 419 /// Pipeline inputs become formal parameters, and pipeline outputs become 420 /// return values. 421 /// @returns false if emission failed. 422 bool EmitEntryPointAsWrapper(); 423 424 /// Creates one or more entry point input parameters corresponding to a 425 /// part of an input variable. The part of the input variable is specfied 426 /// by the `index_prefix`, which successively indexes into the variable. 427 /// Also generates the assignment statements that copy the input parameter 428 /// to the corresponding part of the variable. Assumes the variable 429 /// has already been created in the Private storage class. 430 /// @param var_name The name of the variable 431 /// @param var_type The store type of the variable 432 /// @param decos The variable's decorations 433 /// @param index_prefix Indices stepping into the variable, indicating 434 /// what part of the variable to populate. 435 /// @param tip_type The type of the component inside variable, after indexing 436 /// with the indices in `index_prefix`. 437 /// @param forced_param_type The type forced by WGSL, if the variable is a 438 /// builtin, otherwise the same as var_type. 439 /// @param params The parameter list where the new parameter is appended. 440 /// @param statements The statement list where the assignment is appended. 441 /// @returns false if emission failed 442 bool EmitPipelineInput(std::string var_name, 443 const Type* var_type, 444 ast::DecorationList* decos, 445 std::vector<int> index_prefix, 446 const Type* tip_type, 447 const Type* forced_param_type, 448 ast::VariableList* params, 449 ast::StatementList* statements); 450 451 /// Creates one or more struct members from an output variable, and the 452 /// expressions that compute the value they contribute to the entry point 453 /// return value. The part of the output variable is specfied 454 /// by the `index_prefix`, which successively indexes into the variable. 455 /// Assumes the variable has already been created in the Private storage 456 /// class. 457 /// @param var_name The name of the variable 458 /// @param var_type The store type of the variable 459 /// @param decos The variable's decorations 460 /// @param index_prefix Indices stepping into the variable, indicating 461 /// what part of the variable to populate. 462 /// @param tip_type The type of the component inside variable, after indexing 463 /// with the indices in `index_prefix`. 464 /// @param forced_member_type The type forced by WGSL, if the variable is a 465 /// builtin, otherwise the same as var_type. 466 /// @param return_members The struct member list where the new member is 467 /// added. 468 /// @param return_exprs The expression list where the return expression is 469 /// added. 470 /// @returns false if emission failed 471 bool EmitPipelineOutput(std::string var_name, 472 const Type* var_type, 473 ast::DecorationList* decos, 474 std::vector<int> index_prefix, 475 const Type* tip_type, 476 const Type* forced_member_type, 477 ast::StructMemberList* return_members, 478 ast::ExpressionList* return_exprs); 479 480 /// Updates the decoration list, replacing an existing Location decoration 481 /// with another having one higher location value. Does nothing if no 482 /// location decoration exists. 483 /// Assumes the list contains at most one Location decoration. 484 /// @param decos the decoration list to modify 485 void IncrementLocation(ast::DecorationList* decos); 486 487 /// Returns the Location dcoration, if it exists. 488 /// @param decos the list of decorations to search 489 /// @returns the Location decoration, or nullptr if it doesn't exist 490 const ast::Decoration* GetLocation(const ast::DecorationList& decos); 491 492 /// Create an ast::BlockStatement representing the body of the function. 493 /// This creates the statement stack, which is non-empty for the lifetime 494 /// of the function. 495 /// @returns the body of the function, or null on error 496 const ast::BlockStatement* MakeFunctionBody(); 497 498 /// Emits the function body, populating the bottom entry of the statements 499 /// stack. 500 /// @returns false if emission failed. 501 bool EmitBody(); 502 503 /// Records a mapping from block ID to a BlockInfo struct. 504 /// Populates `block_info_` 505 void RegisterBasicBlocks(); 506 507 /// Verifies that terminators only branch to labels in the current function. 508 /// Assumes basic blocks have been registered. 509 /// @returns true if terminators are valid 510 bool TerminatorsAreValid(); 511 512 /// Populates merge-header cross-links and BlockInfo#is_continue_entire_loop. 513 /// Also verifies that merge instructions go to blocks in the same function. 514 /// Assumes basic blocks have been registered, and terminators are valid. 515 /// @returns false if registration fails 516 bool RegisterMerges(); 517 518 /// Determines the output order for the basic blocks in the function. 519 /// Populates `block_order_` and BlockInfo#pos. 520 /// Assumes basic blocks have been registered. 521 void ComputeBlockOrderAndPositions(); 522 523 /// @returns the reverse structured post order of the basic blocks in 524 /// the function. block_order()525 const std::vector<uint32_t>& block_order() const { return block_order_; } 526 527 /// Verifies that the orderings among a structured header, continue target, 528 /// and merge block are valid. Assumes block order has been computed, and 529 /// merges are valid and recorded. 530 /// @returns false if invalid nesting was detected 531 bool VerifyHeaderContinueMergeOrder(); 532 533 /// Labels each basic block with its nearest enclosing structured construct. 534 /// Populates BlockInfo#construct and the `constructs_` list. 535 /// Assumes terminators are valid and merges have been registered, block 536 /// order has been computed, and each block is labeled with its position. 537 /// Checks nesting of structured control flow constructs. 538 /// @returns false if bad nesting has been detected 539 bool LabelControlFlowConstructs(); 540 541 /// @returns the structured constructs constructs()542 const ConstructList& constructs() const { return constructs_; } 543 544 /// Marks blocks targets of a switch, either as the head of a case or 545 /// as the default target. 546 /// @returns false on failure 547 bool FindSwitchCaseHeaders(); 548 549 /// Classifies the successor CFG edges for the ordered basic blocks. 550 /// Also checks validity of each edge (populates BlockInfo#succ_edge). 551 /// Implicitly checks dominance rules for headers and continue constructs. 552 /// Assumes each block has been labeled with its control flow construct. 553 /// @returns false on failure 554 bool ClassifyCFGEdges(); 555 556 /// Marks the blocks within a selection construct that are the first blocks 557 /// in the "then" clause, the "else" clause, and the "premerge" clause. 558 /// The head of the premerge clause is the block, if it exists, at which 559 /// control flow reconverges from the "then" and "else" clauses, but before 560 /// before the merge block for that selection. The existence of a premerge 561 /// should be an exceptional case, but is allowed by the structured control 562 /// flow rules. 563 /// @returns false if bad nesting has been detected. 564 bool FindIfSelectionInternalHeaders(); 565 566 /// Creates a DefInfo record for each module-scope builtin variable 567 /// that should be handled specially. Either it's ignored, or its store 568 /// type is converted on load. 569 /// Populates the `def_info_` mapping for such IDs. 570 /// @returns false on failure 571 bool RegisterSpecialBuiltInVariables(); 572 573 /// Creates a DefInfo record for each locally defined SPIR-V ID. 574 /// Populates the `def_info_` mapping with basic results for such IDs. 575 /// @returns false on failure 576 bool RegisterLocallyDefinedValues(); 577 578 /// Returns the Tint storage class for the given SPIR-V ID that is a 579 /// pointer value. 580 /// @param id a SPIR-V ID for a pointer value 581 /// @returns the storage class 582 ast::StorageClass GetStorageClassForPointerValue(uint32_t id); 583 584 /// Remaps the storage class for the type of a locally-defined value, 585 /// if necessary. If it's not a pointer type, or if its storage class 586 /// already matches, then the result is a copy of the `type` argument. 587 /// @param type the AST type 588 /// @param result_id the SPIR-V ID for the locally defined value 589 /// @returns an possibly updated type 590 const Type* RemapStorageClass(const Type* type, uint32_t result_id); 591 592 /// Marks locally defined values when they should get a 'const' 593 /// definition in WGSL, or a 'var' definition at an outer scope. 594 /// This occurs in several cases: 595 /// - When a SPIR-V instruction might use the dynamically computed value 596 /// only once, but the WGSL code might reference it multiple times. 597 /// For example, this occurs for the vector operands of OpVectorShuffle. 598 /// In this case the definition's DefInfo#requires_named_const_def property 599 /// is set to true. 600 /// - When a definition and at least one of its uses are not in the 601 /// same structured construct. 602 /// In this case the definition's DefInfo#requires_named_const_def property 603 /// is set to true. 604 /// - When a definition is in a construct that does not enclose all the 605 /// uses. In this case the definition's DefInfo#requires_hoisted_def 606 /// property is set to true. 607 /// Updates the `def_info_` mapping. 608 void FindValuesNeedingNamedOrHoistedDefinition(); 609 610 /// Emits declarations of function variables. 611 /// @returns false if emission failed. 612 bool EmitFunctionVariables(); 613 614 /// Emits statements in the body. 615 /// @returns false if emission failed. 616 bool EmitFunctionBodyStatements(); 617 618 /// Emits a basic block. 619 /// @param block_info the block to emit 620 /// @returns false if emission failed. 621 bool EmitBasicBlock(const BlockInfo& block_info); 622 623 /// Emits an IfStatement, including its condition expression, and sets 624 /// up the statement stack to accumulate subsequent basic blocks into 625 /// the "then" and "else" clauses. 626 /// @param block_info the if-selection header block 627 /// @returns false if emission failed. 628 bool EmitIfStart(const BlockInfo& block_info); 629 630 /// Emits a SwitchStatement, including its condition expression, and sets 631 /// up the statement stack to accumulate subsequent basic blocks into 632 /// the default clause and case clauses. 633 /// @param block_info the switch-selection header block 634 /// @returns false if emission failed. 635 bool EmitSwitchStart(const BlockInfo& block_info); 636 637 /// Emits a LoopStatement, and pushes a new StatementBlock to accumulate 638 /// the remaining instructions in the current block and subsequent blocks 639 /// in the loop. 640 /// @param construct the loop construct 641 /// @returns false if emission failed. 642 bool EmitLoopStart(const Construct* construct); 643 644 /// Emits a ContinuingStatement, and pushes a new StatementBlock to accumulate 645 /// the remaining instructions in the current block and subsequent blocks 646 /// in the continue construct. 647 /// @param construct the continue construct 648 /// @returns false if emission failed. 649 bool EmitContinuingStart(const Construct* construct); 650 651 /// Emits the non-control-flow parts of a basic block, but only once. 652 /// The `already_emitted` parameter indicates whether the code has already 653 /// been emitted, and is used to signal that this invocation actually emitted 654 /// it. 655 /// @param block_info the block to emit 656 /// @param already_emitted the block to emit 657 /// @returns false if the code had not yet been emitted, but emission failed 658 bool EmitStatementsInBasicBlock(const BlockInfo& block_info, 659 bool* already_emitted); 660 661 /// Emits code for terminators, but that aren't part of entering or 662 /// resolving structured control flow. That is, if the basic block 663 /// terminator calls for it, emit the fallthrough, break, continue, return, 664 /// or kill commands. 665 /// @param block_info the block with the terminator to emit (if any) 666 /// @returns false if emission failed 667 bool EmitNormalTerminator(const BlockInfo& block_info); 668 669 /// Returns a new statement to represent the given branch representing a 670 /// "normal" terminator, as in the sense of EmitNormalTerminator. If no 671 /// WGSL statement is required, the statement will be nullptr. This method 672 /// tries to avoid emitting a 'break' statement when that would be redundant 673 /// in WGSL due to implicit breaking out of a switch. 674 /// @param src_info the source block 675 /// @param dest_info the destination block 676 /// @returns the new statement, or a null statement MakeBranch(const BlockInfo & src_info,const BlockInfo & dest_info)677 const ast::Statement* MakeBranch(const BlockInfo& src_info, 678 const BlockInfo& dest_info) const { 679 return MakeBranchDetailed(src_info, dest_info, false, nullptr); 680 } 681 682 /// Returns a new statement to represent the given branch representing a 683 /// "normal" terminator, as in the sense of EmitNormalTerminator. If no 684 /// WGSL statement is required, the statement will be nullptr. 685 /// @param src_info the source block 686 /// @param dest_info the destination block 687 /// @returns the new statement, or a null statement MakeForcedBranch(const BlockInfo & src_info,const BlockInfo & dest_info)688 const ast::Statement* MakeForcedBranch(const BlockInfo& src_info, 689 const BlockInfo& dest_info) const { 690 return MakeBranchDetailed(src_info, dest_info, true, nullptr); 691 } 692 693 /// Returns a new statement to represent the given branch representing a 694 /// "normal" terminator, as in the sense of EmitNormalTerminator. If no 695 /// WGSL statement is required, the statement will be nullptr. When `forced` 696 /// is false, this method tries to avoid emitting a 'break' statement when 697 /// that would be redundant in WGSL due to implicit breaking out of a switch. 698 /// When `forced` is true, the method won't try to avoid emitting that break. 699 /// If the control flow edge is an if-break for an if-selection with a 700 /// control flow guard, then return that guard name via `flow_guard_name_ptr` 701 /// when that parameter is not null. 702 /// @param src_info the source block 703 /// @param dest_info the destination block 704 /// @param forced if true, always emit the branch (if it exists in WGSL) 705 /// @param flow_guard_name_ptr return parameter for control flow guard name 706 /// @returns the new statement, or a null statement 707 const ast::Statement* MakeBranchDetailed( 708 const BlockInfo& src_info, 709 const BlockInfo& dest_info, 710 bool forced, 711 std::string* flow_guard_name_ptr) const; 712 713 /// Returns a new if statement with the given statements as the then-clause 714 /// and the else-clause. Either or both clauses might be nullptr. If both 715 /// are nullptr, then don't make a new statement and instead return nullptr. 716 /// @param condition the branching condition 717 /// @param then_stmt the statement for the then clause of the if, or nullptr 718 /// @param else_stmt the statement for the else clause of the if, or nullptr 719 /// @returns the new statement, or nullptr 720 const ast::Statement* MakeSimpleIf(const ast::Expression* condition, 721 const ast::Statement* then_stmt, 722 const ast::Statement* else_stmt) const; 723 724 /// Emits the statements for an normal-terminator OpBranchConditional 725 /// where one branch is a case fall through (the true branch if and only 726 /// if `fall_through_is_true_branch` is true), and the other branch is 727 /// goes to a different destination, named by `other_dest`. 728 /// @param src_info the basic block from which we're branching 729 /// @param cond the branching condition 730 /// @param other_edge_kind the edge kind from the source block to the other 731 /// destination 732 /// @param other_dest the other branching destination 733 /// @param fall_through_is_true_branch true when the fall-through is the true 734 /// branch 735 /// @returns the false if emission fails 736 bool EmitConditionalCaseFallThrough(const BlockInfo& src_info, 737 const ast::Expression* cond, 738 EdgeKind other_edge_kind, 739 const BlockInfo& other_dest, 740 bool fall_through_is_true_branch); 741 742 /// Emits a normal instruction: not a terminator, label, or variable 743 /// declaration. 744 /// @param inst the instruction 745 /// @returns false if emission failed. 746 bool EmitStatement(const spvtools::opt::Instruction& inst); 747 748 /// Emits a const definition for the typed value in `ast_expr`, and 749 /// records it as the translation for the result ID from `inst`. 750 /// @param inst the SPIR-V instruction defining the value 751 /// @param ast_expr the already-computed AST expression for the value 752 /// @returns false if emission failed. 753 bool EmitConstDefinition(const spvtools::opt::Instruction& inst, 754 TypedExpression ast_expr); 755 756 /// Emits a write of the typed value in `ast_expr` to a hoisted variable 757 /// for the given SPIR-V ID, if that ID has a hoisted declaration. Otherwise, 758 /// emits a const definition instead. 759 /// @param inst the SPIR-V instruction defining the value 760 /// @param ast_expr the already-computed AST expression for the value 761 /// @returns false if emission failed. 762 bool EmitConstDefOrWriteToHoistedVar(const spvtools::opt::Instruction& inst, 763 TypedExpression ast_expr); 764 765 /// If the result ID of the given instruction is hoisted, then emits 766 /// a statement to write the expression to the hoisted variable, and 767 /// returns true. Otherwise return false. 768 /// @param inst the SPIR-V instruction defining a value. 769 /// @param ast_expr the expression to assign. 770 /// @returns true if the instruction has an associated hoisted variable. 771 bool WriteIfHoistedVar(const spvtools::opt::Instruction& inst, 772 TypedExpression ast_expr); 773 774 /// Makes an expression from a SPIR-V ID. 775 /// if the SPIR-V result type is a pointer. 776 /// @param id the SPIR-V ID of the value 777 /// @returns an AST expression for the instruction, or an invalid 778 /// TypedExpression on error. 779 TypedExpression MakeExpression(uint32_t id); 780 781 /// Creates an expression and supporting statements for a combinatorial 782 /// instruction, or returns null. A SPIR-V instruction is combinatorial 783 /// if it has no side effects and its result depends only on its operands, 784 /// and not on accessing external state like memory or the state of other 785 /// invocations. Statements are only created if required to provide values 786 /// to the expression. Supporting statements are not required to be 787 /// combinatorial. 788 /// @param inst a SPIR-V instruction representing an exrpression 789 /// @returns an AST expression for the instruction, or nullptr. 790 TypedExpression MaybeEmitCombinatorialValue( 791 const spvtools::opt::Instruction& inst); 792 793 /// Creates an expression and supporting statements for the a GLSL.std.450 794 /// extended instruction. 795 /// @param inst a SPIR-V OpExtInst instruction from GLSL.std.450 796 /// @returns an AST expression for the instruction, or nullptr. 797 TypedExpression EmitGlslStd450ExtInst(const spvtools::opt::Instruction& inst); 798 799 /// Creates an expression for OpCompositeExtract 800 /// @param inst an OpCompositeExtract instruction. 801 /// @returns an AST expression for the instruction, or nullptr. 802 TypedExpression MakeCompositeExtract(const spvtools::opt::Instruction& inst); 803 804 /// Creates an expression for indexing into a composite value. The literal 805 /// indices that step into the value start at instruction input operand 806 /// `start_index` and run to the end of the instruction. 807 /// @param inst the original instruction 808 /// @param composite the typed expression for the composite 809 /// @param composite_type_id the SPIR-V type ID for the composite 810 /// @param index_start the index of the first operand in `inst` that is an 811 /// index into the composite type 812 /// @returns an AST expression for the decomposed composite, or {} on error 813 TypedExpression MakeCompositeValueDecomposition( 814 const spvtools::opt::Instruction& inst, 815 TypedExpression composite, 816 uint32_t composite_type_id, 817 int index_start); 818 819 /// Creates an expression for OpVectorShuffle 820 /// @param inst an OpVectorShuffle instruction. 821 /// @returns an AST expression for the instruction, or nullptr. 822 TypedExpression MakeVectorShuffle(const spvtools::opt::Instruction& inst); 823 824 /// Creates an expression for a numeric conversion. 825 /// @param inst a numeric conversion instruction 826 /// @returns an AST expression for the instruction, or nullptr. 827 TypedExpression MakeNumericConversion(const spvtools::opt::Instruction& inst); 828 829 /// Gets the block info for a block ID, if any exists 830 /// @param id the SPIR-V ID of the OpLabel instruction starting the block 831 /// @returns the block info for the given ID, if it exists, or nullptr GetBlockInfo(uint32_t id)832 BlockInfo* GetBlockInfo(uint32_t id) const { 833 auto where = block_info_.find(id); 834 if (where == block_info_.end()) { 835 return nullptr; 836 } 837 return where->second.get(); 838 } 839 840 /// Is the block, represented by info, in the structured block order? 841 /// @param info the block 842 /// @returns true if the block is in the structured block order. IsInBlockOrder(const BlockInfo * info)843 bool IsInBlockOrder(const BlockInfo* info) const { 844 return info && info->pos != kInvalidBlockPos; 845 } 846 847 /// Gets the local definition info for a result ID. 848 /// @param id the SPIR-V ID of local definition. 849 /// @returns the definition info for the given ID, if it exists, or nullptr GetDefInfo(uint32_t id)850 DefInfo* GetDefInfo(uint32_t id) const { 851 auto where = def_info_.find(id); 852 if (where == def_info_.end()) { 853 return nullptr; 854 } 855 return where->second.get(); 856 } 857 /// Returns the skip reason for a result ID. 858 /// @param id SPIR-V result ID 859 /// @returns the skip reason for the given ID, or SkipReason::kDontSkip GetSkipReason(uint32_t id)860 SkipReason GetSkipReason(uint32_t id) const { 861 if (auto* def_info = GetDefInfo(id)) { 862 return def_info->skip; 863 } 864 return SkipReason::kDontSkip; 865 } 866 867 /// Returns the most deeply nested structured construct which encloses the 868 /// WGSL scopes of names declared in both block positions. Each position must 869 /// be a valid index into the function block order array. 870 /// @param first_pos the first block position 871 /// @param last_pos the last block position 872 /// @returns the smallest construct containing both positions 873 const Construct* GetEnclosingScope(uint32_t first_pos, 874 uint32_t last_pos) const; 875 876 /// Finds loop construct associated with a continue construct, if it exists. 877 /// Returns nullptr if: 878 /// - the given construct is not a continue construct 879 /// - the continue construct does not have an associated loop construct 880 /// (the continue target is also the loop header block) 881 /// @param c the continue construct 882 /// @returns the associated loop construct, or nullptr 883 const Construct* SiblingLoopConstruct(const Construct* c) const; 884 885 /// Returns an identifier expression for the swizzle name of the given 886 /// index into a vector. Emits an error and returns nullptr if the 887 /// index is out of range, i.e. 4 or higher. 888 /// @param i index of the subcomponent 889 /// @returns the identifier expression for the `i`'th component 890 ast::IdentifierExpression* Swizzle(uint32_t i); 891 892 /// Returns an identifier expression for the swizzle name of the first 893 /// `n` elements of a vector. Emits an error and returns nullptr if `n` 894 /// is out of range, i.e. 4 or higher. 895 /// @param n the number of components in the swizzle 896 /// @returns the swizzle identifier for the first n elements of a vector 897 ast::IdentifierExpression* PrefixSwizzle(uint32_t n); 898 899 /// Converts SPIR-V image coordinates from an image access instruction 900 /// (e.g. OpImageSampledImplicitLod) into an expression list consisting of 901 /// the texture coordinates, and an integral array index if the texture is 902 /// arrayed. The texture coordinate is a scalar for 1D textures, a vector of 903 /// 2 elements for a 2D texture, and a vector of 3 elements for a 3D or 904 /// Cube texture. Excess components are ignored, e.g. if the SPIR-V 905 /// coordinate is a 4-element vector but the image is a 2D non-arrayed 906 /// texture then the 3rd and 4th components are ignored. 907 /// On failure, issues an error and returns an empty expression list. 908 /// @param image_access the image access instruction 909 /// @returns an ExpressionList of the coordinate and array index (if any) 910 ast::ExpressionList MakeCoordinateOperandsForImageAccess( 911 const spvtools::opt::Instruction& image_access); 912 913 /// Returns the given value as an I32. If it's already an I32 then this 914 /// return the given value. Otherwise, wrap the value in a TypeConstructor 915 /// expression. 916 /// @param value the value to pass through or convert 917 /// @returns the value as an I32 value. 918 TypedExpression ToI32(TypedExpression value); 919 920 /// Returns the given value as a signed integer type of the same shape 921 /// if the value is unsigned scalar or vector, by wrapping the value 922 /// with a TypeConstructor expression. Returns the value itself if the 923 /// value otherwise. 924 /// @param value the value to pass through or convert 925 /// @returns the value itself, or converted to signed integral 926 TypedExpression ToSignedIfUnsigned(TypedExpression value); 927 928 /// @param value_id the value identifier to check 929 /// @returns true if the given SPIR-V id represents a constant float 0. 930 bool IsFloatZero(uint32_t value_id); 931 /// @param value_id the value identifier to check 932 /// @returns true if the given SPIR-V id represents a constant float 1. 933 bool IsFloatOne(uint32_t value_id); 934 935 private: 936 /// FunctionDeclaration contains the parsed information for a function header. 937 struct FunctionDeclaration { 938 /// Constructor 939 FunctionDeclaration(); 940 /// Destructor 941 ~FunctionDeclaration(); 942 943 /// Parsed header source 944 Source source; 945 /// Function name 946 std::string name; 947 /// Function parameters 948 ast::VariableList params; 949 /// Function return type 950 const Type* return_type; 951 /// Function decorations 952 ast::DecorationList decorations; 953 }; 954 955 /// Parse the function declaration, which comprises the name, parameters, and 956 /// return type, populating `decl`. 957 /// @param decl the FunctionDeclaration to populate 958 /// @returns true if emission has not yet failed. 959 bool ParseFunctionDeclaration(FunctionDeclaration* decl); 960 961 /// @returns the store type for the OpVariable instruction, or 962 /// null on failure. 963 const Type* GetVariableStoreType( 964 const spvtools::opt::Instruction& var_decl_inst); 965 966 /// Returns an expression for an instruction operand. Signedness conversion is 967 /// performed to match the result type of the SPIR-V instruction. 968 /// @param inst the SPIR-V instruction 969 /// @param operand_index the index of the operand, counting 0 as the first 970 /// input operand 971 /// @returns a new expression node 972 TypedExpression MakeOperand(const spvtools::opt::Instruction& inst, 973 uint32_t operand_index); 974 975 /// Copies a typed expression to the result, but when the type is a pointer 976 /// or reference type, ensures the storage class is not defaulted. That is, 977 /// it changes a storage class of "none" to "function". 978 /// @param expr a typed expression 979 /// @results a copy of the expression, with possibly updated type 980 TypedExpression InferFunctionStorageClass(TypedExpression expr); 981 982 /// Returns an expression for a SPIR-V OpFMod instruction. 983 /// @param inst the SPIR-V instruction 984 /// @returns an expression 985 TypedExpression MakeFMod(const spvtools::opt::Instruction& inst); 986 987 /// Returns an expression for a SPIR-V OpAccessChain or OpInBoundsAccessChain 988 /// instruction. 989 /// @param inst the SPIR-V instruction 990 /// @returns an expression 991 TypedExpression MakeAccessChain(const spvtools::opt::Instruction& inst); 992 993 /// Emits a function call. On failure, emits a diagnostic and returns false. 994 /// @param inst the SPIR-V function call instruction 995 /// @returns false if emission failed 996 bool EmitFunctionCall(const spvtools::opt::Instruction& inst); 997 998 /// Emits a control barrier intrinsic. On failure, emits a diagnostic and 999 /// returns false. 1000 /// @param inst the SPIR-V control barrier instruction 1001 /// @returns false if emission failed 1002 bool EmitControlBarrier(const spvtools::opt::Instruction& inst); 1003 1004 /// Returns an expression for a SPIR-V instruction that maps to a WGSL 1005 /// intrinsic function call. 1006 /// @param inst the SPIR-V instruction 1007 /// @returns an expression 1008 TypedExpression MakeIntrinsicCall(const spvtools::opt::Instruction& inst); 1009 1010 /// Returns an expression for a SPIR-V OpArrayLength instruction. 1011 /// @param inst the SPIR-V instruction 1012 /// @returns an expression 1013 TypedExpression MakeArrayLength(const spvtools::opt::Instruction& inst); 1014 1015 /// Generates an expression for a SPIR-V OpOuterProduct instruction. 1016 /// @param inst the SPIR-V instruction 1017 /// @returns an expression 1018 TypedExpression MakeOuterProduct(const spvtools::opt::Instruction& inst); 1019 1020 /// Generates statements for a SPIR-V OpVectorInsertDynamic instruction. 1021 /// Registers a const declaration for the result. 1022 /// @param inst the SPIR-V instruction 1023 /// @returns an expression 1024 bool MakeVectorInsertDynamic(const spvtools::opt::Instruction& inst); 1025 1026 /// Generates statements for a SPIR-V OpComposite instruction. 1027 /// Registers a const declaration for the result. 1028 /// @param inst the SPIR-V instruction 1029 /// @returns an expression 1030 bool MakeCompositeInsert(const spvtools::opt::Instruction& inst); 1031 1032 /// Get the SPIR-V instruction for the image memory object declaration for 1033 /// the image operand to the given instruction. 1034 /// @param inst the SPIR-V instruction 1035 /// @returns a SPIR-V OpVariable or OpFunctionParameter instruction, or null 1036 /// on error 1037 const spvtools::opt::Instruction* GetImage( 1038 const spvtools::opt::Instruction& inst); 1039 1040 /// Get the AST texture the SPIR-V image memory object declaration. 1041 /// @param inst the SPIR-V memory object declaration for the image. 1042 /// @returns a texture type, or null on error 1043 const Texture* GetImageType(const spvtools::opt::Instruction& inst); 1044 1045 /// Get the expression for the image operand from the first operand to the 1046 /// given instruction. 1047 /// @param inst the SPIR-V instruction 1048 /// @returns an identifier expression, or null on error 1049 const ast::Expression* GetImageExpression( 1050 const spvtools::opt::Instruction& inst); 1051 1052 /// Get the expression for the sampler operand from the first operand to the 1053 /// given instruction. 1054 /// @param inst the SPIR-V instruction 1055 /// @returns an identifier expression, or null on error 1056 const ast::Expression* GetSamplerExpression( 1057 const spvtools::opt::Instruction& inst); 1058 1059 /// Emits a texture builtin function call for a SPIR-V instruction that 1060 /// accesses an image or sampled image. 1061 /// @param inst the SPIR-V instruction 1062 /// @returns an expression 1063 bool EmitImageAccess(const spvtools::opt::Instruction& inst); 1064 1065 /// Emits statements to implement a SPIR-V image query. 1066 /// @param inst the SPIR-V instruction 1067 /// @returns an expression 1068 bool EmitImageQuery(const spvtools::opt::Instruction& inst); 1069 1070 /// Converts the given texel to match the type required for the storage 1071 /// texture with the given type. In WGSL the texel value is always provided 1072 /// as a 4-element vector, but the component type is determined by the 1073 /// texel channel type. See "Texel Formats for Storage Textures" in the WGSL 1074 /// spec. Returns an expression, or emits an error and returns nullptr. 1075 /// @param inst the image access instruction (used for diagnostics) 1076 /// @param texel the texel 1077 /// @param texture_type the type of the storage texture 1078 /// @returns the texel, after necessary conversion. 1079 const ast::Expression* ConvertTexelForStorage( 1080 const spvtools::opt::Instruction& inst, 1081 TypedExpression texel, 1082 const Texture* texture_type); 1083 1084 /// Returns an expression for an OpSelect, if its operands are scalars 1085 /// or vectors. These translate directly to WGSL select. Otherwise, return 1086 /// an expression with a null owned expression 1087 /// @param inst the SPIR-V OpSelect instruction 1088 /// @returns a typed expression, or one with a null owned expression 1089 TypedExpression MakeSimpleSelect(const spvtools::opt::Instruction& inst); 1090 1091 /// Finds the header block for a structured construct that we can "break" 1092 /// out from, from deeply nested control flow, if such a block exists. 1093 /// If the construct is: 1094 /// - a switch selection: return the selection header (ending in OpSwitch) 1095 /// - a loop construct: return the loop header block 1096 /// - a continue construct: return the loop header block 1097 /// Otherwise, return nullptr. 1098 /// @param c a structured construct, or nullptr 1099 /// @returns the block info for the structured header we can "break" from, 1100 /// or nullptr 1101 BlockInfo* HeaderIfBreakable(const Construct* c); 1102 1103 /// Appends a new statement to the top of the statement stack. 1104 /// Does nothing if the statement is null. 1105 /// @param statement the new statement 1106 /// @returns a pointer to the statement. 1107 const ast::Statement* AddStatement(const ast::Statement* statement); 1108 1109 /// AddStatementBuilder() constructs and adds the StatementBuilder of type 1110 /// `T` to the top of the statement stack. 1111 /// @param args the arguments forwarded to the T constructor 1112 /// @return the built StatementBuilder 1113 template <typename T, typename... ARGS> AddStatementBuilder(ARGS &&...args)1114 T* AddStatementBuilder(ARGS&&... args) { 1115 TINT_ASSERT(Reader, !statements_stack_.empty()); 1116 return statements_stack_.back().AddStatementBuilder<T>( 1117 std::forward<ARGS>(args)...); 1118 } 1119 1120 /// Returns the source record for the given instruction. 1121 /// @param inst the SPIR-V instruction 1122 /// @return the Source record, or a default one 1123 Source GetSourceForInst(const spvtools::opt::Instruction& inst) const; 1124 1125 /// @returns the last statetment in the top of the statement stack. 1126 const ast::Statement* LastStatement(); 1127 1128 using CompletionAction = std::function<void(const ast::StatementList&)>; 1129 1130 // A StatementBlock represents a braced-list of statements while it is being 1131 // constructed. 1132 class StatementBlock { 1133 public: 1134 StatementBlock(const Construct* construct, 1135 uint32_t end_id, 1136 CompletionAction completion_action); 1137 StatementBlock(StatementBlock&&); 1138 ~StatementBlock(); 1139 1140 StatementBlock(const StatementBlock&) = delete; 1141 StatementBlock& operator=(const StatementBlock&) = delete; 1142 1143 /// Replaces any StatementBuilders with the built result, and calls the 1144 /// completion callback (if set). Must only be called once, after all 1145 /// statements have been added with Add(). 1146 /// @param builder the program builder 1147 void Finalize(ProgramBuilder* builder); 1148 1149 /// Add() adds `statement` to the block. 1150 /// Add() must not be called after calling Finalize(). 1151 void Add(const ast::Statement* statement); 1152 1153 /// AddStatementBuilder() constructs and adds the StatementBuilder of type 1154 /// `T` to the block. 1155 /// Add() must not be called after calling Finalize(). 1156 /// @param args the arguments forwarded to the T constructor 1157 /// @return the built StatementBuilder 1158 template <typename T, typename... ARGS> AddStatementBuilder(ARGS &&...args)1159 T* AddStatementBuilder(ARGS&&... args) { 1160 auto builder = std::make_unique<T>(std::forward<ARGS>(args)...); 1161 auto* ptr = builder.get(); 1162 Add(ptr); 1163 builders_.emplace_back(std::move(builder)); 1164 return ptr; 1165 } 1166 1167 /// @param construct the construct which this construct constributes to SetConstruct(const Construct * construct)1168 void SetConstruct(const Construct* construct) { construct_ = construct; } 1169 1170 /// @return the construct to which this construct constributes GetConstruct()1171 const Construct* GetConstruct() const { return construct_; } 1172 1173 /// @return the ID of the block at which the completion action should be 1174 /// triggered and this statement block discarded. This is often the `end_id` 1175 /// of `construct` itself. GetEndId()1176 uint32_t GetEndId() const { return end_id_; } 1177 1178 /// @return the list of statements being built, if this construct is not a 1179 /// switch. GetStatements()1180 const ast::StatementList& GetStatements() const { return statements_; } 1181 1182 private: 1183 /// The construct to which this construct constributes. 1184 const Construct* construct_; 1185 /// The ID of the block at which the completion action should be triggered 1186 /// and this statement block discarded. This is often the `end_id` of 1187 /// `construct` itself. 1188 const uint32_t end_id_; 1189 /// The completion action finishes processing this statement block. 1190 FunctionEmitter::CompletionAction const completion_action_; 1191 /// The list of statements being built, if this construct is not a switch. 1192 ast::StatementList statements_; 1193 1194 /// Owned statement builders 1195 std::vector<std::unique_ptr<StatementBuilder>> builders_; 1196 /// True if Finalize() has been called. 1197 bool finalized_ = false; 1198 }; 1199 1200 /// Pushes an empty statement block onto the statements stack. 1201 /// @param action the completion action for this block 1202 void PushNewStatementBlock(const Construct* construct, 1203 uint32_t end_id, 1204 CompletionAction action); 1205 1206 /// Emits an if-statement whose condition is the given flow guard 1207 /// variable, and pushes onto the statement stack the corresponding 1208 /// statement block ending (and not including) the given block. 1209 /// @param flow_guard name of the flow guard variable 1210 /// @param end_id first block after the if construct. 1211 void PushGuard(const std::string& flow_guard, uint32_t end_id); 1212 1213 /// Emits an if-statement with 'true' condition, and pushes onto the 1214 /// statement stack the corresponding statement block ending (and not 1215 /// including) the given block. 1216 /// @param end_id first block after the if construct. 1217 void PushTrueGuard(uint32_t end_id); 1218 1219 /// @returns a boolean true expression. 1220 const ast::Expression* MakeTrue(const Source&) const; 1221 1222 /// @returns a boolean false expression. 1223 const ast::Expression* MakeFalse(const Source&) const; 1224 1225 /// @param expr the expression to take the address of 1226 /// @returns a TypedExpression that is the address-of `expr` (`&expr`) 1227 /// @note `expr` must be a reference type 1228 TypedExpression AddressOf(TypedExpression expr); 1229 1230 /// Returns AddressOf(expr) if expr is has reference type and 1231 /// the instruction has a pointer result type. Otherwise returns expr. 1232 /// @param expr the expression to take the address of 1233 /// @returns a TypedExpression that is the address-of `expr` (`&expr`) 1234 /// @note `expr` must be a reference type 1235 TypedExpression AddressOfIfNeeded(TypedExpression expr, 1236 const spvtools::opt::Instruction* inst); 1237 1238 /// @param expr the expression to dereference 1239 /// @returns a TypedExpression that is the dereference-of `expr` (`*expr`) 1240 /// @note `expr` must be a pointer type 1241 TypedExpression Dereference(TypedExpression expr); 1242 1243 /// Creates a new `ast::Node` owned by the ProgramBuilder. 1244 /// @param args the arguments to pass to the type constructor 1245 /// @returns the node pointer 1246 template <typename T, typename... ARGS> create(ARGS &&...args)1247 T* create(ARGS&&... args) const { 1248 return builder_.create<T>(std::forward<ARGS>(args)...); 1249 } 1250 1251 using StatementsStack = std::vector<StatementBlock>; 1252 using PtrAs = ParserImpl::PtrAs; 1253 1254 ParserImpl& parser_impl_; 1255 TypeManager& ty_; 1256 ProgramBuilder& builder_; 1257 spvtools::opt::IRContext& ir_context_; 1258 spvtools::opt::analysis::DefUseManager* def_use_mgr_; 1259 spvtools::opt::analysis::ConstantManager* constant_mgr_; 1260 spvtools::opt::analysis::TypeManager* type_mgr_; 1261 FailStream& fail_stream_; 1262 Namer& namer_; 1263 const spvtools::opt::Function& function_; 1264 1265 // The SPIR-V ID for the SampleMask input variable. 1266 uint32_t sample_mask_in_id; 1267 // The SPIR-V ID for the SampleMask output variable. 1268 uint32_t sample_mask_out_id; 1269 1270 // A stack of statement lists. Each list is contained in a construct in 1271 // the next deeper element of stack. The 0th entry represents the statements 1272 // for the entire function. This stack is never empty. 1273 // The `construct` member for the 0th element is only valid during the 1274 // lifetime of the EmitFunctionBodyStatements method. 1275 StatementsStack statements_stack_; 1276 1277 // The map of IDs that have already had an identifier name generated for it, 1278 // to their Type. 1279 std::unordered_map<uint32_t, const Type*> identifier_types_; 1280 // Mapping from SPIR-V ID that is used at most once, to its AST expression. 1281 std::unordered_map<uint32_t, TypedExpression> singly_used_values_; 1282 1283 // The IDs of basic blocks, in reverse structured post-order (RSPO). 1284 // This is the output order for the basic blocks. 1285 std::vector<uint32_t> block_order_; 1286 1287 // Mapping from block ID to its bookkeeping info. 1288 std::unordered_map<uint32_t, std::unique_ptr<BlockInfo>> block_info_; 1289 1290 // Mapping from a locally-defined result ID to its bookkeeping info. 1291 std::unordered_map<uint32_t, std::unique_ptr<DefInfo>> def_info_; 1292 1293 // Structured constructs, where enclosing constructs precede their children. 1294 ConstructList constructs_; 1295 1296 // Information about entry point, if this function is referenced by one 1297 const EntryPointInfo* ep_info_ = nullptr; 1298 }; 1299 1300 } // namespace spirv 1301 } // namespace reader 1302 } // namespace tint 1303 1304 #endif // SRC_READER_SPIRV_FUNCTION_H_ 1305