• 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_HLSL_GENERATOR_IMPL_H_
16 #define SRC_WRITER_HLSL_GENERATOR_IMPL_H_
17 
18 #include <string>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <utility>
22 
23 #include "src/ast/assignment_statement.h"
24 #include "src/ast/bitcast_expression.h"
25 #include "src/ast/break_statement.h"
26 #include "src/ast/continue_statement.h"
27 #include "src/ast/discard_statement.h"
28 #include "src/ast/for_loop_statement.h"
29 #include "src/ast/if_statement.h"
30 #include "src/ast/loop_statement.h"
31 #include "src/ast/return_statement.h"
32 #include "src/ast/switch_statement.h"
33 #include "src/ast/unary_op_expression.h"
34 #include "src/program_builder.h"
35 #include "src/scope_stack.h"
36 #include "src/sem/binding_point.h"
37 #include "src/transform/decompose_memory_access.h"
38 #include "src/utils/hash.h"
39 #include "src/writer/array_length_from_uniform_options.h"
40 #include "src/writer/text_generator.h"
41 
42 namespace tint {
43 
44 // Forward declarations
45 namespace sem {
46 class Call;
47 class Intrinsic;
48 class TypeConstructor;
49 class TypeConversion;
50 }  // namespace sem
51 
52 namespace writer {
53 namespace hlsl {
54 
55 /// The result of sanitizing a program for generation.
56 struct SanitizedResult {
57   /// Constructor
58   SanitizedResult();
59   /// Destructor
60   ~SanitizedResult();
61   /// Move constructor
62   SanitizedResult(SanitizedResult&&);
63 
64   /// The sanitized program.
65   Program program;
66   /// Indices into the array_length_from_uniform binding that are statically
67   /// used.
68   std::unordered_set<uint32_t> used_array_length_from_uniform_indices;
69 };
70 
71 /// Sanitize a program in preparation for generating HLSL.
72 /// @param root_constant_binding_point the binding point to use for information
73 /// that will be passed via root constants
74 /// @param disable_workgroup_init `true` to disable workgroup memory zero
75 /// @returns the sanitized program and any supplementary information
76 SanitizedResult Sanitize(
77     const Program* program,
78     sem::BindingPoint root_constant_binding_point = {},
79     bool disable_workgroup_init = false,
80     const ArrayLengthFromUniformOptions& array_length_from_uniform = {});
81 
82 /// Implementation class for HLSL generator
83 class GeneratorImpl : public TextGenerator {
84  public:
85   /// Constructor
86   /// @param program the program to generate
87   explicit GeneratorImpl(const Program* program);
88   ~GeneratorImpl();
89 
90   /// @returns true on successful generation; false otherwise
91   bool Generate();
92 
93   /// Handles an index accessor expression
94   /// @param out the output of the expression stream
95   /// @param expr the expression to emit
96   /// @returns true if the index accessor was emitted
97   bool EmitIndexAccessor(std::ostream& out,
98                          const ast::IndexAccessorExpression* expr);
99   /// Handles an assignment statement
100   /// @param stmt the statement to emit
101   /// @returns true if the statement was emitted successfully
102   bool EmitAssign(const ast::AssignmentStatement* stmt);
103   /// Handles generating a binary expression
104   /// @param out the output of the expression stream
105   /// @param expr the binary expression
106   /// @returns true if the expression was emitted, false otherwise
107   bool EmitBinary(std::ostream& out, const ast::BinaryExpression* expr);
108   /// Handles generating a bitcast expression
109   /// @param out the output of the expression stream
110   /// @param expr the as expression
111   /// @returns true if the bitcast was emitted
112   bool EmitBitcast(std::ostream& out, const ast::BitcastExpression* expr);
113   /// Emits a list of statements
114   /// @param stmts the statement list
115   /// @returns true if the statements were emitted successfully
116   bool EmitStatements(const ast::StatementList& stmts);
117   /// Emits a list of statements with an indentation
118   /// @param stmts the statement list
119   /// @returns true if the statements were emitted successfully
120   bool EmitStatementsWithIndent(const ast::StatementList& stmts);
121   /// Handles a block statement
122   /// @param stmt the statement to emit
123   /// @returns true if the statement was emitted successfully
124   bool EmitBlock(const ast::BlockStatement* stmt);
125   /// Handles a break statement
126   /// @param stmt the statement to emit
127   /// @returns true if the statement was emitted successfully
128   bool EmitBreak(const ast::BreakStatement* stmt);
129   /// Handles generating a call expression
130   /// @param out the output of the expression stream
131   /// @param expr the call expression
132   /// @returns true if the call expression is emitted
133   bool EmitCall(std::ostream& out, const ast::CallExpression* expr);
134   /// Handles generating a function call expression
135   /// @param out the output of the expression stream
136   /// @param call the call expression
137   /// @param function the function being called
138   /// @returns true if the expression is emitted
139   bool EmitFunctionCall(std::ostream& out,
140                         const sem::Call* call,
141                         const sem::Function* function);
142   /// Handles generating an intrinsic call expression
143   /// @param out the output of the expression stream
144   /// @param call the call expression
145   /// @param intrinsic the intrinsic being called
146   /// @returns true if the expression is emitted
147   bool EmitIntrinsicCall(std::ostream& out,
148                          const sem::Call* call,
149                          const sem::Intrinsic* intrinsic);
150   /// Handles generating a type conversion expression
151   /// @param out the output of the expression stream
152   /// @param call the call expression
153   /// @param conv the type conversion
154   /// @returns true if the expression is emitted
155   bool EmitTypeConversion(std::ostream& out,
156                           const sem::Call* call,
157                           const sem::TypeConversion* conv);
158   /// Handles generating a type constructor expression
159   /// @param out the output of the expression stream
160   /// @param call the call expression
161   /// @param ctor the type constructor
162   /// @returns true if the expression is emitted
163   bool EmitTypeConstructor(std::ostream& out,
164                            const sem::Call* call,
165                            const sem::TypeConstructor* ctor);
166   /// Handles generating a call expression to a
167   /// transform::DecomposeMemoryAccess::Intrinsic for a uniform buffer
168   /// @param out the output of the expression stream
169   /// @param expr the call expression
170   /// @param intrinsic the transform::DecomposeMemoryAccess::Intrinsic
171   /// @returns true if the call expression is emitted
172   bool EmitUniformBufferAccess(
173       std::ostream& out,
174       const ast::CallExpression* expr,
175       const transform::DecomposeMemoryAccess::Intrinsic* intrinsic);
176   /// Handles generating a call expression to a
177   /// transform::DecomposeMemoryAccess::Intrinsic for a storage buffer
178   /// @param out the output of the expression stream
179   /// @param expr the call expression
180   /// @param intrinsic the transform::DecomposeMemoryAccess::Intrinsic
181   /// @returns true if the call expression is emitted
182   bool EmitStorageBufferAccess(
183       std::ostream& out,
184       const ast::CallExpression* expr,
185       const transform::DecomposeMemoryAccess::Intrinsic* intrinsic);
186   /// Handles generating a barrier intrinsic call
187   /// @param out the output of the expression stream
188   /// @param intrinsic the semantic information for the barrier intrinsic
189   /// @returns true if the call expression is emitted
190   bool EmitBarrierCall(std::ostream& out, const sem::Intrinsic* intrinsic);
191   /// Handles generating an atomic intrinsic call for a storage buffer variable
192   /// @param out the output of the expression stream
193   /// @param expr the call expression
194   /// @param intrinsic the atomic intrinsic
195   /// @returns true if the call expression is emitted
196   bool EmitStorageAtomicCall(
197       std::ostream& out,
198       const ast::CallExpression* expr,
199       const transform::DecomposeMemoryAccess::Intrinsic* intrinsic);
200   /// Handles generating an atomic intrinsic call for a workgroup variable
201   /// @param out the output of the expression stream
202   /// @param expr the call expression
203   /// @param intrinsic the semantic information for the atomic intrinsic
204   /// @returns true if the call expression is emitted
205   bool EmitWorkgroupAtomicCall(std::ostream& out,
206                                const ast::CallExpression* expr,
207                                const sem::Intrinsic* intrinsic);
208   /// Handles generating a call to a texture function (`textureSample`,
209   /// `textureSampleGrad`, etc)
210   /// @param out the output of the expression stream
211   /// @param call the call expression
212   /// @param intrinsic the semantic information for the texture intrinsic
213   /// @returns true if the call expression is emitted
214   bool EmitTextureCall(std::ostream& out,
215                        const sem::Call* call,
216                        const sem::Intrinsic* intrinsic);
217   /// Handles generating a call to the `select()` intrinsic
218   /// @param out the output of the expression stream
219   /// @param expr the call expression
220   /// @returns true if the call expression is emitted
221   bool EmitSelectCall(std::ostream& out, const ast::CallExpression* expr);
222   /// Handles generating a call to the `modf()` intrinsic
223   /// @param out the output of the expression stream
224   /// @param expr the call expression
225   /// @param intrinsic the semantic information for the intrinsic
226   /// @returns true if the call expression is emitted
227   bool EmitModfCall(std::ostream& out,
228                     const ast::CallExpression* expr,
229                     const sem::Intrinsic* intrinsic);
230   /// Handles generating a call to the `frexp()` intrinsic
231   /// @param out the output of the expression stream
232   /// @param expr the call expression
233   /// @param intrinsic the semantic information for the intrinsic
234   /// @returns true if the call expression is emitted
235   bool EmitFrexpCall(std::ostream& out,
236                      const ast::CallExpression* expr,
237                      const sem::Intrinsic* intrinsic);
238   /// Handles generating a call to the `isNormal()` intrinsic
239   /// @param out the output of the expression stream
240   /// @param expr the call expression
241   /// @param intrinsic the semantic information for the intrinsic
242   /// @returns true if the call expression is emitted
243   bool EmitIsNormalCall(std::ostream& out,
244                         const ast::CallExpression* expr,
245                         const sem::Intrinsic* intrinsic);
246   /// Handles generating a call to data packing intrinsic
247   /// @param out the output of the expression stream
248   /// @param expr the call expression
249   /// @param intrinsic the semantic information for the texture intrinsic
250   /// @returns true if the call expression is emitted
251   bool EmitDataPackingCall(std::ostream& out,
252                            const ast::CallExpression* expr,
253                            const sem::Intrinsic* intrinsic);
254   /// Handles generating a call to data unpacking intrinsic
255   /// @param out the output of the expression stream
256   /// @param expr the call expression
257   /// @param intrinsic the semantic information for the texture intrinsic
258   /// @returns true if the call expression is emitted
259   bool EmitDataUnpackingCall(std::ostream& out,
260                              const ast::CallExpression* expr,
261                              const sem::Intrinsic* intrinsic);
262   /// Handles a case statement
263   /// @param s the switch statement
264   /// @param case_idx the index of the switch case in the switch statement
265   /// @returns true if the statement was emitted successfully
266   bool EmitCase(const ast::SwitchStatement* s, size_t case_idx);
267   /// Handles generating a discard statement
268   /// @param stmt the discard statement
269   /// @returns true if the statement was successfully emitted
270   bool EmitDiscard(const ast::DiscardStatement* stmt);
271   /// Handles a continue statement
272   /// @param stmt the statement to emit
273   /// @returns true if the statement was emitted successfully
274   bool EmitContinue(const ast::ContinueStatement* stmt);
275   /// Handles generate an Expression
276   /// @param out the output of the expression stream
277   /// @param expr the expression
278   /// @returns true if the expression was emitted
279   bool EmitExpression(std::ostream& out, const ast::Expression* expr);
280   /// Handles generating a function
281   /// @param func the function to generate
282   /// @returns true if the function was emitted
283   bool EmitFunction(const ast::Function* func);
284   /// Handles emitting the function body if it discards to work around a FXC
285   /// compilation bug.
286   /// @param func the function with the body to emit
287   /// @returns true if the function was emitted
288   bool EmitFunctionBodyWithDiscard(const ast::Function* func);
289   /// Handles emitting a global variable
290   /// @param global the global variable
291   /// @returns true on success
292   bool EmitGlobalVariable(const ast::Variable* global);
293 
294   /// Handles emitting a global variable with the uniform storage class
295   /// @param var the global variable
296   /// @returns true on success
297   bool EmitUniformVariable(const sem::Variable* var);
298 
299   /// Handles emitting a global variable with the storage storage class
300   /// @param var the global variable
301   /// @returns true on success
302   bool EmitStorageVariable(const sem::Variable* var);
303 
304   /// Handles emitting a global variable with the handle storage class
305   /// @param var the global variable
306   /// @returns true on success
307   bool EmitHandleVariable(const sem::Variable* var);
308 
309   /// Handles emitting a global variable with the private storage class
310   /// @param var the global variable
311   /// @returns true on success
312   bool EmitPrivateVariable(const sem::Variable* var);
313 
314   /// Handles emitting a global variable with the workgroup storage class
315   /// @param var the global variable
316   /// @returns true on success
317   bool EmitWorkgroupVariable(const sem::Variable* var);
318 
319   /// Handles emitting the entry point function
320   /// @param func the entry point
321   /// @returns true if the entry point function was emitted
322   bool EmitEntryPointFunction(const ast::Function* func);
323   /// Handles an if statement
324   /// @param stmt the statement to emit
325   /// @returns true if the statement was successfully emitted
326   bool EmitIf(const ast::IfStatement* stmt);
327   /// Handles a literal
328   /// @param out the output stream
329   /// @param lit the literal to emit
330   /// @returns true if the literal was successfully emitted
331   bool EmitLiteral(std::ostream& out, const ast::LiteralExpression* lit);
332   /// Handles a loop statement
333   /// @param stmt the statement to emit
334   /// @returns true if the statement was emitted
335   bool EmitLoop(const ast::LoopStatement* stmt);
336   /// Handles a for loop statement
337   /// @param stmt the statement to emit
338   /// @returns true if the statement was emitted
339   bool EmitForLoop(const ast::ForLoopStatement* stmt);
340   /// Handles generating an identifier expression
341   /// @param out the output of the expression stream
342   /// @param expr the identifier expression
343   /// @returns true if the identifeir was emitted
344   bool EmitIdentifier(std::ostream& out, const ast::IdentifierExpression* expr);
345   /// Handles a member accessor expression
346   /// @param out the output of the expression stream
347   /// @param expr the member accessor expression
348   /// @returns true if the member accessor was emitted
349   bool EmitMemberAccessor(std::ostream& out,
350                           const ast::MemberAccessorExpression* expr);
351   /// Handles return statements
352   /// @param stmt the statement to emit
353   /// @returns true if the statement was successfully emitted
354   bool EmitReturn(const ast::ReturnStatement* stmt);
355   /// Handles statement
356   /// @param stmt the statement to emit
357   /// @returns true if the statement was emitted
358   bool EmitStatement(const ast::Statement* stmt);
359   /// Handles generating a switch statement
360   /// @param stmt the statement to emit
361   /// @returns true if the statement was emitted
362   bool EmitSwitch(const ast::SwitchStatement* stmt);
363   // Handles generating a switch statement with only a default case
364   /// @param stmt the statement to emit
365   /// @returns true if the statement was emitted
366   bool EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt);
367   /// Handles generating type
368   /// @param out the output stream
369   /// @param type the type to generate
370   /// @param storage_class the storage class of the variable
371   /// @param access the access control type of the variable
372   /// @param name the name of the variable, used for array emission.
373   /// @param name_printed (optional) if not nullptr and an array was printed
374   /// then the boolean is set to true.
375   /// @returns true if the type is emitted
376   bool EmitType(std::ostream& out,
377                 const sem::Type* type,
378                 ast::StorageClass storage_class,
379                 ast::Access access,
380                 const std::string& name,
381                 bool* name_printed = nullptr);
382   /// Handles generating type and name
383   /// @param out the output stream
384   /// @param type the type to generate
385   /// @param storage_class the storage class of the variable
386   /// @param access the access control type of the variable
387   /// @param name the name to emit
388   /// @returns true if the type is emitted
389   bool EmitTypeAndName(std::ostream& out,
390                        const sem::Type* type,
391                        ast::StorageClass storage_class,
392                        ast::Access access,
393                        const std::string& name);
394   /// Handles generating a structure declaration
395   /// @param buffer the text buffer that the type declaration will be written to
396   /// @param ty the struct to generate
397   /// @returns true if the struct is emitted
398   bool EmitStructType(TextBuffer* buffer, const sem::Struct* ty);
399   /// Handles a unary op expression
400   /// @param out the output of the expression stream
401   /// @param expr the expression to emit
402   /// @returns true if the expression was emitted
403   bool EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression* expr);
404   /// Emits the zero value for the given type
405   /// @param out the output stream
406   /// @param type the type to emit the value for
407   /// @returns true if the zero value was successfully emitted.
408   bool EmitZeroValue(std::ostream& out, const sem::Type* type);
409   /// Handles generating a variable
410   /// @param var the variable to generate
411   /// @returns true if the variable was emitted
412   bool EmitVariable(const ast::Variable* var);
413   /// Handles generating a program scope constant variable
414   /// @param var the variable to emit
415   /// @returns true if the variable was emitted
416   bool EmitProgramConstVariable(const ast::Variable* var);
417   /// Emits call to a helper vector assignment function for the input assignment
418   /// statement and vector type. This is used to work around FXC issues where
419   /// assignments to vectors with dynamic indices cause compilation failures.
420   /// @param stmt assignment statement that corresponds to a vector assignment
421   /// via an accessor expression
422   /// @param vec the vector type being assigned to
423   /// @returns true on success
424   bool EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
425                                    const sem::Vector* vec);
426   /// Emits call to a helper matrix assignment function for the input assignment
427   /// statement and matrix type. This is used to work around FXC issues where
428   /// assignment of a vector to a matrix with a dynamic index causes compilation
429   /// failures.
430   /// @param stmt assignment statement that corresponds to a matrix assignment
431   /// via an accessor expression
432   /// @param mat the matrix type being assigned to
433   /// @returns true on success
434   bool EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
435                                          const sem::Matrix* mat);
436   /// Emits call to a helper matrix assignment function for the input assignment
437   /// statement and matrix type. This is used to work around FXC issues where
438   /// assignment of a scalar to a matrix with at least one dynamic index causes
439   /// compilation failures.
440   /// @param stmt assignment statement that corresponds to a matrix assignment
441   /// via an accessor expression
442   /// @param mat the matrix type being assigned to
443   /// @returns true on success
444   bool EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
445                                          const sem::Matrix* mat);
446 
447   /// Handles generating a builtin method name
448   /// @param intrinsic the semantic info for the intrinsic
449   /// @returns the name or "" if not valid
450   std::string generate_builtin_name(const sem::Intrinsic* intrinsic);
451   /// Converts a builtin to an attribute name
452   /// @param builtin the builtin to convert
453   /// @returns the string name of the builtin or blank on error
454   std::string builtin_to_attribute(ast::Builtin builtin) const;
455 
456   /// Converts interpolation attributes to a HLSL modifiers
457   /// @param type the interpolation type
458   /// @param sampling the interpolation sampling
459   /// @returns the string name of the attribute or blank on error
460   std::string interpolation_to_modifiers(
461       ast::InterpolationType type,
462       ast::InterpolationSampling sampling) const;
463 
464  private:
465   enum class VarType { kIn, kOut };
466 
467   struct EntryPointData {
468     std::string struct_name;
469     std::string var_name;
470   };
471 
472   struct DMAIntrinsic {
473     transform::DecomposeMemoryAccess::Intrinsic::Op op;
474     transform::DecomposeMemoryAccess::Intrinsic::DataType type;
475     bool operator==(const DMAIntrinsic& rhs) const {
476       return op == rhs.op && type == rhs.type;
477     }
478     /// Hasher is a std::hash function for DMAIntrinsic
479     struct Hasher {
480       /// @param i the DMAIntrinsic to hash
481       /// @returns the hash of `i`
operatorDMAIntrinsic::Hasher482       inline std::size_t operator()(const DMAIntrinsic& i) const {
483         return utils::Hash(i.op, i.type);
484       }
485     };
486   };
487 
488   /// CallIntrinsicHelper will call the intrinsic helper function, creating it
489   /// if it hasn't been built already. If the intrinsic needs to be built then
490   /// CallIntrinsicHelper will generate the function signature and will call
491   /// `build` to emit the body of the function.
492   /// @param out the output of the expression stream
493   /// @param call the call expression
494   /// @param intrinsic the semantic information for the intrinsic
495   /// @param build a function with the signature:
496   ///        `bool(TextBuffer* buffer, const std::vector<std::string>& params)`
497   ///        Where:
498   ///          `buffer` is the body of the generated function
499   ///          `params` is the name of all the generated function parameters
500   /// @returns true if the call expression is emitted
501   template <typename F>
502   bool CallIntrinsicHelper(std::ostream& out,
503                            const ast::CallExpression* call,
504                            const sem::Intrinsic* intrinsic,
505                            F&& build);
506 
507   TextBuffer helpers_;  // Helper functions emitted at the top of the output
508   std::function<bool()> emit_continuing_;
509   std::unordered_map<DMAIntrinsic, std::string, DMAIntrinsic::Hasher>
510       dma_intrinsics_;
511   std::unordered_map<const sem::Intrinsic*, std::string> intrinsics_;
512   std::unordered_map<const sem::Struct*, std::string> structure_builders_;
513   std::unordered_map<const sem::Vector*, std::string> dynamic_vector_write_;
514   std::unordered_map<const sem::Matrix*, std::string>
515       dynamic_matrix_vector_write_;
516   std::unordered_map<const sem::Matrix*, std::string>
517       dynamic_matrix_scalar_write_;
518 };
519 
520 }  // namespace hlsl
521 }  // namespace writer
522 }  // namespace tint
523 
524 #endif  // SRC_WRITER_HLSL_GENERATOR_IMPL_H_
525