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