• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SRC_READER_SPIRV_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