• 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_MSL_GENERATOR_IMPL_H_
16 #define SRC_WRITER_MSL_GENERATOR_IMPL_H_
17 
18 #include <string>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <vector>
22 
23 #include "src/ast/assignment_statement.h"
24 #include "src/ast/binary_expression.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/expression.h"
30 #include "src/ast/if_statement.h"
31 #include "src/ast/index_accessor_expression.h"
32 #include "src/ast/interpolate_decoration.h"
33 #include "src/ast/loop_statement.h"
34 #include "src/ast/member_accessor_expression.h"
35 #include "src/ast/return_statement.h"
36 #include "src/ast/switch_statement.h"
37 #include "src/ast/unary_op_expression.h"
38 #include "src/program.h"
39 #include "src/scope_stack.h"
40 #include "src/sem/struct.h"
41 #include "src/writer/array_length_from_uniform_options.h"
42 #include "src/writer/text_generator.h"
43 
44 namespace tint {
45 
46 // Forward declarations
47 namespace sem {
48 class Call;
49 class Intrinsic;
50 class TypeConstructor;
51 class TypeConversion;
52 }  // namespace sem
53 
54 namespace writer {
55 namespace msl {
56 
57 /// The result of sanitizing a program for generation.
58 struct SanitizedResult {
59   /// Constructor
60   SanitizedResult();
61   /// Destructor
62   ~SanitizedResult();
63   /// Move constructor
64   SanitizedResult(SanitizedResult&&);
65 
66   /// The sanitized program.
67   Program program;
68   /// True if the shader needs a UBO of buffer sizes.
69   bool needs_storage_buffer_sizes = false;
70   /// Indices into the array_length_from_uniform binding that are statically
71   /// used.
72   std::unordered_set<uint32_t> used_array_length_from_uniform_indices;
73 };
74 
75 /// Sanitize a program in preparation for generating MSL.
76 /// @param buffer_size_ubo_index the index to use for the buffer size UBO
77 /// @param fixed_sample_mask the fixed sample mask to use for fragment shaders
78 /// @param emit_vertex_point_size `true` to emit a vertex point size builtin
79 /// @param disable_workgroup_init `true` to disable workgroup memory zero
80 /// @returns the sanitized program and any supplementary information
81 SanitizedResult Sanitize(
82     const Program* program,
83     uint32_t buffer_size_ubo_index,
84     uint32_t fixed_sample_mask = 0xFFFFFFFF,
85     bool emit_vertex_point_size = false,
86     bool disable_workgroup_init = false,
87     const ArrayLengthFromUniformOptions& array_length_from_uniform = {});
88 
89 /// Implementation class for MSL generator
90 class GeneratorImpl : public TextGenerator {
91  public:
92   /// Constructor
93   /// @param program the program to generate
94   explicit GeneratorImpl(const Program* program);
95   ~GeneratorImpl();
96 
97   /// @returns true on successful generation; false otherwise
98   bool Generate();
99 
100   /// @returns true if an invariant attribute was generated
HasInvariant()101   bool HasInvariant() { return !invariant_define_name_.empty(); }
102 
103   /// @returns a map from entry point to list of required workgroup allocations
104   const std::unordered_map<std::string, std::vector<uint32_t>>&
DynamicWorkgroupAllocations()105   DynamicWorkgroupAllocations() const {
106     return workgroup_allocations_;
107   }
108 
109   /// Handles generating a declared type
110   /// @param ty the declared type to generate
111   /// @returns true if the declared type was emitted
112   bool EmitTypeDecl(const sem::Type* ty);
113   /// Handles an index accessor expression
114   /// @param out the output of the expression stream
115   /// @param expr the expression to emit
116   /// @returns true if the index accessor was emitted
117   bool EmitIndexAccessor(std::ostream& out,
118                          const ast::IndexAccessorExpression* expr);
119   /// Handles an assignment statement
120   /// @param stmt the statement to emit
121   /// @returns true if the statement was emitted successfully
122   bool EmitAssign(const ast::AssignmentStatement* stmt);
123   /// Handles generating a binary expression
124   /// @param out the output of the expression stream
125   /// @param expr the binary expression
126   /// @returns true if the expression was emitted, false otherwise
127   bool EmitBinary(std::ostream& out, const ast::BinaryExpression* expr);
128   /// Handles generating a bitcast expression
129   /// @param out the output of the expression stream
130   /// @param expr the bitcast expression
131   /// @returns true if the bitcast was emitted
132   bool EmitBitcast(std::ostream& out, const ast::BitcastExpression* expr);
133   /// Handles a block statement
134   /// @param stmt the statement to emit
135   /// @returns true if the statement was emitted successfully
136   bool EmitBlock(const ast::BlockStatement* stmt);
137   /// Handles a break statement
138   /// @param stmt the statement to emit
139   /// @returns true if the statement was emitted successfully
140   bool EmitBreak(const ast::BreakStatement* stmt);
141   /// Handles generating a call expression
142   /// @param out the output of the expression stream
143   /// @param expr the call expression
144   /// @returns true if the call expression is emitted
145   bool EmitCall(std::ostream& out, const ast::CallExpression* expr);
146   /// Handles generating an intrinsic call expression
147   /// @param out the output of the expression stream
148   /// @param call the call expression
149   /// @param intrinsic the intrinsic being called
150   /// @returns true if the call expression is emitted
151   bool EmitIntrinsicCall(std::ostream& out,
152                          const sem::Call* call,
153                          const sem::Intrinsic* intrinsic);
154   /// Handles generating a type conversion expression
155   /// @param out the output of the expression stream
156   /// @param call the call expression
157   /// @param conv the type conversion
158   /// @returns true if the expression is emitted
159   bool EmitTypeConversion(std::ostream& out,
160                           const sem::Call* call,
161                           const sem::TypeConversion* conv);
162   /// Handles generating a type constructor
163   /// @param out the output of the expression stream
164   /// @param call the call expression
165   /// @param ctor the type constructor
166   /// @returns true if the constructor is emitted
167   bool EmitTypeConstructor(std::ostream& out,
168                            const sem::Call* call,
169                            const sem::TypeConstructor* ctor);
170   /// Handles generating a function call
171   /// @param out the output of the expression stream
172   /// @param call the call expression
173   /// @param func the target function
174   /// @returns true if the call is emitted
175   bool EmitFunctionCall(std::ostream& out,
176                         const sem::Call* call,
177                         const sem::Function* func);
178   /// Handles generating a call to an atomic function (`atomicAdd`,
179   /// `atomicMax`, etc)
180   /// @param out the output of the expression stream
181   /// @param expr the call expression
182   /// @param intrinsic the semantic information for the atomic intrinsic
183   /// @returns true if the call expression is emitted
184   bool EmitAtomicCall(std::ostream& out,
185                       const ast::CallExpression* expr,
186                       const sem::Intrinsic* intrinsic);
187   /// Handles generating a call to a texture function (`textureSample`,
188   /// `textureSampleGrad`, etc)
189   /// @param out the output of the expression stream
190   /// @param call the call expression
191   /// @param intrinsic the semantic information for the texture intrinsic
192   /// @returns true if the call expression is emitted
193   bool EmitTextureCall(std::ostream& out,
194                        const sem::Call* call,
195                        const sem::Intrinsic* intrinsic);
196   /// Handles generating a call to the `dot()` intrinsic
197   /// @param out the output of the expression stream
198   /// @param expr the call expression
199   /// @param intrinsic the semantic information for the intrinsic
200   /// @returns true if the call expression is emitted
201   bool EmitDotCall(std::ostream& out,
202                    const ast::CallExpression* expr,
203                    const sem::Intrinsic* intrinsic);
204   /// Handles generating a call to the `modf()` intrinsic
205   /// @param out the output of the expression stream
206   /// @param expr the call expression
207   /// @param intrinsic the semantic information for the intrinsic
208   /// @returns true if the call expression is emitted
209   bool EmitModfCall(std::ostream& out,
210                     const ast::CallExpression* expr,
211                     const sem::Intrinsic* intrinsic);
212   /// Handles generating a call to the `frexp()` intrinsic
213   /// @param out the output of the expression stream
214   /// @param expr the call expression
215   /// @param intrinsic the semantic information for the intrinsic
216   /// @returns true if the call expression is emitted
217   bool EmitFrexpCall(std::ostream& out,
218                      const ast::CallExpression* expr,
219                      const sem::Intrinsic* intrinsic);
220   /// Handles a case statement
221   /// @param stmt the statement
222   /// @returns true if the statement was emitted successfully
223   bool EmitCase(const ast::CaseStatement* stmt);
224   /// Handles a continue statement
225   /// @param stmt the statement to emit
226   /// @returns true if the statement was emitted successfully
227   bool EmitContinue(const ast::ContinueStatement* stmt);
228   /// Handles generating a discard statement
229   /// @param stmt the discard statement
230   /// @returns true if the statement was successfully emitted
231   bool EmitDiscard(const ast::DiscardStatement* stmt);
232   /// Handles emitting the entry point function
233   /// @param func the entry point function
234   /// @returns true if the entry point function was emitted
235   bool EmitEntryPointFunction(const ast::Function* func);
236   /// Handles generate an Expression
237   /// @param out the output of the expression stream
238   /// @param expr the expression
239   /// @returns true if the expression was emitted
240   bool EmitExpression(std::ostream& out, const ast::Expression* expr);
241   /// Handles generating a function
242   /// @param func the function to generate
243   /// @returns true if the function was emitted
244   bool EmitFunction(const ast::Function* func);
245   /// Handles generating an identifier expression
246   /// @param out the output of the expression stream
247   /// @param expr the identifier expression
248   /// @returns true if the identifier was emitted
249   bool EmitIdentifier(std::ostream& out, const ast::IdentifierExpression* expr);
250   /// Handles an if statement
251   /// @param stmt the statement to emit
252   /// @returns true if the statement was successfully emitted
253   bool EmitIf(const ast::IfStatement* stmt);
254   /// Handles a literal
255   /// @param out the output of the expression stream
256   /// @param lit the literal to emit
257   /// @returns true if the literal was successfully emitted
258   bool EmitLiteral(std::ostream& out, const ast::LiteralExpression* lit);
259   /// Handles a loop statement
260   /// @param stmt the statement to emit
261   /// @returns true if the statement was emitted
262   bool EmitLoop(const ast::LoopStatement* stmt);
263   /// Handles a for loop statement
264   /// @param stmt the statement to emit
265   /// @returns true if the statement was emitted
266   bool EmitForLoop(const ast::ForLoopStatement* stmt);
267   /// Handles a member accessor expression
268   /// @param out the output of the expression stream
269   /// @param expr the member accessor expression
270   /// @returns true if the member accessor was emitted
271   bool EmitMemberAccessor(std::ostream& out,
272                           const ast::MemberAccessorExpression* expr);
273   /// Handles return statements
274   /// @param stmt the statement to emit
275   /// @returns true if the statement was successfully emitted
276   bool EmitReturn(const ast::ReturnStatement* stmt);
277   /// Handles emitting a pipeline stage name
278   /// @param out the output of the expression stream
279   /// @param stage the stage to emit
280   void EmitStage(std::ostream& out, ast::PipelineStage stage);
281   /// Handles statement
282   /// @param stmt the statement to emit
283   /// @returns true if the statement was emitted
284   bool EmitStatement(const ast::Statement* stmt);
285   /// Emits a list of statements
286   /// @param stmts the statement list
287   /// @returns true if the statements were emitted successfully
288   bool EmitStatements(const ast::StatementList& stmts);
289   /// Emits a list of statements with an indentation
290   /// @param stmts the statement list
291   /// @returns true if the statements were emitted successfully
292   bool EmitStatementsWithIndent(const ast::StatementList& stmts);
293   /// Handles generating a switch statement
294   /// @param stmt the statement to emit
295   /// @returns true if the statement was emitted
296   bool EmitSwitch(const ast::SwitchStatement* stmt);
297   /// Handles generating a type
298   /// @param out the output of the type stream
299   /// @param type the type to generate
300   /// @param name the name of the variable, only used for array emission
301   /// @param name_printed (optional) if not nullptr and an array was printed
302   /// @returns true if the type is emitted
303   bool EmitType(std::ostream& out,
304                 const sem::Type* type,
305                 const std::string& name,
306                 bool* name_printed = nullptr);
307   /// Handles generating type and name
308   /// @param out the output stream
309   /// @param type the type to generate
310   /// @param name the name to emit
311   /// @returns true if the type is emitted
312   bool EmitTypeAndName(std::ostream& out,
313                        const sem::Type* type,
314                        const std::string& name);
315   /// Handles generating a storage class
316   /// @param out the output of the type stream
317   /// @param sc the storage class to generate
318   /// @returns true if the storage class is emitted
319   bool EmitStorageClass(std::ostream& out, ast::StorageClass sc);
320   /// Handles generating an MSL-packed storage type.
321   /// If the type does not have a packed form, the standard non-packed form is
322   /// emitted.
323   /// @param out the output of the type stream
324   /// @param type the type to generate
325   /// @param name the name of the variable, only used for array emission
326   /// @returns true if the type is emitted
327   bool EmitPackedType(std::ostream& out,
328                       const sem::Type* type,
329                       const std::string& name);
330   /// Handles generating a struct declaration
331   /// @param buffer the text buffer that the type declaration will be written to
332   /// @param str the struct to generate
333   /// @returns true if the struct is emitted
334   bool EmitStructType(TextBuffer* buffer, const sem::Struct* str);
335   /// Handles a unary op expression
336   /// @param out the output of the expression stream
337   /// @param expr the expression to emit
338   /// @returns true if the expression was emitted
339   bool EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression* expr);
340   /// Handles generating a variable
341   /// @param var the variable to generate
342   /// @returns true if the variable was emitted
343   bool EmitVariable(const sem::Variable* var);
344   /// Handles generating a program scope constant variable
345   /// @param var the variable to emit
346   /// @returns true if the variable was emitted
347   bool EmitProgramConstVariable(const ast::Variable* var);
348   /// Emits the zero value for the given type
349   /// @param out the output of the expression stream
350   /// @param type the type to emit the value for
351   /// @returns true if the zero value was successfully emitted.
352   bool EmitZeroValue(std::ostream& out, const sem::Type* type);
353 
354   /// Handles generating a builtin name
355   /// @param intrinsic the semantic info for the intrinsic
356   /// @returns the name or "" if not valid
357   std::string generate_builtin_name(const sem::Intrinsic* intrinsic);
358 
359   /// Converts a builtin to an attribute name
360   /// @param builtin the builtin to convert
361   /// @returns the string name of the builtin or blank on error
362   std::string builtin_to_attribute(ast::Builtin builtin) const;
363 
364   /// Converts interpolation attributes to an MSL attribute
365   /// @param type the interpolation type
366   /// @param sampling the interpolation sampling
367   /// @returns the string name of the attribute or blank on error
368   std::string interpolation_to_attribute(
369       ast::InterpolationType type,
370       ast::InterpolationSampling sampling) const;
371 
372  private:
373   // A pair of byte size and alignment `uint32_t`s.
374   struct SizeAndAlign {
375     uint32_t size;
376     uint32_t align;
377   };
378 
379   /// CallIntrinsicHelper will call the intrinsic helper function, creating it
380   /// if it hasn't been built already. If the intrinsic needs to be built then
381   /// CallIntrinsicHelper will generate the function signature and will call
382   /// `build` to emit the body of the function.
383   /// @param out the output of the expression stream
384   /// @param call the call expression
385   /// @param intrinsic the semantic information for the intrinsic
386   /// @param build a function with the signature:
387   ///        `bool(TextBuffer* buffer, const std::vector<std::string>& params)`
388   ///        Where:
389   ///          `buffer` is the body of the generated function
390   ///          `params` is the name of all the generated function parameters
391   /// @returns true if the call expression is emitted
392   template <typename F>
393   bool CallIntrinsicHelper(std::ostream& out,
394                            const ast::CallExpression* call,
395                            const sem::Intrinsic* intrinsic,
396                            F&& build);
397 
398   TextBuffer helpers_;  // Helper functions emitted at the top of the output
399 
400   /// @returns the MSL packed type size and alignment in bytes for the given
401   /// type.
402   SizeAndAlign MslPackedTypeSizeAndAlign(const sem::Type* ty);
403 
404   using StorageClassToString =
405       std::unordered_map<ast::StorageClass, std::string>;
406 
407   std::function<bool()> emit_continuing_;
408 
409   /// Name of atomicCompareExchangeWeak() helper for the given pointer storage
410   /// class.
411   StorageClassToString atomicCompareExchangeWeak_;
412 
413   /// Unique name of the 'TINT_INVARIANT' preprocessor define. Non-empty only if
414   /// an invariant attribute has been generated.
415   std::string invariant_define_name_;
416 
417   /// True if matrix-packed_vector operator overloads have been generated.
418   bool matrix_packed_vector_overloads_ = false;
419 
420   /// A map from entry point name to a list of dynamic workgroup allocations.
421   /// Each entry in the vector is the size of the workgroup allocation that
422   /// should be created for that index.
423   std::unordered_map<std::string, std::vector<uint32_t>> workgroup_allocations_;
424 
425   std::unordered_map<const sem::Intrinsic*, std::string> intrinsics_;
426   std::unordered_map<const sem::Type*, std::string> unary_minus_funcs_;
427   std::unordered_map<uint32_t, std::string> int_dot_funcs_;
428 };
429 
430 }  // namespace msl
431 }  // namespace writer
432 }  // namespace tint
433 
434 #endif  // SRC_WRITER_MSL_GENERATOR_IMPL_H_
435