• 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_WRITER_SPIRV_BUILDER_H_
16 #define SRC_WRITER_SPIRV_BUILDER_H_
17 
18 #include <string>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <vector>
22 
23 #include "spirv/unified1/spirv.h"
24 #include "src/ast/assignment_statement.h"
25 #include "src/ast/bitcast_expression.h"
26 #include "src/ast/break_statement.h"
27 #include "src/ast/continue_statement.h"
28 #include "src/ast/discard_statement.h"
29 #include "src/ast/if_statement.h"
30 #include "src/ast/interpolate_decoration.h"
31 #include "src/ast/loop_statement.h"
32 #include "src/ast/return_statement.h"
33 #include "src/ast/switch_statement.h"
34 #include "src/ast/unary_op_expression.h"
35 #include "src/ast/variable_decl_statement.h"
36 #include "src/program_builder.h"
37 #include "src/scope_stack.h"
38 #include "src/sem/intrinsic.h"
39 #include "src/sem/storage_texture_type.h"
40 #include "src/writer/spirv/function.h"
41 #include "src/writer/spirv/scalar_constant.h"
42 
43 namespace tint {
44 
45 // Forward declarations
46 namespace sem {
47 class Call;
48 class Reference;
49 class TypeConstructor;
50 class TypeConversion;
51 }  // namespace sem
52 
53 namespace writer {
54 namespace spirv {
55 
56 /// The result of sanitizing a program for generation.
57 struct SanitizedResult {
58   /// The sanitized program.
59   Program program;
60 };
61 
62 /// Sanitize a program in preparation for generating SPIR-V.
63 /// @param emit_vertex_point_size `true` to emit a vertex point size builtin
64 /// @param disable_workgroup_init `true` to disable workgroup memory zero
65 /// @returns the sanitized program and any supplementary information
66 SanitizedResult Sanitize(const Program* program,
67                          bool emit_vertex_point_size = false,
68                          bool disable_workgroup_init = false);
69 
70 /// Builder class to create SPIR-V instructions from a module.
71 class Builder {
72  public:
73   /// Contains information for generating accessor chains
74   struct AccessorInfo {
75     AccessorInfo();
76     ~AccessorInfo();
77 
78     /// The ID of the current chain source. The chain source may change as we
79     /// evaluate the access chain. The chain source always points to the ID
80     /// which we will use to evaluate the current set of accessors. This maybe
81     /// the original variable, or maybe an intermediary if we had to evaulate
82     /// the access chain early (in the case of a swizzle of an access chain).
83     uint32_t source_id;
84     /// The type of the current chain source. This type matches the deduced
85     /// result_type of the current source defined above.
86     const sem::Type* source_type;
87     /// A list of access chain indices to emit. Note, we _only_ have access
88     /// chain indices if the source is reference.
89     std::vector<uint32_t> access_chain_indices;
90   };
91 
92   /// Constructor
93   /// @param program the program
94   explicit Builder(const Program* program);
95   ~Builder();
96 
97   /// Generates the SPIR-V instructions for the given program
98   /// @returns true if the SPIR-V was successfully built
99   bool Build();
100 
101   /// @returns the error string or blank if no error was reported.
error()102   const std::string& error() const { return error_; }
103   /// @returns true if the builder encountered an error
has_error()104   bool has_error() const { return !error_.empty(); }
105 
106   /// @returns the number of uint32_t's needed to make up the results
107   uint32_t total_size() const;
108 
109   /// @returns the id bound for this program
id_bound()110   uint32_t id_bound() const { return next_id_; }
111 
112   /// @returns the next id to be used
next_id()113   uint32_t next_id() {
114     auto id = next_id_;
115     next_id_ += 1;
116     return id;
117   }
118 
119   /// Iterates over all the instructions in the correct order and calls the
120   /// given callback
121   /// @param cb the callback to execute
122   void iterate(std::function<void(const Instruction&)> cb) const;
123 
124   /// Adds an instruction to the list of capabilities, if the capability
125   /// hasn't already been added.
126   /// @param cap the capability to set
127   void push_capability(uint32_t cap);
128   /// @returns the capabilities
capabilities()129   const InstructionList& capabilities() const { return capabilities_; }
130   /// Adds an instruction to the extensions
131   /// @param op the op to set
132   /// @param operands the operands for the instruction
push_extension(spv::Op op,const OperandList & operands)133   void push_extension(spv::Op op, const OperandList& operands) {
134     extensions_.push_back(Instruction{op, operands});
135   }
136   /// @returns the extensions
extensions()137   const InstructionList& extensions() const { return extensions_; }
138   /// Adds an instruction to the ext import
139   /// @param op the op to set
140   /// @param operands the operands for the instruction
push_ext_import(spv::Op op,const OperandList & operands)141   void push_ext_import(spv::Op op, const OperandList& operands) {
142     ext_imports_.push_back(Instruction{op, operands});
143   }
144   /// @returns the ext imports
ext_imports()145   const InstructionList& ext_imports() const { return ext_imports_; }
146   /// Adds an instruction to the memory model
147   /// @param op the op to set
148   /// @param operands the operands for the instruction
push_memory_model(spv::Op op,const OperandList & operands)149   void push_memory_model(spv::Op op, const OperandList& operands) {
150     memory_model_.push_back(Instruction{op, operands});
151   }
152   /// @returns the memory model
memory_model()153   const InstructionList& memory_model() const { return memory_model_; }
154   /// Adds an instruction to the entry points
155   /// @param op the op to set
156   /// @param operands the operands for the instruction
push_entry_point(spv::Op op,const OperandList & operands)157   void push_entry_point(spv::Op op, const OperandList& operands) {
158     entry_points_.push_back(Instruction{op, operands});
159   }
160   /// @returns the entry points
entry_points()161   const InstructionList& entry_points() const { return entry_points_; }
162   /// Adds an instruction to the execution modes
163   /// @param op the op to set
164   /// @param operands the operands for the instruction
push_execution_mode(spv::Op op,const OperandList & operands)165   void push_execution_mode(spv::Op op, const OperandList& operands) {
166     execution_modes_.push_back(Instruction{op, operands});
167   }
168   /// @returns the execution modes
execution_modes()169   const InstructionList& execution_modes() const { return execution_modes_; }
170   /// Adds an instruction to the debug
171   /// @param op the op to set
172   /// @param operands the operands for the instruction
push_debug(spv::Op op,const OperandList & operands)173   void push_debug(spv::Op op, const OperandList& operands) {
174     debug_.push_back(Instruction{op, operands});
175   }
176   /// @returns the debug instructions
debug()177   const InstructionList& debug() const { return debug_; }
178   /// Adds an instruction to the types
179   /// @param op the op to set
180   /// @param operands the operands for the instruction
push_type(spv::Op op,const OperandList & operands)181   void push_type(spv::Op op, const OperandList& operands) {
182     types_.push_back(Instruction{op, operands});
183   }
184   /// @returns the type instructions
types()185   const InstructionList& types() const { return types_; }
186   /// Adds an instruction to the annotations
187   /// @param op the op to set
188   /// @param operands the operands for the instruction
push_annot(spv::Op op,const OperandList & operands)189   void push_annot(spv::Op op, const OperandList& operands) {
190     annotations_.push_back(Instruction{op, operands});
191   }
192   /// @returns the annotations
annots()193   const InstructionList& annots() const { return annotations_; }
194 
195   /// Adds a function to the builder
196   /// @param func the function to add
push_function(const Function & func)197   void push_function(const Function& func) {
198     functions_.push_back(func);
199     current_label_id_ = func.label_id();
200   }
201   /// @returns the functions
functions()202   const std::vector<Function>& functions() const { return functions_; }
203   /// Pushes an instruction to the current function. If we're outside
204   /// a function then issue an internal error and return false.
205   /// @param op the operation
206   /// @param operands the operands
207   /// @returns true if we succeeded
208   bool push_function_inst(spv::Op op, const OperandList& operands);
209   /// Pushes a variable to the current function
210   /// @param operands the variable operands
push_function_var(const OperandList & operands)211   void push_function_var(const OperandList& operands) {
212     if (functions_.empty()) {
213       TINT_ICE(Writer, builder_.Diagnostics())
214           << "push_function_var() called without a function";
215     }
216     functions_.back().push_var(operands);
217   }
218 
219   /// Converts a storage class to a SPIR-V storage class.
220   /// @param klass the storage class to convert
221   /// @returns the SPIR-V storage class or SpvStorageClassMax on error.
222   SpvStorageClass ConvertStorageClass(ast::StorageClass klass) const;
223   /// Converts a builtin to a SPIR-V builtin and pushes a capability if needed.
224   /// @param builtin the builtin to convert
225   /// @param storage the storage class that this builtin is being used with
226   /// @returns the SPIR-V builtin or SpvBuiltInMax on error.
227   SpvBuiltIn ConvertBuiltin(ast::Builtin builtin, ast::StorageClass storage);
228 
229   /// Converts an interpolate attribute to SPIR-V decorations and pushes a
230   /// capability if needed.
231   /// @param id the id to decorate
232   /// @param type the interpolation type
233   /// @param sampling the interpolation sampling
234   void AddInterpolationDecorations(uint32_t id,
235                                    ast::InterpolationType type,
236                                    ast::InterpolationSampling sampling);
237 
238   /// Generates a label for the given id. Emits an error and returns false if
239   /// we're currently outside a function.
240   /// @param id the id to use for the label
241   /// @returns true on success.
242   bool GenerateLabel(uint32_t id);
243   /// Generates an assignment statement
244   /// @param assign the statement to generate
245   /// @returns true if the statement was successfully generated
246   bool GenerateAssignStatement(const ast::AssignmentStatement* assign);
247   /// Generates a block statement, wrapped in a push/pop scope
248   /// @param stmt the statement to generate
249   /// @returns true if the statement was successfully generated
250   bool GenerateBlockStatement(const ast::BlockStatement* stmt);
251   /// Generates a block statement
252   /// @param stmt the statement to generate
253   /// @returns true if the statement was successfully generated
254   bool GenerateBlockStatementWithoutScoping(const ast::BlockStatement* stmt);
255   /// Generates a break statement
256   /// @param stmt the statement to generate
257   /// @returns true if the statement was successfully generated
258   bool GenerateBreakStatement(const ast::BreakStatement* stmt);
259   /// Generates a continue statement
260   /// @param stmt the statement to generate
261   /// @returns true if the statement was successfully generated
262   bool GenerateContinueStatement(const ast::ContinueStatement* stmt);
263   /// Generates a discard statement
264   /// @param stmt the statement to generate
265   /// @returns true if the statement was successfully generated
266   bool GenerateDiscardStatement(const ast::DiscardStatement* stmt);
267   /// Generates an entry point instruction
268   /// @param func the function
269   /// @param id the id of the function
270   /// @returns true if the instruction was generated, false otherwise
271   bool GenerateEntryPoint(const ast::Function* func, uint32_t id);
272   /// Generates execution modes for an entry point
273   /// @param func the function
274   /// @param id the id of the function
275   /// @returns false on failure
276   bool GenerateExecutionModes(const ast::Function* func, uint32_t id);
277   /// Generates an expression
278   /// @param expr the expression to generate
279   /// @returns the resulting ID of the expression or 0 on error
280   uint32_t GenerateExpression(const ast::Expression* expr);
281   /// Generates the instructions for a function
282   /// @param func the function to generate
283   /// @returns true if the instructions were generated
284   bool GenerateFunction(const ast::Function* func);
285   /// Generates a function type if not already created
286   /// @param func the function to generate for
287   /// @returns the ID to use for the function type. Returns 0 on failure.
288   uint32_t GenerateFunctionTypeIfNeeded(const sem::Function* func);
289   /// Generates access control annotations if needed
290   /// @param type the type to generate for
291   /// @param struct_id the struct id
292   /// @param member_idx the member index
293   void GenerateMemberAccessIfNeeded(const sem::Type* type,
294                                     uint32_t struct_id,
295                                     uint32_t member_idx);
296   /// Generates a function variable
297   /// @param var the variable
298   /// @returns true if the variable was generated
299   bool GenerateFunctionVariable(const ast::Variable* var);
300   /// Generates a global variable
301   /// @param var the variable to generate
302   /// @returns true if the variable is emited.
303   bool GenerateGlobalVariable(const ast::Variable* var);
304   /// Generates an index accessor expression.
305   ///
306   /// For more information on accessors see the "Pointer evaluation" section of
307   /// the WGSL specification.
308   ///
309   /// @param expr the expresssion to generate
310   /// @returns the id of the expression or 0 on failure
311   uint32_t GenerateAccessorExpression(const ast::Expression* expr);
312   /// Generates an index accessor
313   /// @param expr the accessor to generate
314   /// @param info the current accessor information
315   /// @returns true if the accessor was generated successfully
316   bool GenerateIndexAccessor(const ast::IndexAccessorExpression* expr,
317                              AccessorInfo* info);
318   /// Generates a member accessor
319   /// @param expr the accessor to generate
320   /// @param info the current accessor information
321   /// @returns true if the accessor was generated successfully
322   bool GenerateMemberAccessor(const ast::MemberAccessorExpression* expr,
323                               AccessorInfo* info);
324   /// Generates an identifier expression
325   /// @param expr the expresssion to generate
326   /// @returns the id of the expression or 0 on failure
327   uint32_t GenerateIdentifierExpression(const ast::IdentifierExpression* expr);
328   /// Generates a unary op expression
329   /// @param expr the expression to generate
330   /// @returns the id of the expression or 0 on failure
331   uint32_t GenerateUnaryOpExpression(const ast::UnaryOpExpression* expr);
332   /// Generates an if statement
333   /// @param stmt the statement to generate
334   /// @returns true on success
335   bool GenerateIfStatement(const ast::IfStatement* stmt);
336   /// Generates an import instruction for the "GLSL.std.450" extended
337   /// instruction set, if one doesn't exist yet, and returns the import ID.
338   /// @returns the import ID, or 0 on error.
339   uint32_t GetGLSLstd450Import();
340   /// Generates a constructor expression
341   /// @param var the variable generated for, nullptr if no variable associated.
342   /// @param expr the expression to generate
343   /// @returns the ID of the expression or 0 on failure.
344   uint32_t GenerateConstructorExpression(const ast::Variable* var,
345                                          const ast::Expression* expr);
346   /// Generates a literal constant if needed
347   /// @param var the variable generated for, nullptr if no variable associated.
348   /// @param lit the literal to generate
349   /// @returns the ID on success or 0 on failure
350   uint32_t GenerateLiteralIfNeeded(const ast::Variable* var,
351                                    const ast::LiteralExpression* lit);
352   /// Generates a binary expression
353   /// @param expr the expression to generate
354   /// @returns the expression ID on success or 0 otherwise
355   uint32_t GenerateBinaryExpression(const ast::BinaryExpression* expr);
356   /// Generates a bitcast expression
357   /// @param expr the expression to generate
358   /// @returns the expression ID on success or 0 otherwise
359   uint32_t GenerateBitcastExpression(const ast::BitcastExpression* expr);
360   /// Generates a short circuting binary expression
361   /// @param expr the expression to generate
362   /// @returns teh expression ID on success or 0 otherwise
363   uint32_t GenerateShortCircuitBinaryExpression(
364       const ast::BinaryExpression* expr);
365   /// Generates a call expression
366   /// @param expr the expression to generate
367   /// @returns the expression ID on success or 0 otherwise
368   uint32_t GenerateCallExpression(const ast::CallExpression* expr);
369   /// Handles generating a function call expression
370   /// @param call the call expression
371   /// @param function the function being called
372   /// @returns the expression ID on success or 0 otherwise
373   uint32_t GenerateFunctionCall(const sem::Call* call,
374                                 const sem::Function* function);
375   /// Handles generating an intrinsic call expression
376   /// @param call the call expression
377   /// @param intrinsic the intrinsic being called
378   /// @returns the expression ID on success or 0 otherwise
379   uint32_t GenerateIntrinsicCall(const sem::Call* call,
380                                  const sem::Intrinsic* intrinsic);
381   /// Handles generating a type constructor or type conversion expression
382   /// @param call the call expression
383   /// @param var the variable that is being initialized. May be null.
384   /// @returns the expression ID on success or 0 otherwise
385   uint32_t GenerateTypeConstructorOrConversion(const sem::Call* call,
386                                                const ast::Variable* var);
387   /// Generates a texture intrinsic call. Emits an error and returns false if
388   /// we're currently outside a function.
389   /// @param call the call expression
390   /// @param intrinsic the semantic information for the texture intrinsic
391   /// @param result_type result type operand of the texture instruction
392   /// @param result_id result identifier operand of the texture instruction
393   /// parameters
394   /// @returns true on success
395   bool GenerateTextureIntrinsic(const sem::Call* call,
396                                 const sem::Intrinsic* intrinsic,
397                                 spirv::Operand result_type,
398                                 spirv::Operand result_id);
399   /// Generates a control barrier statement.
400   /// @param intrinsic the semantic information for the barrier intrinsic call
401   /// @returns true on success
402   bool GenerateControlBarrierIntrinsic(const sem::Intrinsic* intrinsic);
403   /// Generates an atomic intrinsic call.
404   /// @param call the call expression
405   /// @param intrinsic the semantic information for the atomic intrinsic call
406   /// @param result_type result type operand of the texture instruction
407   /// @param result_id result identifier operand of the texture instruction
408   /// @returns true on success
409   bool GenerateAtomicIntrinsic(const sem::Call* call,
410                                const sem::Intrinsic* intrinsic,
411                                Operand result_type,
412                                Operand result_id);
413   /// Generates a sampled image
414   /// @param texture_type the texture type
415   /// @param texture_operand the texture operand
416   /// @param sampler_operand the sampler operand
417   /// @returns the expression ID
418   uint32_t GenerateSampledImage(const sem::Type* texture_type,
419                                 Operand texture_operand,
420                                 Operand sampler_operand);
421   /// Generates a cast or object copy for the expression result,
422   /// or return the ID generated the expression if it is already
423   /// of the right type.
424   /// @param to_type the type we're casting too
425   /// @param from_expr the expression to cast
426   /// @param is_global_init if this is a global initializer
427   /// @returns the expression ID on success or 0 otherwise
428   uint32_t GenerateCastOrCopyOrPassthrough(const sem::Type* to_type,
429                                            const ast::Expression* from_expr,
430                                            bool is_global_init);
431   /// Generates a loop statement
432   /// @param stmt the statement to generate
433   /// @returns true on successful generation
434   bool GenerateLoopStatement(const ast::LoopStatement* stmt);
435   /// Generates a return statement
436   /// @param stmt the statement to generate
437   /// @returns true on success, false otherwise
438   bool GenerateReturnStatement(const ast::ReturnStatement* stmt);
439   /// Generates a switch statement
440   /// @param stmt the statement to generate
441   /// @returns ture on success, false otherwise
442   bool GenerateSwitchStatement(const ast::SwitchStatement* stmt);
443   /// Generates a conditional section merge block
444   /// @param cond the condition
445   /// @param true_body the statements making up the true block
446   /// @param cur_else_idx the index of the current else statement to process
447   /// @param else_stmts the list of all else statements
448   /// @returns true on success, false on failure
449   bool GenerateConditionalBlock(const ast::Expression* cond,
450                                 const ast::BlockStatement* true_body,
451                                 size_t cur_else_idx,
452                                 const ast::ElseStatementList& else_stmts);
453   /// Generates a statement
454   /// @param stmt the statement to generate
455   /// @returns true if the statement was generated
456   bool GenerateStatement(const ast::Statement* stmt);
457   /// Geneates an OpLoad
458   /// @param type the type to load
459   /// @param id the variable id to load
460   /// @returns the ID of the loaded value or `id` if type is not a reference
461   uint32_t GenerateLoadIfNeeded(const sem::Type* type, uint32_t id);
462   /// Generates an OpStore. Emits an error and returns false if we're
463   /// currently outside a function.
464   /// @param to the ID to store too
465   /// @param from the ID to store from
466   /// @returns true on success
467   bool GenerateStore(uint32_t to, uint32_t from);
468   /// Generates a type if not already created
469   /// @param type the type to create
470   /// @returns the ID to use for the given type. Returns 0 on unknown type.
471   uint32_t GenerateTypeIfNeeded(const sem::Type* type);
472   /// Generates a texture type declaration
473   /// @param texture the texture to generate
474   /// @param result the result operand
475   /// @returns true if the texture was successfully generated
476   bool GenerateTextureType(const sem::Texture* texture, const Operand& result);
477   /// Generates an array type declaration
478   /// @param ary the array to generate
479   /// @param result the result operand
480   /// @returns true if the array was successfully generated
481   bool GenerateArrayType(const sem::Array* ary, const Operand& result);
482   /// Generates a matrix type declaration
483   /// @param mat the matrix to generate
484   /// @param result the result operand
485   /// @returns true if the matrix was successfully generated
486   bool GenerateMatrixType(const sem::Matrix* mat, const Operand& result);
487   /// Generates a pointer type declaration
488   /// @param ptr the pointer type to generate
489   /// @param result the result operand
490   /// @returns true if the pointer was successfully generated
491   bool GeneratePointerType(const sem::Pointer* ptr, const Operand& result);
492   /// Generates a reference type declaration
493   /// @param ref the reference type to generate
494   /// @param result the result operand
495   /// @returns true if the reference was successfully generated
496   bool GenerateReferenceType(const sem::Reference* ref, const Operand& result);
497   /// Generates a vector type declaration
498   /// @param struct_type the vector to generate
499   /// @param result the result operand
500   /// @returns true if the vector was successfully generated
501   bool GenerateStructType(const sem::Struct* struct_type,
502                           const Operand& result);
503   /// Generates a struct member
504   /// @param struct_id the id of the parent structure
505   /// @param idx the index of the member
506   /// @param member the member to generate
507   /// @returns the id of the struct member or 0 on error.
508   uint32_t GenerateStructMember(uint32_t struct_id,
509                                 uint32_t idx,
510                                 const sem::StructMember* member);
511   /// Generates a variable declaration statement
512   /// @param stmt the statement to generate
513   /// @returns true on successfull generation
514   bool GenerateVariableDeclStatement(const ast::VariableDeclStatement* stmt);
515   /// Generates a vector type declaration
516   /// @param vec the vector to generate
517   /// @param result the result operand
518   /// @returns true if the vector was successfully generated
519   bool GenerateVectorType(const sem::Vector* vec, const Operand& result);
520 
521   /// Generates instructions to splat `scalar_id` into a vector of type
522   /// `vec_type`
523   /// @param scalar_id scalar to splat
524   /// @param vec_type type of vector
525   /// @returns id of the new vector
526   uint32_t GenerateSplat(uint32_t scalar_id, const sem::Type* vec_type);
527 
528   /// Generates instructions to add or subtract two matrices
529   /// @param lhs_id id of multiplicand
530   /// @param rhs_id id of multiplier
531   /// @param type type of both matrices and of result
532   /// @param op one of `spv::Op::OpFAdd` or `spv::Op::OpFSub`
533   /// @returns id of the result matrix
534   uint32_t GenerateMatrixAddOrSub(uint32_t lhs_id,
535                                   uint32_t rhs_id,
536                                   const sem::Matrix* type,
537                                   spv::Op op);
538 
539   /// Converts AST image format to SPIR-V and pushes an appropriate capability.
540   /// @param format AST image format type
541   /// @returns SPIR-V image format type
542   SpvImageFormat convert_image_format_to_spv(const ast::ImageFormat format);
543 
544   /// Determines if the given type constructor is created from constant values
545   /// @param expr the expression to check
546   /// @returns true if the constructor is constant
547   bool IsConstructorConst(const ast::Expression* expr);
548 
549  private:
550   /// @returns an Operand with a new result ID in it. Increments the next_id_
551   /// automatically.
552   Operand result_op();
553 
554   /// @returns the resolved type of the ast::Expression `expr`
555   /// @param expr the expression
TypeOf(const ast::Expression * expr)556   const sem::Type* TypeOf(const ast::Expression* expr) const {
557     return builder_.TypeOf(expr);
558   }
559 
560   /// Generates a scalar constant if needed
561   /// @param constant the constant to generate.
562   /// @returns the ID on success or 0 on failure
563   uint32_t GenerateConstantIfNeeded(const ScalarConstant& constant);
564 
565   /// Generates a constant-null of the given type, if needed
566   /// @param type the type of the constant null to generate.
567   /// @returns the ID on success or 0 on failure
568   uint32_t GenerateConstantNullIfNeeded(const sem::Type* type);
569 
570   /// Generates a vector constant splat if needed
571   /// @param type the type of the vector to generate
572   /// @param value_id the ID of the scalar value to splat
573   /// @returns the ID on success or 0 on failure
574   uint32_t GenerateConstantVectorSplatIfNeeded(const sem::Vector* type,
575                                                uint32_t value_id);
576 
577   ProgramBuilder builder_;
578   std::string error_;
579   uint32_t next_id_ = 1;
580   uint32_t current_label_id_ = 0;
581   InstructionList capabilities_;
582   InstructionList extensions_;
583   InstructionList ext_imports_;
584   InstructionList memory_model_;
585   InstructionList entry_points_;
586   InstructionList execution_modes_;
587   InstructionList debug_;
588   InstructionList types_;
589   InstructionList annotations_;
590   std::vector<Function> functions_;
591 
592   std::unordered_map<std::string, uint32_t> import_name_to_id_;
593   std::unordered_map<Symbol, uint32_t> func_symbol_to_id_;
594   std::unordered_map<sem::CallTargetSignature, uint32_t> func_sig_to_id_;
595   std::unordered_map<std::string, uint32_t> type_name_to_id_;
596   std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
597   std::unordered_map<std::string, uint32_t> type_constructor_to_id_;
598   std::unordered_map<std::string, uint32_t> const_null_to_id_;
599   std::unordered_map<uint64_t, uint32_t> const_splat_to_id_;
600   std::unordered_map<std::string, uint32_t>
601       texture_type_name_to_sampled_image_type_id_;
602   ScopeStack<uint32_t> scope_stack_;
603   std::unordered_map<uint32_t, const ast::Variable*> spirv_id_to_variable_;
604   std::vector<uint32_t> merge_stack_;
605   std::vector<uint32_t> continue_stack_;
606   std::unordered_set<uint32_t> capability_set_;
607   bool has_overridable_workgroup_size_ = false;
608 
609   struct ContinuingInfo {
610     ContinuingInfo(const ast::Statement* last_statement,
611                    uint32_t loop_header_id,
612                    uint32_t break_target_id);
613     // The last statement in the continiung block.
614     const ast::Statement* const last_statement = nullptr;
615     // The ID of the loop header
616     const uint32_t loop_header_id = 0u;
617     // The ID of the merge block for the loop.
618     const uint32_t break_target_id = 0u;
619   };
620   // Stack of nodes, where each is the last statement in a surrounding
621   // continuing block.
622   std::vector<ContinuingInfo> continuing_stack_;
623 
624   // The instruction to emit as the backedge of a loop.
625   struct Backedge {
626     Backedge(spv::Op, OperandList);
627     Backedge(const Backedge&);
628     Backedge& operator=(const Backedge&);
629     ~Backedge();
630 
631     spv::Op opcode;
632     OperandList operands;
633   };
634   std::vector<Backedge> backedge_stack_;
635 };
636 
637 }  // namespace spirv
638 }  // namespace writer
639 }  // namespace tint
640 
641 #endif  // SRC_WRITER_SPIRV_BUILDER_H_
642