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