1 // 2 // Copyright (C) 2016 Google, Inc. 3 // Copyright (C) 2016 LunarG, Inc. 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions 9 // are met: 10 // 11 // Redistributions of source code must retain the above copyright 12 // notice, this list of conditions and the following disclaimer. 13 // 14 // Redistributions in binary form must reproduce the above 15 // copyright notice, this list of conditions and the following 16 // disclaimer in the documentation and/or other materials provided 17 // with the distribution. 18 // 19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20 // contributors may be used to endorse or promote products derived 21 // from this software without specific prior written permission. 22 // 23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 // POSSIBILITY OF SUCH DAMAGE. 35 // 36 37 #include "hlslParseHelper.h" 38 #include "hlslScanContext.h" 39 #include "hlslGrammar.h" 40 #include "hlslAttributes.h" 41 42 #include "../glslang/MachineIndependent/Scan.h" 43 #include "../glslang/MachineIndependent/preprocessor/PpContext.h" 44 45 #include "../glslang/OSDependent/osinclude.h" 46 47 #include <algorithm> 48 #include <functional> 49 #include <cctype> 50 #include <array> 51 #include <set> 52 53 namespace glslang { 54 55 HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, 56 int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, 57 TInfoSink& infoSink, 58 const TString sourceEntryPointName, 59 bool forwardCompatible, EShMessages messages) : 60 TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, 61 forwardCompatible, messages), 62 annotationNestingLevel(0), 63 inputPatch(nullptr), 64 nextInLocation(0), nextOutLocation(0), 65 sourceEntryPointName(sourceEntryPointName), 66 entryPointFunction(nullptr), 67 entryPointFunctionBody(nullptr), 68 gsStreamOutput(nullptr), 69 clipDistanceInput(nullptr), 70 cullDistanceInput(nullptr), 71 clipDistanceOutput(nullptr), 72 cullDistanceOutput(nullptr) 73 { 74 globalUniformDefaults.clear(); 75 globalUniformDefaults.layoutMatrix = ElmRowMajor; 76 globalUniformDefaults.layoutPacking = ElpStd140; 77 78 globalBufferDefaults.clear(); 79 globalBufferDefaults.layoutMatrix = ElmRowMajor; 80 globalBufferDefaults.layoutPacking = ElpStd430; 81 82 globalInputDefaults.clear(); 83 globalOutputDefaults.clear(); 84 85 clipSemanticNSizeIn.fill(0); 86 cullSemanticNSizeIn.fill(0); 87 clipSemanticNSizeOut.fill(0); 88 cullSemanticNSizeOut.fill(0); 89 90 // "Shaders in the transform 91 // feedback capturing mode have an initial global default of 92 // layout(xfb_buffer = 0) out;" 93 if (language == EShLangVertex || 94 language == EShLangTessControl || 95 language == EShLangTessEvaluation || 96 language == EShLangGeometry) 97 globalOutputDefaults.layoutXfbBuffer = 0; 98 99 if (language == EShLangGeometry) 100 globalOutputDefaults.layoutStream = 0; 101 102 if (spvVersion.spv == 0 || spvVersion.vulkan == 0) 103 infoSink.info << "ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.\n"; 104 } 105 106 HlslParseContext::~HlslParseContext() 107 { 108 } 109 110 void HlslParseContext::initializeExtensionBehavior() 111 { 112 TParseContextBase::initializeExtensionBehavior(); 113 114 // HLSL allows #line by default. 115 extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable; 116 } 117 118 void HlslParseContext::setLimits(const TBuiltInResource& r) 119 { 120 resources = r; 121 intermediate.setLimits(resources); 122 } 123 124 // 125 // Parse an array of strings using the parser in HlslRules. 126 // 127 // Returns true for successful acceptance of the shader, false if any errors. 128 // 129 bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) 130 { 131 currentScanner = &input; 132 ppContext.setInput(input, versionWillBeError); 133 134 HlslScanContext scanContext(*this, ppContext); 135 HlslGrammar grammar(scanContext, *this); 136 if (!grammar.parse()) { 137 // Print a message formated such that if you click on the message it will take you right to 138 // the line through most UIs. 139 const glslang::TSourceLoc& sourceLoc = input.getSourceLoc(); 140 infoSink.info << sourceLoc.name << "(" << sourceLoc.line << "): error at column " << sourceLoc.column 141 << ", HLSL parsing failed.\n"; 142 ++numErrors; 143 return false; 144 } 145 146 finish(); 147 148 return numErrors == 0; 149 } 150 151 // 152 // Return true if this l-value node should be converted in some manner. 153 // For instance: turning a load aggregate into a store in an l-value. 154 // 155 bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const 156 { 157 if (node == nullptr || node->getAsTyped() == nullptr) 158 return false; 159 160 const TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); 161 const TIntermBinary* lhsAsBinary = node->getAsBinaryNode(); 162 163 // If it's a swizzled/indexed aggregate, look at the left node instead. 164 if (lhsAsBinary != nullptr && 165 (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) 166 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); 167 if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad) 168 return true; 169 170 // If it's a syntactic write to a sampler, we will use that to establish 171 // a compile-time alias. 172 if (node->getAsTyped()->getBasicType() == EbtSampler) 173 return true; 174 175 return false; 176 } 177 178 void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, 179 TTypeList* newTypeList) 180 { 181 newTypeList = nullptr; 182 correctUniform(memberType.getQualifier()); 183 if (memberType.isStruct()) { 184 auto it = ioTypeMap.find(memberType.getStruct()); 185 if (it != ioTypeMap.end() && it->second.uniform) 186 newTypeList = it->second.uniform; 187 } 188 TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList); 189 } 190 191 // 192 // Return a TLayoutFormat corresponding to the given texture type. 193 // 194 TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType) 195 { 196 if (txType.isStruct()) { 197 // TODO: implement. 198 error(loc, "unimplemented: structure type in image or buffer", "", ""); 199 return ElfNone; 200 } 201 202 const int components = txType.getVectorSize(); 203 const TBasicType txBasicType = txType.getBasicType(); 204 205 const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat { 206 if (intermediate.getNoStorageFormat()) 207 return ElfNone; 208 209 return components == 1 ? v1 : 210 components == 2 ? v2 : v4; 211 }; 212 213 switch (txBasicType) { 214 case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f); 215 case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i); 216 case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui); 217 default: 218 error(loc, "unknown basic type in image format", "", ""); 219 return ElfNone; 220 } 221 } 222 223 // 224 // Both test and if necessary, spit out an error, to see if the node is really 225 // an l-value that can be operated on this way. 226 // 227 // Returns true if there was an error. 228 // 229 bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) 230 { 231 if (shouldConvertLValue(node)) { 232 // if we're writing to a texture, it must be an RW form. 233 234 TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); 235 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); 236 237 if (!object->getType().getSampler().isImage()) { 238 error(loc, "operator[] on a non-RW texture must be an r-value", "", ""); 239 return true; 240 } 241 } 242 243 // Let the base class check errors 244 return TParseContextBase::lValueErrorCheck(loc, op, node); 245 } 246 247 // 248 // This function handles l-value conversions and verifications. It uses, but is not synonymous 249 // with lValueErrorCheck. That function accepts an l-value directly, while this one must be 250 // given the surrounding tree - e.g, with an assignment, so we can convert the assign into a 251 // series of other image operations. 252 // 253 // Most things are passed through unmodified, except for error checking. 254 // 255 TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) 256 { 257 if (node == nullptr) 258 return nullptr; 259 260 TIntermBinary* nodeAsBinary = node->getAsBinaryNode(); 261 TIntermUnary* nodeAsUnary = node->getAsUnaryNode(); 262 TIntermAggregate* sequence = nullptr; 263 264 TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() : 265 nodeAsBinary ? nodeAsBinary->getLeft() : 266 nullptr; 267 268 // Early bail out if there is no conversion to apply 269 if (!shouldConvertLValue(lhs)) { 270 if (lhs != nullptr) 271 if (lValueErrorCheck(loc, op, lhs)) 272 return nullptr; 273 return node; 274 } 275 276 // *** If we get here, we're going to apply some conversion to an l-value. 277 278 // Spin off sampler aliasing 279 if (node->getAsTyped()->getBasicType() == EbtSampler) 280 return handleSamplerLvalue(loc, op, node); 281 282 // Helper to create a load. 283 const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) { 284 TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad); 285 loadOp->setLoc(loc); 286 loadOp->getSequence().push_back(object); 287 loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode())); 288 loadOp->setType(derefType); 289 290 sequence = intermediate.growAggregate(sequence, 291 intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc), 292 loc); 293 }; 294 295 // Helper to create a store. 296 const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) { 297 TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore); 298 storeOp->getSequence().push_back(object); 299 storeOp->getSequence().push_back(coord); 300 storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp)); 301 storeOp->setLoc(loc); 302 storeOp->setType(TType(EbtVoid)); 303 304 sequence = intermediate.growAggregate(sequence, storeOp); 305 }; 306 307 // Helper to create an assign. 308 const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) { 309 sequence = intermediate.growAggregate(sequence, 310 intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()), 311 loc); 312 }; 313 314 // Helper to complete sequence by adding trailing variable, so we evaluate to the right value. 315 const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* { 316 // Add a trailing use of the temp, so the sequence returns the proper value. 317 sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp)); 318 sequence->setOperator(EOpSequence); 319 sequence->setLoc(loc); 320 sequence->setType(derefType); 321 322 return sequence; 323 }; 324 325 // Helper to add unary op 326 const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) { 327 sequence = intermediate.growAggregate(sequence, 328 intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc, 329 rhsTmp->getType()), 330 loc); 331 }; 332 333 // Return true if swizzle or index writes all components of the given variable. 334 const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool { 335 if (swizzle == nullptr) // not a swizzle or index 336 return true; 337 338 // Track which components are being set. 339 std::array<bool, 4> compIsSet; 340 compIsSet.fill(false); 341 342 const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion(); 343 const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate(); 344 345 // This could be either a direct index, or a swizzle. 346 if (asConst) { 347 compIsSet[asConst->getConstArray()[0].getIConst()] = true; 348 } else if (asAggregate) { 349 const TIntermSequence& seq = asAggregate->getSequence(); 350 for (int comp=0; comp<int(seq.size()); ++comp) 351 compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true; 352 } else { 353 assert(0); 354 } 355 356 // Return true if all components are being set by the index or swizzle 357 return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(), 358 [](bool isSet) { return isSet; } ); 359 }; 360 361 // Create swizzle matching input swizzle 362 const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* { 363 if (swizzle) 364 return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType()); 365 else 366 return var; 367 }; 368 369 TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode(); 370 TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate(); 371 bool lhsIsSwizzle = false; 372 373 // If it's a swizzled L-value, remember the swizzle, and use the LHS. 374 if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) { 375 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); 376 lhsIsSwizzle = true; 377 } 378 379 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); 380 TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped(); 381 382 const TSampler& texSampler = object->getType().getSampler(); 383 384 TType objDerefType; 385 getTextureReturnType(texSampler, objDerefType); 386 387 if (nodeAsBinary) { 388 TIntermTyped* rhs = nodeAsBinary->getRight(); 389 const TOperator assignOp = nodeAsBinary->getOp(); 390 391 bool isModifyOp = false; 392 393 switch (assignOp) { 394 case EOpAddAssign: 395 case EOpSubAssign: 396 case EOpMulAssign: 397 case EOpVectorTimesMatrixAssign: 398 case EOpVectorTimesScalarAssign: 399 case EOpMatrixTimesScalarAssign: 400 case EOpMatrixTimesMatrixAssign: 401 case EOpDivAssign: 402 case EOpModAssign: 403 case EOpAndAssign: 404 case EOpInclusiveOrAssign: 405 case EOpExclusiveOrAssign: 406 case EOpLeftShiftAssign: 407 case EOpRightShiftAssign: 408 isModifyOp = true; 409 // fall through... 410 case EOpAssign: 411 { 412 // Since this is an lvalue, we'll convert an image load to a sequence like this 413 // (to still provide the value): 414 // OpSequence 415 // OpImageStore(object, lhs, rhs) 416 // rhs 417 // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS, 418 // so we'll convert instead to this: 419 // OpSequence 420 // rhsTmp = rhs 421 // OpImageStore(object, coord, rhsTmp) 422 // rhsTmp 423 // If this is a read-modify-write op, like +=, we issue: 424 // OpSequence 425 // coordtmp = load's param1 426 // rhsTmp = OpImageLoad(object, coordTmp) 427 // rhsTmp op= rhs 428 // OpImageStore(object, coordTmp, rhsTmp) 429 // rhsTmp 430 // 431 // If the lvalue is swizzled, we apply that when writing the temp variable, like so: 432 // ... 433 // rhsTmp.some_swizzle = ... 434 // For partial writes, an error is generated. 435 436 TIntermSymbol* rhsTmp = rhs->getAsSymbolNode(); 437 TIntermTyped* coordTmp = coord; 438 439 if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) { 440 rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); 441 442 // Partial updates not yet supported 443 if (!writesAllComponents(rhsTmp, lhsAsBinary)) { 444 error(loc, "unimplemented: partial image updates", "", ""); 445 } 446 447 // Assign storeTemp = rhs 448 if (isModifyOp) { 449 // We have to make a temp var for the coordinate, to avoid evaluating it twice. 450 coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); 451 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] 452 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) 453 } 454 455 // rhsTmp op= rhs. 456 makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs); 457 } 458 459 makeStore(object, coordTmp, rhsTmp); // add a store 460 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence 461 } 462 463 default: 464 break; 465 } 466 } 467 468 if (nodeAsUnary) { 469 const TOperator assignOp = nodeAsUnary->getOp(); 470 471 switch (assignOp) { 472 case EOpPreIncrement: 473 case EOpPreDecrement: 474 { 475 // We turn this into: 476 // OpSequence 477 // coordtmp = load's param1 478 // rhsTmp = OpImageLoad(object, coordTmp) 479 // rhsTmp op 480 // OpImageStore(object, coordTmp, rhsTmp) 481 // rhsTmp 482 483 TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); 484 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); 485 486 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] 487 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) 488 makeUnary(assignOp, rhsTmp); // op rhsTmp 489 makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp) 490 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence 491 } 492 493 case EOpPostIncrement: 494 case EOpPostDecrement: 495 { 496 // We turn this into: 497 // OpSequence 498 // coordtmp = load's param1 499 // rhsTmp1 = OpImageLoad(object, coordTmp) 500 // rhsTmp2 = rhsTmp1 501 // rhsTmp2 op 502 // OpImageStore(object, coordTmp, rhsTmp2) 503 // rhsTmp1 (pre-op value) 504 TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType); 505 TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType); 506 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); 507 508 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] 509 makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp) 510 makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1 511 makeUnary(assignOp, rhsTmp2); // rhsTmp op 512 makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2) 513 return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence 514 } 515 516 default: 517 break; 518 } 519 } 520 521 if (lhs) 522 if (lValueErrorCheck(loc, op, lhs)) 523 return nullptr; 524 525 return node; 526 } 527 528 // Deal with sampler aliasing: turning assignments into aliases 529 // Return a placeholder node for higher-level code that think assignments must 530 // generate code. 531 TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) 532 { 533 // Can only alias an assignment: "s1 = s2" 534 TIntermBinary* binary = node->getAsBinaryNode(); 535 if (binary == nullptr || node->getAsOperator()->getOp() != EOpAssign || 536 binary->getLeft()->getAsSymbolNode() == nullptr || 537 binary->getRight()->getAsSymbolNode() == nullptr) { 538 error(loc, "can't modify sampler", op, ""); 539 return node; 540 } 541 542 if (controlFlowNestingLevel > 0) 543 warn(loc, "sampler or image aliased under control flow; consumption must be in same path", op, ""); 544 545 TIntermTyped* set = setOpaqueLvalue(binary->getLeft(), binary->getRight()); 546 if (set == nullptr) 547 warn(loc, "could not create alias for sampler", op, ""); 548 else 549 node = set; 550 551 return node; 552 } 553 554 // Do an opaque assignment that needs to turn into an alias. 555 // Return nullptr if it can't be done, otherwise return a placeholder 556 // node for higher-level code that think assignments must generate code. 557 TIntermTyped* HlslParseContext::setOpaqueLvalue(TIntermTyped* leftTyped, TIntermTyped* rightTyped) 558 { 559 // Best is if we are aliasing a flattened struct member "S.s1 = s2", 560 // in which case we want to update the flattening information with the alias, 561 // making everything else work seamlessly. 562 TIntermSymbol* left = leftTyped->getAsSymbolNode(); 563 TIntermSymbol* right = rightTyped->getAsSymbolNode(); 564 for (auto fit = flattenMap.begin(); fit != flattenMap.end(); ++fit) { 565 for (auto mit = fit->second.members.begin(); mit != fit->second.members.end(); ++mit) { 566 if ((*mit)->getUniqueId() == left->getId()) { 567 // found it: update with alias to the existing variable, and don't emit any code 568 (*mit) = new TVariable(&right->getName(), right->getType()); 569 (*mit)->setUniqueId(right->getId()); 570 // replace node (rest of compiler expects either an error or code to generate) 571 // with pointless access 572 return right; 573 } 574 } 575 } 576 577 return nullptr; 578 } 579 580 void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) 581 { 582 if (pragmaCallback) 583 pragmaCallback(loc.line, tokens); 584 585 if (tokens.size() == 0) 586 return; 587 588 // These pragmas are case insensitive in HLSL, so we'll compare in lower case. 589 TVector<TString> lowerTokens = tokens; 590 591 for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it) 592 std::transform(it->begin(), it->end(), it->begin(), ::tolower); 593 594 // Handle pack_matrix 595 if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") { 596 // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense. 597 // Row major becomes column major and vice versa. 598 599 if (lowerTokens[2] == "row_major") { 600 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor; 601 } else if (lowerTokens[2] == "column_major") { 602 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; 603 } else { 604 // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major) 605 warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), ""); 606 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; 607 } 608 } 609 } 610 611 // 612 // Look at a '.' matrix selector string and change it into components 613 // for a matrix. There are two types: 614 // 615 // _21 second row, first column (one based) 616 // _m21 third row, second column (zero based) 617 // 618 // Returns true if there is no error. 619 // 620 bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows, 621 TSwizzleSelectors<TMatrixSelector>& components) 622 { 623 int startPos[MaxSwizzleSelectors]; 624 int numComps = 0; 625 TString compString = fields; 626 627 // Find where each component starts, 628 // recording the first character position after the '_'. 629 for (size_t c = 0; c < compString.size(); ++c) { 630 if (compString[c] == '_') { 631 if (numComps >= MaxSwizzleSelectors) { 632 error(loc, "matrix component swizzle has too many components", compString.c_str(), ""); 633 return false; 634 } 635 if (c > compString.size() - 3 || 636 ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) { 637 error(loc, "matrix component swizzle missing", compString.c_str(), ""); 638 return false; 639 } 640 startPos[numComps++] = (int)c + 1; 641 } 642 } 643 644 // Process each component 645 for (int i = 0; i < numComps; ++i) { 646 int pos = startPos[i]; 647 int bias = -1; 648 if (compString[pos] == 'm' || compString[pos] == 'M') { 649 bias = 0; 650 ++pos; 651 } 652 TMatrixSelector comp; 653 comp.coord1 = compString[pos+0] - '0' + bias; 654 comp.coord2 = compString[pos+1] - '0' + bias; 655 if (comp.coord1 < 0 || comp.coord1 >= cols) { 656 error(loc, "matrix row component out of range", compString.c_str(), ""); 657 return false; 658 } 659 if (comp.coord2 < 0 || comp.coord2 >= rows) { 660 error(loc, "matrix column component out of range", compString.c_str(), ""); 661 return false; 662 } 663 components.push_back(comp); 664 } 665 666 return true; 667 } 668 669 // If the 'comps' express a column of a matrix, 670 // return the column. Column means the first coords all match. 671 // 672 // Otherwise, return -1. 673 // 674 int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector) 675 { 676 int col = -1; 677 678 // right number of comps? 679 if (selector.size() != rows) 680 return -1; 681 682 // all comps in the same column? 683 // rows in order? 684 col = selector[0].coord1; 685 for (int i = 0; i < rows; ++i) { 686 if (col != selector[i].coord1) 687 return -1; 688 if (i != selector[i].coord2) 689 return -1; 690 } 691 692 return col; 693 } 694 695 // 696 // Handle seeing a variable identifier in the grammar. 697 // 698 TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string) 699 { 700 int thisDepth; 701 TSymbol* symbol = symbolTable.find(*string, thisDepth); 702 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { 703 error(loc, "expected symbol, not user-defined type", string->c_str(), ""); 704 return nullptr; 705 } 706 707 // Error check for requiring specific extensions present. 708 if (symbol && symbol->getNumExtensions()) 709 requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); 710 711 const TVariable* variable = nullptr; 712 const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; 713 TIntermTyped* node = nullptr; 714 if (anon) { 715 // It was a member of an anonymous container, which could be a 'this' structure. 716 717 // Create a subtree for its dereference. 718 if (thisDepth > 0) { 719 variable = getImplicitThis(thisDepth); 720 if (variable == nullptr) 721 error(loc, "cannot access member variables (static member function?)", "this", ""); 722 } 723 if (variable == nullptr) 724 variable = anon->getAnonContainer().getAsVariable(); 725 726 TIntermTyped* container = intermediate.addSymbol(*variable, loc); 727 TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); 728 node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); 729 730 node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); 731 if (node->getType().hiddenMember()) 732 error(loc, "member of nameless block was not redeclared", string->c_str(), ""); 733 } else { 734 // Not a member of an anonymous container. 735 736 // The symbol table search was done in the lexical phase. 737 // See if it was a variable. 738 variable = symbol ? symbol->getAsVariable() : nullptr; 739 if (variable) { 740 if ((variable->getType().getBasicType() == EbtBlock || 741 variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) { 742 error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); 743 variable = nullptr; 744 } 745 } else { 746 if (symbol) 747 error(loc, "variable name expected", string->c_str(), ""); 748 } 749 750 // Recovery, if it wasn't found or was not a variable. 751 if (variable == nullptr) { 752 error(loc, "unknown variable", string->c_str(), ""); 753 variable = new TVariable(string, TType(EbtVoid)); 754 } 755 756 if (variable->getType().getQualifier().isFrontEndConstant()) 757 node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); 758 else 759 node = intermediate.addSymbol(*variable, loc); 760 } 761 762 if (variable->getType().getQualifier().isIo()) 763 intermediate.addIoAccessed(*string); 764 765 return node; 766 } 767 768 // 769 // Handle operator[] on any objects it applies to. Currently: 770 // Textures 771 // Buffers 772 // 773 TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) 774 { 775 // handle r-value operator[] on textures and images. l-values will be processed later. 776 if (base->getType().getBasicType() == EbtSampler && !base->isArray()) { 777 const TSampler& sampler = base->getType().getSampler(); 778 if (sampler.isImage() || sampler.isTexture()) { 779 if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) { 780 // The first operator[] to a .mips[] sequence is the mip level. We'll remember it. 781 mipsOperatorMipArg.back().mipLevel = index; 782 return base; // next [] index is to the same base. 783 } else { 784 TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch); 785 786 TType sampReturnType; 787 getTextureReturnType(sampler, sampReturnType); 788 789 load->setType(sampReturnType); 790 load->setLoc(loc); 791 load->getSequence().push_back(base); 792 load->getSequence().push_back(index); 793 794 // Textures need a MIP. If we saw one go by, use it. Otherwise, use zero. 795 if (sampler.isTexture()) { 796 if (! mipsOperatorMipArg.empty()) { 797 load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel); 798 mipsOperatorMipArg.pop_back(); 799 } else { 800 load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true)); 801 } 802 } 803 804 return load; 805 } 806 } 807 } 808 809 // Handle operator[] on structured buffers: this indexes into the array element of the buffer. 810 // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO). 811 TIntermTyped* sbArray = indexStructBufferContent(loc, base); 812 if (sbArray != nullptr) { 813 if (sbArray == nullptr) 814 return nullptr; 815 816 // Now we'll apply the [] index to that array 817 const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; 818 819 TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc); 820 const TType derefType(sbArray->getType(), 0); 821 element->setType(derefType); 822 return element; 823 } 824 825 return nullptr; 826 } 827 828 // 829 // Cast index value to a uint if it isn't already (for operator[], load indexes, etc) 830 TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index) 831 { 832 const TBasicType indexBasicType = index->getType().getBasicType(); 833 const int vecSize = index->getType().getVectorSize(); 834 835 // We can use int types directly as the index 836 if (indexBasicType == EbtInt || indexBasicType == EbtUint || 837 indexBasicType == EbtInt64 || indexBasicType == EbtUint64) 838 return index; 839 840 // Cast index to unsigned integer if it isn't one. 841 return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index); 842 } 843 844 // 845 // Handle seeing a base[index] dereference in the grammar. 846 // 847 TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) 848 { 849 index = makeIntegerIndex(index); 850 851 if (index == nullptr) { 852 error(loc, " unknown index type ", "", ""); 853 return nullptr; 854 } 855 856 TIntermTyped* result = handleBracketOperator(loc, base, index); 857 858 if (result != nullptr) 859 return result; // it was handled as an operator[] 860 861 bool flattened = false; 862 int indexValue = 0; 863 if (index->getQualifier().storage == EvqConst) { 864 indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); 865 checkIndex(loc, base->getType(), indexValue); 866 } 867 868 variableCheck(base); 869 if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) { 870 if (base->getAsSymbolNode()) 871 error(loc, " left of '[' is not of type array, matrix, or vector ", 872 base->getAsSymbolNode()->getName().c_str(), ""); 873 else 874 error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); 875 } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) 876 return intermediate.foldDereference(base, indexValue, loc); 877 else { 878 // at least one of base and index is variable... 879 880 if (base->getAsSymbolNode() && wasFlattened(base)) { 881 if (index->getQualifier().storage != EvqConst) 882 error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), ""); 883 884 result = flattenAccess(base, indexValue); 885 flattened = (result != base); 886 } else { 887 if (index->getQualifier().storage == EvqConst) { 888 if (base->getType().isImplicitlySizedArray()) 889 updateImplicitArraySize(loc, base, indexValue); 890 result = intermediate.addIndex(EOpIndexDirect, base, index, loc); 891 } else { 892 result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); 893 } 894 } 895 } 896 897 if (result == nullptr) { 898 // Insert dummy error-recovery result 899 result = intermediate.addConstantUnion(0.0, EbtFloat, loc); 900 } else { 901 // If the array reference was flattened, it has the correct type. E.g, if it was 902 // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps. 903 // In that case, we preserve the qualifiers. 904 if (!flattened) { 905 // Insert valid dereferenced result 906 TType newType(base->getType(), 0); // dereferenced type 907 if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) 908 newType.getQualifier().storage = EvqConst; 909 else 910 newType.getQualifier().storage = EvqTemporary; 911 result->setType(newType); 912 } 913 } 914 915 return result; 916 } 917 918 void HlslParseContext::checkIndex(const TSourceLoc& /*loc*/, const TType& /*type*/, int& /*index*/) 919 { 920 // HLSL todo: any rules for index fixups? 921 } 922 923 // Handle seeing a binary node with a math operation. 924 TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, 925 TIntermTyped* left, TIntermTyped* right) 926 { 927 TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc); 928 if (result == nullptr) 929 binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); 930 931 return result; 932 } 933 934 // Handle seeing a unary node with a math operation. 935 TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, 936 TIntermTyped* childNode) 937 { 938 TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc); 939 940 if (result) 941 return result; 942 else 943 unaryOpError(loc, str, childNode->getCompleteString()); 944 945 return childNode; 946 } 947 // 948 // Return true if the name is a struct buffer method 949 // 950 bool HlslParseContext::isStructBufferMethod(const TString& name) const 951 { 952 return 953 name == "GetDimensions" || 954 name == "Load" || 955 name == "Load2" || 956 name == "Load3" || 957 name == "Load4" || 958 name == "Store" || 959 name == "Store2" || 960 name == "Store3" || 961 name == "Store4" || 962 name == "InterlockedAdd" || 963 name == "InterlockedAnd" || 964 name == "InterlockedCompareExchange" || 965 name == "InterlockedCompareStore" || 966 name == "InterlockedExchange" || 967 name == "InterlockedMax" || 968 name == "InterlockedMin" || 969 name == "InterlockedOr" || 970 name == "InterlockedXor" || 971 name == "IncrementCounter" || 972 name == "DecrementCounter" || 973 name == "Append" || 974 name == "Consume"; 975 } 976 977 // 978 // Handle seeing a base.field dereference in the grammar, where 'field' is a 979 // swizzle or member variable. 980 // 981 TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) 982 { 983 variableCheck(base); 984 985 if (base->isArray()) { 986 error(loc, "cannot apply to an array:", ".", field.c_str()); 987 return base; 988 } 989 990 TIntermTyped* result = base; 991 992 if (base->getType().getBasicType() == EbtSampler) { 993 // Handle .mips[mipid][pos] operation on textures 994 const TSampler& sampler = base->getType().getSampler(); 995 if (sampler.isTexture() && field == "mips") { 996 // Push a null to signify that we expect a mip level under operator[] next. 997 mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr)); 998 // Keep 'result' pointing to 'base', since we expect an operator[] to go by next. 999 } else { 1000 if (field == "mips") 1001 error(loc, "unexpected texture type for .mips[][] operator:", 1002 base->getType().getCompleteString().c_str(), ""); 1003 else 1004 error(loc, "unexpected operator on texture type:", field.c_str(), 1005 base->getType().getCompleteString().c_str()); 1006 } 1007 } else if (base->isVector() || base->isScalar()) { 1008 TSwizzleSelectors<TVectorSelector> selectors; 1009 parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); 1010 1011 if (base->isScalar()) { 1012 if (selectors.size() == 1) 1013 return result; 1014 else { 1015 TType type(base->getBasicType(), EvqTemporary, selectors.size()); 1016 return addConstructor(loc, base, type); 1017 } 1018 } 1019 if (base->getVectorSize() == 1) { 1020 TType scalarType(base->getBasicType(), EvqTemporary, 1); 1021 if (selectors.size() == 1) 1022 return addConstructor(loc, base, scalarType); 1023 else { 1024 TType vectorType(base->getBasicType(), EvqTemporary, selectors.size()); 1025 return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType); 1026 } 1027 } 1028 1029 if (base->getType().getQualifier().isFrontEndConstant()) 1030 result = intermediate.foldSwizzle(base, selectors, loc); 1031 else { 1032 if (selectors.size() == 1) { 1033 TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); 1034 result = intermediate.addIndex(EOpIndexDirect, base, index, loc); 1035 result->setType(TType(base->getBasicType(), EvqTemporary)); 1036 } else { 1037 TIntermTyped* index = intermediate.addSwizzle(selectors, loc); 1038 result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); 1039 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, 1040 selectors.size())); 1041 } 1042 } 1043 } else if (base->isMatrix()) { 1044 TSwizzleSelectors<TMatrixSelector> selectors; 1045 if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors)) 1046 return result; 1047 1048 if (selectors.size() == 1) { 1049 // Representable by m[c][r] 1050 if (base->getType().getQualifier().isFrontEndConstant()) { 1051 result = intermediate.foldDereference(base, selectors[0].coord1, loc); 1052 result = intermediate.foldDereference(result, selectors[0].coord2, loc); 1053 } else { 1054 result = intermediate.addIndex(EOpIndexDirect, base, 1055 intermediate.addConstantUnion(selectors[0].coord1, loc), 1056 loc); 1057 TType dereferencedCol(base->getType(), 0); 1058 result->setType(dereferencedCol); 1059 result = intermediate.addIndex(EOpIndexDirect, result, 1060 intermediate.addConstantUnion(selectors[0].coord2, loc), 1061 loc); 1062 TType dereferenced(dereferencedCol, 0); 1063 result->setType(dereferenced); 1064 } 1065 } else { 1066 int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors); 1067 if (column >= 0) { 1068 // Representable by m[c] 1069 if (base->getType().getQualifier().isFrontEndConstant()) 1070 result = intermediate.foldDereference(base, column, loc); 1071 else { 1072 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc), 1073 loc); 1074 TType dereferenced(base->getType(), 0); 1075 result->setType(dereferenced); 1076 } 1077 } else { 1078 // general case, not a column, not a single component 1079 TIntermTyped* index = intermediate.addSwizzle(selectors, loc); 1080 result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc); 1081 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, 1082 selectors.size())); 1083 } 1084 } 1085 } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { 1086 const TTypeList* fields = base->getType().getStruct(); 1087 bool fieldFound = false; 1088 int member; 1089 for (member = 0; member < (int)fields->size(); ++member) { 1090 if ((*fields)[member].type->getFieldName() == field) { 1091 fieldFound = true; 1092 break; 1093 } 1094 } 1095 if (fieldFound) { 1096 if (base->getAsSymbolNode() && wasFlattened(base)) { 1097 result = flattenAccess(base, member); 1098 } else { 1099 if (base->getType().getQualifier().storage == EvqConst) 1100 result = intermediate.foldDereference(base, member, loc); 1101 else { 1102 TIntermTyped* index = intermediate.addConstantUnion(member, loc); 1103 result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); 1104 result->setType(*(*fields)[member].type); 1105 } 1106 } 1107 } else 1108 error(loc, "no such field in structure", field.c_str(), ""); 1109 } else 1110 error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); 1111 1112 return result; 1113 } 1114 1115 // 1116 // Return true if the field should be treated as a built-in method. 1117 // Return false otherwise. 1118 // 1119 bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field) 1120 { 1121 if (base == nullptr) 1122 return false; 1123 1124 variableCheck(base); 1125 1126 if (base->getType().getBasicType() == EbtSampler) { 1127 return true; 1128 } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) { 1129 return true; 1130 } else if (field == "Append" || 1131 field == "RestartStrip") { 1132 // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but 1133 // the code is around in the shader source. 1134 return true; 1135 } else 1136 return false; 1137 } 1138 1139 // Independently establish a built-in that is a member of a structure. 1140 // 'arraySizes' are what's desired for the independent built-in, whatever 1141 // the higher-level source/expression of them was. 1142 void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes, 1143 const TQualifier& outerQualifier) 1144 { 1145 // Because of arrays of structs, we might be asked more than once, 1146 // but the arraySizes passed in should have captured the whole thing 1147 // the first time. 1148 // However, clip/cull rely on multiple updates. 1149 if (!isClipOrCullDistance(memberType)) 1150 if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) != 1151 splitBuiltIns.end()) 1152 return; 1153 1154 TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType); 1155 1156 if (arraySizes != nullptr && !memberType.isArray()) 1157 ioVar->getWritableType().newArraySizes(*arraySizes); 1158 1159 splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar; 1160 if (!isClipOrCullDistance(ioVar->getType())) 1161 trackLinkage(*ioVar); 1162 1163 // Merge qualifier from the user structure 1164 mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier); 1165 1166 // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the 1167 // shader declared them). This is done after mergeQualifiers(), in case fixBuiltInIoType looks 1168 // at the qualifier to determine e.g, in or out qualifications. 1169 fixBuiltInIoType(ioVar->getWritableType()); 1170 1171 // But, not location, we're losing that 1172 ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; 1173 } 1174 1175 // Split a type into 1176 // 1. a struct of non-I/O members 1177 // 2. a collection of independent I/O variables 1178 void HlslParseContext::split(const TVariable& variable) 1179 { 1180 // Create a new variable: 1181 const TType& clonedType = *variable.getType().clone(); 1182 const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier()); 1183 splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType); 1184 } 1185 1186 // Recursive implementation of split(). 1187 // Returns reference to the modified type. 1188 const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier) 1189 { 1190 if (type.isStruct()) { 1191 TTypeList* userStructure = type.getWritableStruct(); 1192 for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) { 1193 if (ioType->type->isBuiltIn()) { 1194 // move out the built-in 1195 splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier); 1196 ioType = userStructure->erase(ioType); 1197 } else { 1198 split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier); 1199 ++ioType; 1200 } 1201 } 1202 } 1203 1204 return type; 1205 } 1206 1207 // Is this a uniform array or structure which should be flattened? 1208 bool HlslParseContext::shouldFlatten(const TType& type) const 1209 { 1210 const TStorageQualifier qualifier = type.getQualifier().storage; 1211 1212 return (qualifier == EvqUniform && type.isArray() && intermediate.getFlattenUniformArrays()) || 1213 (type.isStruct() && type.containsOpaque()); 1214 } 1215 1216 // Top level variable flattening: construct data 1217 void HlslParseContext::flatten(const TVariable& variable, bool linkage) 1218 { 1219 const TType& type = variable.getType(); 1220 1221 // If it's a standalone built-in, there is nothing to flatten 1222 if (type.isBuiltIn() && !type.isStruct()) 1223 return; 1224 1225 auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(), 1226 TFlattenData(type.getQualifier().layoutBinding, 1227 type.getQualifier().layoutLocation))); 1228 1229 // the item is a map pair, so first->second is the TFlattenData itself. 1230 flatten(variable, type, entry.first->second, variable.getName(), linkage, type.getQualifier(), nullptr); 1231 } 1232 1233 // Recursively flatten the given variable at the provided type, building the flattenData as we go. 1234 // 1235 // This is mutually recursive with flattenStruct and flattenArray. 1236 // We are going to flatten an arbitrarily nested composite structure into a linear sequence of 1237 // members, and later on, we want to turn a path through the tree structure into a final 1238 // location in this linear sequence. 1239 // 1240 // If the tree was N-ary, that can be directly calculated. However, we are dealing with 1241 // arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must 1242 // build a data structure to allow the sequence of bracket and dot operators on arrays and 1243 // structs to arrive at the proper member. 1244 // 1245 // To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers. 1246 // The leaves are the indexes into the flattened member array. 1247 // Each level will have the next location for the Nth item stored sequentially, so for instance: 1248 // 1249 // struct { float2 a[2]; int b; float4 c[3] }; 1250 // 1251 // This will produce the following flattened tree: 1252 // Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 1253 // (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5} 1254 // 1255 // Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse: 1256 // (0+2) = 8 --> (8+1) = 12 --> 12 = 4 1257 // 1258 // so the 4th flattened member in traversal order is ours. 1259 // 1260 int HlslParseContext::flatten(const TVariable& variable, const TType& type, 1261 TFlattenData& flattenData, TString name, bool linkage, 1262 const TQualifier& outerQualifier, 1263 const TArraySizes* builtInArraySizes) 1264 { 1265 // If something is an arrayed struct, the array flattener will recursively call flatten() 1266 // to then flatten the struct, so this is an "if else": we don't do both. 1267 if (type.isArray()) 1268 return flattenArray(variable, type, flattenData, name, linkage, outerQualifier); 1269 else if (type.isStruct()) 1270 return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes); 1271 else { 1272 assert(0); // should never happen 1273 return -1; 1274 } 1275 } 1276 1277 // Add a single flattened member to the flattened data being tracked for the composite 1278 // Returns true for the final flattening level. 1279 int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData, 1280 const TString& memberName, bool linkage, 1281 const TQualifier& outerQualifier, 1282 const TArraySizes* builtInArraySizes) 1283 { 1284 if (isFinalFlattening(type)) { 1285 // This is as far as we flatten. Insert the variable. 1286 TVariable* memberVariable = makeInternalVariable(memberName, type); 1287 mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier()); 1288 1289 if (flattenData.nextBinding != TQualifier::layoutBindingEnd) 1290 memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++; 1291 1292 if (memberVariable->getType().isBuiltIn()) { 1293 // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number) 1294 memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; 1295 } else { 1296 // inherited locations must be auto bumped, not replicated 1297 if (flattenData.nextLocation != TQualifier::layoutLocationEnd) { 1298 memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation; 1299 flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType()); 1300 nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation); 1301 } 1302 } 1303 1304 flattenData.offsets.push_back(static_cast<int>(flattenData.members.size())); 1305 flattenData.members.push_back(memberVariable); 1306 1307 if (linkage) 1308 trackLinkage(*memberVariable); 1309 1310 return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference 1311 } else { 1312 // Further recursion required 1313 return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes); 1314 } 1315 } 1316 1317 // Figure out the mapping between an aggregate's top members and an 1318 // equivalent set of individual variables. 1319 // 1320 // Assumes shouldFlatten() or equivalent was called first. 1321 int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type, 1322 TFlattenData& flattenData, TString name, bool linkage, 1323 const TQualifier& outerQualifier, 1324 const TArraySizes* builtInArraySizes) 1325 { 1326 assert(type.isStruct()); 1327 1328 auto members = *type.getStruct(); 1329 1330 // Reserve space for this tree level. 1331 int start = static_cast<int>(flattenData.offsets.size()); 1332 int pos = start; 1333 flattenData.offsets.resize(int(pos + members.size()), -1); 1334 1335 for (int member = 0; member < (int)members.size(); ++member) { 1336 TType& dereferencedType = *members[member].type; 1337 if (dereferencedType.isBuiltIn()) 1338 splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier); 1339 else { 1340 const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, 1341 name + "." + dereferencedType.getFieldName(), 1342 linkage, outerQualifier, 1343 builtInArraySizes == nullptr && dereferencedType.isArray() 1344 ? &dereferencedType.getArraySizes() 1345 : builtInArraySizes); 1346 flattenData.offsets[pos++] = mpos; 1347 } 1348 } 1349 1350 return start; 1351 } 1352 1353 // Figure out mapping between an array's members and an 1354 // equivalent set of individual variables. 1355 // 1356 // Assumes shouldFlatten() or equivalent was called first. 1357 int HlslParseContext::flattenArray(const TVariable& variable, const TType& type, 1358 TFlattenData& flattenData, TString name, bool linkage, 1359 const TQualifier& outerQualifier) 1360 { 1361 assert(type.isArray() && !type.isImplicitlySizedArray()); 1362 1363 const int size = type.getOuterArraySize(); 1364 const TType dereferencedType(type, 0); 1365 1366 if (name.empty()) 1367 name = variable.getName(); 1368 1369 // Reserve space for this tree level. 1370 int start = static_cast<int>(flattenData.offsets.size()); 1371 int pos = start; 1372 flattenData.offsets.resize(int(pos + size), -1); 1373 1374 for (int element=0; element < size; ++element) { 1375 char elementNumBuf[20]; // sufficient for MAXINT 1376 snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element); 1377 const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, 1378 name + elementNumBuf, linkage, outerQualifier, 1379 type.getArraySizes()); 1380 1381 flattenData.offsets[pos++] = mpos; 1382 } 1383 1384 return start; 1385 } 1386 1387 // Return true if we have flattened this node. 1388 bool HlslParseContext::wasFlattened(const TIntermTyped* node) const 1389 { 1390 return node != nullptr && node->getAsSymbolNode() != nullptr && 1391 wasFlattened(node->getAsSymbolNode()->getId()); 1392 } 1393 1394 // Return true if we have split this structure 1395 bool HlslParseContext::wasSplit(const TIntermTyped* node) const 1396 { 1397 return node != nullptr && node->getAsSymbolNode() != nullptr && 1398 wasSplit(node->getAsSymbolNode()->getId()); 1399 } 1400 1401 // Turn an access into an aggregate that was flattened to instead be 1402 // an access to the individual variable the member was flattened to. 1403 // Assumes wasFlattened() or equivalent was called first. 1404 TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member) 1405 { 1406 const TType dereferencedType(base->getType(), member); // dereferenced type 1407 const TIntermSymbol& symbolNode = *base->getAsSymbolNode(); 1408 TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, dereferencedType, symbolNode.getFlattenSubset()); 1409 1410 return flattened ? flattened : base; 1411 } 1412 TIntermTyped* HlslParseContext::flattenAccess(int uniqueId, int member, const TType& dereferencedType, int subset) 1413 { 1414 const auto flattenData = flattenMap.find(uniqueId); 1415 1416 if (flattenData == flattenMap.end()) 1417 return nullptr; 1418 1419 // Calculate new cumulative offset from the packed tree 1420 int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member]; 1421 1422 TIntermSymbol* subsetSymbol; 1423 if (isFinalFlattening(dereferencedType)) { 1424 // Finished flattening: create symbol for variable 1425 member = flattenData->second.offsets[newSubset]; 1426 const TVariable* memberVariable = flattenData->second.members[member]; 1427 subsetSymbol = intermediate.addSymbol(*memberVariable); 1428 subsetSymbol->setFlattenSubset(-1); 1429 } else { 1430 1431 // If this is not the final flattening, accumulate the position and return 1432 // an object of the partially dereferenced type. 1433 subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", dereferencedType); 1434 subsetSymbol->setFlattenSubset(newSubset); 1435 } 1436 1437 return subsetSymbol; 1438 } 1439 1440 // Find and return the split IO TVariable for id, or nullptr if none. 1441 TVariable* HlslParseContext::getSplitNonIoVar(int id) const 1442 { 1443 const auto splitNonIoVar = splitNonIoVars.find(id); 1444 if (splitNonIoVar == splitNonIoVars.end()) 1445 return nullptr; 1446 1447 return splitNonIoVar->second; 1448 } 1449 1450 // Pass through to base class after remembering built-in mappings. 1451 void HlslParseContext::trackLinkage(TSymbol& symbol) 1452 { 1453 TBuiltInVariable biType = symbol.getType().getQualifier().builtIn; 1454 1455 if (biType != EbvNone) 1456 builtInTessLinkageSymbols[biType] = symbol.clone(); 1457 1458 TParseContextBase::trackLinkage(symbol); 1459 } 1460 1461 1462 // Returns true if the built-in is a clip or cull distance variable. 1463 bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn) 1464 { 1465 return builtIn == EbvClipDistance || builtIn == EbvCullDistance; 1466 } 1467 1468 // Some types require fixed array sizes in SPIR-V, but can be scalars or 1469 // arrays of sizes SPIR-V doesn't allow. For example, tessellation factors. 1470 // This creates the right size. A conversion is performed when the internal 1471 // type is copied to or from the external type. This corrects the externally 1472 // facing input or output type to abide downstream semantics. 1473 void HlslParseContext::fixBuiltInIoType(TType& type) 1474 { 1475 int requiredArraySize = 0; 1476 1477 switch (type.getQualifier().builtIn) { 1478 case EbvTessLevelOuter: requiredArraySize = 4; break; 1479 case EbvTessLevelInner: requiredArraySize = 2; break; 1480 1481 case EbvTessCoord: 1482 { 1483 // tesscoord is always a vec3 for the IO variable, no matter the shader's 1484 // declared vector size. 1485 TType tessCoordType(type.getBasicType(), type.getQualifier().storage, 3); 1486 1487 tessCoordType.getQualifier() = type.getQualifier(); 1488 type.shallowCopy(tessCoordType); 1489 1490 break; 1491 } 1492 default: 1493 if (isClipOrCullDistance(type)) { 1494 const int loc = type.getQualifier().layoutLocation; 1495 1496 if (type.getQualifier().builtIn == EbvClipDistance) { 1497 if (type.getQualifier().storage == EvqVaryingIn) 1498 clipSemanticNSizeIn[loc] = type.getVectorSize(); 1499 else 1500 clipSemanticNSizeOut[loc] = type.getVectorSize(); 1501 } else { 1502 if (type.getQualifier().storage == EvqVaryingIn) 1503 cullSemanticNSizeIn[loc] = type.getVectorSize(); 1504 else 1505 cullSemanticNSizeOut[loc] = type.getVectorSize(); 1506 } 1507 } 1508 1509 return; 1510 } 1511 1512 // Alter or set array size as needed. 1513 if (requiredArraySize > 0) { 1514 if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) { 1515 TArraySizes arraySizes; 1516 arraySizes.addInnerSize(requiredArraySize); 1517 type.newArraySizes(arraySizes); 1518 } 1519 } 1520 } 1521 1522 // Variables that correspond to the user-interface in and out of a stage 1523 // (not the built-in interface) are 1524 // - assigned locations 1525 // - registered as a linkage node (part of the stage's external interface). 1526 // Assumes it is called in the order in which locations should be assigned. 1527 void HlslParseContext::assignToInterface(TVariable& variable) 1528 { 1529 const auto assignLocation = [&](TVariable& variable) { 1530 TType& type = variable.getWritableType(); 1531 if (!type.isStruct() || type.getStruct()->size() > 0) { 1532 TQualifier& qualifier = type.getQualifier(); 1533 if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) { 1534 if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) { 1535 // Strip off the outer array dimension for those having an extra one. 1536 int size; 1537 if (type.isArray() && qualifier.isArrayedIo(language)) { 1538 TType elementType(type, 0); 1539 size = intermediate.computeTypeLocationSize(elementType); 1540 } else 1541 size = intermediate.computeTypeLocationSize(type); 1542 1543 if (qualifier.storage == EvqVaryingIn) { 1544 variable.getWritableType().getQualifier().layoutLocation = nextInLocation; 1545 nextInLocation += size; 1546 } else { 1547 variable.getWritableType().getQualifier().layoutLocation = nextOutLocation; 1548 nextOutLocation += size; 1549 } 1550 } 1551 trackLinkage(variable); 1552 } 1553 } 1554 }; 1555 1556 if (wasFlattened(variable.getUniqueId())) { 1557 auto& memberList = flattenMap[variable.getUniqueId()].members; 1558 for (auto member = memberList.begin(); member != memberList.end(); ++member) 1559 assignLocation(**member); 1560 } else if (wasSplit(variable.getUniqueId())) { 1561 TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId()); 1562 assignLocation(*splitIoVar); 1563 } else { 1564 assignLocation(variable); 1565 } 1566 } 1567 1568 // 1569 // Handle seeing a function declarator in the grammar. This is the precursor 1570 // to recognizing a function prototype or function definition. 1571 // 1572 void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) 1573 { 1574 // 1575 // Multiple declarations of the same function name are allowed. 1576 // 1577 // If this is a definition, the definition production code will check for redefinitions 1578 // (we don't know at this point if it's a definition or not). 1579 // 1580 bool builtIn; 1581 TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); 1582 const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; 1583 1584 if (prototype) { 1585 // All built-in functions are defined, even though they don't have a body. 1586 // Count their prototype as a definition instead. 1587 if (symbolTable.atBuiltInLevel()) 1588 function.setDefined(); 1589 else { 1590 if (prevDec && ! builtIn) 1591 symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const 1592 function.setPrototyped(); 1593 } 1594 } 1595 1596 // This insert won't actually insert it if it's a duplicate signature, but it will still check for 1597 // other forms of name collisions. 1598 if (! symbolTable.insert(function)) 1599 error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); 1600 } 1601 1602 // For struct buffers with counters, we must pass the counter buffer as hidden parameter. 1603 // This adds the hidden parameter to the parameter list in 'paramNodes' if needed. 1604 // Otherwise, it's a no-op 1605 void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param, 1606 TIntermAggregate*& paramNodes) 1607 { 1608 if (! hasStructBuffCounter(*param.type)) 1609 return; 1610 1611 const TString counterBlockName(getStructBuffCounterName(*param.name)); 1612 1613 TType counterType; 1614 counterBufferType(loc, counterType); 1615 TVariable *variable = makeInternalVariable(counterBlockName, counterType); 1616 1617 if (! symbolTable.insert(*variable)) 1618 error(loc, "redefinition", variable->getName().c_str(), ""); 1619 1620 paramNodes = intermediate.growAggregate(paramNodes, 1621 intermediate.addSymbol(*variable, loc), 1622 loc); 1623 } 1624 1625 // 1626 // Handle seeing the function prototype in front of a function definition in the grammar. 1627 // The body is handled after this function returns. 1628 // 1629 // Returns an aggregate of parameter-symbol nodes. 1630 // 1631 TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function, 1632 const TAttributeMap& attributes, 1633 TIntermNode*& entryPointTree) 1634 { 1635 currentCaller = function.getMangledName(); 1636 TSymbol* symbol = symbolTable.find(function.getMangledName()); 1637 TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; 1638 1639 if (prevDec == nullptr) 1640 error(loc, "can't find function", function.getName().c_str(), ""); 1641 // Note: 'prevDec' could be 'function' if this is the first time we've seen function 1642 // as it would have just been put in the symbol table. Otherwise, we're looking up 1643 // an earlier occurrence. 1644 1645 if (prevDec && prevDec->isDefined()) { 1646 // Then this function already has a body. 1647 error(loc, "function already has a body", function.getName().c_str(), ""); 1648 } 1649 if (prevDec && ! prevDec->isDefined()) { 1650 prevDec->setDefined(); 1651 1652 // Remember the return type for later checking for RETURN statements. 1653 currentFunctionType = &(prevDec->getType()); 1654 } else 1655 currentFunctionType = new TType(EbtVoid); 1656 functionReturnsValue = false; 1657 1658 // Entry points need different I/O and other handling, transform it so the 1659 // rest of this function doesn't care. 1660 entryPointTree = transformEntryPoint(loc, function, attributes); 1661 1662 // 1663 // New symbol table scope for body of function plus its arguments 1664 // 1665 pushScope(); 1666 1667 // 1668 // Insert parameters into the symbol table. 1669 // If the parameter has no name, it's not an error, just don't insert it 1670 // (could be used for unused args). 1671 // 1672 // Also, accumulate the list of parameters into the AST, so lower level code 1673 // knows where to find parameters. 1674 // 1675 TIntermAggregate* paramNodes = new TIntermAggregate; 1676 for (int i = 0; i < function.getParamCount(); i++) { 1677 TParameter& param = function[i]; 1678 if (param.name != nullptr) { 1679 TVariable *variable = new TVariable(param.name, *param.type); 1680 1681 if (i == 0 && function.hasImplicitThis()) { 1682 // Anonymous 'this' members are already in a symbol-table level, 1683 // and we need to know what function parameter to map them to. 1684 symbolTable.makeInternalVariable(*variable); 1685 pushImplicitThis(variable); 1686 } 1687 1688 // Insert the parameters with name in the symbol table. 1689 if (! symbolTable.insert(*variable)) 1690 error(loc, "redefinition", variable->getName().c_str(), ""); 1691 1692 // Add parameters to the AST list. 1693 if (shouldFlatten(variable->getType())) { 1694 // Expand the AST parameter nodes (but not the name mangling or symbol table view) 1695 // for structures that need to be flattened. 1696 flatten(*variable, false); 1697 const TTypeList* structure = variable->getType().getStruct(); 1698 for (int mem = 0; mem < (int)structure->size(); ++mem) { 1699 paramNodes = intermediate.growAggregate(paramNodes, 1700 flattenAccess(variable->getUniqueId(), mem, 1701 *(*structure)[mem].type), 1702 loc); 1703 } 1704 } else { 1705 // Add the parameter to the AST 1706 paramNodes = intermediate.growAggregate(paramNodes, 1707 intermediate.addSymbol(*variable, loc), 1708 loc); 1709 } 1710 1711 // Add hidden AST parameter for struct buffer counters, if needed. 1712 addStructBufferHiddenCounterParam(loc, param, paramNodes); 1713 } else 1714 paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); 1715 } 1716 if (function.hasIllegalImplicitThis()) 1717 pushImplicitThis(nullptr); 1718 1719 intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); 1720 loopNestingLevel = 0; 1721 controlFlowNestingLevel = 0; 1722 postEntryPointReturn = false; 1723 1724 return paramNodes; 1725 } 1726 1727 1728 // Handle all [attrib] attribute for the shader entry point 1729 void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes) 1730 { 1731 // Handle entry-point function attributes 1732 const TIntermAggregate* numThreads = attributes[EatNumThreads]; 1733 if (numThreads != nullptr) { 1734 const TIntermSequence& sequence = numThreads->getSequence(); 1735 1736 for (int lid = 0; lid < int(sequence.size()); ++lid) 1737 intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst()); 1738 } 1739 1740 // MaxVertexCount 1741 const TIntermAggregate* maxVertexCount = attributes[EatMaxVertexCount]; 1742 if (maxVertexCount != nullptr) { 1743 if (! intermediate.setVertices(maxVertexCount->getSequence()[0]->getAsConstantUnion()-> 1744 getConstArray()[0].getIConst())) { 1745 error(loc, "cannot change previously set maxvertexcount attribute", "", ""); 1746 } 1747 } 1748 1749 // Handle [patchconstantfunction("...")] 1750 const TIntermAggregate* pcfAttr = attributes[EatPatchConstantFunc]; 1751 if (pcfAttr != nullptr) { 1752 const TConstUnion& pcfName = pcfAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0]; 1753 1754 if (pcfName.getType() != EbtString) { 1755 error(loc, "invalid patch constant function", "", ""); 1756 } else { 1757 patchConstantFunctionName = *pcfName.getSConst(); 1758 } 1759 } 1760 1761 // Handle [domain("...")] 1762 const TIntermAggregate* domainAttr = attributes[EatDomain]; 1763 if (domainAttr != nullptr) { 1764 const TConstUnion& domainType = domainAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0]; 1765 if (domainType.getType() != EbtString) { 1766 error(loc, "invalid domain", "", ""); 1767 } else { 1768 TString domainStr = *domainType.getSConst(); 1769 std::transform(domainStr.begin(), domainStr.end(), domainStr.begin(), ::tolower); 1770 1771 TLayoutGeometry domain = ElgNone; 1772 1773 if (domainStr == "tri") { 1774 domain = ElgTriangles; 1775 } else if (domainStr == "quad") { 1776 domain = ElgQuads; 1777 } else if (domainStr == "isoline") { 1778 domain = ElgIsolines; 1779 } else { 1780 error(loc, "unsupported domain type", domainStr.c_str(), ""); 1781 } 1782 1783 if (language == EShLangTessEvaluation) { 1784 if (! intermediate.setInputPrimitive(domain)) 1785 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); 1786 } else { 1787 if (! intermediate.setOutputPrimitive(domain)) 1788 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); 1789 } 1790 } 1791 } 1792 1793 // Handle [outputtopology("...")] 1794 const TIntermAggregate* topologyAttr = attributes[EatOutputTopology]; 1795 if (topologyAttr != nullptr) { 1796 const TConstUnion& topoType = topologyAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0]; 1797 if (topoType.getType() != EbtString) { 1798 error(loc, "invalid outputtopology", "", ""); 1799 } else { 1800 TString topologyStr = *topoType.getSConst(); 1801 std::transform(topologyStr.begin(), topologyStr.end(), topologyStr.begin(), ::tolower); 1802 1803 TVertexOrder vertexOrder = EvoNone; 1804 TLayoutGeometry primitive = ElgNone; 1805 1806 if (topologyStr == "point") { 1807 intermediate.setPointMode(); 1808 } else if (topologyStr == "line") { 1809 primitive = ElgIsolines; 1810 } else if (topologyStr == "triangle_cw") { 1811 vertexOrder = EvoCw; 1812 primitive = ElgTriangles; 1813 } else if (topologyStr == "triangle_ccw") { 1814 vertexOrder = EvoCcw; 1815 primitive = ElgTriangles; 1816 } else { 1817 error(loc, "unsupported outputtopology type", topologyStr.c_str(), ""); 1818 } 1819 1820 if (vertexOrder != EvoNone) { 1821 if (! intermediate.setVertexOrder(vertexOrder)) { 1822 error(loc, "cannot change previously set outputtopology", 1823 TQualifier::getVertexOrderString(vertexOrder), ""); 1824 } 1825 } 1826 if (primitive != ElgNone) 1827 intermediate.setOutputPrimitive(primitive); 1828 } 1829 } 1830 1831 // Handle [partitioning("...")] 1832 const TIntermAggregate* partitionAttr = attributes[EatPartitioning]; 1833 if (partitionAttr != nullptr) { 1834 const TConstUnion& partType = partitionAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0]; 1835 if (partType.getType() != EbtString) { 1836 error(loc, "invalid partitioning", "", ""); 1837 } else { 1838 TString partitionStr = *partType.getSConst(); 1839 std::transform(partitionStr.begin(), partitionStr.end(), partitionStr.begin(), ::tolower); 1840 1841 TVertexSpacing partitioning = EvsNone; 1842 1843 if (partitionStr == "integer") { 1844 partitioning = EvsEqual; 1845 } else if (partitionStr == "fractional_even") { 1846 partitioning = EvsFractionalEven; 1847 } else if (partitionStr == "fractional_odd") { 1848 partitioning = EvsFractionalOdd; 1849 //} else if (partition == "pow2") { // TODO: currently nothing to map this to. 1850 } else { 1851 error(loc, "unsupported partitioning type", partitionStr.c_str(), ""); 1852 } 1853 1854 if (! intermediate.setVertexSpacing(partitioning)) 1855 error(loc, "cannot change previously set partitioning", 1856 TQualifier::getVertexSpacingString(partitioning), ""); 1857 } 1858 } 1859 1860 // Handle [outputcontrolpoints("...")] 1861 const TIntermAggregate* outputControlPoints = attributes[EatOutputControlPoints]; 1862 if (outputControlPoints != nullptr) { 1863 const TConstUnion& ctrlPointConst = 1864 outputControlPoints->getSequence()[0]->getAsConstantUnion()->getConstArray()[0]; 1865 if (ctrlPointConst.getType() != EbtInt) { 1866 error(loc, "invalid outputcontrolpoints", "", ""); 1867 } else { 1868 const int ctrlPoints = ctrlPointConst.getIConst(); 1869 if (! intermediate.setVertices(ctrlPoints)) { 1870 error(loc, "cannot change previously set outputcontrolpoints attribute", "", ""); 1871 } 1872 } 1873 } 1874 } 1875 1876 // 1877 // Do all special handling for the entry point, including wrapping 1878 // the shader's entry point with the official entry point that will call it. 1879 // 1880 // The following: 1881 // 1882 // retType shaderEntryPoint(args...) // shader declared entry point 1883 // { body } 1884 // 1885 // Becomes 1886 // 1887 // out retType ret; 1888 // in iargs<that are input>...; 1889 // out oargs<that are output> ...; 1890 // 1891 // void shaderEntryPoint() // synthesized, but official, entry point 1892 // { 1893 // args<that are input> = iargs...; 1894 // ret = @shaderEntryPoint(args...); 1895 // oargs = args<that are output>...; 1896 // } 1897 // retType @shaderEntryPoint(args...) 1898 // { body } 1899 // 1900 // The symbol table will still map the original entry point name to the 1901 // the modified function and its new name: 1902 // 1903 // symbol table: shaderEntryPoint -> @shaderEntryPoint 1904 // 1905 // Returns nullptr if no entry-point tree was built, otherwise, returns 1906 // a subtree that creates the entry point. 1907 // 1908 TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, 1909 const TAttributeMap& attributes) 1910 { 1911 // Return true if this is a tessellation patch constant function input to a domain shader. 1912 const auto isDsPcfInput = [this](const TType& type) { 1913 return language == EShLangTessEvaluation && 1914 type.contains([](const TType* t) { 1915 return t->getQualifier().builtIn == EbvTessLevelOuter || 1916 t->getQualifier().builtIn == EbvTessLevelInner; 1917 }); 1918 }; 1919 1920 // if we aren't in the entry point, fix the IO as such and exit 1921 if (userFunction.getName().compare(intermediate.getEntryPointName().c_str()) != 0) { 1922 remapNonEntryPointIO(userFunction); 1923 return nullptr; 1924 } 1925 1926 entryPointFunction = &userFunction; // needed in finish() 1927 1928 // Handle entry point attributes 1929 handleEntryPointAttributes(loc, attributes); 1930 1931 // entry point logic... 1932 1933 // Move parameters and return value to shader in/out 1934 TVariable* entryPointOutput; // gets created in remapEntryPointIO 1935 TVector<TVariable*> inputs; 1936 TVector<TVariable*> outputs; 1937 remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs); 1938 1939 // Further this return/in/out transform by flattening, splitting, and assigning locations 1940 const auto makeVariableInOut = [&](TVariable& variable) { 1941 if (variable.getType().isStruct()) { 1942 if (variable.getType().getQualifier().isArrayedIo(language)) { 1943 if (variable.getType().containsBuiltIn()) 1944 split(variable); 1945 } else 1946 flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */); 1947 } 1948 // TODO: flatten arrays too 1949 // TODO: flatten everything in I/O 1950 // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed 1951 1952 // For clip and cull distance, multiple output variables potentially get merged 1953 // into one in assignClipCullDistance. That code in assignClipCullDistance 1954 // handles the interface logic, so we avoid it here in that case. 1955 if (!isClipOrCullDistance(variable.getType())) 1956 assignToInterface(variable); 1957 }; 1958 if (entryPointOutput != nullptr) 1959 makeVariableInOut(*entryPointOutput); 1960 for (auto it = inputs.begin(); it != inputs.end(); ++it) 1961 if (!isDsPcfInput((*it)->getType())) // wait until the end for PCF input (see comment below) 1962 makeVariableInOut(*(*it)); 1963 for (auto it = outputs.begin(); it != outputs.end(); ++it) 1964 makeVariableInOut(*(*it)); 1965 1966 // In the domain shader, PCF input must be at the end of the linkage. That's because in the 1967 // hull shader there is no ordering: the output comes from the separate PCF, which does not 1968 // participate in the argument list. That is always put at the end of the HS linkage, so the 1969 // input side of the DS must match. The argument may be in any position in the DS argument list 1970 // however, so this ensures the linkage is built in the correct order regardless of argument order. 1971 if (language == EShLangTessEvaluation) { 1972 for (auto it = inputs.begin(); it != inputs.end(); ++it) 1973 if (isDsPcfInput((*it)->getType())) 1974 makeVariableInOut(*(*it)); 1975 } 1976 1977 // Synthesize the call 1978 1979 pushScope(); // matches the one in handleFunctionBody() 1980 1981 // new signature 1982 TType voidType(EbtVoid); 1983 TFunction synthEntryPoint(&userFunction.getName(), voidType); 1984 TIntermAggregate* synthParams = new TIntermAggregate(); 1985 intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc); 1986 intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str()); 1987 intermediate.incrementEntryPointCount(); 1988 TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table 1989 1990 // change original name 1991 userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table 1992 1993 // Copy inputs (shader-in -> calling arg), while building up the call node 1994 TVector<TVariable*> argVars; 1995 TIntermAggregate* synthBody = new TIntermAggregate(); 1996 auto inputIt = inputs.begin(); 1997 TIntermTyped* callingArgs = nullptr; 1998 1999 for (int i = 0; i < userFunction.getParamCount(); i++) { 2000 TParameter& param = userFunction[i]; 2001 argVars.push_back(makeInternalVariable(*param.name, *param.type)); 2002 argVars.back()->getWritableType().getQualifier().makeTemporary(); 2003 TIntermSymbol* arg = intermediate.addSymbol(*argVars.back()); 2004 handleFunctionArgument(&callee, callingArgs, arg); 2005 if (param.type->getQualifier().isParamInput()) { 2006 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, 2007 intermediate.addSymbol(**inputIt))); 2008 inputIt++; 2009 } 2010 } 2011 2012 // Call 2013 currentCaller = synthEntryPoint.getMangledName(); 2014 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); 2015 currentCaller = userFunction.getMangledName(); 2016 2017 // Return value 2018 if (entryPointOutput) { 2019 TIntermTyped* returnAssign; 2020 2021 // For hull shaders, the wrapped entry point return value is written to 2022 // an array element as indexed by invocation ID, which we might have to make up. 2023 // This is required to match SPIR-V semantics. 2024 if (language == EShLangTessControl) { 2025 TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); 2026 2027 // If there is no user declared invocation ID, we must make one. 2028 if (invocationIdSym == nullptr) { 2029 TType invocationIdType(EbtUint, EvqIn, 1); 2030 TString* invocationIdName = NewPoolTString("InvocationId"); 2031 invocationIdType.getQualifier().builtIn = EbvInvocationId; 2032 2033 TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType); 2034 2035 globalQualifierFix(loc, variable->getWritableType().getQualifier()); 2036 trackLinkage(*variable); 2037 2038 invocationIdSym = intermediate.addSymbol(*variable); 2039 } 2040 2041 TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput), 2042 invocationIdSym, loc); 2043 element->setType(callReturn->getType()); 2044 2045 returnAssign = handleAssign(loc, EOpAssign, element, callReturn); 2046 } else { 2047 returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn); 2048 } 2049 intermediate.growAggregate(synthBody, returnAssign); 2050 } else 2051 intermediate.growAggregate(synthBody, callReturn); 2052 2053 // Output copies 2054 auto outputIt = outputs.begin(); 2055 for (int i = 0; i < userFunction.getParamCount(); i++) { 2056 TParameter& param = userFunction[i]; 2057 2058 // GS outputs are via emit, so we do not copy them here. 2059 if (param.type->getQualifier().isParamOutput()) { 2060 if (param.getDeclaredBuiltIn() == EbvGsOutputStream) { 2061 // GS output stream does not assign outputs here: it's the Append() method 2062 // which writes to the output, probably multiple times separated by Emit. 2063 // We merely remember the output to use, here. 2064 gsStreamOutput = *outputIt; 2065 } else { 2066 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, 2067 intermediate.addSymbol(**outputIt), 2068 intermediate.addSymbol(*argVars[i]))); 2069 } 2070 2071 outputIt++; 2072 } 2073 } 2074 2075 // Put the pieces together to form a full function subtree 2076 // for the synthesized entry point. 2077 synthBody->setOperator(EOpSequence); 2078 TIntermNode* synthFunctionDef = synthParams; 2079 handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef); 2080 2081 entryPointFunctionBody = synthBody; 2082 2083 return synthFunctionDef; 2084 } 2085 2086 void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, 2087 TIntermNode*& node) 2088 { 2089 node = intermediate.growAggregate(node, functionBody); 2090 intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc); 2091 node->getAsAggregate()->setName(function.getMangledName().c_str()); 2092 2093 popScope(); 2094 if (function.hasImplicitThis()) 2095 popImplicitThis(); 2096 2097 if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue) 2098 error(loc, "function does not return a value:", "", function.getName().c_str()); 2099 } 2100 2101 // AST I/O is done through shader globals declared in the 'in' or 'out' 2102 // storage class. An HLSL entry point has a return value, input parameters 2103 // and output parameters. These need to get remapped to the AST I/O. 2104 void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue, 2105 TVector<TVariable*>& inputs, TVector<TVariable*>& outputs) 2106 { 2107 // We might have in input structure type with no decorations that caused it 2108 // to look like an input type, yet it has (e.g.) interpolation types that 2109 // must be modified that turn it into an input type. 2110 // Hence, a missing ioTypeMap for 'input' might need to be synthesized. 2111 const auto synthesizeEditedInput = [this](TType& type) { 2112 // True if a type needs to be 'flat' 2113 const auto needsFlat = [](const TType& type) { 2114 return type.containsBasicType(EbtInt) || 2115 type.containsBasicType(EbtUint) || 2116 type.containsBasicType(EbtInt64) || 2117 type.containsBasicType(EbtUint64) || 2118 type.containsBasicType(EbtBool) || 2119 type.containsBasicType(EbtDouble); 2120 }; 2121 2122 if (language == EShLangFragment && needsFlat(type)) { 2123 if (type.isStruct()) { 2124 TTypeList* finalList = nullptr; 2125 auto it = ioTypeMap.find(type.getStruct()); 2126 if (it == ioTypeMap.end() || it->second.input == nullptr) { 2127 // Getting here means we have no input struct, but we need one. 2128 auto list = new TTypeList; 2129 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { 2130 TType* newType = new TType; 2131 newType->shallowCopy(*member->type); 2132 TTypeLoc typeLoc = { newType, member->loc }; 2133 list->push_back(typeLoc); 2134 } 2135 // install the new input type 2136 if (it == ioTypeMap.end()) { 2137 tIoKinds newLists = { list, nullptr, nullptr }; 2138 ioTypeMap[type.getStruct()] = newLists; 2139 } else 2140 it->second.input = list; 2141 finalList = list; 2142 } else 2143 finalList = it->second.input; 2144 // edit for 'flat' 2145 for (auto member = finalList->begin(); member != finalList->end(); ++member) { 2146 if (needsFlat(*member->type)) { 2147 member->type->getQualifier().clearInterpolation(); 2148 member->type->getQualifier().flat = true; 2149 } 2150 } 2151 } else { 2152 type.getQualifier().clearInterpolation(); 2153 type.getQualifier().flat = true; 2154 } 2155 } 2156 }; 2157 2158 // Do the actual work to make a type be a shader input or output variable, 2159 // and clear the original to be non-IO (for use as a normal function parameter/return). 2160 const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* { 2161 TVariable* ioVariable = makeInternalVariable(name, type); 2162 clearUniformInputOutput(type.getQualifier()); 2163 if (type.isStruct()) { 2164 auto newLists = ioTypeMap.find(ioVariable->getType().getStruct()); 2165 if (newLists != ioTypeMap.end()) { 2166 if (storage == EvqVaryingIn && newLists->second.input) 2167 ioVariable->getWritableType().setStruct(newLists->second.input); 2168 else if (storage == EvqVaryingOut && newLists->second.output) 2169 ioVariable->getWritableType().setStruct(newLists->second.output); 2170 } 2171 } 2172 if (storage == EvqVaryingIn) { 2173 correctInput(ioVariable->getWritableType().getQualifier()); 2174 if (language == EShLangTessEvaluation) 2175 if (!ioVariable->getType().isArray()) 2176 ioVariable->getWritableType().getQualifier().patch = true; 2177 } else { 2178 correctOutput(ioVariable->getWritableType().getQualifier()); 2179 } 2180 ioVariable->getWritableType().getQualifier().storage = storage; 2181 2182 fixBuiltInIoType(ioVariable->getWritableType()); 2183 2184 return ioVariable; 2185 }; 2186 2187 // return value is actually a shader-scoped output (out) 2188 if (function.getType().getBasicType() == EbtVoid) { 2189 returnValue = nullptr; 2190 } else { 2191 if (language == EShLangTessControl) { 2192 // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an 2193 // array in SPIR-V semantics. We'll write to it indexed by invocation ID. 2194 2195 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); 2196 2197 TType outputType; 2198 outputType.shallowCopy(function.getType()); 2199 2200 // vertices has necessarily already been set when handling entry point attributes. 2201 TArraySizes arraySizes; 2202 arraySizes.addInnerSize(intermediate.getVertices()); 2203 outputType.newArraySizes(arraySizes); 2204 2205 clearUniformInputOutput(function.getWritableType().getQualifier()); 2206 returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut); 2207 } else { 2208 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); 2209 } 2210 } 2211 2212 // parameters are actually shader-scoped inputs and outputs (in or out) 2213 for (int i = 0; i < function.getParamCount(); i++) { 2214 TType& paramType = *function[i].type; 2215 if (paramType.getQualifier().isParamInput()) { 2216 synthesizeEditedInput(paramType); 2217 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn); 2218 inputs.push_back(argAsGlobal); 2219 2220 if (function[i].getDeclaredBuiltIn() == EbvInputPatch) 2221 inputPatch = argAsGlobal; 2222 } 2223 if (paramType.getQualifier().isParamOutput()) { 2224 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut); 2225 outputs.push_back(argAsGlobal); 2226 } 2227 } 2228 } 2229 2230 // An HLSL function that looks like an entry point, but is not, 2231 // declares entry point IO built-ins, but these have to be undone. 2232 void HlslParseContext::remapNonEntryPointIO(TFunction& function) 2233 { 2234 // return value 2235 if (function.getType().getBasicType() != EbtVoid) 2236 clearUniformInputOutput(function.getWritableType().getQualifier()); 2237 2238 // parameters. 2239 // References to structuredbuffer types are left unmodified 2240 for (int i = 0; i < function.getParamCount(); i++) 2241 if (!isReference(*function[i].type)) 2242 clearUniformInputOutput(function[i].type->getQualifier()); 2243 } 2244 2245 // Handle function returns, including type conversions to the function return type 2246 // if necessary. 2247 TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) 2248 { 2249 functionReturnsValue = true; 2250 2251 if (currentFunctionType->getBasicType() == EbtVoid) { 2252 error(loc, "void function cannot return a value", "return", ""); 2253 return intermediate.addBranch(EOpReturn, loc); 2254 } else if (*currentFunctionType != value->getType()) { 2255 value = intermediate.addConversion(EOpReturn, *currentFunctionType, value); 2256 if (value && *currentFunctionType != value->getType()) 2257 value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value); 2258 if (value == nullptr || *currentFunctionType != value->getType()) { 2259 error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); 2260 return value; 2261 } 2262 } 2263 2264 return intermediate.addBranch(EOpReturn, value, loc); 2265 } 2266 2267 void HlslParseContext::handleFunctionArgument(TFunction* function, 2268 TIntermTyped*& arguments, TIntermTyped* newArg) 2269 { 2270 TParameter param = { 0, new TType, nullptr }; 2271 param.type->shallowCopy(newArg->getType()); 2272 2273 function->addParameter(param); 2274 if (arguments) 2275 arguments = intermediate.growAggregate(arguments, newArg); 2276 else 2277 arguments = newArg; 2278 } 2279 2280 // Clip and cull distance require special handling due to a semantic mismatch. In HLSL, 2281 // these can be float scalar, float vector, or arrays of float scalar or float vector. 2282 // In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components 2283 // (e.g, both x and y components of a float2) out into the destination float array. 2284 // 2285 // The values are assigned to sequential members of the output array. The inner dimension 2286 // is vector components. The outer dimension is array elements. 2287 TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId, 2288 TIntermTyped* left, TIntermTyped* right) 2289 { 2290 switch (language) { 2291 case EShLangFragment: 2292 case EShLangVertex: 2293 case EShLangGeometry: 2294 break; 2295 default: 2296 error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", ""); 2297 return nullptr; 2298 } 2299 2300 TVariable** clipCullVar = nullptr; 2301 2302 // Figure out if we are assigning to, or from, clip or cull distance. 2303 const bool isOutput = isClipOrCullDistance(left->getType()); 2304 2305 // This is the rvalue or lvalue holding the clip or cull distance. 2306 TIntermTyped* clipCullNode = isOutput ? left : right; 2307 // This is the value going into or out of the clip or cull distance. 2308 TIntermTyped* internalNode = isOutput ? right : left; 2309 2310 const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn; 2311 2312 decltype(clipSemanticNSizeIn)* semanticNSize = nullptr; 2313 2314 // Refer to either the clip or the cull distance, depending on semantic. 2315 switch (builtInType) { 2316 case EbvClipDistance: 2317 clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput; 2318 semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn; 2319 break; 2320 case EbvCullDistance: 2321 clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput; 2322 semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn; 2323 break; 2324 2325 // called invalidly: we expected a clip or a cull distance. 2326 // static compile time problem: should not happen. 2327 default: assert(0); return nullptr; 2328 } 2329 2330 // This is the offset in the destination array of a given semantic's data 2331 std::array<int, maxClipCullRegs> semanticOffset; 2332 2333 // Calculate offset of variable of semantic N in destination array 2334 int arrayLoc = 0; 2335 int vecItems = 0; 2336 2337 for (int x = 0; x < maxClipCullRegs; ++x) { 2338 // See if we overflowed the vec4 packing 2339 if ((vecItems + (*semanticNSize)[x]) > 4) { 2340 arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4 2341 vecItems = 0; 2342 } 2343 2344 semanticOffset[x] = arrayLoc; 2345 vecItems += (*semanticNSize)[x]; 2346 arrayLoc += (*semanticNSize)[x]; 2347 } 2348 2349 2350 // It can have up to 2 array dimensions (in the case of geometry shader inputs) 2351 const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes(); 2352 const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0; 2353 // vector sizes: 2354 const int internalVectorSize = internalNode->getType().getVectorSize(); 2355 // array sizes, or 1 if it's not an array: 2356 const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1); 2357 const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1); 2358 2359 // The created type may be an array of arrays, e.g, for geometry shader inputs. 2360 const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput); 2361 2362 // If we haven't created the output already, create it now. 2363 if (*clipCullVar == nullptr) { 2364 // ClipDistance and CullDistance are handled specially in the entry point input/output copy 2365 // algorithm, because they may need to be unpacked from components of vectors (or a scalar) 2366 // into a float array, or vice versa. Here, we make the array the right size and type, 2367 // which depends on the incoming data, which has several potential dimensions: 2368 // * Semantic ID 2369 // * vector size 2370 // * array size 2371 // Of those, semantic ID and array size cannot appear simultaneously. 2372 // 2373 // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two 2374 // array dimensions. The shader's declaration may have one or two array dimensions. One is always 2375 // the geometry's dimension. 2376 2377 const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed; 2378 2379 const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1); 2380 const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1; 2381 2382 TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1); 2383 clipCullType.getQualifier() = clipCullNode->getType().getQualifier(); 2384 2385 // Create required array dimension 2386 TArraySizes arraySizes; 2387 if (isImplicitlyArrayed) 2388 arraySizes.addInnerSize(requiredOuterArraySize); 2389 arraySizes.addInnerSize(requiredInnerArraySize); 2390 clipCullType.newArraySizes(arraySizes); 2391 2392 // Obtain symbol name: we'll use that for the symbol we introduce. 2393 TIntermSymbol* sym = clipCullNode->getAsSymbolNode(); 2394 assert(sym != nullptr); 2395 2396 // We are moving the semantic ID from the layout location, so it is no longer needed or 2397 // desired there. 2398 clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd; 2399 2400 // Create variable and track its linkage 2401 *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType); 2402 2403 trackLinkage(**clipCullVar); 2404 } 2405 2406 // Create symbol for the clip or cull variable. 2407 TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar); 2408 2409 // vector sizes: 2410 const int clipCullVectorSize = clipCullSym->getType().getVectorSize(); 2411 2412 // array sizes, or 1 if it's not an array: 2413 const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes(); 2414 const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1; 2415 const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0); 2416 2417 // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics. 2418 // fixBuiltInIoType() should have handled that upstream. 2419 assert(clipCullSym->getType().isArray()); 2420 assert(clipCullSym->getType().getVectorSize() == 1); 2421 assert(clipCullSym->getType().getBasicType() == EbtFloat); 2422 2423 // We may be creating multiple sub-assignments. This is an aggregate to hold them. 2424 // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed. 2425 TIntermAggregate* assignList = nullptr; 2426 2427 // Holds individual component assignments as we make them. 2428 TIntermTyped* clipCullAssign = nullptr; 2429 2430 // If the types are homomorphic, use a simple assign. No need to mess about with 2431 // individual components. 2432 if (clipCullSym->getType().isArray() == internalNode->getType().isArray() && 2433 clipCullInnerArraySize == internalInnerArraySize && 2434 clipCullOuterArraySize == internalOuterArraySize && 2435 clipCullVectorSize == internalVectorSize) { 2436 2437 if (isOutput) 2438 clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc); 2439 else 2440 clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc); 2441 2442 assignList = intermediate.growAggregate(assignList, clipCullAssign); 2443 assignList->setOperator(EOpSequence); 2444 2445 return assignList; 2446 } 2447 2448 // We are going to copy each component of the internal (per array element if indicated) to sequential 2449 // array elements of the clipCullSym. This tracks the lhs element we're writing to as we go along. 2450 // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above. 2451 int clipCullInnerArrayPos = semanticOffset[semanticId]; 2452 int clipCullOuterArrayPos = 0; 2453 2454 // Lambda to add an index to a node, set the type of the result, and return the new node. 2455 const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* { 2456 const TType derefType(node->getType(), 0); 2457 node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc); 2458 node->setType(derefType); 2459 return node; 2460 }; 2461 2462 // Loop through every component of every element of the internal, and copy to or from the matching external. 2463 for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) { 2464 for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) { 2465 for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) { 2466 // clip/cull array member to read from / write to: 2467 TIntermTyped* clipCullMember = clipCullSym; 2468 2469 // If implicitly arrayed, there is an outer array dimension involved 2470 if (isImplicitlyArrayed) 2471 clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos); 2472 2473 // Index into proper array position for clip cull member 2474 clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++); 2475 2476 // if needed, start over with next outer array slice. 2477 if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) { 2478 clipCullInnerArrayPos = semanticOffset[semanticId]; 2479 ++clipCullOuterArrayPos; 2480 } 2481 2482 // internal member to read from / write to: 2483 TIntermTyped* internalMember = internalNode; 2484 2485 // If internal node has outer array dimension, index appropriately. 2486 if (internalArrayDims > 1) 2487 internalMember = addIndex(internalMember, internalOuterArrayPos); 2488 2489 // If internal node has inner array dimension, index appropriately. 2490 if (internalArrayDims > 0) 2491 internalMember = addIndex(internalMember, internalInnerArrayPos); 2492 2493 // If internal node is a vector, extract the component of interest. 2494 if (internalNode->getType().isVector()) 2495 internalMember = addIndex(internalMember, internalComponent); 2496 2497 // Create an assignment: output from internal to clip cull, or input from clip cull to internal. 2498 if (isOutput) 2499 clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc); 2500 else 2501 clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc); 2502 2503 // Track assignment in the sequence. 2504 assignList = intermediate.growAggregate(assignList, clipCullAssign); 2505 } 2506 } 2507 } 2508 2509 assert(assignList != nullptr); 2510 assignList->setOperator(EOpSequence); 2511 2512 return assignList; 2513 } 2514 2515 // For a declaration with an initializer, where the initialized symbol is flattened, 2516 // and possibly contains opaque values, such that the initializer should never exist 2517 // as emitted code, because even creating the initializer would write opaques. 2518 // 2519 // If possible, decompose this into individual member-wise assignments, which themselves 2520 // are expected to then not exist for opaque types, because they will turn into aliases. 2521 // 2522 // Return a node that contains the non-aliased assignments that must continue to exist. 2523 TIntermTyped* HlslParseContext::executeFlattenedInitializer(const TSourceLoc& loc, TIntermSymbol* symbol, 2524 TIntermAggregate& initializer) 2525 { 2526 // We need individual RHS initializers per member to do this 2527 const TTypeList* typeList = symbol->getType().getStruct(); 2528 if (typeList == nullptr || initializer.getSequence().size() != typeList->size()) { 2529 warn(loc, "cannot do member-wise aliasing for opaque members with this initializer", "=", ""); 2530 return handleAssign(loc, EOpAssign, symbol, &initializer); 2531 } 2532 2533 TIntermAggregate* initList = nullptr; 2534 // synthesize an access to each member, and then an assignment to it 2535 for (int member = 0; member < (int)typeList->size(); ++member) { 2536 TIntermTyped* memberInitializer = initializer.getSequence()[member]->getAsTyped(); 2537 TIntermTyped* flattenedMember = flattenAccess(symbol, member); 2538 if (flattenedMember->getType().containsOpaque()) 2539 setOpaqueLvalue(flattenedMember, memberInitializer); 2540 else 2541 initList = intermediate.growAggregate(initList, handleAssign(loc, EOpAssign, flattenedMember, 2542 memberInitializer)); 2543 } 2544 2545 if (initList) 2546 initList->setOperator(EOpSequence); 2547 return initList; 2548 } 2549 2550 // Some simple source assignments need to be flattened to a sequence 2551 // of AST assignments. Catch these and flatten, otherwise, pass through 2552 // to intermediate.addAssign(). 2553 // 2554 // Also, assignment to matrix swizzles requires multiple component assignments, 2555 // intercept those as well. 2556 TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, 2557 TIntermTyped* right) 2558 { 2559 if (left == nullptr || right == nullptr) 2560 return nullptr; 2561 2562 if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle) 2563 return handleAssignToMatrixSwizzle(loc, op, left, right); 2564 2565 const bool isSplitLeft = wasSplit(left); 2566 const bool isSplitRight = wasSplit(right); 2567 2568 const bool isFlattenLeft = wasFlattened(left); 2569 const bool isFlattenRight = wasFlattened(right); 2570 2571 // OK to do a single assign if both are split, or both are unsplit. But if one is and the other 2572 // isn't, we fall back to a member-wise copy. 2573 if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) { 2574 // Clip and cull distance requires more processing. See comment above assignClipCullDistance. 2575 if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) { 2576 const bool isOutput = isClipOrCullDistance(left->getType()); 2577 2578 const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation; 2579 return assignClipCullDistance(loc, op, semanticId, left, right); 2580 } 2581 2582 return intermediate.addAssign(op, left, right, loc); 2583 } 2584 2585 TIntermAggregate* assignList = nullptr; 2586 const TVector<TVariable*>* leftVariables = nullptr; 2587 const TVector<TVariable*>* rightVariables = nullptr; 2588 2589 // A temporary to store the right node's value, so we don't keep indirecting into it 2590 // if it's not a simple symbol. 2591 TVariable* rhsTempVar = nullptr; 2592 2593 // If the RHS is a simple symbol node, we'll copy it for each member. 2594 TIntermSymbol* cloneSymNode = nullptr; 2595 2596 int memberCount = 0; 2597 2598 // Track how many items there are to copy. 2599 if (left->getType().isStruct()) 2600 memberCount = (int)left->getType().getStruct()->size(); 2601 if (left->getType().isArray()) 2602 memberCount = left->getType().getCumulativeArraySize(); 2603 2604 if (isFlattenLeft) 2605 leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second.members; 2606 2607 if (isFlattenRight) { 2608 rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second.members; 2609 } else { 2610 // The RHS is not flattened. There are several cases: 2611 // 1. 1 item to copy: Use the RHS directly. 2612 // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp. 2613 // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member. 2614 2615 if (memberCount <= 1) { 2616 // case 1: we'll use the symbol directly below. Nothing to do. 2617 } else { 2618 if (right->getAsSymbolNode() != nullptr) { 2619 // case 2: we'll copy the symbol per iteration below. 2620 cloneSymNode = right->getAsSymbolNode(); 2621 } else { 2622 // case 3: assign to a temp, and indirect into that. 2623 rhsTempVar = makeInternalVariable("flattenTemp", right->getType()); 2624 rhsTempVar->getWritableType().getQualifier().makeTemporary(); 2625 TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc); 2626 2627 // Add this to the aggregate being built. 2628 assignList = intermediate.growAggregate(assignList, 2629 intermediate.addAssign(op, noFlattenRHS, right, loc), loc); 2630 } 2631 } 2632 } 2633 2634 int memberIdx = 0; 2635 2636 // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in 2637 // variables, which is awkward when copying between split and unsplit structures. This variable tracks 2638 // array indirections so they can be percolated from outer structs to inner variables. 2639 std::vector <int> arrayElement; 2640 2641 // We track the outer-most aggregate, so that we can use its storage class later. 2642 const TIntermTyped* outerLeft = left; 2643 const TIntermTyped* outerRight = right; 2644 2645 const auto getMember = [&](bool isLeft, TIntermTyped* node, int member, TIntermTyped* splitNode, int splitMember) 2646 -> TIntermTyped * { 2647 const bool flattened = isLeft ? isFlattenLeft : isFlattenRight; 2648 const bool split = isLeft ? isSplitLeft : isSplitRight; 2649 2650 TIntermTyped* subTree; 2651 const TType derefType(node->getType(), member); 2652 const TVariable* builtInVar = nullptr; 2653 if ((flattened || split) && derefType.isBuiltIn()) { 2654 const TIntermTyped* outer = isLeft ? outerLeft : outerRight; 2655 auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData( 2656 derefType.getQualifier().builtIn, 2657 outer->getType().getQualifier().storage)); 2658 if (splitPair != splitBuiltIns.end()) 2659 builtInVar = splitPair->second; 2660 } 2661 if (builtInVar != nullptr) { 2662 // copy from interstage IO built-in if needed 2663 subTree = intermediate.addSymbol(*builtInVar); 2664 2665 // Arrayness of builtIn symbols isn't handled by the normal recursion: 2666 // it's been extracted and moved to the built-in. 2667 if (subTree->getType().isArray() && !arrayElement.empty()) { 2668 const TType splitDerefType(subTree->getType(), arrayElement.back()); 2669 subTree = intermediate.addIndex(EOpIndexDirect, subTree, 2670 intermediate.addConstantUnion(arrayElement.back(), loc), loc); 2671 subTree->setType(splitDerefType); 2672 } 2673 } else if (flattened && isFinalFlattening(derefType)) { 2674 const TVector<TVariable*>& flatVariables = isLeft ? *leftVariables : *rightVariables; 2675 subTree = intermediate.addSymbol(*flatVariables[memberIdx++]); 2676 } else { 2677 // Index operator if it's an aggregate, else EOpNull 2678 const TOperator accessOp = node->getType().isArray() ? EOpIndexDirect 2679 : node->getType().isStruct() ? EOpIndexDirectStruct 2680 : EOpNull; 2681 if (accessOp == EOpNull) { 2682 subTree = splitNode; 2683 } else { 2684 subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc), 2685 loc); 2686 const TType splitDerefType(splitNode->getType(), splitMember); 2687 subTree->setType(splitDerefType); 2688 } 2689 } 2690 2691 return subTree; 2692 }; 2693 2694 // Use the proper RHS node: a new symbol from a TVariable, copy 2695 // of an TIntermSymbol node, or sometimes the right node directly. 2696 right = rhsTempVar != nullptr ? intermediate.addSymbol(*rhsTempVar, loc) : 2697 cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) : 2698 right; 2699 2700 // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the 2701 // whole thing. So, we'll resort to an explicit type via std::function. 2702 const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight)> 2703 traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight) -> void { 2704 // If we get here, we are assigning to or from a whole array or struct that must be 2705 // flattened, so have to do member-by-member assignment: 2706 2707 if (left->getType().isArray() || right->getType().isArray()) { 2708 const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1; 2709 const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1; 2710 2711 // The arrays may not be the same size, e.g, if the size has been forced for EbvTessLevelInner or Outer. 2712 const int elementsToCopy = std::min(elementsL, elementsR); 2713 2714 // array case 2715 for (int element = 0; element < elementsToCopy; ++element) { 2716 arrayElement.push_back(element); 2717 2718 // Add a new AST symbol node if we have a temp variable holding a complex RHS. 2719 TIntermTyped* subLeft = getMember(true, left, element, left, element); 2720 TIntermTyped* subRight = getMember(false, right, element, right, element); 2721 2722 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, element, splitLeft, element) 2723 : subLeft; 2724 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, element, splitRight, element) 2725 : subRight; 2726 2727 traverse(subLeft, subRight, subSplitLeft, subSplitRight); 2728 2729 arrayElement.pop_back(); 2730 } 2731 } else if (left->getType().isStruct()) { 2732 // struct case 2733 const auto& membersL = *left->getType().getStruct(); 2734 const auto& membersR = *right->getType().getStruct(); 2735 2736 // These track the members in the split structures corresponding to the same in the unsplit structures, 2737 // which we traverse in parallel. 2738 int memberL = 0; 2739 int memberR = 0; 2740 2741 // Handle empty structure assignment 2742 if (int(membersL.size()) == 0 && int(membersR.size()) == 0) 2743 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); 2744 2745 for (int member = 0; member < int(membersL.size()); ++member) { 2746 const TType& typeL = *membersL[member].type; 2747 const TType& typeR = *membersR[member].type; 2748 2749 TIntermTyped* subLeft = getMember(true, left, member, left, member); 2750 TIntermTyped* subRight = getMember(false, right, member, right, member); 2751 2752 // If there is no splitting, use the same values to avoid inefficiency. 2753 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, member, splitLeft, memberL) 2754 : subLeft; 2755 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, member, splitRight, memberR) 2756 : subRight; 2757 2758 if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) { 2759 // Clip and cull distance built-in assignment is complex in its own right, and is handled in 2760 // a separate function dedicated to that task. See comment above assignClipCullDistance; 2761 2762 const bool isOutput = isClipOrCullDistance(subSplitLeft->getType()); 2763 2764 // Since all clip/cull semantics boil down to the same built-in type, we need to get the 2765 // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping. 2766 const TType derefType((isOutput ? left : right)->getType(), member); 2767 const int semanticId = derefType.getQualifier().layoutLocation; 2768 2769 TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId, 2770 subSplitLeft, subSplitRight); 2771 2772 assignList = intermediate.growAggregate(assignList, clipCullAssign, loc); 2773 2774 } else if (!isFlattenLeft && !isFlattenRight && 2775 !typeL.containsBuiltIn() && 2776 !typeR.containsBuiltIn()) { 2777 // If this is the final flattening (no nested types below to flatten) 2778 // we'll copy the member, else recurse into the type hierarchy. 2779 // However, if splitting the struct, that means we can copy a whole 2780 // subtree here IFF it does not itself contain any interstage built-in 2781 // IO variables, so we only have to recurse into it if there's something 2782 // for splitting to do. That can save a lot of AST verbosity for 2783 // a bunch of memberwise copies. 2784 2785 assignList = intermediate.growAggregate(assignList, 2786 intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), 2787 loc); 2788 } else { 2789 traverse(subLeft, subRight, subSplitLeft, subSplitRight); 2790 } 2791 2792 memberL += (typeL.isBuiltIn() ? 0 : 1); 2793 memberR += (typeR.isBuiltIn() ? 0 : 1); 2794 } 2795 } else { 2796 // Member copy 2797 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); 2798 } 2799 2800 }; 2801 2802 TIntermTyped* splitLeft = left; 2803 TIntermTyped* splitRight = right; 2804 2805 // If either left or right was a split structure, we must read or write it, but still have to 2806 // parallel-recurse through the unsplit structure to identify the built-in IO vars. 2807 if (isSplitLeft) 2808 splitLeft = intermediate.addSymbol(*getSplitNonIoVar(left->getAsSymbolNode()->getId()), loc); 2809 2810 if (isSplitRight) 2811 splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc); 2812 2813 // This makes the whole assignment, recursing through subtypes as needed. 2814 traverse(left, right, splitLeft, splitRight); 2815 2816 assert(assignList != nullptr); 2817 assignList->setOperator(EOpSequence); 2818 2819 return assignList; 2820 } 2821 2822 // An assignment to matrix swizzle must be decomposed into individual assignments. 2823 // These must be selected component-wise from the RHS and stored component-wise 2824 // into the LHS. 2825 TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, 2826 TIntermTyped* right) 2827 { 2828 assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle); 2829 2830 if (op != EOpAssign) 2831 error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", ""); 2832 2833 // isolate the matrix and swizzle nodes 2834 TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped(); 2835 const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence(); 2836 2837 // if the RHS isn't already a simple vector, let's store into one 2838 TIntermSymbol* vector = right->getAsSymbolNode(); 2839 TIntermTyped* vectorAssign = nullptr; 2840 if (vector == nullptr) { 2841 // create a new intermediate vector variable to assign to 2842 TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2); 2843 vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc); 2844 2845 // assign the right to the new vector 2846 vectorAssign = handleAssign(loc, op, vector, right); 2847 } 2848 2849 // Assign the vector components to the matrix components. 2850 // Store this as a sequence, so a single aggregate node represents this 2851 // entire operation. 2852 TIntermAggregate* result = intermediate.makeAggregate(vectorAssign); 2853 TType columnType(matrix->getType(), 0); 2854 TType componentType(columnType, 0); 2855 TType indexType(EbtInt); 2856 for (int i = 0; i < (int)swizzle.size(); i += 2) { 2857 // the right component, single index into the RHS vector 2858 TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector, 2859 intermediate.addConstantUnion(i/2, loc), loc); 2860 2861 // the left component, double index into the LHS matrix 2862 TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix, 2863 intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(), 2864 indexType, loc), 2865 loc); 2866 leftComp->setType(columnType); 2867 leftComp = intermediate.addIndex(EOpIndexDirect, leftComp, 2868 intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(), 2869 indexType, loc), 2870 loc); 2871 leftComp->setType(componentType); 2872 2873 // Add the assignment to the aggregate 2874 result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc)); 2875 } 2876 2877 result->setOp(EOpSequence); 2878 2879 return result; 2880 } 2881 2882 // 2883 // HLSL atomic operations have slightly different arguments than 2884 // GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic. 2885 // This provides the post-decomposition equivalent opcode. 2886 // 2887 TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage) 2888 { 2889 switch (op) { 2890 case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd; 2891 case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd; 2892 case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap; 2893 case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax; 2894 case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin; 2895 case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr; 2896 case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor; 2897 case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange; 2898 case EOpInterlockedCompareStore: // TODO: ... 2899 default: 2900 error(loc, "unknown atomic operation", "unknown op", ""); 2901 return EOpNull; 2902 } 2903 } 2904 2905 // 2906 // Create a combined sampler/texture from separate sampler and texture. 2907 // 2908 TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, 2909 TIntermTyped* argSampler) 2910 { 2911 TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler); 2912 2913 txcombine->getSequence().push_back(argTex); 2914 txcombine->getSequence().push_back(argSampler); 2915 2916 TSampler samplerType = argTex->getType().getSampler(); 2917 samplerType.combined = true; 2918 samplerType.shadow = argSampler->getType().getSampler().shadow; 2919 2920 txcombine->setType(TType(samplerType, EvqTemporary)); 2921 txcombine->setLoc(loc); 2922 2923 return txcombine; 2924 } 2925 2926 // Return true if this a buffer type that has an associated counter buffer. 2927 bool HlslParseContext::hasStructBuffCounter(const TType& type) const 2928 { 2929 switch (type.getQualifier().declaredBuiltIn) { 2930 case EbvAppendConsume: // fall through... 2931 case EbvRWStructuredBuffer: // ... 2932 return true; 2933 default: 2934 return false; // the other structuredbuffer types do not have a counter. 2935 } 2936 } 2937 2938 void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type) 2939 { 2940 // Counter type 2941 TType* counterType = new TType(EbtInt, EvqBuffer); 2942 counterType->setFieldName("@count"); 2943 2944 TTypeList* blockStruct = new TTypeList; 2945 TTypeLoc member = { counterType, loc }; 2946 blockStruct->push_back(member); 2947 2948 TType blockType(blockStruct, "", counterType->getQualifier()); 2949 blockType.getQualifier().storage = EvqBuffer; 2950 2951 type.shallowCopy(blockType); 2952 shareStructBufferType(type); 2953 } 2954 2955 // knowledge of how to construct block name, in one place instead of N places. 2956 TString HlslParseContext::getStructBuffCounterName(const TString& blockName) const 2957 { 2958 return blockName + "@count"; 2959 } 2960 2961 // declare counter for a structured buffer type 2962 void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name) 2963 { 2964 // Bail out if not a struct buffer 2965 if (! isStructBufferType(bufferType)) 2966 return; 2967 2968 if (! hasStructBuffCounter(bufferType)) 2969 return; 2970 2971 TType blockType; 2972 counterBufferType(loc, blockType); 2973 2974 TString* blockName = new TString(getStructBuffCounterName(name)); 2975 2976 // Counter buffer does not have its own counter buffer. TODO: there should be a better way to track this. 2977 structBufferCounter[*blockName] = false; 2978 2979 shareStructBufferType(blockType); 2980 declareBlock(loc, blockType, blockName); 2981 } 2982 2983 // return the counter that goes with a given structuredbuffer 2984 TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer) 2985 { 2986 // Bail out if not a struct buffer 2987 if (buffer == nullptr || ! isStructBufferType(buffer->getType())) 2988 return nullptr; 2989 2990 const TString counterBlockName(getStructBuffCounterName(buffer->getAsSymbolNode()->getName())); 2991 2992 // Mark the counter as being used 2993 structBufferCounter[counterBlockName] = true; 2994 2995 TIntermTyped* counterVar = handleVariable(loc, &counterBlockName); // find the block structure 2996 TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct 2997 2998 TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc); 2999 counterMember->setType(TType(EbtInt)); 3000 return counterMember; 3001 } 3002 3003 3004 // 3005 // Decompose structure buffer methods into AST 3006 // 3007 void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 3008 { 3009 if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr) 3010 return; 3011 3012 const TOperator op = node->getAsOperator()->getOp(); 3013 TIntermAggregate* argAggregate = arguments->getAsAggregate(); 3014 3015 // Buffer is the object upon which method is called, so always arg 0 3016 TIntermTyped* bufferObj = nullptr; 3017 3018 // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params. 3019 if (argAggregate) { 3020 if (argAggregate->getSequence().empty()) 3021 return; 3022 bufferObj = argAggregate->getSequence()[0]->getAsTyped(); 3023 } else { 3024 bufferObj = arguments->getAsSymbolNode(); 3025 } 3026 3027 if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr) 3028 return; 3029 3030 // Some methods require a hidden internal counter, obtained via getStructBufferCounter(). 3031 // This lambda adds something to it and returns the old value. 3032 const auto incDecCounter = [&](int incval) -> TIntermTyped* { 3033 TIntermTyped* incrementValue = intermediate.addConstantUnion(incval, loc, true); 3034 TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member 3035 3036 if (counter == nullptr) 3037 return nullptr; 3038 3039 TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd); 3040 counterIncrement->setType(TType(EbtUint, EvqTemporary)); 3041 counterIncrement->setLoc(loc); 3042 counterIncrement->getSequence().push_back(counter); 3043 counterIncrement->getSequence().push_back(incrementValue); 3044 3045 return counterIncrement; 3046 }; 3047 3048 // Index to obtain the runtime sized array out of the buffer. 3049 TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj); 3050 if (argArray == nullptr) 3051 return; // It might not be a struct buffer method. 3052 3053 switch (op) { 3054 case EOpMethodLoad: 3055 { 3056 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index 3057 3058 const TType& bufferType = bufferObj->getType(); 3059 3060 const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn; 3061 3062 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address 3063 // buffer then, but that's what it calls itself. 3064 const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer || 3065 builtInType == EbvRWByteAddressBuffer); 3066 3067 3068 if (isByteAddressBuffer) 3069 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, 3070 intermediate.addConstantUnion(2, loc, true), 3071 loc, TType(EbtInt)); 3072 3073 // Index into the array to find the item being loaded. 3074 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; 3075 3076 node = intermediate.addIndex(idxOp, argArray, argIndex, loc); 3077 3078 const TType derefType(argArray->getType(), 0); 3079 node->setType(derefType); 3080 } 3081 3082 break; 3083 3084 case EOpMethodLoad2: 3085 case EOpMethodLoad3: 3086 case EOpMethodLoad4: 3087 { 3088 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index 3089 3090 TOperator constructOp = EOpNull; 3091 int size = 0; 3092 3093 switch (op) { 3094 case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break; 3095 case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break; 3096 case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break; 3097 default: assert(0); 3098 } 3099 3100 TIntermTyped* body = nullptr; 3101 3102 // First, we'll store the address in a variable to avoid multiple shifts 3103 // (we must convert the byte address to an item address) 3104 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, 3105 intermediate.addConstantUnion(2, loc, true), 3106 loc, TType(EbtInt)); 3107 3108 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); 3109 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); 3110 3111 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); 3112 3113 TIntermTyped* vec = nullptr; 3114 3115 // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2 3116 // address conversion. 3117 for (int idx=0; idx<size; ++idx) { 3118 TIntermTyped* offsetIdx = byteAddrIdxVar; 3119 3120 // add index offset 3121 if (idx != 0) 3122 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, 3123 intermediate.addConstantUnion(idx, loc, true), 3124 loc, TType(EbtInt)); 3125 3126 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect 3127 : EOpIndexIndirect; 3128 3129 vec = intermediate.growAggregate(vec, intermediate.addIndex(idxOp, argArray, offsetIdx, loc)); 3130 } 3131 3132 vec->setType(TType(argArray->getBasicType(), EvqTemporary, size)); 3133 vec->getAsAggregate()->setOperator(constructOp); 3134 3135 body = intermediate.growAggregate(body, vec); 3136 body->setType(vec->getType()); 3137 body->getAsAggregate()->setOperator(EOpSequence); 3138 3139 node = body; 3140 } 3141 3142 break; 3143 3144 case EOpMethodStore: 3145 case EOpMethodStore2: 3146 case EOpMethodStore3: 3147 case EOpMethodStore4: 3148 { 3149 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index 3150 TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value 3151 3152 // Index into the array to find the item being loaded. 3153 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address 3154 // buffer then, but that's what it calls itself. 3155 3156 int size = 0; 3157 3158 switch (op) { 3159 case EOpMethodStore: size = 1; break; 3160 case EOpMethodStore2: size = 2; break; 3161 case EOpMethodStore3: size = 3; break; 3162 case EOpMethodStore4: size = 4; break; 3163 default: assert(0); 3164 } 3165 3166 TIntermAggregate* body = nullptr; 3167 3168 // First, we'll store the address in a variable to avoid multiple shifts 3169 // (we must convert the byte address to an item address) 3170 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, 3171 intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt)); 3172 3173 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); 3174 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); 3175 3176 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); 3177 3178 for (int idx=0; idx<size; ++idx) { 3179 TIntermTyped* offsetIdx = byteAddrIdxVar; 3180 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); 3181 3182 // add index offset 3183 if (idx != 0) 3184 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt)); 3185 3186 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect 3187 : EOpIndexIndirect; 3188 3189 TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc); 3190 TIntermTyped* rValue = (size == 1) ? argValue : 3191 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc); 3192 3193 TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc); 3194 3195 body = intermediate.growAggregate(body, assign); 3196 } 3197 3198 body->setOperator(EOpSequence); 3199 node = body; 3200 } 3201 3202 break; 3203 3204 case EOpMethodGetDimensions: 3205 { 3206 const int numArgs = (int)argAggregate->getSequence().size(); 3207 TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items 3208 TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride 3209 3210 TIntermAggregate* body = nullptr; 3211 3212 // Length output: 3213 if (argArray->getType().isRuntimeSizedArray()) { 3214 TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray, 3215 argNumItems->getType()); 3216 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc); 3217 body = intermediate.growAggregate(body, assign, loc); 3218 } else { 3219 const int length = argArray->getType().getOuterArraySize(); 3220 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, 3221 intermediate.addConstantUnion(length, loc, true), loc); 3222 body = intermediate.growAggregate(body, assign, loc); 3223 } 3224 3225 // Stride output: 3226 if (argStride != nullptr) { 3227 int size; 3228 int stride; 3229 intermediate.getBaseAlignment(argArray->getType(), size, stride, false, 3230 argArray->getType().getQualifier().layoutMatrix == ElmRowMajor); 3231 3232 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride, 3233 intermediate.addConstantUnion(stride, loc, true), loc); 3234 3235 body = intermediate.growAggregate(body, assign); 3236 } 3237 3238 body->setOperator(EOpSequence); 3239 node = body; 3240 } 3241 3242 break; 3243 3244 case EOpInterlockedAdd: 3245 case EOpInterlockedAnd: 3246 case EOpInterlockedExchange: 3247 case EOpInterlockedMax: 3248 case EOpInterlockedMin: 3249 case EOpInterlockedOr: 3250 case EOpInterlockedXor: 3251 case EOpInterlockedCompareExchange: 3252 case EOpInterlockedCompareStore: 3253 { 3254 // We'll replace the first argument with the block dereference, and let 3255 // downstream decomposition handle the rest. 3256 3257 TIntermSequence& sequence = argAggregate->getSequence(); 3258 3259 TIntermTyped* argIndex = makeIntegerIndex(sequence[1]->getAsTyped()); // index 3260 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true), 3261 loc, TType(EbtInt)); 3262 3263 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; 3264 TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc); 3265 3266 const TType derefType(argArray->getType(), 0); 3267 element->setType(derefType); 3268 3269 // Replace the numeric byte offset parameter with array reference. 3270 sequence[1] = element; 3271 sequence.erase(sequence.begin(), sequence.begin()+1); 3272 } 3273 break; 3274 3275 case EOpMethodIncrementCounter: 3276 { 3277 node = incDecCounter(1); 3278 break; 3279 } 3280 3281 case EOpMethodDecrementCounter: 3282 { 3283 TIntermTyped* preIncValue = incDecCounter(-1); // result is original value 3284 node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc, 3285 preIncValue->getType()); 3286 break; 3287 } 3288 3289 case EOpMethodAppend: 3290 { 3291 TIntermTyped* oldCounter = incDecCounter(1); 3292 3293 TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc); 3294 TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped(); 3295 3296 const TType derefType(argArray->getType(), 0); 3297 lValue->setType(derefType); 3298 3299 node = intermediate.addAssign(EOpAssign, lValue, rValue, loc); 3300 3301 break; 3302 } 3303 3304 case EOpMethodConsume: 3305 { 3306 TIntermTyped* oldCounter = incDecCounter(-1); 3307 3308 TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter, 3309 intermediate.addConstantUnion(-1, loc, true), loc, 3310 oldCounter->getType()); 3311 3312 node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc); 3313 3314 const TType derefType(argArray->getType(), 0); 3315 node->setType(derefType); 3316 3317 break; 3318 } 3319 3320 default: 3321 break; // most pass through unchanged 3322 } 3323 } 3324 3325 // Create array of standard sample positions for given sample count. 3326 // TODO: remove when a real method to query sample pos exists in SPIR-V. 3327 TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count) 3328 { 3329 struct tSamplePos { float x, y; }; 3330 3331 static const tSamplePos pos1[] = { 3332 { 0.0/16.0, 0.0/16.0 }, 3333 }; 3334 3335 // standard sample positions for 2, 4, 8, and 16 samples. 3336 static const tSamplePos pos2[] = { 3337 { 4.0/16.0, 4.0/16.0 }, {-4.0/16.0, -4.0/16.0 }, 3338 }; 3339 3340 static const tSamplePos pos4[] = { 3341 {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0, 2.0/16.0 }, { 2.0/16.0, 6.0/16.0 }, 3342 }; 3343 3344 static const tSamplePos pos8[] = { 3345 { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0, 3.0/16.0 }, { 5.0/16.0, 1.0/16.0 }, {-3.0/16.0, -5.0/16.0 }, 3346 {-5.0/16.0, 5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0, 7.0/16.0 }, { 7.0/16.0, -7.0/16.0 }, 3347 }; 3348 3349 static const tSamplePos pos16[] = { 3350 { 1.0/16.0, 1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0, 2.0/16.0 }, { 4.0/16.0, -1.0/16.0 }, 3351 {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0, 5.0/16.0 }, { 5.0/16.0, 3.0/16.0 }, { 3.0/16.0, -5.0/16.0 }, 3352 {-2.0/16.0, 6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0, 4.0/16.0 }, 3353 {-8.0/16.0, 0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0, 7.0/16.0 }, {-7.0/16.0, -8.0/16.0 }, 3354 }; 3355 3356 const tSamplePos* sampleLoc = nullptr; 3357 int numSamples = count; 3358 3359 switch (count) { 3360 case 2: sampleLoc = pos2; break; 3361 case 4: sampleLoc = pos4; break; 3362 case 8: sampleLoc = pos8; break; 3363 case 16: sampleLoc = pos16; break; 3364 default: 3365 sampleLoc = pos1; 3366 numSamples = 1; 3367 } 3368 3369 TConstUnionArray* values = new TConstUnionArray(numSamples*2); 3370 3371 for (int pos=0; pos<count; ++pos) { 3372 TConstUnion x, y; 3373 x.setDConst(sampleLoc[pos].x); 3374 y.setDConst(sampleLoc[pos].y); 3375 3376 (*values)[pos*2+0] = x; 3377 (*values)[pos*2+1] = y; 3378 } 3379 3380 TType retType(EbtFloat, EvqConst, 2); 3381 3382 if (numSamples != 1) { 3383 TArraySizes arraySizes; 3384 arraySizes.addInnerSize(numSamples); 3385 retType.newArraySizes(arraySizes); 3386 } 3387 3388 return new TIntermConstantUnion(*values, retType); 3389 } 3390 3391 // 3392 // Decompose DX9 and DX10 sample intrinsics & object methods into AST 3393 // 3394 void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 3395 { 3396 if (node == nullptr || !node->getAsOperator()) 3397 return; 3398 3399 // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it. 3400 const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* { 3401 result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize())); 3402 3403 TIntermTyped* convertedResult = nullptr; 3404 3405 TType retType; 3406 getTextureReturnType(sampler, retType); 3407 3408 if (retType.isStruct()) { 3409 // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here) 3410 TIntermAggregate* conversionAggregate = new TIntermAggregate; 3411 convertedResult = conversionAggregate; 3412 3413 // Convert vector output to return structure. We will need a temp symbol to copy the results to. 3414 TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType); 3415 3416 // We also need a temp symbol to hold the result of the texture. We don't want to re-fetch the 3417 // sample each time we'll index into the result, so we'll copy to this, and index into the copy. 3418 TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType()); 3419 3420 // Initial copy from texture to our sample result shadow. 3421 TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc), 3422 result, loc); 3423 3424 conversionAggregate->getSequence().push_back(shadowCopy); 3425 3426 unsigned vec4Pos = 0; 3427 3428 for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) { 3429 const TType memberType(retType, m); // dereferenced type of the member we're about to assign. 3430 3431 // Check for bad struct members. This should have been caught upstream. Complain, because 3432 // wwe don't know what to do with it. This algorithm could be generalized to handle 3433 // other things, e.g, sub-structures, but HLSL doesn't allow them. 3434 if (!memberType.isVector() && !memberType.isScalar()) { 3435 error(loc, "expected: scalar or vector type in texture structure", "", ""); 3436 return nullptr; 3437 } 3438 3439 // Index into the struct variable to find the member to assign. 3440 TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct, 3441 intermediate.addSymbol(*structVar, loc), 3442 intermediate.addConstantUnion(m, loc), loc); 3443 3444 structMember->setType(memberType); 3445 3446 // Assign each component of (possible) vector in struct member. 3447 for (int component = 0; component < memberType.getVectorSize(); ++component) { 3448 TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect, 3449 intermediate.addSymbol(*sampleShadow, loc), 3450 intermediate.addConstantUnion(vec4Pos++, loc), loc); 3451 vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1)); 3452 3453 TIntermTyped* memberAssign = nullptr; 3454 3455 if (memberType.isVector()) { 3456 // Vector member: we need to create an access chain to the vector component. 3457 3458 TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember, 3459 intermediate.addConstantUnion(component, loc), loc); 3460 3461 memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc); 3462 } else { 3463 // Scalar member: we can assign to it directly. 3464 memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc); 3465 } 3466 3467 3468 conversionAggregate->getSequence().push_back(memberAssign); 3469 } 3470 } 3471 3472 // Add completed variable so the expression results in the whole struct value we just built. 3473 conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc)); 3474 3475 // Make it a sequence. 3476 intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc); 3477 } else { 3478 // vector clamp the output if template vector type is smaller than sample result. 3479 if (retType.getVectorSize() < node->getVectorSize()) { 3480 // Too many components. Construct shorter vector from it. 3481 const TOperator op = intermediate.mapTypeToConstructorOp(retType); 3482 3483 convertedResult = constructBuiltIn(retType, op, result, loc, false); 3484 } else { 3485 // Enough components. Use directly. 3486 convertedResult = result; 3487 } 3488 } 3489 3490 convertedResult->setLoc(loc); 3491 return convertedResult; 3492 }; 3493 3494 const TOperator op = node->getAsOperator()->getOp(); 3495 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; 3496 3497 // Bail out if not a sampler method. 3498 // Note though this is odd to do before checking the op, because the op 3499 // could be something that takes the arguments, and the function in question 3500 // takes the result of the op. So, this is not the final word. 3501 if (arguments != nullptr) { 3502 if (argAggregate == nullptr) { 3503 if (arguments->getAsTyped()->getBasicType() != EbtSampler) 3504 return; 3505 } else { 3506 if (argAggregate->getSequence().size() == 0 || 3507 argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler) 3508 return; 3509 } 3510 } 3511 3512 switch (op) { 3513 // **** DX9 intrinsics: **** 3514 case EOpTexture: 3515 { 3516 // Texture with ddx & ddy is really gradient form in HLSL 3517 if (argAggregate->getSequence().size() == 4) 3518 node->getAsAggregate()->setOperator(EOpTextureGrad); 3519 3520 break; 3521 } 3522 3523 case EOpTextureBias: 3524 { 3525 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler 3526 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord 3527 3528 // HLSL puts bias in W component of coordinate. We extract it and add it to 3529 // the argument list, instead 3530 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); 3531 TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); 3532 3533 TOperator constructOp = EOpNull; 3534 const TSampler& sampler = arg0->getType().getSampler(); 3535 3536 switch (sampler.dim) { 3537 case Esd1D: constructOp = EOpConstructFloat; break; // 1D 3538 case Esd2D: constructOp = EOpConstructVec2; break; // 2D 3539 case Esd3D: constructOp = EOpConstructVec3; break; // 3D 3540 case EsdCube: constructOp = EOpConstructVec3; break; // also 3D 3541 default: break; 3542 } 3543 3544 TIntermAggregate* constructCoord = new TIntermAggregate(constructOp); 3545 constructCoord->getSequence().push_back(arg1); 3546 constructCoord->setLoc(loc); 3547 3548 // The input vector should never be less than 2, since there's always a bias. 3549 // The max is for safety, and should be a no-op. 3550 constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0))); 3551 3552 TIntermAggregate* tex = new TIntermAggregate(EOpTexture); 3553 tex->getSequence().push_back(arg0); // sampler 3554 tex->getSequence().push_back(constructCoord); // coordinate 3555 tex->getSequence().push_back(bias); // bias 3556 3557 node = convertReturn(tex, sampler); 3558 3559 break; 3560 } 3561 3562 // **** DX10 methods: **** 3563 case EOpMethodSample: // fall through 3564 case EOpMethodSampleBias: // ... 3565 { 3566 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3567 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 3568 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 3569 TIntermTyped* argBias = nullptr; 3570 TIntermTyped* argOffset = nullptr; 3571 const TSampler& sampler = argTex->getType().getSampler(); 3572 3573 int nextArg = 3; 3574 3575 if (op == EOpMethodSampleBias) // SampleBias has a bias arg 3576 argBias = argAggregate->getSequence()[nextArg++]->getAsTyped(); 3577 3578 TOperator textureOp = EOpTexture; 3579 3580 if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form 3581 textureOp = EOpTextureOffset; 3582 argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped(); 3583 } 3584 3585 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 3586 3587 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 3588 txsample->getSequence().push_back(txcombine); 3589 txsample->getSequence().push_back(argCoord); 3590 3591 if (argBias != nullptr) 3592 txsample->getSequence().push_back(argBias); 3593 3594 if (argOffset != nullptr) 3595 txsample->getSequence().push_back(argOffset); 3596 3597 node = convertReturn(txsample, sampler); 3598 3599 break; 3600 } 3601 3602 case EOpMethodSampleGrad: // ... 3603 { 3604 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3605 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 3606 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 3607 TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped(); 3608 TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped(); 3609 TIntermTyped* argOffset = nullptr; 3610 const TSampler& sampler = argTex->getType().getSampler(); 3611 3612 TOperator textureOp = EOpTextureGrad; 3613 3614 if (argAggregate->getSequence().size() == 6) { // last parameter is offset form 3615 textureOp = EOpTextureGradOffset; 3616 argOffset = argAggregate->getSequence()[5]->getAsTyped(); 3617 } 3618 3619 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 3620 3621 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 3622 txsample->getSequence().push_back(txcombine); 3623 txsample->getSequence().push_back(argCoord); 3624 txsample->getSequence().push_back(argDDX); 3625 txsample->getSequence().push_back(argDDY); 3626 3627 if (argOffset != nullptr) 3628 txsample->getSequence().push_back(argOffset); 3629 3630 node = convertReturn(txsample, sampler); 3631 3632 break; 3633 } 3634 3635 case EOpMethodGetDimensions: 3636 { 3637 // AST returns a vector of results, which we break apart component-wise into 3638 // separate values to assign to the HLSL method's outputs, ala: 3639 // tx . GetDimensions(width, height); 3640 // float2 sizeQueryTemp = EOpTextureQuerySize 3641 // width = sizeQueryTemp.X; 3642 // height = sizeQueryTemp.Y; 3643 3644 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3645 const TType& texType = argTex->getType(); 3646 3647 assert(texType.getBasicType() == EbtSampler); 3648 3649 const TSampler& sampler = texType.getSampler(); 3650 const TSamplerDim dim = sampler.dim; 3651 const bool isImage = sampler.isImage(); 3652 const bool isMs = sampler.isMultiSample(); 3653 const int numArgs = (int)argAggregate->getSequence().size(); 3654 3655 int numDims = 0; 3656 3657 switch (dim) { 3658 case Esd1D: numDims = 1; break; // W 3659 case Esd2D: numDims = 2; break; // W, H 3660 case Esd3D: numDims = 3; break; // W, H, D 3661 case EsdCube: numDims = 2; break; // W, H (cube) 3662 case EsdBuffer: numDims = 1; break; // W (buffers) 3663 case EsdRect: numDims = 2; break; // W, H (rect) 3664 default: 3665 assert(0 && "unhandled texture dimension"); 3666 } 3667 3668 // Arrayed adds another dimension for the number of array elements 3669 if (sampler.isArrayed()) 3670 ++numDims; 3671 3672 // Establish whether the method itself is querying mip levels. This can be false even 3673 // if the underlying query requires a MIP level, due to the available HLSL method overloads. 3674 const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0))); 3675 3676 // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query). 3677 // True if: 3678 // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query) 3679 // or, 3680 // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec) 3681 const bool mipRequired = 3682 ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1... 3683 mipQuery; // 2... 3684 3685 // AST assumes integer return. Will be converted to float if required. 3686 TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize); 3687 sizeQuery->getSequence().push_back(argTex); 3688 3689 // If we're building an LOD query, add the LOD. 3690 if (mipRequired) { 3691 // If the base HLSL query had no MIP level given, use level 0. 3692 TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() : 3693 intermediate.addConstantUnion(0, loc, true); 3694 sizeQuery->getSequence().push_back(queryLod); 3695 } 3696 3697 sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims)); 3698 sizeQuery->setLoc(loc); 3699 3700 // Return value from size query 3701 TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType()); 3702 tempArg->getWritableType().getQualifier().makeTemporary(); 3703 TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign, 3704 intermediate.addSymbol(*tempArg, loc), 3705 sizeQuery, loc); 3706 3707 // Compound statement for assigning outputs 3708 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc); 3709 // Index of first output parameter 3710 const int outParamBase = mipQuery ? 2 : 1; 3711 3712 for (int compNum = 0; compNum < numDims; ++compNum) { 3713 TIntermTyped* indexedOut = nullptr; 3714 TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc); 3715 3716 if (numDims > 1) { 3717 TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true); 3718 indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc); 3719 indexedOut->setType(TType(EbtUint, EvqTemporary, 1)); 3720 indexedOut->setLoc(loc); 3721 } else { 3722 indexedOut = sizeQueryReturn; 3723 } 3724 3725 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped(); 3726 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc); 3727 3728 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 3729 } 3730 3731 // handle mip level parameter 3732 if (mipQuery) { 3733 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); 3734 3735 TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels); 3736 levelsQuery->getSequence().push_back(argTex); 3737 levelsQuery->setType(TType(EbtUint, EvqTemporary, 1)); 3738 levelsQuery->setLoc(loc); 3739 3740 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc); 3741 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 3742 } 3743 3744 // 2DMS formats query # samples, which needs a different query op 3745 if (sampler.isMultiSample()) { 3746 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); 3747 3748 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); 3749 samplesQuery->getSequence().push_back(argTex); 3750 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); 3751 samplesQuery->setLoc(loc); 3752 3753 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc); 3754 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 3755 } 3756 3757 compoundStatement->setOperator(EOpSequence); 3758 compoundStatement->setLoc(loc); 3759 compoundStatement->setType(TType(EbtVoid)); 3760 3761 node = compoundStatement; 3762 3763 break; 3764 } 3765 3766 case EOpMethodSampleCmp: // fall through... 3767 case EOpMethodSampleCmpLevelZero: 3768 { 3769 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3770 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 3771 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 3772 TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped(); 3773 TIntermTyped* argOffset = nullptr; 3774 3775 // Sampler argument should be a sampler. 3776 if (argSamp->getType().getBasicType() != EbtSampler) { 3777 error(loc, "expected: sampler type", "", ""); 3778 return; 3779 } 3780 3781 // Sampler should be a SamplerComparisonState 3782 if (! argSamp->getType().getSampler().isShadow()) { 3783 error(loc, "expected: SamplerComparisonState", "", ""); 3784 return; 3785 } 3786 3787 // optional offset value 3788 if (argAggregate->getSequence().size() > 4) 3789 argOffset = argAggregate->getSequence()[4]->getAsTyped(); 3790 3791 const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp 3792 3793 // AST wants comparison value as one of the texture coordinates 3794 TOperator constructOp = EOpNull; 3795 switch (coordDimWithCmpVal) { 3796 // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val 3797 case 2: constructOp = EOpConstructVec2; break; 3798 case 3: constructOp = EOpConstructVec3; break; 3799 case 4: constructOp = EOpConstructVec4; break; 3800 case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg. 3801 default: assert(0); break; 3802 } 3803 3804 TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp); 3805 coordWithCmp->getSequence().push_back(argCoord); 3806 if (coordDimWithCmpVal != 5) // cube array shadow is special. 3807 coordWithCmp->getSequence().push_back(argCmpVal); 3808 coordWithCmp->setLoc(loc); 3809 coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4))); 3810 3811 TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture); 3812 if (argOffset != nullptr) 3813 textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset); 3814 3815 // Create combined sampler & texture op 3816 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 3817 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 3818 txsample->getSequence().push_back(txcombine); 3819 txsample->getSequence().push_back(coordWithCmp); 3820 3821 if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord. 3822 txsample->getSequence().push_back(argCmpVal); 3823 3824 // the LevelZero form uses 0 as an explicit LOD 3825 if (op == EOpMethodSampleCmpLevelZero) 3826 txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true)); 3827 3828 // Add offset if present 3829 if (argOffset != nullptr) 3830 txsample->getSequence().push_back(argOffset); 3831 3832 txsample->setType(node->getType()); 3833 txsample->setLoc(loc); 3834 node = txsample; 3835 3836 break; 3837 } 3838 3839 case EOpMethodLoad: 3840 { 3841 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3842 TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped(); 3843 TIntermTyped* argOffset = nullptr; 3844 TIntermTyped* lodComponent = nullptr; 3845 TIntermTyped* coordSwizzle = nullptr; 3846 3847 const TSampler& sampler = argTex->getType().getSampler(); 3848 const bool isMS = sampler.isMultiSample(); 3849 const bool isBuffer = sampler.dim == EsdBuffer; 3850 const bool isImage = sampler.isImage(); 3851 const TBasicType coordBaseType = argCoord->getType().getBasicType(); 3852 3853 // Last component of coordinate is the mip level, for non-MS. we separate them here: 3854 if (isMS || isBuffer || isImage) { 3855 // MS, Buffer, and Image have no LOD 3856 coordSwizzle = argCoord; 3857 } else { 3858 // Extract coordinate 3859 int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1); 3860 TSwizzleSelectors<TVectorSelector> coordFields; 3861 for (int i = 0; i < swizzleSize; ++i) 3862 coordFields.push_back(i); 3863 TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc); 3864 coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc); 3865 coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size())); 3866 3867 // Extract LOD 3868 TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true); 3869 lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc); 3870 lodComponent->setType(TType(coordBaseType, EvqTemporary, 1)); 3871 } 3872 3873 const int numArgs = (int)argAggregate->getSequence().size(); 3874 const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4)); 3875 3876 // Create texel fetch 3877 const TOperator fetchOp = (isImage ? EOpImageLoad : 3878 hasOffset ? EOpTextureFetchOffset : 3879 EOpTextureFetch); 3880 TIntermAggregate* txfetch = new TIntermAggregate(fetchOp); 3881 3882 // Build up the fetch 3883 txfetch->getSequence().push_back(argTex); 3884 txfetch->getSequence().push_back(coordSwizzle); 3885 3886 if (isMS) { 3887 // add 2DMS sample index 3888 TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped(); 3889 txfetch->getSequence().push_back(argSampleIdx); 3890 } else if (isBuffer) { 3891 // Nothing else to do for buffers. 3892 } else if (isImage) { 3893 // Nothing else to do for images. 3894 } else { 3895 // 2DMS and buffer have no LOD, but everything else does. 3896 txfetch->getSequence().push_back(lodComponent); 3897 } 3898 3899 // Obtain offset arg, if there is one. 3900 if (hasOffset) { 3901 const int offsetPos = (isMS ? 3 : 2); 3902 argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped(); 3903 txfetch->getSequence().push_back(argOffset); 3904 } 3905 3906 node = convertReturn(txfetch, sampler); 3907 3908 break; 3909 } 3910 3911 case EOpMethodSampleLevel: 3912 { 3913 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3914 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 3915 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 3916 TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped(); 3917 TIntermTyped* argOffset = nullptr; 3918 const TSampler& sampler = argTex->getType().getSampler(); 3919 3920 const int numArgs = (int)argAggregate->getSequence().size(); 3921 3922 if (numArgs == 5) // offset, if present 3923 argOffset = argAggregate->getSequence()[4]->getAsTyped(); 3924 3925 const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset); 3926 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 3927 3928 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 3929 3930 txsample->getSequence().push_back(txcombine); 3931 txsample->getSequence().push_back(argCoord); 3932 txsample->getSequence().push_back(argLod); 3933 3934 if (argOffset != nullptr) 3935 txsample->getSequence().push_back(argOffset); 3936 3937 node = convertReturn(txsample, sampler); 3938 3939 break; 3940 } 3941 3942 case EOpMethodGather: 3943 { 3944 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 3945 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 3946 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 3947 TIntermTyped* argOffset = nullptr; 3948 3949 // Offset is optional 3950 if (argAggregate->getSequence().size() > 3) 3951 argOffset = argAggregate->getSequence()[3]->getAsTyped(); 3952 3953 const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset); 3954 TIntermAggregate* txgather = new TIntermAggregate(textureOp); 3955 3956 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 3957 3958 txgather->getSequence().push_back(txcombine); 3959 txgather->getSequence().push_back(argCoord); 3960 // Offset if not given is implicitly channel 0 (red) 3961 3962 if (argOffset != nullptr) 3963 txgather->getSequence().push_back(argOffset); 3964 3965 txgather->setType(node->getType()); 3966 txgather->setLoc(loc); 3967 node = txgather; 3968 3969 break; 3970 } 3971 3972 case EOpMethodGatherRed: // fall through... 3973 case EOpMethodGatherGreen: // ... 3974 case EOpMethodGatherBlue: // ... 3975 case EOpMethodGatherAlpha: // ... 3976 case EOpMethodGatherCmpRed: // ... 3977 case EOpMethodGatherCmpGreen: // ... 3978 case EOpMethodGatherCmpBlue: // ... 3979 case EOpMethodGatherCmpAlpha: // ... 3980 { 3981 int channel = 0; // the channel we are gathering 3982 int cmpValues = 0; // 1 if there is a compare value (handier than a bool below) 3983 3984 switch (op) { 3985 case EOpMethodGatherCmpRed: cmpValues = 1; // fall through 3986 case EOpMethodGatherRed: channel = 0; break; 3987 case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through 3988 case EOpMethodGatherGreen: channel = 1; break; 3989 case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through 3990 case EOpMethodGatherBlue: channel = 2; break; 3991 case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through 3992 case EOpMethodGatherAlpha: channel = 3; break; 3993 default: assert(0); break; 3994 } 3995 3996 // For now, we have nothing to map the component-wise comparison forms 3997 // to, because neither GLSL nor SPIR-V has such an opcode. Issue an 3998 // unimplemented error instead. Most of the machinery is here if that 3999 // should ever become available. However, red can be passed through 4000 // to OpImageDrefGather. G/B/A cannot, because that opcode does not 4001 // accept a component. 4002 if (cmpValues != 0 && op != EOpMethodGatherCmpRed) { 4003 error(loc, "unimplemented: component-level gather compare", "", ""); 4004 return; 4005 } 4006 4007 int arg = 0; 4008 4009 TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped(); 4010 TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped(); 4011 TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped(); 4012 TIntermTyped* argOffset = nullptr; 4013 TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr }; 4014 // TIntermTyped* argStatus = nullptr; // TODO: residency 4015 TIntermTyped* argCmp = nullptr; 4016 4017 const TSamplerDim dim = argTex->getType().getSampler().dim; 4018 4019 const int argSize = (int)argAggregate->getSequence().size(); 4020 bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues)); 4021 bool hasOffset1 = false; 4022 bool hasOffset4 = false; 4023 4024 // Sampler argument should be a sampler. 4025 if (argSamp->getType().getBasicType() != EbtSampler) { 4026 error(loc, "expected: sampler type", "", ""); 4027 return; 4028 } 4029 4030 // Cmp forms require SamplerComparisonState 4031 if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) { 4032 error(loc, "expected: SamplerComparisonState", "", ""); 4033 return; 4034 } 4035 4036 // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets. 4037 if (dim == Esd2D) { 4038 hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues)); 4039 hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues)); 4040 } 4041 4042 assert(!(hasOffset1 && hasOffset4)); 4043 4044 TOperator textureOp = EOpTextureGather; 4045 4046 // Compare forms have compare value 4047 if (cmpValues != 0) 4048 argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); 4049 4050 // Some forms have single offset 4051 if (hasOffset1) { 4052 textureOp = EOpTextureGatherOffset; // single offset form 4053 argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); 4054 } 4055 4056 // Some forms have 4 gather offsets 4057 if (hasOffset4) { 4058 textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form 4059 for (int offsetNum = 0; offsetNum < 4; ++offsetNum) 4060 argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped(); 4061 } 4062 4063 // Residency status 4064 if (hasStatus) { 4065 // argStatus = argAggregate->getSequence()[arg++]->getAsTyped(); 4066 error(loc, "unimplemented: residency status", "", ""); 4067 return; 4068 } 4069 4070 TIntermAggregate* txgather = new TIntermAggregate(textureOp); 4071 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 4072 4073 TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true); 4074 4075 txgather->getSequence().push_back(txcombine); 4076 txgather->getSequence().push_back(argCoord); 4077 4078 // AST wants an array of 4 offsets, where HLSL has separate args. Here 4079 // we construct an array from the separate args. 4080 if (hasOffset4) { 4081 TType arrayType(EbtInt, EvqTemporary, 2); 4082 TArraySizes arraySizes; 4083 arraySizes.addInnerSize(4); 4084 arrayType.newArraySizes(arraySizes); 4085 4086 TIntermAggregate* initList = new TIntermAggregate(EOpNull); 4087 4088 for (int offsetNum = 0; offsetNum < 4; ++offsetNum) 4089 initList->getSequence().push_back(argOffsets[offsetNum]); 4090 4091 argOffset = addConstructor(loc, initList, arrayType); 4092 } 4093 4094 // Add comparison value if we have one 4095 if (argCmp != nullptr) 4096 txgather->getSequence().push_back(argCmp); 4097 4098 // Add offset (either 1, or an array of 4) if we have one 4099 if (argOffset != nullptr) 4100 txgather->getSequence().push_back(argOffset); 4101 4102 // Add channel value if the sampler is not shadow 4103 if (! argSamp->getType().getSampler().isShadow()) 4104 txgather->getSequence().push_back(argChannel); 4105 4106 txgather->setType(node->getType()); 4107 txgather->setLoc(loc); 4108 node = txgather; 4109 4110 break; 4111 } 4112 4113 case EOpMethodCalculateLevelOfDetail: 4114 case EOpMethodCalculateLevelOfDetailUnclamped: 4115 { 4116 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 4117 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 4118 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 4119 4120 TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod); 4121 4122 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 4123 txquerylod->getSequence().push_back(txcombine); 4124 txquerylod->getSequence().push_back(argCoord); 4125 4126 TIntermTyped* lodComponent = intermediate.addConstantUnion(0, loc, true); 4127 TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc); 4128 lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1)); 4129 4130 node = lodComponentIdx; 4131 4132 // We cannot currently obtain the unclamped LOD 4133 if (op == EOpMethodCalculateLevelOfDetailUnclamped) 4134 error(loc, "unimplemented: CalculateLevelOfDetailUnclamped", "", ""); 4135 4136 break; 4137 } 4138 4139 case EOpMethodGetSamplePosition: 4140 { 4141 // TODO: this entire decomposition exists because there is not yet a way to query 4142 // the sample position directly through SPIR-V. Instead, we return fixed sample 4143 // positions for common cases. *** If the sample positions are set differently, 4144 // this will be wrong. *** 4145 4146 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 4147 TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped(); 4148 4149 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); 4150 samplesQuery->getSequence().push_back(argTex); 4151 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); 4152 samplesQuery->setLoc(loc); 4153 4154 TIntermAggregate* compoundStatement = nullptr; 4155 4156 TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint)); 4157 outSampleCount->getWritableType().getQualifier().makeTemporary(); 4158 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc), 4159 samplesQuery, loc); 4160 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 4161 4162 TIntermTyped* idxtest[4]; 4163 4164 // Create tests against 2, 4, 8, and 16 sample values 4165 int count = 0; 4166 for (int val = 2; val <= 16; val *= 2) 4167 idxtest[count++] = 4168 intermediate.addBinaryNode(EOpEqual, 4169 intermediate.addSymbol(*outSampleCount, loc), 4170 intermediate.addConstantUnion(val, loc), 4171 loc, TType(EbtBool)); 4172 4173 const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; 4174 4175 // Create index ops into position arrays given sample index. 4176 // TODO: should it be clamped? 4177 TIntermTyped* index[4]; 4178 count = 0; 4179 for (int val = 2; val <= 16; val *= 2) { 4180 index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc); 4181 index[count++]->setType(TType(EbtFloat, EvqTemporary, 2)); 4182 } 4183 4184 // Create expression as: 4185 // (sampleCount == 2) ? pos2[idx] : 4186 // (sampleCount == 4) ? pos4[idx] : 4187 // (sampleCount == 8) ? pos8[idx] : 4188 // (sampleCount == 16) ? pos16[idx] : float2(0,0); 4189 TIntermTyped* test = 4190 intermediate.addSelection(idxtest[0], index[0], 4191 intermediate.addSelection(idxtest[1], index[1], 4192 intermediate.addSelection(idxtest[2], index[2], 4193 intermediate.addSelection(idxtest[3], index[3], 4194 getSamplePosArray(1), loc), loc), loc), loc); 4195 4196 compoundStatement = intermediate.growAggregate(compoundStatement, test); 4197 compoundStatement->setOperator(EOpSequence); 4198 compoundStatement->setLoc(loc); 4199 compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2)); 4200 4201 node = compoundStatement; 4202 4203 break; 4204 } 4205 4206 default: 4207 break; // most pass through unchanged 4208 } 4209 } 4210 4211 // 4212 // Decompose geometry shader methods 4213 // 4214 void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 4215 { 4216 if (node == nullptr || !node->getAsOperator()) 4217 return; 4218 4219 const TOperator op = node->getAsOperator()->getOp(); 4220 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; 4221 4222 switch (op) { 4223 case EOpMethodAppend: 4224 if (argAggregate) { 4225 // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. 4226 if (language != EShLangGeometry) { 4227 node = nullptr; 4228 return; 4229 } 4230 4231 TIntermAggregate* sequence = nullptr; 4232 TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex); 4233 4234 emit->setLoc(loc); 4235 emit->setType(TType(EbtVoid)); 4236 4237 // find the matching output 4238 if (gsStreamOutput == nullptr) { 4239 error(loc, "unable to find output symbol for Append()", "", ""); 4240 return; 4241 } 4242 4243 sequence = intermediate.growAggregate(sequence, 4244 handleAssign(loc, EOpAssign, 4245 intermediate.addSymbol(*gsStreamOutput, loc), 4246 argAggregate->getSequence()[1]->getAsTyped()), 4247 loc); 4248 4249 sequence = intermediate.growAggregate(sequence, emit); 4250 4251 sequence->setOperator(EOpSequence); 4252 sequence->setLoc(loc); 4253 sequence->setType(TType(EbtVoid)); 4254 node = sequence; 4255 } 4256 break; 4257 4258 case EOpMethodRestartStrip: 4259 { 4260 // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. 4261 if (language != EShLangGeometry) { 4262 node = nullptr; 4263 return; 4264 } 4265 4266 TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive); 4267 cut->setLoc(loc); 4268 cut->setType(TType(EbtVoid)); 4269 node = cut; 4270 } 4271 break; 4272 4273 default: 4274 break; // most pass through unchanged 4275 } 4276 } 4277 4278 // 4279 // Optionally decompose intrinsics to AST opcodes. 4280 // 4281 void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 4282 { 4283 // Helper to find image data for image atomics: 4284 // OpImageLoad(image[idx]) 4285 // We take the image load apart and add its params to the atomic op aggregate node 4286 const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) { 4287 TIntermAggregate* loadOp = load->getAsAggregate(); 4288 if (loadOp == nullptr) { 4289 error(loc, "unknown image type in atomic operation", "", ""); 4290 node = nullptr; 4291 return; 4292 } 4293 4294 atomic->getSequence().push_back(loadOp->getSequence()[0]); 4295 atomic->getSequence().push_back(loadOp->getSequence()[1]); 4296 }; 4297 4298 // Return true if this is an imageLoad, which we will change to an image atomic. 4299 const auto isImageParam = [](TIntermTyped* image) -> bool { 4300 TIntermAggregate* imageAggregate = image->getAsAggregate(); 4301 return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad; 4302 }; 4303 4304 // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST 4305 // opcodes for compatibility with existing software stacks. 4306 static const bool decomposeHlslIntrinsics = true; 4307 4308 if (!decomposeHlslIntrinsics || !node || !node->getAsOperator()) 4309 return; 4310 4311 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; 4312 TIntermUnary* fnUnary = node->getAsUnaryNode(); 4313 const TOperator op = node->getAsOperator()->getOp(); 4314 4315 switch (op) { 4316 case EOpGenMul: 4317 { 4318 // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul 4319 // Since we are treating HLSL rows like GLSL columns (the first matrix indirection), 4320 // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc. 4321 TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped(); 4322 TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped(); 4323 4324 if (arg0->isVector() && arg1->isVector()) { // vec * vec 4325 node->getAsAggregate()->setOperator(EOpDot); 4326 } else { 4327 node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1); 4328 } 4329 4330 break; 4331 } 4332 4333 case EOpRcp: 4334 { 4335 // rcp(a) -> 1 / a 4336 TIntermTyped* arg0 = fnUnary->getOperand(); 4337 TBasicType type0 = arg0->getBasicType(); 4338 TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true); 4339 node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0); 4340 4341 break; 4342 } 4343 4344 case EOpAny: // fall through 4345 case EOpAll: 4346 { 4347 TIntermTyped* typedArg = arguments->getAsTyped(); 4348 4349 // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool. 4350 // We'll convert here. Note that for efficiency, we could add a smarter 4351 // decomposition for some type cases, e.g, maybe by decomposing a dot product. 4352 if (typedArg->getType().getBasicType() != EbtBool) { 4353 const TType boolType(EbtBool, EvqTemporary, 4354 typedArg->getVectorSize(), 4355 typedArg->getMatrixCols(), 4356 typedArg->getMatrixRows(), 4357 typedArg->isVector()); 4358 4359 typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg); 4360 node->getAsUnaryNode()->setOperand(typedArg); 4361 } 4362 4363 break; 4364 } 4365 4366 case EOpSaturate: 4367 { 4368 // saturate(a) -> clamp(a,0,1) 4369 TIntermTyped* arg0 = fnUnary->getOperand(); 4370 TBasicType type0 = arg0->getBasicType(); 4371 TIntermAggregate* clamp = new TIntermAggregate(EOpClamp); 4372 4373 clamp->getSequence().push_back(arg0); 4374 clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true)); 4375 clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true)); 4376 clamp->setLoc(loc); 4377 clamp->setType(node->getType()); 4378 clamp->getWritableType().getQualifier().makeTemporary(); 4379 node = clamp; 4380 4381 break; 4382 } 4383 4384 case EOpSinCos: 4385 { 4386 // sincos(a,b,c) -> b = sin(a), c = cos(a) 4387 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 4388 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 4389 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); 4390 4391 TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0); 4392 TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0); 4393 TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc); 4394 TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc); 4395 4396 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc); 4397 compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign); 4398 compoundStatement->setOperator(EOpSequence); 4399 compoundStatement->setLoc(loc); 4400 compoundStatement->setType(TType(EbtVoid)); 4401 4402 node = compoundStatement; 4403 4404 break; 4405 } 4406 4407 case EOpClip: 4408 { 4409 // clip(a) -> if (any(a<0)) discard; 4410 TIntermTyped* arg0 = fnUnary->getOperand(); 4411 TBasicType type0 = arg0->getBasicType(); 4412 TIntermTyped* compareNode = nullptr; 4413 4414 // For non-scalars: per experiment with FXC compiler, discard if any component < 0. 4415 if (!arg0->isScalar()) { 4416 // component-wise compare: a < 0 4417 TIntermAggregate* less = new TIntermAggregate(EOpLessThan); 4418 less->getSequence().push_back(arg0); 4419 less->setLoc(loc); 4420 4421 // make vec or mat of bool matching dimensions of input 4422 less->setType(TType(EbtBool, EvqTemporary, 4423 arg0->getType().getVectorSize(), 4424 arg0->getType().getMatrixCols(), 4425 arg0->getType().getMatrixRows(), 4426 arg0->getType().isVector())); 4427 4428 // calculate # of components for comparison const 4429 const int constComponentCount = 4430 std::max(arg0->getType().getVectorSize(), 1) * 4431 std::max(arg0->getType().getMatrixCols(), 1) * 4432 std::max(arg0->getType().getMatrixRows(), 1); 4433 4434 TConstUnion zero; 4435 zero.setDConst(0.0); 4436 TConstUnionArray zeros(constComponentCount, zero); 4437 4438 less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true)); 4439 4440 compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool)); 4441 } else { 4442 TIntermTyped* zero = intermediate.addConstantUnion(0, type0, loc, true); 4443 compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero); 4444 } 4445 4446 TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc); 4447 4448 node = new TIntermSelection(compareNode, killNode, nullptr); 4449 node->setLoc(loc); 4450 4451 break; 4452 } 4453 4454 case EOpLog10: 4455 { 4456 // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10)) 4457 TIntermTyped* arg0 = fnUnary->getOperand(); 4458 TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0); 4459 TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true); 4460 4461 node = handleBinaryMath(loc, "mul", EOpMul, log2, base); 4462 4463 break; 4464 } 4465 4466 case EOpDst: 4467 { 4468 // dest.x = 1; 4469 // dest.y = src0.y * src1.y; 4470 // dest.z = src0.z; 4471 // dest.w = src1.w; 4472 4473 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 4474 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 4475 4476 TIntermTyped* y = intermediate.addConstantUnion(1, loc, true); 4477 TIntermTyped* z = intermediate.addConstantUnion(2, loc, true); 4478 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); 4479 4480 TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc); 4481 TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc); 4482 TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc); 4483 TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); 4484 4485 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); 4486 4487 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 4488 dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y)); 4489 dst->getSequence().push_back(src0z); 4490 dst->getSequence().push_back(src1w); 4491 dst->setType(TType(EbtFloat, EvqTemporary, 4)); 4492 dst->setLoc(loc); 4493 node = dst; 4494 4495 break; 4496 } 4497 4498 case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value 4499 case EOpInterlockedMin: // ... 4500 case EOpInterlockedMax: // ... 4501 case EOpInterlockedAnd: // ... 4502 case EOpInterlockedOr: // ... 4503 case EOpInterlockedXor: // ... 4504 case EOpInterlockedExchange: // always has output arg 4505 { 4506 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest 4507 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value 4508 TIntermTyped* arg2 = nullptr; 4509 4510 if (argAggregate->getSequence().size() > 2) 4511 arg2 = argAggregate->getSequence()[2]->getAsTyped(); 4512 4513 const bool isImage = isImageParam(arg0); 4514 const TOperator atomicOp = mapAtomicOp(loc, op, isImage); 4515 TIntermAggregate* atomic = new TIntermAggregate(atomicOp); 4516 atomic->setType(arg0->getType()); 4517 atomic->getWritableType().getQualifier().makeTemporary(); 4518 atomic->setLoc(loc); 4519 4520 if (isImage) { 4521 // orig_value = imageAtomicOp(image, loc, data) 4522 imageAtomicParams(atomic, arg0); 4523 atomic->getSequence().push_back(arg1); 4524 4525 if (argAggregate->getSequence().size() > 2) { 4526 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); 4527 } else { 4528 node = atomic; // no assignment needed, as there was no out var. 4529 } 4530 } else { 4531 // Normal memory variable: 4532 // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value 4533 if (argAggregate->getSequence().size() > 2) { 4534 // optional output param is present. return value goes to arg2. 4535 atomic->getSequence().push_back(arg0); 4536 atomic->getSequence().push_back(arg1); 4537 4538 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); 4539 } else { 4540 // Set the matching operator. Since output is absent, this is all we need to do. 4541 node->getAsAggregate()->setOperator(atomicOp); 4542 } 4543 } 4544 4545 break; 4546 } 4547 4548 case EOpInterlockedCompareExchange: 4549 { 4550 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest 4551 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp 4552 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value 4553 TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig 4554 4555 const bool isImage = isImageParam(arg0); 4556 TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage)); 4557 atomic->setLoc(loc); 4558 atomic->setType(arg2->getType()); 4559 atomic->getWritableType().getQualifier().makeTemporary(); 4560 4561 if (isImage) { 4562 imageAtomicParams(atomic, arg0); 4563 } else { 4564 atomic->getSequence().push_back(arg0); 4565 } 4566 4567 atomic->getSequence().push_back(arg1); 4568 atomic->getSequence().push_back(arg2); 4569 node = intermediate.addAssign(EOpAssign, arg3, atomic, loc); 4570 4571 break; 4572 } 4573 4574 case EOpEvaluateAttributeSnapped: 4575 { 4576 // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels 4577 // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y: 4578 // iU = (iU<<28)>>28 4579 // fU = ((float)iU)/16 4580 // Targets might handle this natively, in which case they can disable 4581 // decompositions. 4582 4583 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value 4584 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset 4585 4586 TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true); 4587 TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift, 4588 handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28), 4589 i28); 4590 4591 TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true); 4592 TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul, 4593 intermediate.addConversion(EOpConstructFloat, 4594 TType(EbtFloat, EvqTemporary, 2), iU), 4595 recip16); 4596 4597 TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset); 4598 interp->getSequence().push_back(arg0); 4599 interp->getSequence().push_back(floatOffset); 4600 interp->setLoc(loc); 4601 interp->setType(arg0->getType()); 4602 interp->getWritableType().getQualifier().makeTemporary(); 4603 4604 node = interp; 4605 4606 break; 4607 } 4608 4609 case EOpLit: 4610 { 4611 TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped(); 4612 TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped(); 4613 TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped(); 4614 4615 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); 4616 4617 // Ambient 4618 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 4619 4620 // Diffuse: 4621 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); 4622 TIntermAggregate* diffuse = new TIntermAggregate(EOpMax); 4623 diffuse->getSequence().push_back(n_dot_l); 4624 diffuse->getSequence().push_back(zero); 4625 diffuse->setLoc(loc); 4626 diffuse->setType(TType(EbtFloat)); 4627 dst->getSequence().push_back(diffuse); 4628 4629 // Specular: 4630 TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin); 4631 min_ndot->getSequence().push_back(n_dot_l); 4632 min_ndot->getSequence().push_back(n_dot_h); 4633 min_ndot->setLoc(loc); 4634 min_ndot->setType(TType(EbtFloat)); 4635 4636 TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero); 4637 TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m 4638 4639 dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc)); 4640 4641 // One: 4642 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 4643 4644 dst->setLoc(loc); 4645 dst->setType(TType(EbtFloat, EvqTemporary, 4)); 4646 node = dst; 4647 break; 4648 } 4649 4650 case EOpAsDouble: 4651 { 4652 // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must 4653 // first construct a uint64. 4654 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 4655 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 4656 4657 if (arg0->getType().isVector()) { // TODO: ... 4658 error(loc, "double2 conversion not implemented", "asdouble", ""); 4659 break; 4660 } 4661 4662 TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2); 4663 4664 uint64->getSequence().push_back(arg0); 4665 uint64->getSequence().push_back(arg1); 4666 uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2 4667 uint64->setLoc(loc); 4668 4669 // bitcast uint2 to a double 4670 TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble); 4671 convert->getAsUnaryNode()->setOperand(uint64); 4672 convert->setLoc(loc); 4673 convert->setType(TType(EbtDouble, EvqTemporary)); 4674 node = convert; 4675 4676 break; 4677 } 4678 4679 case EOpF16tof32: 4680 { 4681 // input uvecN with low 16 bits of each component holding a float16. convert to float32. 4682 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); 4683 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); 4684 const int vecSize = argValue->getType().getVectorSize(); 4685 4686 TOperator constructOp = EOpNull; 4687 switch (vecSize) { 4688 case 1: constructOp = EOpNull; break; // direct use, no construct needed 4689 case 2: constructOp = EOpConstructVec2; break; 4690 case 3: constructOp = EOpConstructVec3; break; 4691 case 4: constructOp = EOpConstructVec4; break; 4692 default: assert(0); break; 4693 } 4694 4695 // For scalar case, we don't need to construct another type. 4696 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; 4697 4698 if (result) { 4699 result->setType(TType(EbtFloat, EvqTemporary, vecSize)); 4700 result->setLoc(loc); 4701 } 4702 4703 for (int idx = 0; idx < vecSize; ++idx) { 4704 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); 4705 TIntermTyped* component = argValue->getType().isVector() ? 4706 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; 4707 4708 if (component != argValue) 4709 component->setType(TType(argValue->getBasicType(), EvqTemporary)); 4710 4711 TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16); 4712 unpackOp->setType(TType(EbtFloat, EvqTemporary, 2)); 4713 unpackOp->getAsUnaryNode()->setOperand(component); 4714 unpackOp->setLoc(loc); 4715 4716 TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc); 4717 4718 if (result != nullptr) { 4719 result->getSequence().push_back(lowOrder); 4720 node = result; 4721 } else { 4722 node = lowOrder; 4723 } 4724 } 4725 4726 break; 4727 } 4728 4729 case EOpF32tof16: 4730 { 4731 // input floatN converted to 16 bit float in low order bits of each component of uintN 4732 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); 4733 4734 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); 4735 const int vecSize = argValue->getType().getVectorSize(); 4736 4737 TOperator constructOp = EOpNull; 4738 switch (vecSize) { 4739 case 1: constructOp = EOpNull; break; // direct use, no construct needed 4740 case 2: constructOp = EOpConstructUVec2; break; 4741 case 3: constructOp = EOpConstructUVec3; break; 4742 case 4: constructOp = EOpConstructUVec4; break; 4743 default: assert(0); break; 4744 } 4745 4746 // For scalar case, we don't need to construct another type. 4747 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; 4748 4749 if (result) { 4750 result->setType(TType(EbtUint, EvqTemporary, vecSize)); 4751 result->setLoc(loc); 4752 } 4753 4754 for (int idx = 0; idx < vecSize; ++idx) { 4755 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); 4756 TIntermTyped* component = argValue->getType().isVector() ? 4757 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; 4758 4759 if (component != argValue) 4760 component->setType(TType(argValue->getBasicType(), EvqTemporary)); 4761 4762 TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2); 4763 vec2ComponentAndZero->getSequence().push_back(component); 4764 vec2ComponentAndZero->getSequence().push_back(zero); 4765 vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2)); 4766 vec2ComponentAndZero->setLoc(loc); 4767 4768 TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16); 4769 packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero); 4770 packOp->setLoc(loc); 4771 packOp->setType(TType(EbtUint, EvqTemporary)); 4772 4773 if (result != nullptr) { 4774 result->getSequence().push_back(packOp); 4775 node = result; 4776 } else { 4777 node = packOp; 4778 } 4779 } 4780 4781 break; 4782 } 4783 4784 case EOpD3DCOLORtoUBYTE4: 4785 { 4786 // ivec4 ( x.zyxw * 255.001953 ); 4787 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); 4788 TSwizzleSelectors<TVectorSelector> selectors; 4789 selectors.push_back(2); 4790 selectors.push_back(1); 4791 selectors.push_back(0); 4792 selectors.push_back(3); 4793 TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc); 4794 TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc); 4795 swizzled->setType(arg0->getType()); 4796 swizzled->getWritableType().getQualifier().makeTemporary(); 4797 4798 TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true); 4799 TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled); 4800 rangeConverted->setType(arg0->getType()); 4801 rangeConverted->getWritableType().getQualifier().makeTemporary(); 4802 4803 node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted); 4804 node->setLoc(loc); 4805 node->setType(TType(EbtInt, EvqTemporary, 4)); 4806 break; 4807 } 4808 4809 case EOpIsFinite: 4810 { 4811 // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate 4812 // it to !isnan && !isinf 4813 4814 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); 4815 4816 // We'll make a temporary in case the RHS is cmoplex 4817 TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType()); 4818 tempArg->getWritableType().getQualifier().makeTemporary(); 4819 4820 TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign, 4821 intermediate.addSymbol(*tempArg, loc), 4822 arg0, loc); 4823 4824 TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc); 4825 4826 const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(), 4827 arg0->getMatrixRows()); 4828 4829 TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc)); 4830 isnan->setType(boolType); 4831 4832 TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan); 4833 notnan->setType(boolType); 4834 4835 TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc)); 4836 isinf->setType(boolType); 4837 4838 TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf); 4839 notinf->setType(boolType); 4840 4841 TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf); 4842 andNode->setType(boolType); 4843 4844 compoundStatement = intermediate.growAggregate(compoundStatement, andNode); 4845 compoundStatement->setOperator(EOpSequence); 4846 compoundStatement->setLoc(loc); 4847 compoundStatement->setType(boolType); 4848 4849 node = compoundStatement; 4850 4851 break; 4852 } 4853 4854 default: 4855 break; // most pass through unchanged 4856 } 4857 } 4858 4859 // 4860 // Handle seeing function call syntax in the grammar, which could be any of 4861 // - .length() method 4862 // - constructor 4863 // - a call to a built-in function mapped to an operator 4864 // - a call to a built-in function that will remain a function call (e.g., texturing) 4865 // - user function 4866 // - subroutine call (not implemented yet) 4867 // 4868 TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments) 4869 { 4870 TIntermTyped* result = nullptr; 4871 4872 TOperator op = function->getBuiltInOp(); 4873 if (op != EOpNull) { 4874 // 4875 // Then this should be a constructor. 4876 // Don't go through the symbol table for constructors. 4877 // Their parameters will be verified algorithmically. 4878 // 4879 TType type(EbtVoid); // use this to get the type back 4880 if (! constructorError(loc, arguments, *function, op, type)) { 4881 // 4882 // It's a constructor, of type 'type'. 4883 // 4884 result = handleConstructor(loc, arguments, type); 4885 if (result == nullptr) 4886 error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); 4887 } 4888 } else { 4889 // 4890 // Find it in the symbol table. 4891 // 4892 const TFunction* fnCandidate = nullptr; 4893 bool builtIn = false; 4894 int thisDepth = 0; 4895 4896 TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr; 4897 4898 // TODO: this needs improvement: there's no way at present to look up a signature in 4899 // the symbol table for an arbitrary type. This is a temporary hack until that ability exists. 4900 // It will have false positives, since it doesn't check arg counts or types. 4901 if (arguments) { 4902 // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we 4903 // look for either case. 4904 4905 TIntermTyped* arg0 = nullptr; 4906 4907 if (aggregate && aggregate->getSequence().size() > 0) 4908 arg0 = aggregate->getSequence()[0]->getAsTyped(); 4909 else if (arguments->getAsSymbolNode()) 4910 arg0 = arguments->getAsSymbolNode(); 4911 4912 if (arg0 != nullptr && isStructBufferType(arg0->getType())) { 4913 static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1; 4914 4915 if (function->getName().length() > methodPrefixSize && 4916 isStructBufferMethod(function->getName().substr(methodPrefixSize))) { 4917 const TString mangle = function->getName() + "("; 4918 TSymbol* symbol = symbolTable.find(mangle, &builtIn); 4919 4920 if (symbol) 4921 fnCandidate = symbol->getAsFunction(); 4922 } 4923 } 4924 } 4925 4926 if (fnCandidate == nullptr) 4927 fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments); 4928 4929 if (fnCandidate) { 4930 // This is a declared function that might map to 4931 // - a built-in operator, 4932 // - a built-in function not mapped to an operator, or 4933 // - a user function. 4934 4935 // Error check for a function requiring specific extensions present. 4936 if (builtIn && fnCandidate->getNumExtensions()) 4937 requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), 4938 fnCandidate->getName().c_str()); 4939 4940 // turn an implicit member-function resolution into an explicit call 4941 TString callerName; 4942 if (thisDepth == 0) 4943 callerName = fnCandidate->getMangledName(); 4944 else { 4945 // get the explicit (full) name of the function 4946 callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth]; 4947 callerName += fnCandidate->getMangledName(); 4948 // insert the implicit calling argument 4949 pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments); 4950 } 4951 4952 // Convert 'in' arguments, so that types match. 4953 // However, skip those that need expansion, that is covered next. 4954 if (arguments) 4955 addInputArgumentConversions(*fnCandidate, arguments); 4956 4957 // Expand arguments. Some arguments must physically expand to a different set 4958 // than what the shader declared and passes. 4959 if (arguments && !builtIn) 4960 expandArguments(loc, *fnCandidate, arguments); 4961 4962 // Expansion may have changed the form of arguments 4963 aggregate = arguments ? arguments->getAsAggregate() : nullptr; 4964 4965 op = fnCandidate->getBuiltInOp(); 4966 if (builtIn && op != EOpNull) { 4967 // A function call mapped to a built-in operation. 4968 result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, 4969 fnCandidate->getType()); 4970 if (result == nullptr) { 4971 error(arguments->getLoc(), " wrong operand type", "Internal Error", 4972 "built in unary operator function. Type: %s", 4973 static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str()); 4974 } else if (result->getAsOperator()) { 4975 builtInOpCheck(loc, *fnCandidate, *result->getAsOperator()); 4976 } 4977 } else { 4978 // This is a function call not mapped to built-in operator. 4979 // It could still be a built-in function, but only if PureOperatorBuiltins == false. 4980 result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); 4981 TIntermAggregate* call = result->getAsAggregate(); 4982 call->setName(callerName); 4983 4984 // this is how we know whether the given function is a built-in function or a user-defined function 4985 // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also 4986 // if builtIn == true, it's definitely a built-in function with EOpNull 4987 if (! builtIn) { 4988 call->setUserDefined(); 4989 intermediate.addToCallGraph(infoSink, currentCaller, callerName); 4990 } 4991 } 4992 4993 // for decompositions, since we want to operate on the function node, not the aggregate holding 4994 // output conversions. 4995 const TIntermTyped* fnNode = result; 4996 4997 decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions 4998 decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions 4999 decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions 5000 decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions 5001 5002 // Create the qualifier list, carried in the AST for the call. 5003 // Because some arguments expand to multiple arguments, the qualifier list will 5004 // be longer than the formal parameter list. 5005 if (result == fnNode && result->getAsAggregate()) { 5006 TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); 5007 for (int i = 0; i < fnCandidate->getParamCount(); ++i) { 5008 TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; 5009 if (hasStructBuffCounter(*(*fnCandidate)[i].type)) { 5010 // add buffer and counter buffer argument qualifier 5011 qualifierList.push_back(qual); 5012 qualifierList.push_back(qual); 5013 } else if (shouldFlatten(*(*fnCandidate)[i].type)) { 5014 // add structure member expansion 5015 for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb) 5016 qualifierList.push_back(qual); 5017 } else { 5018 // Normal 1:1 case 5019 qualifierList.push_back(qual); 5020 } 5021 } 5022 } 5023 5024 // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. 5025 // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. 5026 // Also, build the qualifier list for user function calls, which are always called with an aggregate. 5027 // We don't do this is if there has been a decomposition, which will have added its own conversions 5028 // for output parameters. 5029 if (result == fnNode && result->getAsAggregate()) 5030 result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator()); 5031 } 5032 } 5033 5034 // generic error recovery 5035 // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to 5036 // reduce cascades 5037 if (result == nullptr) 5038 result = intermediate.addConstantUnion(0.0, EbtFloat, loc); 5039 5040 return result; 5041 } 5042 5043 // An initial argument list is difficult: it can be null, or a single node, 5044 // or an aggregate if more than one argument. Add one to the front, maintaining 5045 // this lack of uniformity. 5046 void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments) 5047 { 5048 if (arguments == nullptr) 5049 arguments = front; 5050 else if (arguments->getAsAggregate() != nullptr) 5051 arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front); 5052 else 5053 arguments = intermediate.growAggregate(front, arguments); 5054 } 5055 5056 // 5057 // Add any needed implicit conversions for function-call arguments to input parameters. 5058 // 5059 void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments) 5060 { 5061 TIntermAggregate* aggregate = arguments->getAsAggregate(); 5062 5063 // Replace a single argument with a single argument. 5064 const auto setArg = [&](int paramNum, TIntermTyped* arg) { 5065 if (function.getParamCount() == 1) 5066 arguments = arg; 5067 else { 5068 if (aggregate == nullptr) 5069 arguments = arg; 5070 else 5071 aggregate->getSequence()[paramNum] = arg; 5072 } 5073 }; 5074 5075 // Process each argument's conversion 5076 for (int param = 0; param < function.getParamCount(); ++param) { 5077 if (! function[param].type->getQualifier().isParamInput()) 5078 continue; 5079 5080 // At this early point there is a slight ambiguity between whether an aggregate 'arguments' 5081 // is the single argument itself or its children are the arguments. Only one argument 5082 // means take 'arguments' itself as the one argument. 5083 TIntermTyped* arg = function.getParamCount() == 1 5084 ? arguments->getAsTyped() 5085 : (aggregate ? 5086 aggregate->getSequence()[param]->getAsTyped() : 5087 arguments->getAsTyped()); 5088 if (*function[param].type != arg->getType()) { 5089 // In-qualified arguments just need an extra node added above the argument to 5090 // convert to the correct type. 5091 TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg); 5092 if (convArg != nullptr) 5093 convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg); 5094 if (convArg != nullptr) 5095 setArg(param, convArg); 5096 else 5097 error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param); 5098 } else { 5099 if (wasFlattened(arg)) { 5100 // If both formal and calling arg are to be flattened, leave that to argument 5101 // expansion, not conversion. 5102 if (!shouldFlatten(*function[param].type)) { 5103 // Will make a two-level subtree. 5104 // The deepest will copy member-by-member to build the structure to pass. 5105 // The level above that will be a two-operand EOpComma sequence that follows the copy by the 5106 // object itself. 5107 TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type); 5108 internalAggregate->getWritableType().getQualifier().makeTemporary(); 5109 TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(), 5110 internalAggregate->getName(), 5111 internalAggregate->getType()); 5112 internalSymbolNode->setLoc(arg->getLoc()); 5113 // This makes the deepest level, the member-wise copy 5114 TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign, 5115 internalSymbolNode, arg)->getAsAggregate(); 5116 5117 // Now, pair that with the resulting aggregate. 5118 assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc()); 5119 assignAgg->setOperator(EOpComma); 5120 assignAgg->setType(internalAggregate->getType()); 5121 setArg(param, assignAgg); 5122 } 5123 } 5124 } 5125 } 5126 } 5127 5128 // 5129 // Add any needed implicit expansion of calling arguments from what the shader listed to what's 5130 // internally needed for the AST (given the constraints downstream). 5131 // 5132 void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments) 5133 { 5134 TIntermAggregate* aggregate = arguments->getAsAggregate(); 5135 int functionParamNumberOffset = 0; 5136 5137 // Replace a single argument with a single argument. 5138 const auto setArg = [&](int paramNum, TIntermTyped* arg) { 5139 if (function.getParamCount() + functionParamNumberOffset == 1) 5140 arguments = arg; 5141 else { 5142 if (aggregate == nullptr) 5143 arguments = arg; 5144 else 5145 aggregate->getSequence()[paramNum] = arg; 5146 } 5147 }; 5148 5149 // Replace a single argument with a list of arguments 5150 const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) { 5151 if (args.size() == 1) 5152 setArg(paramNum, args.front()); 5153 else if (args.size() > 1) { 5154 if (function.getParamCount() + functionParamNumberOffset == 1) { 5155 arguments = intermediate.makeAggregate(args.front()); 5156 std::for_each(args.begin() + 1, args.end(), 5157 [&](TIntermTyped* arg) { 5158 arguments = intermediate.growAggregate(arguments, arg); 5159 }); 5160 } else { 5161 auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum); 5162 aggregate->getSequence().insert(it, args.begin(), args.end()); 5163 } 5164 functionParamNumberOffset += (int)(args.size() - 1); 5165 } 5166 }; 5167 5168 // Process each argument's conversion 5169 for (int param = 0; param < function.getParamCount(); ++param) { 5170 // At this early point there is a slight ambiguity between whether an aggregate 'arguments' 5171 // is the single argument itself or its children are the arguments. Only one argument 5172 // means take 'arguments' itself as the one argument. 5173 TIntermTyped* arg = function.getParamCount() == 1 5174 ? arguments->getAsTyped() 5175 : (aggregate ? 5176 aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() : 5177 arguments->getAsTyped()); 5178 5179 if (wasFlattened(arg) && shouldFlatten(*function[param].type)) { 5180 // Need to pass the structure members instead of the structure. 5181 TVector<TIntermTyped*> memberArgs; 5182 for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb) 5183 memberArgs.push_back(flattenAccess(arg, memb)); 5184 setArgList(param + functionParamNumberOffset, memberArgs); 5185 } 5186 } 5187 5188 // TODO: if we need both hidden counter args (below) and struct expansion (above) 5189 // the two algorithms need to be merged: Each assumes the list starts out 1:1 between 5190 // parameters and arguments. 5191 5192 // If any argument is a pass-by-reference struct buffer with an associated counter 5193 // buffer, we have to add another hidden parameter for that counter. 5194 if (aggregate) 5195 addStructBuffArguments(loc, aggregate); 5196 } 5197 5198 // 5199 // Add any needed implicit output conversions for function-call arguments. This 5200 // can require a new tree topology, complicated further by whether the function 5201 // has a return value. 5202 // 5203 // Returns a node of a subtree that evaluates to the return value of the function. 5204 // 5205 TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode) 5206 { 5207 assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr); 5208 5209 const TSourceLoc& loc = intermNode.getLoc(); 5210 5211 TIntermSequence argSequence; // temp sequence for unary node args 5212 5213 if (intermNode.getAsUnaryNode()) 5214 argSequence.push_back(intermNode.getAsUnaryNode()->getOperand()); 5215 5216 TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence; 5217 5218 const auto needsConversion = [&](int argNum) { 5219 return function[argNum].type->getQualifier().isParamOutput() && 5220 (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() || 5221 shouldConvertLValue(arguments[argNum]) || 5222 wasFlattened(arguments[argNum]->getAsTyped())); 5223 }; 5224 5225 // Will there be any output conversions? 5226 bool outputConversions = false; 5227 for (int i = 0; i < function.getParamCount(); ++i) { 5228 if (needsConversion(i)) { 5229 outputConversions = true; 5230 break; 5231 } 5232 } 5233 5234 if (! outputConversions) 5235 return &intermNode; 5236 5237 // Setup for the new tree, if needed: 5238 // 5239 // Output conversions need a different tree topology. 5240 // Out-qualified arguments need a temporary of the correct type, with the call 5241 // followed by an assignment of the temporary to the original argument: 5242 // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) 5243 // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) 5244 // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. 5245 TIntermTyped* conversionTree = nullptr; 5246 TVariable* tempRet = nullptr; 5247 if (intermNode.getBasicType() != EbtVoid) { 5248 // do the "tempRet = function(...), " bit from above 5249 tempRet = makeInternalVariable("tempReturn", intermNode.getType()); 5250 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); 5251 conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc); 5252 } else 5253 conversionTree = &intermNode; 5254 5255 conversionTree = intermediate.makeAggregate(conversionTree); 5256 5257 // Process each argument's conversion 5258 for (int i = 0; i < function.getParamCount(); ++i) { 5259 if (needsConversion(i)) { 5260 // Out-qualified arguments needing conversion need to use the topology setup above. 5261 // Do the " ...(tempArg, ...), arg = tempArg" bit from above. 5262 5263 // Make a temporary for what the function expects the argument to look like. 5264 TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type); 5265 tempArg->getWritableType().getQualifier().makeTemporary(); 5266 TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc); 5267 5268 // This makes the deepest level, the member-wise copy 5269 TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(), 5270 tempArgNode); 5271 tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign); 5272 conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); 5273 5274 // replace the argument with another node for the same tempArg variable 5275 arguments[i] = intermediate.addSymbol(*tempArg, loc); 5276 } 5277 } 5278 5279 // Finalize the tree topology (see bigger comment above). 5280 if (tempRet) { 5281 // do the "..., tempRet" bit from above 5282 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); 5283 conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc); 5284 } 5285 5286 conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc); 5287 5288 return conversionTree; 5289 } 5290 5291 // 5292 // Add any needed "hidden" counter buffer arguments for function calls. 5293 // 5294 // Modifies the 'aggregate' argument if needed. Otherwise, is no-op. 5295 // 5296 void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate) 5297 { 5298 // See if there are any SB types with counters. 5299 const bool hasStructBuffArg = 5300 std::any_of(aggregate->getSequence().begin(), 5301 aggregate->getSequence().end(), 5302 [this](const TIntermNode* node) { 5303 return (node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType()); 5304 }); 5305 5306 // Nothing to do, if we didn't find one. 5307 if (! hasStructBuffArg) 5308 return; 5309 5310 TIntermSequence argsWithCounterBuffers; 5311 5312 for (int param = 0; param < int(aggregate->getSequence().size()); ++param) { 5313 argsWithCounterBuffers.push_back(aggregate->getSequence()[param]); 5314 5315 if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) { 5316 const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode(); 5317 if (blockSym != nullptr) { 5318 TType counterType; 5319 counterBufferType(loc, counterType); 5320 5321 const TString counterBlockName(getStructBuffCounterName(blockSym->getName())); 5322 5323 TVariable* variable = makeInternalVariable(counterBlockName, counterType); 5324 5325 // Mark this buffer as requiring a counter block. TODO: there should be a better 5326 // way to track it. 5327 structBufferCounter[counterBlockName] = true; 5328 5329 TIntermSymbol* sym = intermediate.addSymbol(*variable, loc); 5330 argsWithCounterBuffers.push_back(sym); 5331 } 5332 } 5333 } 5334 5335 // Swap with the temp list we've built up. 5336 aggregate->getSequence().swap(argsWithCounterBuffers); 5337 } 5338 5339 5340 // 5341 // Do additional checking of built-in function calls that is not caught 5342 // by normal semantic checks on argument type, extension tagging, etc. 5343 // 5344 // Assumes there has been a semantically correct match to a built-in function prototype. 5345 // 5346 void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) 5347 { 5348 // Set up convenience accessors to the argument(s). There is almost always 5349 // multiple arguments for the cases below, but when there might be one, 5350 // check the unaryArg first. 5351 const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference 5352 const TIntermTyped* unaryArg = nullptr; 5353 const TIntermTyped* arg0 = nullptr; 5354 if (callNode.getAsAggregate()) { 5355 argp = &callNode.getAsAggregate()->getSequence(); 5356 if (argp->size() > 0) 5357 arg0 = (*argp)[0]->getAsTyped(); 5358 } else { 5359 assert(callNode.getAsUnaryNode()); 5360 unaryArg = callNode.getAsUnaryNode()->getOperand(); 5361 arg0 = unaryArg; 5362 } 5363 const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr 5364 5365 switch (callNode.getOp()) { 5366 case EOpTextureGather: 5367 case EOpTextureGatherOffset: 5368 case EOpTextureGatherOffsets: 5369 { 5370 // Figure out which variants are allowed by what extensions, 5371 // and what arguments must be constant for which situations. 5372 5373 TString featureString = fnCandidate.getName() + "(...)"; 5374 const char* feature = featureString.c_str(); 5375 int compArg = -1; // track which argument, if any, is the constant component argument 5376 switch (callNode.getOp()) { 5377 case EOpTextureGather: 5378 // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, 5379 // otherwise, need GL_ARB_texture_gather. 5380 if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || 5381 fnCandidate[0].type->getSampler().shadow) { 5382 if (! fnCandidate[0].type->getSampler().shadow) 5383 compArg = 2; 5384 } 5385 break; 5386 case EOpTextureGatherOffset: 5387 // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument 5388 if (! fnCandidate[0].type->getSampler().shadow) 5389 compArg = 3; 5390 break; 5391 case EOpTextureGatherOffsets: 5392 if (! fnCandidate[0].type->getSampler().shadow) 5393 compArg = 3; 5394 break; 5395 default: 5396 break; 5397 } 5398 5399 if (compArg > 0 && compArg < fnCandidate.getParamCount()) { 5400 if (aggArgs[compArg]->getAsConstantUnion()) { 5401 int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); 5402 if (value < 0 || value > 3) 5403 error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); 5404 } else 5405 error(loc, "must be a compile-time constant:", feature, "component argument"); 5406 } 5407 5408 break; 5409 } 5410 5411 case EOpTextureOffset: 5412 case EOpTextureFetchOffset: 5413 case EOpTextureProjOffset: 5414 case EOpTextureLodOffset: 5415 case EOpTextureProjLodOffset: 5416 case EOpTextureGradOffset: 5417 case EOpTextureProjGradOffset: 5418 { 5419 // Handle texture-offset limits checking 5420 // Pick which argument has to hold constant offsets 5421 int arg = -1; 5422 switch (callNode.getOp()) { 5423 case EOpTextureOffset: arg = 2; break; 5424 case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break; 5425 case EOpTextureProjOffset: arg = 2; break; 5426 case EOpTextureLodOffset: arg = 3; break; 5427 case EOpTextureProjLodOffset: arg = 3; break; 5428 case EOpTextureGradOffset: arg = 4; break; 5429 case EOpTextureProjGradOffset: arg = 4; break; 5430 default: 5431 assert(0); 5432 break; 5433 } 5434 5435 if (arg > 0) { 5436 if (aggArgs[arg]->getAsConstantUnion() == nullptr) 5437 error(loc, "argument must be compile-time constant", "texel offset", ""); 5438 else { 5439 const TType& type = aggArgs[arg]->getAsTyped()->getType(); 5440 for (int c = 0; c < type.getVectorSize(); ++c) { 5441 int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); 5442 if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) 5443 error(loc, "value is out of range:", "texel offset", 5444 "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); 5445 } 5446 } 5447 } 5448 5449 break; 5450 } 5451 5452 case EOpTextureQuerySamples: 5453 case EOpImageQuerySamples: 5454 break; 5455 5456 case EOpImageAtomicAdd: 5457 case EOpImageAtomicMin: 5458 case EOpImageAtomicMax: 5459 case EOpImageAtomicAnd: 5460 case EOpImageAtomicOr: 5461 case EOpImageAtomicXor: 5462 case EOpImageAtomicExchange: 5463 case EOpImageAtomicCompSwap: 5464 break; 5465 5466 case EOpInterpolateAtCentroid: 5467 case EOpInterpolateAtSample: 5468 case EOpInterpolateAtOffset: 5469 // Make sure the first argument is an interpolant, or an array element of an interpolant 5470 if (arg0->getType().getQualifier().storage != EvqVaryingIn) { 5471 // It might still be an array element. 5472 // 5473 // We could check more, but the semantics of the first argument are already met; the 5474 // only way to turn an array into a float/vec* is array dereference and swizzle. 5475 // 5476 // ES and desktop 4.3 and earlier: swizzles may not be used 5477 // desktop 4.4 and later: swizzles may be used 5478 const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true); 5479 if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) 5480 error(loc, "first argument must be an interpolant, or interpolant-array element", 5481 fnCandidate.getName().c_str(), ""); 5482 } 5483 break; 5484 5485 default: 5486 break; 5487 } 5488 } 5489 5490 // 5491 // Handle seeing something in a grammar production that can be done by calling 5492 // a constructor. 5493 // 5494 // The constructor still must be "handled" by handleFunctionCall(), which will 5495 // then call handleConstructor(). 5496 // 5497 TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type) 5498 { 5499 TOperator op = intermediate.mapTypeToConstructorOp(type); 5500 5501 if (op == EOpNull) { 5502 error(loc, "cannot construct this type", type.getBasicString(), ""); 5503 return nullptr; 5504 } 5505 5506 TString empty(""); 5507 5508 return new TFunction(&empty, type, op); 5509 } 5510 5511 // 5512 // Handle seeing a "COLON semantic" at the end of a type declaration, 5513 // by updating the type according to the semantic. 5514 // 5515 void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn, 5516 const TString& upperCase) 5517 { 5518 // Parse and return semantic number. If limit is 0, it will be ignored. Otherwise, if the parsed 5519 // semantic number is >= limit, errorMsg is issued and 0 is returned. 5520 // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet 5521 // accept those in lambda functions. 5522 const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int { 5523 size_t pos = semantic.find_last_not_of("0123456789"); 5524 if (pos == std::string::npos) 5525 return 0u; 5526 5527 unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1); 5528 5529 if (limit != 0 && semanticNum >= limit) { 5530 error(loc, errorMsg, semantic.c_str(), ""); 5531 return 0u; 5532 } 5533 5534 return semanticNum; 5535 }; 5536 5537 switch(builtIn) { 5538 case EbvNone: 5539 // Get location numbers from fragment outputs, instead of 5540 // auto-assigning them. 5541 if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) { 5542 qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr); 5543 nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u); 5544 } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) { 5545 builtIn = EbvClipDistance; 5546 qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic"); 5547 } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) { 5548 builtIn = EbvCullDistance; 5549 qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic"); 5550 } 5551 break; 5552 case EbvPosition: 5553 // adjust for stage in/out 5554 if (language == EShLangFragment) 5555 builtIn = EbvFragCoord; 5556 break; 5557 case EbvFragStencilRef: 5558 error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", ""); 5559 break; 5560 case EbvTessLevelInner: 5561 case EbvTessLevelOuter: 5562 qualifier.patch = true; 5563 break; 5564 default: 5565 break; 5566 } 5567 5568 qualifier.builtIn = builtIn; 5569 qualifier.semanticName = intermediate.addSemanticName(upperCase); 5570 } 5571 5572 // 5573 // Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN" 5574 // 5575 // 'location' has the "c[Subcomponent]" part. 5576 // 'component' points to the "component" part, or nullptr if not present. 5577 // 5578 void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location, 5579 const glslang::TString* component) 5580 { 5581 if (location.size() == 0 || location[0] != 'c') { 5582 error(loc, "expected 'c'", "packoffset", ""); 5583 return; 5584 } 5585 if (location.size() == 1) 5586 return; 5587 if (! isdigit(location[1])) { 5588 error(loc, "expected number after 'c'", "packoffset", ""); 5589 return; 5590 } 5591 5592 qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str()); 5593 if (component != nullptr) { 5594 int componentOffset = 0; 5595 switch ((*component)[0]) { 5596 case 'x': componentOffset = 0; break; 5597 case 'y': componentOffset = 4; break; 5598 case 'z': componentOffset = 8; break; 5599 case 'w': componentOffset = 12; break; 5600 default: 5601 componentOffset = -1; 5602 break; 5603 } 5604 if (componentOffset < 0 || component->size() > 1) { 5605 error(loc, "expected {x, y, z, w} for component", "packoffset", ""); 5606 return; 5607 } 5608 qualifier.layoutOffset += componentOffset; 5609 } 5610 } 5611 5612 // 5613 // Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN" 5614 // 5615 // 'profile' points to the shader_profile part, or nullptr if not present. 5616 // 'desc' is the type# part. 5617 // 5618 void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile, 5619 const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc) 5620 { 5621 if (profile != nullptr) 5622 warn(loc, "ignoring shader_profile", "register", ""); 5623 5624 if (desc.size() < 1) { 5625 error(loc, "expected register type", "register", ""); 5626 return; 5627 } 5628 5629 int regNumber = 0; 5630 if (desc.size() > 1) { 5631 if (isdigit(desc[1])) 5632 regNumber = atoi(desc.substr(1, desc.size()).c_str()); 5633 else { 5634 error(loc, "expected register number after register type", "register", ""); 5635 return; 5636 } 5637 } 5638 5639 // TODO: learn what all these really mean and how they interact with regNumber and subComponent 5640 const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding(); 5641 switch (std::tolower(desc[0])) { 5642 case 'b': 5643 case 't': 5644 case 'c': 5645 case 's': 5646 case 'u': 5647 qualifier.layoutBinding = regNumber + subComponent; 5648 5649 // This handles per-register layout sets numbers. For the global mode which sets 5650 // every symbol to the same value, see setLinkageLayoutSets(). 5651 if ((resourceInfo.size() % 3) == 0) { 5652 // Apply per-symbol resource set and binding. 5653 for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) { 5654 if (strcmp(desc.c_str(), it[0].c_str()) == 0) { 5655 qualifier.layoutSet = atoi(it[1].c_str()); 5656 qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent; 5657 break; 5658 } 5659 } 5660 } 5661 break; 5662 default: 5663 warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]); 5664 break; 5665 } 5666 5667 // space 5668 unsigned int setNumber; 5669 const auto crackSpace = [&]() -> bool { 5670 const int spaceLen = 5; 5671 if (spaceDesc->size() < spaceLen + 1) 5672 return false; 5673 if (spaceDesc->compare(0, spaceLen, "space") != 0) 5674 return false; 5675 if (! isdigit((*spaceDesc)[spaceLen])) 5676 return false; 5677 setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str()); 5678 return true; 5679 }; 5680 5681 if (spaceDesc) { 5682 if (! crackSpace()) { 5683 error(loc, "expected spaceN", "register", ""); 5684 return; 5685 } 5686 qualifier.layoutSet = setNumber; 5687 } 5688 } 5689 5690 // Convert to a scalar boolean, or if not allowed by HLSL semantics, 5691 // report an error and return nullptr. 5692 TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition, 5693 bool mustBeScalar) 5694 { 5695 if (mustBeScalar && !condition->getType().isScalarOrVec1()) { 5696 error(loc, "requires a scalar", "conditional expression", ""); 5697 return nullptr; 5698 } 5699 5700 return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()), 5701 condition); 5702 } 5703 5704 // 5705 // Same error message for all places assignments don't work. 5706 // 5707 void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) 5708 { 5709 error(loc, "", op, "cannot convert from '%s' to '%s'", 5710 right.c_str(), left.c_str()); 5711 } 5712 5713 // 5714 // Same error message for all places unary operations don't work. 5715 // 5716 void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) 5717 { 5718 error(loc, " wrong operand type", op, 5719 "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", 5720 op, operand.c_str()); 5721 } 5722 5723 // 5724 // Same error message for all binary operations don't work. 5725 // 5726 void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) 5727 { 5728 error(loc, " wrong operand types:", op, 5729 "no operation '%s' exists that takes a left-hand operand of type '%s' and " 5730 "a right operand of type '%s' (or there is no acceptable conversion)", 5731 op, left.c_str(), right.c_str()); 5732 } 5733 5734 // 5735 // A basic type of EbtVoid is a key that the name string was seen in the source, but 5736 // it was not found as a variable in the symbol table. If so, give the error 5737 // message and insert a dummy variable in the symbol table to prevent future errors. 5738 // 5739 void HlslParseContext::variableCheck(TIntermTyped*& nodePtr) 5740 { 5741 TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); 5742 if (! symbol) 5743 return; 5744 5745 if (symbol->getType().getBasicType() == EbtVoid) { 5746 error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), ""); 5747 5748 // Add to symbol table to prevent future error messages on the same name 5749 if (symbol->getName().size() > 0) { 5750 TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); 5751 symbolTable.insert(*fakeVariable); 5752 5753 // substitute a symbol node for this new variable 5754 nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); 5755 } 5756 } 5757 } 5758 5759 // 5760 // Both test, and if necessary spit out an error, to see if the node is really 5761 // a constant. 5762 // 5763 void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token) 5764 { 5765 if (node->getQualifier().storage != EvqConst) 5766 error(node->getLoc(), "constant expression required", token, ""); 5767 } 5768 5769 // 5770 // Both test, and if necessary spit out an error, to see if the node is really 5771 // an integer. 5772 // 5773 void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token) 5774 { 5775 if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) 5776 return; 5777 5778 error(node->getLoc(), "scalar integer expression required", token, ""); 5779 } 5780 5781 // 5782 // Both test, and if necessary spit out an error, to see if we are currently 5783 // globally scoped. 5784 // 5785 void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token) 5786 { 5787 if (! symbolTable.atGlobalLevel()) 5788 error(loc, "not allowed in nested scope", token, ""); 5789 } 5790 5791 bool HlslParseContext::builtInName(const TString& /*identifier*/) 5792 { 5793 return false; 5794 } 5795 5796 // 5797 // Make sure there is enough data and not too many arguments provided to the 5798 // constructor to build something of the type of the constructor. Also returns 5799 // the type of the constructor. 5800 // 5801 // Returns true if there was an error in construction. 5802 // 5803 bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, 5804 TOperator op, TType& type) 5805 { 5806 type.shallowCopy(function.getType()); 5807 5808 bool constructingMatrix = false; 5809 switch (op) { 5810 case EOpConstructTextureSampler: 5811 return constructorTextureSamplerError(loc, function); 5812 case EOpConstructMat2x2: 5813 case EOpConstructMat2x3: 5814 case EOpConstructMat2x4: 5815 case EOpConstructMat3x2: 5816 case EOpConstructMat3x3: 5817 case EOpConstructMat3x4: 5818 case EOpConstructMat4x2: 5819 case EOpConstructMat4x3: 5820 case EOpConstructMat4x4: 5821 case EOpConstructDMat2x2: 5822 case EOpConstructDMat2x3: 5823 case EOpConstructDMat2x4: 5824 case EOpConstructDMat3x2: 5825 case EOpConstructDMat3x3: 5826 case EOpConstructDMat3x4: 5827 case EOpConstructDMat4x2: 5828 case EOpConstructDMat4x3: 5829 case EOpConstructDMat4x4: 5830 case EOpConstructIMat2x2: 5831 case EOpConstructIMat2x3: 5832 case EOpConstructIMat2x4: 5833 case EOpConstructIMat3x2: 5834 case EOpConstructIMat3x3: 5835 case EOpConstructIMat3x4: 5836 case EOpConstructIMat4x2: 5837 case EOpConstructIMat4x3: 5838 case EOpConstructIMat4x4: 5839 case EOpConstructUMat2x2: 5840 case EOpConstructUMat2x3: 5841 case EOpConstructUMat2x4: 5842 case EOpConstructUMat3x2: 5843 case EOpConstructUMat3x3: 5844 case EOpConstructUMat3x4: 5845 case EOpConstructUMat4x2: 5846 case EOpConstructUMat4x3: 5847 case EOpConstructUMat4x4: 5848 case EOpConstructBMat2x2: 5849 case EOpConstructBMat2x3: 5850 case EOpConstructBMat2x4: 5851 case EOpConstructBMat3x2: 5852 case EOpConstructBMat3x3: 5853 case EOpConstructBMat3x4: 5854 case EOpConstructBMat4x2: 5855 case EOpConstructBMat4x3: 5856 case EOpConstructBMat4x4: 5857 constructingMatrix = true; 5858 break; 5859 default: 5860 break; 5861 } 5862 5863 // 5864 // Walk the arguments for first-pass checks and collection of information. 5865 // 5866 5867 int size = 0; 5868 bool constType = true; 5869 bool full = false; 5870 bool overFull = false; 5871 bool matrixInMatrix = false; 5872 bool arrayArg = false; 5873 for (int arg = 0; arg < function.getParamCount(); ++arg) { 5874 if (function[arg].type->isArray()) { 5875 if (! function[arg].type->isExplicitlySizedArray()) { 5876 // Can't construct from an unsized array. 5877 error(loc, "array argument must be sized", "constructor", ""); 5878 return true; 5879 } 5880 arrayArg = true; 5881 } 5882 if (constructingMatrix && function[arg].type->isMatrix()) 5883 matrixInMatrix = true; 5884 5885 // 'full' will go to true when enough args have been seen. If we loop 5886 // again, there is an extra argument. 5887 if (full) { 5888 // For vectors and matrices, it's okay to have too many components 5889 // available, but not okay to have unused arguments. 5890 overFull = true; 5891 } 5892 5893 size += function[arg].type->computeNumComponents(); 5894 if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) 5895 full = true; 5896 5897 if (function[arg].type->getQualifier().storage != EvqConst) 5898 constType = false; 5899 } 5900 5901 if (constType) 5902 type.getQualifier().storage = EvqConst; 5903 5904 if (type.isArray()) { 5905 if (function.getParamCount() == 0) { 5906 error(loc, "array constructor must have at least one argument", "constructor", ""); 5907 return true; 5908 } 5909 5910 if (type.isImplicitlySizedArray()) { 5911 // auto adapt the constructor type to the number of arguments 5912 type.changeOuterArraySize(function.getParamCount()); 5913 } else if (type.getOuterArraySize() != function.getParamCount() && 5914 type.computeNumComponents() > size) { 5915 error(loc, "array constructor needs one argument per array element", "constructor", ""); 5916 return true; 5917 } 5918 5919 if (type.isArrayOfArrays()) { 5920 // Types have to match, but we're still making the type. 5921 // Finish making the type, and the comparison is done later 5922 // when checking for conversion. 5923 TArraySizes& arraySizes = type.getArraySizes(); 5924 5925 // At least the dimensionalities have to match. 5926 if (! function[0].type->isArray() || 5927 arraySizes.getNumDims() != function[0].type->getArraySizes().getNumDims() + 1) { 5928 error(loc, "array constructor argument not correct type to construct array element", "constructor", ""); 5929 return true; 5930 } 5931 5932 if (arraySizes.isInnerImplicit()) { 5933 // "Arrays of arrays ..., and the size for any dimension is optional" 5934 // That means we need to adopt (from the first argument) the other array sizes into the type. 5935 for (int d = 1; d < arraySizes.getNumDims(); ++d) { 5936 if (arraySizes.getDimSize(d) == UnsizedArraySize) { 5937 arraySizes.setDimSize(d, function[0].type->getArraySizes().getDimSize(d - 1)); 5938 } 5939 } 5940 } 5941 } 5942 } 5943 5944 // Some array -> array type casts are okay 5945 if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() && 5946 !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() && 5947 type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1) 5948 return false; 5949 5950 if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { 5951 error(loc, "constructing non-array constituent from array argument", "constructor", ""); 5952 return true; 5953 } 5954 5955 if (matrixInMatrix && ! type.isArray()) { 5956 return false; 5957 } 5958 5959 if (overFull) { 5960 error(loc, "too many arguments", "constructor", ""); 5961 return true; 5962 } 5963 5964 if (op == EOpConstructStruct && ! type.isArray() && isScalarConstructor(node)) 5965 return false; 5966 5967 if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) { 5968 error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); 5969 return true; 5970 } 5971 5972 if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || 5973 (op == EOpConstructStruct && size < type.computeNumComponents())) { 5974 error(loc, "not enough data provided for construction", "constructor", ""); 5975 return true; 5976 } 5977 5978 return false; 5979 } 5980 5981 // See if 'node', in the context of constructing aggregates, is a scalar argument 5982 // to a constructor. 5983 // 5984 bool HlslParseContext::isScalarConstructor(const TIntermNode* node) 5985 { 5986 // Obviously, it must be a scalar, but an aggregate node might not be fully 5987 // completed yet: holding a sequence of initializers under an aggregate 5988 // would not yet be typed, so don't check it's type. This corresponds to 5989 // the aggregate operator also not being set yet. (An aggregate operation 5990 // that legitimately yields a scalar will have a getOp() of that operator, 5991 // not EOpNull.) 5992 5993 return node->getAsTyped() != nullptr && 5994 node->getAsTyped()->isScalar() && 5995 (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull); 5996 } 5997 5998 // Verify all the correct semantics for constructing a combined texture/sampler. 5999 // Return true if the semantics are incorrect. 6000 bool HlslParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) 6001 { 6002 TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change 6003 const char* token = constructorName.c_str(); 6004 6005 // exactly two arguments needed 6006 if (function.getParamCount() != 2) { 6007 error(loc, "sampler-constructor requires two arguments", token, ""); 6008 return true; 6009 } 6010 6011 // For now, not allowing arrayed constructors, the rest of this function 6012 // is set up to allow them, if this test is removed: 6013 if (function.getType().isArray()) { 6014 error(loc, "sampler-constructor cannot make an array of samplers", token, ""); 6015 return true; 6016 } 6017 6018 // first argument 6019 // * the constructor's first argument must be a texture type 6020 // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array) 6021 // of the texture type must match that of the constructed sampler type 6022 // (that is, the suffixes of the type of the first argument and the 6023 // type of the constructor will be spelled the same way) 6024 if (function[0].type->getBasicType() != EbtSampler || 6025 ! function[0].type->getSampler().isTexture() || 6026 function[0].type->isArray()) { 6027 error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, ""); 6028 return true; 6029 } 6030 // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() 6031 TSampler texture = function.getType().getSampler(); 6032 texture.combined = false; 6033 texture.shadow = false; 6034 if (texture != function[0].type->getSampler()) { 6035 error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, ""); 6036 return true; 6037 } 6038 6039 // second argument 6040 // * the constructor's second argument must be a scalar of type 6041 // *sampler* or *samplerShadow* 6042 // * the presence or absence of depth comparison (Shadow) must match 6043 // between the constructed sampler type and the type of the second argument 6044 if (function[1].type->getBasicType() != EbtSampler || 6045 ! function[1].type->getSampler().isPureSampler() || 6046 function[1].type->isArray()) { 6047 error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, ""); 6048 return true; 6049 } 6050 if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) { 6051 error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow", 6052 token, ""); 6053 return true; 6054 } 6055 6056 return false; 6057 } 6058 6059 // Checks to see if a void variable has been declared and raise an error message for such a case 6060 // 6061 // returns true in case of an error 6062 // 6063 bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) 6064 { 6065 if (basicType == EbtVoid) { 6066 error(loc, "illegal use of type 'void'", identifier.c_str(), ""); 6067 return true; 6068 } 6069 6070 return false; 6071 } 6072 6073 // 6074 // Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. 6075 // 6076 void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier) 6077 { 6078 // move from parameter/unknown qualifiers to pipeline in/out qualifiers 6079 switch (qualifier.storage) { 6080 case EvqIn: 6081 qualifier.storage = EvqVaryingIn; 6082 break; 6083 case EvqOut: 6084 qualifier.storage = EvqVaryingOut; 6085 break; 6086 default: 6087 break; 6088 } 6089 } 6090 6091 // 6092 // Merge characteristics of the 'src' qualifier into the 'dst'. 6093 // If there is duplication, issue error messages, unless 'force' 6094 // is specified, which means to just override default settings. 6095 // 6096 // Also, when force is false, it will be assumed that 'src' follows 6097 // 'dst', for the purpose of error checking order for versions 6098 // that require specific orderings of qualifiers. 6099 // 6100 void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src) 6101 { 6102 // Storage qualification 6103 if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) 6104 dst.storage = src.storage; 6105 else if ((dst.storage == EvqIn && src.storage == EvqOut) || 6106 (dst.storage == EvqOut && src.storage == EvqIn)) 6107 dst.storage = EvqInOut; 6108 else if ((dst.storage == EvqIn && src.storage == EvqConst) || 6109 (dst.storage == EvqConst && src.storage == EvqIn)) 6110 dst.storage = EvqConstReadOnly; 6111 6112 // Layout qualifiers 6113 mergeObjectLayoutQualifiers(dst, src, false); 6114 6115 // individual qualifiers 6116 bool repeated = false; 6117 #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; 6118 MERGE_SINGLETON(invariant); 6119 MERGE_SINGLETON(noContraction); 6120 MERGE_SINGLETON(centroid); 6121 MERGE_SINGLETON(smooth); 6122 MERGE_SINGLETON(flat); 6123 MERGE_SINGLETON(nopersp); 6124 MERGE_SINGLETON(patch); 6125 MERGE_SINGLETON(sample); 6126 MERGE_SINGLETON(coherent); 6127 MERGE_SINGLETON(volatil); 6128 MERGE_SINGLETON(restrict); 6129 MERGE_SINGLETON(readonly); 6130 MERGE_SINGLETON(writeonly); 6131 MERGE_SINGLETON(specConstant); 6132 } 6133 6134 // used to flatten the sampler type space into a single dimension 6135 // correlates with the declaration of defaultSamplerPrecision[] 6136 int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler) 6137 { 6138 int arrayIndex = sampler.arrayed ? 1 : 0; 6139 int shadowIndex = sampler.shadow ? 1 : 0; 6140 int externalIndex = sampler.external ? 1 : 0; 6141 6142 return EsdNumDims * 6143 (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim; 6144 } 6145 6146 // 6147 // Do size checking for an array type's size. 6148 // 6149 void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair) 6150 { 6151 bool isConst = false; 6152 sizePair.size = 1; 6153 sizePair.node = nullptr; 6154 6155 TIntermConstantUnion* constant = expr->getAsConstantUnion(); 6156 if (constant) { 6157 // handle true (non-specialization) constant 6158 sizePair.size = constant->getConstArray()[0].getIConst(); 6159 isConst = true; 6160 } else { 6161 // see if it's a specialization constant instead 6162 if (expr->getQualifier().isSpecConstant()) { 6163 isConst = true; 6164 sizePair.node = expr; 6165 TIntermSymbol* symbol = expr->getAsSymbolNode(); 6166 if (symbol && symbol->getConstArray().size() > 0) 6167 sizePair.size = symbol->getConstArray()[0].getIConst(); 6168 } 6169 } 6170 6171 if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { 6172 error(loc, "array size must be a constant integer expression", "", ""); 6173 return; 6174 } 6175 6176 if (sizePair.size <= 0) { 6177 error(loc, "array size must be a positive integer", "", ""); 6178 return; 6179 } 6180 } 6181 6182 // 6183 // Require array to be completely sized 6184 // 6185 void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) 6186 { 6187 if (arraySizes.isImplicit()) 6188 error(loc, "array size required", "", ""); 6189 } 6190 6191 void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) 6192 { 6193 const TTypeList& structure = *type.getStruct(); 6194 for (int m = 0; m < (int)structure.size(); ++m) { 6195 const TType& member = *structure[m].type; 6196 if (member.isArray()) 6197 arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); 6198 } 6199 } 6200 6201 // Merge array dimensions listed in 'sizes' onto the type's array dimensions. 6202 // 6203 // From the spec: "vec4[2] a[3]; // size-3 array of size-2 array of vec4" 6204 // 6205 // That means, the 'sizes' go in front of the 'type' as outermost sizes. 6206 // 'type' is the type part of the declaration (to the left) 6207 // 'sizes' is the arrayness tagged on the identifier (to the right) 6208 // 6209 void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes) 6210 { 6211 if (sizes) 6212 type.addArrayOuterSizes(*sizes); 6213 } 6214 6215 // 6216 // Do all the semantic checking for declaring or redeclaring an array, with and 6217 // without a size, and make the right changes to the symbol table. 6218 // 6219 void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, 6220 TSymbol*& symbol, bool track) 6221 { 6222 if (symbol == nullptr) { 6223 bool currentScope; 6224 symbol = symbolTable.find(identifier, nullptr, ¤tScope); 6225 6226 if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { 6227 // bad shader (errors already reported) trying to redeclare a built-in name as an array 6228 return; 6229 } 6230 if (symbol == nullptr || ! currentScope) { 6231 // 6232 // Successfully process a new definition. 6233 // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) 6234 // 6235 symbol = new TVariable(&identifier, type); 6236 symbolTable.insert(*symbol); 6237 if (track && symbolTable.atGlobalLevel()) 6238 trackLinkage(*symbol); 6239 6240 return; 6241 } 6242 if (symbol->getAsAnonMember()) { 6243 error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); 6244 symbol = nullptr; 6245 return; 6246 } 6247 } 6248 6249 // 6250 // Process a redeclaration. 6251 // 6252 6253 if (symbol == nullptr) { 6254 error(loc, "array variable name expected", identifier.c_str(), ""); 6255 return; 6256 } 6257 6258 // redeclareBuiltinVariable() should have already done the copyUp() 6259 TType& existingType = symbol->getWritableType(); 6260 6261 if (existingType.isExplicitlySizedArray()) { 6262 // be more lenient for input arrays to geometry shaders and tessellation control outputs, 6263 // where the redeclaration is the same size 6264 return; 6265 } 6266 6267 existingType.updateArraySizes(type); 6268 } 6269 6270 void HlslParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *node, int index) 6271 { 6272 // maybe there is nothing to do... 6273 TIntermTyped* typedNode = node->getAsTyped(); 6274 if (typedNode->getType().getImplicitArraySize() > index) 6275 return; 6276 6277 // something to do... 6278 6279 // Figure out what symbol to lookup, as we will use its type to edit for the size change, 6280 // as that type will be shared through shallow copies for future references. 6281 TSymbol* symbol = nullptr; 6282 int blockIndex = -1; 6283 const TString* lookupName = nullptr; 6284 if (node->getAsSymbolNode()) 6285 lookupName = &node->getAsSymbolNode()->getName(); 6286 else if (node->getAsBinaryNode()) { 6287 const TIntermBinary* deref = node->getAsBinaryNode(); 6288 // This has to be the result of a block dereference, unless it's bad shader code 6289 // If it's a uniform block, then an error will be issued elsewhere, but 6290 // return early now to avoid crashing later in this function. 6291 if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock || 6292 deref->getLeft()->getType().getQualifier().storage == EvqUniform || 6293 deref->getRight()->getAsConstantUnion() == nullptr) 6294 return; 6295 6296 blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); 6297 6298 lookupName = &deref->getLeft()->getAsSymbolNode()->getName(); 6299 if (IsAnonymous(*lookupName)) 6300 lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName(); 6301 } 6302 6303 // Lookup the symbol, should only fail if shader code is incorrect 6304 symbol = symbolTable.find(*lookupName); 6305 if (symbol == nullptr) 6306 return; 6307 6308 if (symbol->getAsFunction()) { 6309 error(loc, "array variable name expected", symbol->getName().c_str(), ""); 6310 return; 6311 } 6312 6313 symbol->getWritableType().setImplicitArraySize(index + 1); 6314 } 6315 6316 // 6317 // Enforce non-initializer type/qualifier rules. 6318 // 6319 void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type, 6320 TIntermTyped*& initializer) 6321 { 6322 // 6323 // Make the qualifier make sense, given that there is an initializer. 6324 // 6325 if (initializer == nullptr) { 6326 if (type.getQualifier().storage == EvqConst || 6327 type.getQualifier().storage == EvqConstReadOnly) { 6328 initializer = intermediate.makeAggregate(loc); 6329 warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), ""); 6330 } 6331 } 6332 } 6333 6334 // 6335 // See if the identifier is a built-in symbol that can be redeclared, and if so, 6336 // copy the symbol table's read-only built-in variable to the current 6337 // global level, where it can be modified based on the passed in type. 6338 // 6339 // Returns nullptr if no redeclaration took place; meaning a normal declaration still 6340 // needs to occur for it, not necessarily an error. 6341 // 6342 // Returns a redeclared and type-modified variable if a redeclared occurred. 6343 // 6344 TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier, 6345 const TQualifier& /*qualifier*/, 6346 const TShaderQualifiers& /*publicType*/) 6347 { 6348 if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) 6349 return nullptr; 6350 6351 return nullptr; 6352 } 6353 6354 // 6355 // Generate index to the array element in a structure buffer (SSBO) 6356 // 6357 TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const 6358 { 6359 // Bail out if not a struct buffer 6360 if (buffer == nullptr || ! isStructBufferType(buffer->getType())) 6361 return nullptr; 6362 6363 // Runtime sized array is always the last element. 6364 const TTypeList* bufferStruct = buffer->getType().getStruct(); 6365 TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc); 6366 6367 TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc); 6368 argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type); 6369 6370 return argArray; 6371 } 6372 6373 // 6374 // IFF type is a structuredbuffer/byteaddressbuffer type, return the content 6375 // (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr. 6376 // 6377 TType* HlslParseContext::getStructBufferContentType(const TType& type) const 6378 { 6379 if (type.getBasicType() != EbtBlock) 6380 return nullptr; 6381 6382 const int memberCount = (int)type.getStruct()->size(); 6383 assert(memberCount > 0); 6384 6385 TType* contentType = (*type.getStruct())[memberCount-1].type; 6386 6387 return contentType->isRuntimeSizedArray() ? contentType : nullptr; 6388 } 6389 6390 // 6391 // If an existing struct buffer has a sharable type, then share it. 6392 // 6393 void HlslParseContext::shareStructBufferType(TType& type) 6394 { 6395 // PackOffset must be equivalent to share types on a per-member basis. 6396 // Note: cannot use auto type due to recursion. Thus, this is a std::function. 6397 const std::function<bool(TType& lhs, TType& rhs)> 6398 compareQualifiers = [&](TType& lhs, TType& rhs) -> bool { 6399 if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset) 6400 return false; 6401 6402 if (lhs.isStruct() != rhs.isStruct()) 6403 return false; 6404 6405 if (lhs.isStruct() && rhs.isStruct()) { 6406 if (lhs.getStruct()->size() != rhs.getStruct()->size()) 6407 return false; 6408 6409 for (int i = 0; i < int(lhs.getStruct()->size()); ++i) 6410 if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type)) 6411 return false; 6412 } 6413 6414 return true; 6415 }; 6416 6417 // We need to compare certain qualifiers in addition to the type. 6418 const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool { 6419 if (lhs.getQualifier().readonly != rhs.getQualifier().readonly) 6420 return false; 6421 6422 // If both are structures, recursively look for packOffset equality 6423 // as well as type equality. 6424 return compareQualifiers(lhs, rhs) && lhs == rhs; 6425 }; 6426 6427 // This is an exhaustive O(N) search, but real world shaders have 6428 // only a small number of these. 6429 for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) { 6430 // If the deep structure matches, modulo qualifiers, use it 6431 if (typeEqual(*structBufferTypes[idx], type)) { 6432 type.shallowCopy(*structBufferTypes[idx]); 6433 return; 6434 } 6435 } 6436 6437 // Otherwise, remember it: 6438 TType* typeCopy = new TType; 6439 typeCopy->shallowCopy(type); 6440 structBufferTypes.push_back(typeCopy); 6441 } 6442 6443 void HlslParseContext::paramFix(TType& type) 6444 { 6445 switch (type.getQualifier().storage) { 6446 case EvqConst: 6447 type.getQualifier().storage = EvqConstReadOnly; 6448 break; 6449 case EvqGlobal: 6450 case EvqUniform: 6451 case EvqTemporary: 6452 type.getQualifier().storage = EvqIn; 6453 break; 6454 case EvqBuffer: 6455 { 6456 // SSBO parameter. These do not go through the declareBlock path since they are fn parameters. 6457 correctUniform(type.getQualifier()); 6458 TQualifier bufferQualifier = globalBufferDefaults; 6459 mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true); 6460 bufferQualifier.storage = type.getQualifier().storage; 6461 bufferQualifier.readonly = type.getQualifier().readonly; 6462 bufferQualifier.coherent = type.getQualifier().coherent; 6463 bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn; 6464 type.getQualifier() = bufferQualifier; 6465 break; 6466 } 6467 default: 6468 break; 6469 } 6470 } 6471 6472 void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) 6473 { 6474 if (type.containsSpecializationSize()) 6475 error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); 6476 } 6477 6478 // 6479 // Layout qualifier stuff. 6480 // 6481 6482 // Put the id's layout qualification into the public type, for qualifiers not having a number set. 6483 // This is before we know any type information for error checking. 6484 void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id) 6485 { 6486 std::transform(id.begin(), id.end(), id.begin(), ::tolower); 6487 6488 if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { 6489 qualifier.layoutMatrix = ElmRowMajor; 6490 return; 6491 } 6492 if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { 6493 qualifier.layoutMatrix = ElmColumnMajor; 6494 return; 6495 } 6496 if (id == "push_constant") { 6497 requireVulkan(loc, "push_constant"); 6498 qualifier.layoutPushConstant = true; 6499 return; 6500 } 6501 if (language == EShLangGeometry || language == EShLangTessEvaluation) { 6502 if (id == TQualifier::getGeometryString(ElgTriangles)) { 6503 // publicType.shaderQualifiers.geometry = ElgTriangles; 6504 warn(loc, "ignored", id.c_str(), ""); 6505 return; 6506 } 6507 if (language == EShLangGeometry) { 6508 if (id == TQualifier::getGeometryString(ElgPoints)) { 6509 // publicType.shaderQualifiers.geometry = ElgPoints; 6510 warn(loc, "ignored", id.c_str(), ""); 6511 return; 6512 } 6513 if (id == TQualifier::getGeometryString(ElgLineStrip)) { 6514 // publicType.shaderQualifiers.geometry = ElgLineStrip; 6515 warn(loc, "ignored", id.c_str(), ""); 6516 return; 6517 } 6518 if (id == TQualifier::getGeometryString(ElgLines)) { 6519 // publicType.shaderQualifiers.geometry = ElgLines; 6520 warn(loc, "ignored", id.c_str(), ""); 6521 return; 6522 } 6523 if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { 6524 // publicType.shaderQualifiers.geometry = ElgLinesAdjacency; 6525 warn(loc, "ignored", id.c_str(), ""); 6526 return; 6527 } 6528 if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { 6529 // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; 6530 warn(loc, "ignored", id.c_str(), ""); 6531 return; 6532 } 6533 if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { 6534 // publicType.shaderQualifiers.geometry = ElgTriangleStrip; 6535 warn(loc, "ignored", id.c_str(), ""); 6536 return; 6537 } 6538 } else { 6539 assert(language == EShLangTessEvaluation); 6540 6541 // input primitive 6542 if (id == TQualifier::getGeometryString(ElgTriangles)) { 6543 // publicType.shaderQualifiers.geometry = ElgTriangles; 6544 warn(loc, "ignored", id.c_str(), ""); 6545 return; 6546 } 6547 if (id == TQualifier::getGeometryString(ElgQuads)) { 6548 // publicType.shaderQualifiers.geometry = ElgQuads; 6549 warn(loc, "ignored", id.c_str(), ""); 6550 return; 6551 } 6552 if (id == TQualifier::getGeometryString(ElgIsolines)) { 6553 // publicType.shaderQualifiers.geometry = ElgIsolines; 6554 warn(loc, "ignored", id.c_str(), ""); 6555 return; 6556 } 6557 6558 // vertex spacing 6559 if (id == TQualifier::getVertexSpacingString(EvsEqual)) { 6560 // publicType.shaderQualifiers.spacing = EvsEqual; 6561 warn(loc, "ignored", id.c_str(), ""); 6562 return; 6563 } 6564 if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { 6565 // publicType.shaderQualifiers.spacing = EvsFractionalEven; 6566 warn(loc, "ignored", id.c_str(), ""); 6567 return; 6568 } 6569 if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { 6570 // publicType.shaderQualifiers.spacing = EvsFractionalOdd; 6571 warn(loc, "ignored", id.c_str(), ""); 6572 return; 6573 } 6574 6575 // triangle order 6576 if (id == TQualifier::getVertexOrderString(EvoCw)) { 6577 // publicType.shaderQualifiers.order = EvoCw; 6578 warn(loc, "ignored", id.c_str(), ""); 6579 return; 6580 } 6581 if (id == TQualifier::getVertexOrderString(EvoCcw)) { 6582 // publicType.shaderQualifiers.order = EvoCcw; 6583 warn(loc, "ignored", id.c_str(), ""); 6584 return; 6585 } 6586 6587 // point mode 6588 if (id == "point_mode") { 6589 // publicType.shaderQualifiers.pointMode = true; 6590 warn(loc, "ignored", id.c_str(), ""); 6591 return; 6592 } 6593 } 6594 } 6595 if (language == EShLangFragment) { 6596 if (id == "origin_upper_left") { 6597 // publicType.shaderQualifiers.originUpperLeft = true; 6598 warn(loc, "ignored", id.c_str(), ""); 6599 return; 6600 } 6601 if (id == "pixel_center_integer") { 6602 // publicType.shaderQualifiers.pixelCenterInteger = true; 6603 warn(loc, "ignored", id.c_str(), ""); 6604 return; 6605 } 6606 if (id == "early_fragment_tests") { 6607 // publicType.shaderQualifiers.earlyFragmentTests = true; 6608 warn(loc, "ignored", id.c_str(), ""); 6609 return; 6610 } 6611 for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) { 6612 if (id == TQualifier::getLayoutDepthString(depth)) { 6613 // publicType.shaderQualifiers.layoutDepth = depth; 6614 warn(loc, "ignored", id.c_str(), ""); 6615 return; 6616 } 6617 } 6618 if (id.compare(0, 13, "blend_support") == 0) { 6619 bool found = false; 6620 for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { 6621 if (id == TQualifier::getBlendEquationString(be)) { 6622 requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation"); 6623 intermediate.addBlendEquation(be); 6624 // publicType.shaderQualifiers.blendEquation = true; 6625 warn(loc, "ignored", id.c_str(), ""); 6626 found = true; 6627 break; 6628 } 6629 } 6630 if (! found) 6631 error(loc, "unknown blend equation", "blend_support", ""); 6632 return; 6633 } 6634 } 6635 error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); 6636 } 6637 6638 // Put the id's layout qualifier value into the public type, for qualifiers having a number set. 6639 // This is before we know any type information for error checking. 6640 void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, 6641 const TIntermTyped* node) 6642 { 6643 const char* feature = "layout-id value"; 6644 // const char* nonLiteralFeature = "non-literal layout-id value"; 6645 6646 integerCheck(node, feature); 6647 const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); 6648 int value = 0; 6649 if (constUnion) { 6650 value = constUnion->getConstArray()[0].getIConst(); 6651 } 6652 6653 std::transform(id.begin(), id.end(), id.begin(), ::tolower); 6654 6655 if (id == "offset") { 6656 qualifier.layoutOffset = value; 6657 return; 6658 } else if (id == "align") { 6659 // "The specified alignment must be a power of 2, or a compile-time error results." 6660 if (! IsPow2(value)) 6661 error(loc, "must be a power of 2", "align", ""); 6662 else 6663 qualifier.layoutAlign = value; 6664 return; 6665 } else if (id == "location") { 6666 if ((unsigned int)value >= TQualifier::layoutLocationEnd) 6667 error(loc, "location is too large", id.c_str(), ""); 6668 else 6669 qualifier.layoutLocation = value; 6670 return; 6671 } else if (id == "set") { 6672 if ((unsigned int)value >= TQualifier::layoutSetEnd) 6673 error(loc, "set is too large", id.c_str(), ""); 6674 else 6675 qualifier.layoutSet = value; 6676 return; 6677 } else if (id == "binding") { 6678 if ((unsigned int)value >= TQualifier::layoutBindingEnd) 6679 error(loc, "binding is too large", id.c_str(), ""); 6680 else 6681 qualifier.layoutBinding = value; 6682 return; 6683 } else if (id == "component") { 6684 if ((unsigned)value >= TQualifier::layoutComponentEnd) 6685 error(loc, "component is too large", id.c_str(), ""); 6686 else 6687 qualifier.layoutComponent = value; 6688 return; 6689 } else if (id.compare(0, 4, "xfb_") == 0) { 6690 // "Any shader making any static use (after preprocessing) of any of these 6691 // *xfb_* qualifiers will cause the shader to be in a transform feedback 6692 // capturing mode and hence responsible for describing the transform feedback 6693 // setup." 6694 intermediate.setXfbMode(); 6695 if (id == "xfb_buffer") { 6696 // "It is a compile-time error to specify an *xfb_buffer* that is greater than 6697 // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." 6698 if (value >= resources.maxTransformFeedbackBuffers) 6699 error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", 6700 resources.maxTransformFeedbackBuffers); 6701 if (value >= (int)TQualifier::layoutXfbBufferEnd) 6702 error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1); 6703 else 6704 qualifier.layoutXfbBuffer = value; 6705 return; 6706 } else if (id == "xfb_offset") { 6707 if (value >= (int)TQualifier::layoutXfbOffsetEnd) 6708 error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1); 6709 else 6710 qualifier.layoutXfbOffset = value; 6711 return; 6712 } else if (id == "xfb_stride") { 6713 // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the 6714 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." 6715 if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) 6716 error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", 6717 resources.maxTransformFeedbackInterleavedComponents); 6718 else if (value >= (int)TQualifier::layoutXfbStrideEnd) 6719 error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1); 6720 if (value < (int)TQualifier::layoutXfbStrideEnd) 6721 qualifier.layoutXfbStride = value; 6722 return; 6723 } 6724 } 6725 6726 if (id == "input_attachment_index") { 6727 requireVulkan(loc, "input_attachment_index"); 6728 if (value >= (int)TQualifier::layoutAttachmentEnd) 6729 error(loc, "attachment index is too large", id.c_str(), ""); 6730 else 6731 qualifier.layoutAttachment = value; 6732 return; 6733 } 6734 if (id == "constant_id") { 6735 requireSpv(loc, "constant_id"); 6736 if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { 6737 error(loc, "specialization-constant id is too large", id.c_str(), ""); 6738 } else { 6739 qualifier.layoutSpecConstantId = value; 6740 qualifier.specConstant = true; 6741 if (! intermediate.addUsedConstantId(value)) 6742 error(loc, "specialization-constant id already used", id.c_str(), ""); 6743 } 6744 return; 6745 } 6746 6747 switch (language) { 6748 case EShLangVertex: 6749 break; 6750 6751 case EShLangTessControl: 6752 if (id == "vertices") { 6753 if (value == 0) 6754 error(loc, "must be greater than 0", "vertices", ""); 6755 else 6756 // publicType.shaderQualifiers.vertices = value; 6757 warn(loc, "ignored", id.c_str(), ""); 6758 return; 6759 } 6760 break; 6761 6762 case EShLangTessEvaluation: 6763 break; 6764 6765 case EShLangGeometry: 6766 if (id == "invocations") { 6767 if (value == 0) 6768 error(loc, "must be at least 1", "invocations", ""); 6769 else 6770 // publicType.shaderQualifiers.invocations = value; 6771 warn(loc, "ignored", id.c_str(), ""); 6772 return; 6773 } 6774 if (id == "max_vertices") { 6775 // publicType.shaderQualifiers.vertices = value; 6776 warn(loc, "ignored", id.c_str(), ""); 6777 if (value > resources.maxGeometryOutputVertices) 6778 error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); 6779 return; 6780 } 6781 if (id == "stream") { 6782 qualifier.layoutStream = value; 6783 return; 6784 } 6785 break; 6786 6787 case EShLangFragment: 6788 if (id == "index") { 6789 qualifier.layoutIndex = value; 6790 return; 6791 } 6792 break; 6793 6794 case EShLangCompute: 6795 if (id.compare(0, 11, "local_size_") == 0) { 6796 if (id == "local_size_x") { 6797 // publicType.shaderQualifiers.localSize[0] = value; 6798 warn(loc, "ignored", id.c_str(), ""); 6799 return; 6800 } 6801 if (id == "local_size_y") { 6802 // publicType.shaderQualifiers.localSize[1] = value; 6803 warn(loc, "ignored", id.c_str(), ""); 6804 return; 6805 } 6806 if (id == "local_size_z") { 6807 // publicType.shaderQualifiers.localSize[2] = value; 6808 warn(loc, "ignored", id.c_str(), ""); 6809 return; 6810 } 6811 if (spvVersion.spv != 0) { 6812 if (id == "local_size_x_id") { 6813 // publicType.shaderQualifiers.localSizeSpecId[0] = value; 6814 warn(loc, "ignored", id.c_str(), ""); 6815 return; 6816 } 6817 if (id == "local_size_y_id") { 6818 // publicType.shaderQualifiers.localSizeSpecId[1] = value; 6819 warn(loc, "ignored", id.c_str(), ""); 6820 return; 6821 } 6822 if (id == "local_size_z_id") { 6823 // publicType.shaderQualifiers.localSizeSpecId[2] = value; 6824 warn(loc, "ignored", id.c_str(), ""); 6825 return; 6826 } 6827 } 6828 } 6829 break; 6830 6831 default: 6832 break; 6833 } 6834 6835 error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); 6836 } 6837 6838 // Merge any layout qualifier information from src into dst, leaving everything else in dst alone 6839 // 6840 // "More than one layout qualifier may appear in a single declaration. 6841 // Additionally, the same layout-qualifier-name can occur multiple times 6842 // within a layout qualifier or across multiple layout qualifiers in the 6843 // same declaration. When the same layout-qualifier-name occurs 6844 // multiple times, in a single declaration, the last occurrence overrides 6845 // the former occurrence(s). Further, if such a layout-qualifier-name 6846 // will effect subsequent declarations or other observable behavior, it 6847 // is only the last occurrence that will have any effect, behaving as if 6848 // the earlier occurrence(s) within the declaration are not present. 6849 // This is also true for overriding layout-qualifier-names, where one 6850 // overrides the other (e.g., row_major vs. column_major); only the last 6851 // occurrence has any effect." 6852 // 6853 void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) 6854 { 6855 if (src.hasMatrix()) 6856 dst.layoutMatrix = src.layoutMatrix; 6857 if (src.hasPacking()) 6858 dst.layoutPacking = src.layoutPacking; 6859 6860 if (src.hasStream()) 6861 dst.layoutStream = src.layoutStream; 6862 6863 if (src.hasFormat()) 6864 dst.layoutFormat = src.layoutFormat; 6865 6866 if (src.hasXfbBuffer()) 6867 dst.layoutXfbBuffer = src.layoutXfbBuffer; 6868 6869 if (src.hasAlign()) 6870 dst.layoutAlign = src.layoutAlign; 6871 6872 if (! inheritOnly) { 6873 if (src.hasLocation()) 6874 dst.layoutLocation = src.layoutLocation; 6875 if (src.hasComponent()) 6876 dst.layoutComponent = src.layoutComponent; 6877 if (src.hasIndex()) 6878 dst.layoutIndex = src.layoutIndex; 6879 6880 if (src.hasOffset()) 6881 dst.layoutOffset = src.layoutOffset; 6882 6883 if (src.hasSet()) 6884 dst.layoutSet = src.layoutSet; 6885 if (src.layoutBinding != TQualifier::layoutBindingEnd) 6886 dst.layoutBinding = src.layoutBinding; 6887 6888 if (src.hasXfbStride()) 6889 dst.layoutXfbStride = src.layoutXfbStride; 6890 if (src.hasXfbOffset()) 6891 dst.layoutXfbOffset = src.layoutXfbOffset; 6892 if (src.hasAttachment()) 6893 dst.layoutAttachment = src.layoutAttachment; 6894 if (src.hasSpecConstantId()) 6895 dst.layoutSpecConstantId = src.layoutSpecConstantId; 6896 6897 if (src.layoutPushConstant) 6898 dst.layoutPushConstant = true; 6899 } 6900 } 6901 6902 // 6903 // Look up a function name in the symbol table, and make sure it is a function. 6904 // 6905 // First, look for an exact match. If there is none, use the generic selector 6906 // TParseContextBase::selectFunction() to find one, parameterized by the 6907 // convertible() and better() predicates defined below. 6908 // 6909 // Return the function symbol if found, otherwise nullptr. 6910 // 6911 const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, 6912 TIntermTyped*& args) 6913 { 6914 if (symbolTable.isFunctionNameVariable(call.getName())) { 6915 error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); 6916 return nullptr; 6917 } 6918 6919 // first, look for an exact match 6920 bool dummyScope; 6921 TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth); 6922 if (symbol) 6923 return symbol->getAsFunction(); 6924 6925 // no exact match, use the generic selector, parameterized by the GLSL rules 6926 6927 // create list of candidates to send 6928 TVector<const TFunction*> candidateList; 6929 symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); 6930 6931 // These built-in ops can accept any type, so we bypass the argument selection 6932 if (candidateList.size() == 1 && builtIn && 6933 (candidateList[0]->getBuiltInOp() == EOpMethodAppend || 6934 candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip || 6935 candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter || 6936 candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter || 6937 candidateList[0]->getBuiltInOp() == EOpMethodAppend || 6938 candidateList[0]->getBuiltInOp() == EOpMethodConsume)) { 6939 return candidateList[0]; 6940 } 6941 6942 bool allowOnlyUpConversions = true; 6943 6944 // can 'from' convert to 'to'? 6945 const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool { 6946 if (from == to) 6947 return true; 6948 6949 // no aggregate conversions 6950 if (from.isArray() || to.isArray() || 6951 from.isStruct() || to.isStruct()) 6952 return false; 6953 6954 switch (op) { 6955 case EOpInterlockedAdd: 6956 case EOpInterlockedAnd: 6957 case EOpInterlockedCompareExchange: 6958 case EOpInterlockedCompareStore: 6959 case EOpInterlockedExchange: 6960 case EOpInterlockedMax: 6961 case EOpInterlockedMin: 6962 case EOpInterlockedOr: 6963 case EOpInterlockedXor: 6964 // We do not promote the texture or image type for these ocodes. Normally that would not 6965 // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this 6966 // stage it's merely e.g, a basic integer type. 6967 // 6968 // Instead, we want to promote other arguments, but stay within the same family. In other 6969 // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor, 6970 // but it is allowed to promote its other arguments. 6971 if (arg == 0) 6972 return false; 6973 break; 6974 case EOpMethodSample: 6975 case EOpMethodSampleBias: 6976 case EOpMethodSampleCmp: 6977 case EOpMethodSampleCmpLevelZero: 6978 case EOpMethodSampleGrad: 6979 case EOpMethodSampleLevel: 6980 case EOpMethodLoad: 6981 case EOpMethodGetDimensions: 6982 case EOpMethodGetSamplePosition: 6983 case EOpMethodGather: 6984 case EOpMethodCalculateLevelOfDetail: 6985 case EOpMethodCalculateLevelOfDetailUnclamped: 6986 case EOpMethodGatherRed: 6987 case EOpMethodGatherGreen: 6988 case EOpMethodGatherBlue: 6989 case EOpMethodGatherAlpha: 6990 case EOpMethodGatherCmp: 6991 case EOpMethodGatherCmpRed: 6992 case EOpMethodGatherCmpGreen: 6993 case EOpMethodGatherCmpBlue: 6994 case EOpMethodGatherCmpAlpha: 6995 case EOpMethodAppend: 6996 case EOpMethodRestartStrip: 6997 // those are method calls, the object type can not be changed 6998 // they are equal if the dim and type match (is dim sufficient?) 6999 if (arg == 0) 7000 return from.getSampler().type == to.getSampler().type && 7001 from.getSampler().arrayed == to.getSampler().arrayed && 7002 from.getSampler().shadow == to.getSampler().shadow && 7003 from.getSampler().ms == to.getSampler().ms && 7004 from.getSampler().dim == to.getSampler().dim; 7005 break; 7006 default: 7007 break; 7008 } 7009 7010 // basic types have to be convertible 7011 if (allowOnlyUpConversions) 7012 if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall)) 7013 return false; 7014 7015 // shapes have to be convertible 7016 if ((from.isScalarOrVec1() && to.isScalarOrVec1()) || 7017 (from.isScalarOrVec1() && to.isVector()) || 7018 (from.isScalarOrVec1() && to.isMatrix()) || 7019 (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize())) 7020 return true; 7021 7022 // TODO: what are the matrix rules? they go here 7023 7024 return false; 7025 }; 7026 7027 // Is 'to2' a better conversion than 'to1'? 7028 // Ties should not be considered as better. 7029 // Assumes 'convertible' already said true. 7030 const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { 7031 // exact match is always better than mismatch 7032 if (from == to2) 7033 return from != to1; 7034 if (from == to1) 7035 return false; 7036 7037 // shape changes are always worse 7038 if (from.isScalar() || from.isVector()) { 7039 if (from.getVectorSize() == to2.getVectorSize() && 7040 from.getVectorSize() != to1.getVectorSize()) 7041 return true; 7042 if (from.getVectorSize() == to1.getVectorSize() && 7043 from.getVectorSize() != to2.getVectorSize()) 7044 return false; 7045 } 7046 7047 // Handle sampler betterness: An exact sampler match beats a non-exact match. 7048 // (If we just looked at basic type, all EbtSamplers would look the same). 7049 // If any type is not a sampler, just use the linearize function below. 7050 if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) { 7051 // We can ignore the vector size in the comparison. 7052 TSampler to1Sampler = to1.getSampler(); 7053 TSampler to2Sampler = to2.getSampler(); 7054 7055 to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize; 7056 7057 if (from.getSampler() == to2Sampler) 7058 return from.getSampler() != to1Sampler; 7059 if (from.getSampler() == to1Sampler) 7060 return false; 7061 } 7062 7063 // Might or might not be changing shape, which means basic type might 7064 // or might not match, so within that, the question is how big a 7065 // basic-type conversion is being done. 7066 // 7067 // Use a hierarchy of domains, translated to order of magnitude 7068 // in a linearized view: 7069 // - floating-point vs. integer 7070 // - 32 vs. 64 bit (or width in general) 7071 // - bool vs. non bool 7072 // - signed vs. not signed 7073 const auto linearize = [](const TBasicType& basicType) -> int { 7074 switch (basicType) { 7075 case EbtBool: return 1; 7076 case EbtInt: return 10; 7077 case EbtUint: return 11; 7078 case EbtInt64: return 20; 7079 case EbtUint64: return 21; 7080 case EbtFloat: return 100; 7081 case EbtDouble: return 110; 7082 default: return 0; 7083 } 7084 }; 7085 7086 return std::abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) < 7087 std::abs(linearize(to1.getBasicType()) - linearize(from.getBasicType())); 7088 }; 7089 7090 // for ambiguity reporting 7091 bool tie = false; 7092 7093 // send to the generic selector 7094 const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); 7095 7096 if (bestMatch == nullptr) { 7097 // If there is nothing selected by allowing only up-conversions (to a larger linearize() value), 7098 // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any 7099 // upconversions possible. 7100 allowOnlyUpConversions = false; 7101 bestMatch = selectFunction(candidateList, call, convertible, better, tie); 7102 } 7103 7104 if (bestMatch == nullptr) { 7105 error(loc, "no matching overloaded function found", call.getName().c_str(), ""); 7106 return nullptr; 7107 } 7108 7109 // For built-ins, we can convert across the arguments. This will happen in several steps: 7110 // Step 1: If there's an exact match, use it. 7111 // Step 2a: Otherwise, get the operator from the best match and promote arguments: 7112 // Step 2b: reconstruct the TFunction based on the new arg types 7113 // Step 3: Re-select after type promotion is applied, to find proper candidate. 7114 if (builtIn) { 7115 // Step 1: If there's an exact match, use it. 7116 if (call.getMangledName() == bestMatch->getMangledName()) 7117 return bestMatch; 7118 7119 // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we 7120 // are that kind of operator. 7121 if (args != nullptr) { 7122 // The arg list can be a unary node, or an aggregate. We have to handle both. 7123 // We will use the normal promote() facilities, which require an interm node. 7124 TIntermOperator* promote = nullptr; 7125 7126 if (call.getParamCount() == 1) { 7127 promote = new TIntermUnary(bestMatch->getBuiltInOp()); 7128 promote->getAsUnaryNode()->setOperand(args->getAsTyped()); 7129 } else { 7130 promote = new TIntermAggregate(bestMatch->getBuiltInOp()); 7131 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); 7132 } 7133 7134 if (! intermediate.promote(promote)) 7135 return nullptr; 7136 7137 // Obtain the promoted arg list. 7138 if (call.getParamCount() == 1) { 7139 args = promote->getAsUnaryNode()->getOperand(); 7140 } else { 7141 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); 7142 } 7143 } 7144 7145 // Step 2b: reconstruct the TFunction based on the new arg types 7146 TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp()); 7147 7148 if (args->getAsAggregate()) { 7149 // Handle aggregates: put all args into the new function call 7150 for (int arg=0; arg<int(args->getAsAggregate()->getSequence().size()); ++arg) { 7151 // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly. 7152 TParameter param = { 0, new TType, nullptr }; 7153 param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType()); 7154 convertedCall.addParameter(param); 7155 } 7156 } else if (args->getAsUnaryNode()) { 7157 // Handle unaries: put all args into the new function call 7158 TParameter param = { 0, new TType, nullptr }; 7159 param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType()); 7160 convertedCall.addParameter(param); 7161 } else if (args->getAsTyped()) { 7162 // Handle bare e.g, floats, not in an aggregate. 7163 TParameter param = { 0, new TType, nullptr }; 7164 param.type->shallowCopy(args->getAsTyped()->getType()); 7165 convertedCall.addParameter(param); 7166 } else { 7167 assert(0); // unknown argument list. 7168 return nullptr; 7169 } 7170 7171 // Step 3: Re-select after type promotion, to find proper candidate 7172 // send to the generic selector 7173 bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie); 7174 7175 // At this point, there should be no tie. 7176 } 7177 7178 if (tie) 7179 error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); 7180 7181 // Append default parameter values if needed 7182 if (!tie && bestMatch != nullptr) { 7183 for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) { 7184 handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue); 7185 } 7186 } 7187 7188 return bestMatch; 7189 } 7190 7191 // 7192 // Do everything necessary to handle a typedef declaration, for a single symbol. 7193 // 7194 // 'parseType' is the type part of the declaration (to the left) 7195 // 'arraySizes' is the arrayness tagged on the identifier (to the right) 7196 // 7197 void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType) 7198 { 7199 TVariable* typeSymbol = new TVariable(&identifier, parseType, true); 7200 if (! symbolTable.insert(*typeSymbol)) 7201 error(loc, "name already defined", "typedef", identifier.c_str()); 7202 } 7203 7204 // Do everything necessary to handle a struct declaration, including 7205 // making IO aliases because HLSL allows mixed IO in a struct that specializes 7206 // based on the usage (input, output, uniform, none). 7207 void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type) 7208 { 7209 // If it was named, which means the type can be reused later, add 7210 // it to the symbol table. (Unless it's a block, in which 7211 // case the name is not a type.) 7212 if (type.getBasicType() == EbtBlock || structName.size() == 0) 7213 return; 7214 7215 TVariable* userTypeDef = new TVariable(&structName, type, true); 7216 if (! symbolTable.insert(*userTypeDef)) { 7217 error(loc, "redefinition", structName.c_str(), "struct"); 7218 return; 7219 } 7220 7221 // See if we need IO aliases for the structure typeList 7222 7223 const auto condAlloc = [](bool pred, TTypeList*& list) { 7224 if (pred && list == nullptr) 7225 list = new TTypeList; 7226 }; 7227 7228 tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found 7229 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { 7230 condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform); 7231 condAlloc( hasInput(member->type->getQualifier()), newLists.input); 7232 condAlloc( hasOutput(member->type->getQualifier()), newLists.output); 7233 7234 if (member->type->isStruct()) { 7235 auto it = ioTypeMap.find(member->type->getStruct()); 7236 if (it != ioTypeMap.end()) { 7237 condAlloc(it->second.uniform != nullptr, newLists.uniform); 7238 condAlloc(it->second.input != nullptr, newLists.input); 7239 condAlloc(it->second.output != nullptr, newLists.output); 7240 } 7241 } 7242 } 7243 if (newLists.uniform == nullptr && 7244 newLists.input == nullptr && 7245 newLists.output == nullptr) { 7246 // Won't do any IO caching, clear up the type and get out now. 7247 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) 7248 clearUniformInputOutput(member->type->getQualifier()); 7249 return; 7250 } 7251 7252 // We have IO involved. 7253 7254 // Make a pure typeList for the symbol table, and cache side copies of IO versions. 7255 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { 7256 const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) { 7257 if (s != nullptr) { 7258 ioMember.type = new TType; 7259 ioMember.type->shallowCopy(*member->type); 7260 ioMember.type->setStruct(s); 7261 } 7262 }; 7263 const auto newMember = [&](TTypeLoc& m) { 7264 if (m.type == nullptr) { 7265 m.type = new TType; 7266 m.type->shallowCopy(*member->type); 7267 } 7268 }; 7269 7270 TTypeLoc newUniformMember = { nullptr, member->loc }; 7271 TTypeLoc newInputMember = { nullptr, member->loc }; 7272 TTypeLoc newOutputMember = { nullptr, member->loc }; 7273 if (member->type->isStruct()) { 7274 // swap in an IO child if there is one 7275 auto it = ioTypeMap.find(member->type->getStruct()); 7276 if (it != ioTypeMap.end()) { 7277 inheritStruct(it->second.uniform, newUniformMember); 7278 inheritStruct(it->second.input, newInputMember); 7279 inheritStruct(it->second.output, newOutputMember); 7280 } 7281 } 7282 if (newLists.uniform) { 7283 newMember(newUniformMember); 7284 7285 // inherit default matrix layout (changeable via #pragma pack_matrix), if none given. 7286 if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone) 7287 newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix; 7288 7289 correctUniform(newUniformMember.type->getQualifier()); 7290 newLists.uniform->push_back(newUniformMember); 7291 } 7292 if (newLists.input) { 7293 newMember(newInputMember); 7294 correctInput(newInputMember.type->getQualifier()); 7295 newLists.input->push_back(newInputMember); 7296 } 7297 if (newLists.output) { 7298 newMember(newOutputMember); 7299 correctOutput(newOutputMember.type->getQualifier()); 7300 newLists.output->push_back(newOutputMember); 7301 } 7302 7303 // make original pure 7304 clearUniformInputOutput(member->type->getQualifier()); 7305 } 7306 ioTypeMap[type.getStruct()] = newLists; 7307 } 7308 7309 // Lookup a user-type by name. 7310 // If found, fill in the type and return the defining symbol. 7311 // If not found, return nullptr. 7312 TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type) 7313 { 7314 TSymbol* symbol = symbolTable.find(typeName); 7315 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { 7316 type.shallowCopy(symbol->getType()); 7317 return symbol; 7318 } else 7319 return nullptr; 7320 } 7321 7322 // 7323 // Do everything necessary to handle a variable (non-block) declaration. 7324 // Either redeclaring a variable, or making a new one, updating the symbol 7325 // table, and all error checking. 7326 // 7327 // Returns a subtree node that computes an initializer, if needed. 7328 // Returns nullptr if there is no code to execute for initialization. 7329 // 7330 // 'parseType' is the type part of the declaration (to the left) 7331 // 'arraySizes' is the arrayness tagged on the identifier (to the right) 7332 // 7333 TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type, 7334 TIntermTyped* initializer) 7335 { 7336 if (voidErrorCheck(loc, identifier, type.getBasicType())) 7337 return nullptr; 7338 7339 // Global consts with initializers that are non-const act like EvqGlobal in HLSL. 7340 // This test is implicitly recursive, because initializers propagate constness 7341 // up the aggregate node tree during creation. E.g, for: 7342 // { { 1, 2 }, { 3, 4 } } 7343 // the initializer list is marked EvqConst at the top node, and remains so here. However: 7344 // { 1, { myvar, 2 }, 3 } 7345 // is not a const intializer, and still becomes EvqGlobal here. 7346 7347 const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst); 7348 7349 if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) { 7350 // Force to global 7351 type.getQualifier().storage = EvqGlobal; 7352 } 7353 7354 // make const and initialization consistent 7355 fixConstInit(loc, identifier, type, initializer); 7356 7357 // Check for redeclaration of built-ins and/or attempting to declare a reserved name 7358 TSymbol* symbol = nullptr; 7359 7360 inheritGlobalDefaults(type.getQualifier()); 7361 7362 const bool flattenVar = shouldFlatten(type); 7363 7364 // correct IO in the type 7365 switch (type.getQualifier().storage) { 7366 case EvqGlobal: 7367 case EvqTemporary: 7368 clearUniformInputOutput(type.getQualifier()); 7369 break; 7370 case EvqUniform: 7371 case EvqBuffer: 7372 correctUniform(type.getQualifier()); 7373 if (type.isStruct()) { 7374 auto it = ioTypeMap.find(type.getStruct()); 7375 if (it != ioTypeMap.end()) 7376 type.setStruct(it->second.uniform); 7377 } 7378 7379 break; 7380 default: 7381 break; 7382 } 7383 7384 // Declare the variable 7385 if (type.isArray()) { 7386 // array case 7387 declareArray(loc, identifier, type, symbol, !flattenVar); 7388 } else { 7389 // non-array case 7390 if (symbol == nullptr) 7391 symbol = declareNonArray(loc, identifier, type, !flattenVar); 7392 else if (type != symbol->getType()) 7393 error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); 7394 } 7395 7396 if (symbol == nullptr) 7397 return nullptr; 7398 7399 if (flattenVar) 7400 flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel()); 7401 7402 if (initializer == nullptr) 7403 return nullptr; 7404 7405 // Deal with initializer 7406 TVariable* variable = symbol->getAsVariable(); 7407 if (variable == nullptr) { 7408 error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); 7409 return nullptr; 7410 } 7411 return executeInitializer(loc, initializer, variable, flattenVar); 7412 } 7413 7414 // Pick up global defaults from the provide global defaults into dst. 7415 void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const 7416 { 7417 if (dst.storage == EvqVaryingOut) { 7418 if (! dst.hasStream() && language == EShLangGeometry) 7419 dst.layoutStream = globalOutputDefaults.layoutStream; 7420 if (! dst.hasXfbBuffer()) 7421 dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; 7422 } 7423 } 7424 7425 // 7426 // Make an internal-only variable whose name is for debug purposes only 7427 // and won't be searched for. Callers will only use the return value to use 7428 // the variable, not the name to look it up. It is okay if the name 7429 // is the same as other names; there won't be any conflict. 7430 // 7431 TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const 7432 { 7433 TString* nameString = NewPoolTString(name); 7434 TVariable* variable = new TVariable(nameString, type); 7435 symbolTable.makeInternalVariable(*variable); 7436 7437 return variable; 7438 } 7439 7440 // Make a symbol node holding a new internal temporary variable. 7441 TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, 7442 const TType& type) const 7443 { 7444 TVariable* tmpVar = makeInternalVariable(name, type); 7445 tmpVar->getWritableType().getQualifier().makeTemporary(); 7446 7447 return intermediate.addSymbol(*tmpVar, loc); 7448 } 7449 7450 // 7451 // Declare a non-array variable, the main point being there is no redeclaration 7452 // for resizing allowed. 7453 // 7454 // Return the successfully declared variable. 7455 // 7456 TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type, 7457 bool track) 7458 { 7459 // make a new variable 7460 TVariable* variable = new TVariable(&identifier, type); 7461 7462 // add variable to symbol table 7463 if (symbolTable.insert(*variable)) { 7464 if (track && symbolTable.atGlobalLevel()) 7465 trackLinkage(*variable); 7466 return variable; 7467 } 7468 7469 error(loc, "redefinition", variable->getName().c_str(), ""); 7470 return nullptr; 7471 } 7472 7473 // 7474 // Handle all types of initializers from the grammar. 7475 // 7476 // Returning nullptr just means there is no code to execute to handle the 7477 // initializer, which will, for example, be the case for constant initializers. 7478 // 7479 TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable, 7480 bool flattened) 7481 { 7482 // 7483 // Identifier must be of type constant, a global, or a temporary, and 7484 // starting at version 120, desktop allows uniforms to have initializers. 7485 // 7486 TStorageQualifier qualifier = variable->getType().getQualifier().storage; 7487 7488 // 7489 // If the initializer was from braces { ... }, we convert the whole subtree to a 7490 // constructor-style subtree, allowing the rest of the code to operate 7491 // identically for both kinds of initializers. 7492 // 7493 // 7494 // Type can't be deduced from the initializer list, so a skeletal type to 7495 // follow has to be passed in. Constness and specialization-constness 7496 // should be deduced bottom up, not dictated by the skeletal type. 7497 // 7498 TType skeletalType; 7499 skeletalType.shallowCopy(variable->getType()); 7500 skeletalType.getQualifier().makeTemporary(); 7501 if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull) 7502 initializer = convertInitializerList(loc, skeletalType, initializer, nullptr); 7503 if (initializer == nullptr) { 7504 // error recovery; don't leave const without constant values 7505 if (qualifier == EvqConst) 7506 variable->getWritableType().getQualifier().storage = EvqTemporary; 7507 return nullptr; 7508 } 7509 7510 // Fix outer arrayness if variable is unsized, getting size from the initializer 7511 if (initializer->getType().isExplicitlySizedArray() && 7512 variable->getType().isImplicitlySizedArray()) 7513 variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); 7514 7515 // Inner arrayness can also get set by an initializer 7516 if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && 7517 initializer->getType().getArraySizes()->getNumDims() == 7518 variable->getType().getArraySizes()->getNumDims()) { 7519 // adopt unsized sizes from the initializer's sizes 7520 for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { 7521 if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) 7522 variable->getWritableType().getArraySizes().setDimSize(d, initializer->getType().getArraySizes()->getDimSize(d)); 7523 } 7524 } 7525 7526 // Uniform and global consts require a constant initializer 7527 if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) { 7528 error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); 7529 variable->getWritableType().getQualifier().storage = EvqTemporary; 7530 return nullptr; 7531 } 7532 7533 // Const variables require a constant initializer 7534 if (qualifier == EvqConst) { 7535 if (initializer->getType().getQualifier().storage != EvqConst) { 7536 variable->getWritableType().getQualifier().storage = EvqConstReadOnly; 7537 qualifier = EvqConstReadOnly; 7538 } 7539 } 7540 7541 if (qualifier == EvqConst || qualifier == EvqUniform) { 7542 // Compile-time tagging of the variable with its constant value... 7543 7544 initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); 7545 if (initializer != nullptr && variable->getType() != initializer->getType()) 7546 initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer); 7547 if (initializer == nullptr || !initializer->getAsConstantUnion() || 7548 variable->getType() != initializer->getType()) { 7549 error(loc, "non-matching or non-convertible constant type for const initializer", 7550 variable->getType().getStorageQualifierString(), ""); 7551 variable->getWritableType().getQualifier().storage = EvqTemporary; 7552 return nullptr; 7553 } 7554 7555 variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); 7556 } else { 7557 // normal assigning of a value to a variable... 7558 specializationCheck(loc, initializer->getType(), "initializer"); 7559 TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); 7560 7561 // If we are flattening, it could be due to setting opaques, which must be handled 7562 // as aliases, and the 'initializer' node cannot actually be emitted, because it 7563 // itself stores the result of the constructor, and we can't store to opaques. 7564 // handleAssign() will emit the initializer. 7565 TIntermNode* initNode = nullptr; 7566 if (flattened && intermSymbol->getType().containsOpaque()) 7567 return executeFlattenedInitializer(loc, intermSymbol, *initializer->getAsAggregate()); 7568 else { 7569 initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer); 7570 if (initNode == nullptr) 7571 assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); 7572 return initNode; 7573 } 7574 } 7575 7576 return nullptr; 7577 } 7578 7579 // 7580 // Reprocess any initializer-list { ... } parts of the initializer. 7581 // Need to hierarchically assign correct types and implicit 7582 // conversions. Will do this mimicking the same process used for 7583 // creating a constructor-style initializer, ensuring we get the 7584 // same form. 7585 // 7586 // Returns a node representing an expression for the initializer list expressed 7587 // as the correct type. 7588 // 7589 // Returns nullptr if there is an error. 7590 // 7591 TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, 7592 TIntermTyped* initializer, TIntermTyped* scalarInit) 7593 { 7594 // Will operate recursively. Once a subtree is found that is constructor style, 7595 // everything below it is already good: Only the "top part" of the initializer 7596 // can be an initializer list, where "top part" can extend for several (or all) levels. 7597 7598 // see if we have bottomed out in the tree within the initializer-list part 7599 TIntermAggregate* initList = initializer->getAsAggregate(); 7600 if (initList == nullptr || initList->getOp() != EOpNull) { 7601 // We don't have a list, but if it's a scalar and the 'type' is a 7602 // composite, we need to lengthen below to make it useful. 7603 // Otherwise, this is an already formed object to initialize with. 7604 if (type.isScalar() || !initializer->getType().isScalar()) 7605 return initializer; 7606 else 7607 initList = intermediate.makeAggregate(initializer); 7608 } 7609 7610 // Of the initializer-list set of nodes, need to process bottom up, 7611 // so recurse deep, then process on the way up. 7612 7613 // Go down the tree here... 7614 if (type.isArray()) { 7615 // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. 7616 // Later on, initializer execution code will deal with array size logic. 7617 TType arrayType; 7618 arrayType.shallowCopy(type); // sharing struct stuff is fine 7619 arrayType.newArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below 7620 7621 // edit array sizes to fill in unsized dimensions 7622 if (type.isImplicitlySizedArray()) 7623 arrayType.changeOuterArraySize((int)initList->getSequence().size()); 7624 7625 // set unsized array dimensions that can be derived from the initializer's first element 7626 if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) { 7627 TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); 7628 if (firstInit->getType().isArray() && 7629 arrayType.getArraySizes().getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { 7630 for (int d = 1; d < arrayType.getArraySizes().getNumDims(); ++d) { 7631 if (arrayType.getArraySizes().getDimSize(d) == UnsizedArraySize) 7632 arrayType.getArraySizes().setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); 7633 } 7634 } 7635 } 7636 7637 // lengthen list to be long enough 7638 lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit); 7639 7640 // recursively process each element 7641 TType elementType(arrayType, 0); // dereferenced type 7642 for (int i = 0; i < arrayType.getOuterArraySize(); ++i) { 7643 initList->getSequence()[i] = convertInitializerList(loc, elementType, 7644 initList->getSequence()[i]->getAsTyped(), scalarInit); 7645 if (initList->getSequence()[i] == nullptr) 7646 return nullptr; 7647 } 7648 7649 return addConstructor(loc, initList, arrayType); 7650 } else if (type.isStruct()) { 7651 // lengthen list to be long enough 7652 lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit); 7653 7654 if (type.getStruct()->size() != initList->getSequence().size()) { 7655 error(loc, "wrong number of structure members", "initializer list", ""); 7656 return nullptr; 7657 } 7658 for (size_t i = 0; i < type.getStruct()->size(); ++i) { 7659 initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, 7660 initList->getSequence()[i]->getAsTyped(), scalarInit); 7661 if (initList->getSequence()[i] == nullptr) 7662 return nullptr; 7663 } 7664 } else if (type.isMatrix()) { 7665 if (type.computeNumComponents() == (int)initList->getSequence().size()) { 7666 // This means the matrix is initialized component-wise, rather than as 7667 // a series of rows and columns. We can just use the list directly as 7668 // a constructor; no further processing needed. 7669 } else { 7670 // lengthen list to be long enough 7671 lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit); 7672 7673 if (type.getMatrixCols() != (int)initList->getSequence().size()) { 7674 error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); 7675 return nullptr; 7676 } 7677 TType vectorType(type, 0); // dereferenced type 7678 for (int i = 0; i < type.getMatrixCols(); ++i) { 7679 initList->getSequence()[i] = convertInitializerList(loc, vectorType, 7680 initList->getSequence()[i]->getAsTyped(), scalarInit); 7681 if (initList->getSequence()[i] == nullptr) 7682 return nullptr; 7683 } 7684 } 7685 } else if (type.isVector()) { 7686 // lengthen list to be long enough 7687 lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit); 7688 7689 // error check; we're at bottom, so work is finished below 7690 if (type.getVectorSize() != (int)initList->getSequence().size()) { 7691 error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", 7692 type.getCompleteString().c_str()); 7693 return nullptr; 7694 } 7695 } else if (type.isScalar()) { 7696 // lengthen list to be long enough 7697 lengthenList(loc, initList->getSequence(), 1, scalarInit); 7698 7699 if ((int)initList->getSequence().size() != 1) { 7700 error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str()); 7701 return nullptr; 7702 } 7703 } else { 7704 error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); 7705 return nullptr; 7706 } 7707 7708 // Now that the subtree is processed, process this node as if the 7709 // initializer list is a set of arguments to a constructor. 7710 TIntermTyped* emulatedConstructorArguments; 7711 if (initList->getSequence().size() == 1) 7712 emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped(); 7713 else 7714 emulatedConstructorArguments = initList; 7715 7716 return addConstructor(loc, emulatedConstructorArguments, type); 7717 } 7718 7719 // Lengthen list to be long enough to cover any gap from the current list size 7720 // to 'size'. If the list is longer, do nothing. 7721 // The value to lengthen with is the default for short lists. 7722 // 7723 // By default, lists that are too short due to lack of initializers initialize to zero. 7724 // Alternatively, it could be a scalar initializer for a structure. Both cases are handled, 7725 // based on whether something is passed in as 'scalarInit'. 7726 // 7727 // 'scalarInit' must be safe to use each time this is called (no side effects replication). 7728 // 7729 void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit) 7730 { 7731 for (int c = (int)list.size(); c < size; ++c) { 7732 if (scalarInit == nullptr) 7733 list.push_back(intermediate.addConstantUnion(0, loc)); 7734 else 7735 list.push_back(scalarInit); 7736 } 7737 } 7738 7739 // 7740 // Test for the correctness of the parameters passed to various constructor functions 7741 // and also convert them to the right data type, if allowed and required. 7742 // 7743 // Returns nullptr for an error or the constructed node (aggregate or typed) for no error. 7744 // 7745 TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) 7746 { 7747 if (node == nullptr) 7748 return nullptr; 7749 7750 // Handle the idiom "(struct type)<scalar value>" 7751 if (type.isStruct() && isScalarConstructor(node)) { 7752 // 'node' will almost always get used multiple times, so should not be used directly, 7753 // it would create a DAG instead of a tree, which might be okay (would 7754 // like to formalize that for constants and symbols), but if it has 7755 // side effects, they would get executed multiple times, which is not okay. 7756 if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) { 7757 TIntermAggregate* seq = intermediate.makeAggregate(loc); 7758 TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType()); 7759 seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc)); 7760 seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy)); 7761 seq->setOp(EOpComma); 7762 seq->setType(type); 7763 return seq; 7764 } else 7765 return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node); 7766 } 7767 7768 return addConstructor(loc, node, type); 7769 } 7770 7771 // Add a constructor, either from the grammar, or other programmatic reasons. 7772 // 7773 // 'node' is what to construct from. 7774 // 'type' is what type to construct. 7775 // 7776 // Returns the constructed object. 7777 // Return nullptr if it can't be done. 7778 // 7779 TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) 7780 { 7781 TOperator op = intermediate.mapTypeToConstructorOp(type); 7782 7783 // Combined texture-sampler constructors are completely semantic checked 7784 // in constructorTextureSamplerError() 7785 if (op == EOpConstructTextureSampler) 7786 return intermediate.setAggregateOperator(node->getAsAggregate(), op, type, loc); 7787 7788 TTypeList::const_iterator memberTypes; 7789 if (op == EOpConstructStruct) 7790 memberTypes = type.getStruct()->begin(); 7791 7792 TType elementType; 7793 if (type.isArray()) { 7794 TType dereferenced(type, 0); 7795 elementType.shallowCopy(dereferenced); 7796 } else 7797 elementType.shallowCopy(type); 7798 7799 bool singleArg; 7800 TIntermAggregate* aggrNode = node->getAsAggregate(); 7801 if (aggrNode != nullptr) { 7802 if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1) 7803 singleArg = true; 7804 else 7805 singleArg = false; 7806 } else 7807 singleArg = true; 7808 7809 TIntermTyped *newNode; 7810 if (singleArg) { 7811 // Handle array -> array conversion 7812 // Constructing an array of one type from an array of another type is allowed, 7813 // assuming there are enough components available (semantic-checked earlier). 7814 if (type.isArray() && node->isArray()) 7815 newNode = convertArray(node, type); 7816 7817 // If structure constructor or array constructor is being called 7818 // for only one parameter inside the structure, we need to call constructAggregate function once. 7819 else if (type.isArray()) 7820 newNode = constructAggregate(node, elementType, 1, node->getLoc()); 7821 else if (op == EOpConstructStruct) 7822 newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); 7823 else { 7824 // shape conversion for matrix constructor from scalar. HLSL semantics are: scalar 7825 // is replicated into every element of the matrix (not just the diagnonal), so 7826 // that is handled specially here. 7827 if (type.isMatrix() && node->getType().isScalarOrVec1()) 7828 node = intermediate.addShapeConversion(type, node); 7829 7830 newNode = constructBuiltIn(type, op, node, node->getLoc(), false); 7831 } 7832 7833 if (newNode && (type.isArray() || op == EOpConstructStruct)) 7834 newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); 7835 7836 return newNode; 7837 } 7838 7839 // 7840 // Handle list of arguments. 7841 // 7842 TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor 7843 // if the structure constructor contains more than one parameter, then construct 7844 // each parameter 7845 7846 int paramCount = 0; // keeps a track of the constructor parameter number being checked 7847 7848 // for each parameter to the constructor call, check to see if the right type is passed or convert them 7849 // to the right type if possible (and allowed). 7850 // for structure constructors, just check if the right type is passed, no conversion is allowed. 7851 7852 for (TIntermSequence::iterator p = sequenceVector.begin(); 7853 p != sequenceVector.end(); p++, paramCount++) { 7854 if (type.isArray()) 7855 newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc()); 7856 else if (op == EOpConstructStruct) 7857 newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc()); 7858 else 7859 newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); 7860 7861 if (newNode) 7862 *p = newNode; 7863 else 7864 return nullptr; 7865 } 7866 7867 TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); 7868 7869 return constructor; 7870 } 7871 7872 // Function for constructor implementation. Calls addUnaryMath with appropriate EOp value 7873 // for the parameter to the constructor (passed to this function). Essentially, it converts 7874 // the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a 7875 // float, then float is converted to int. 7876 // 7877 // Returns nullptr for an error or the constructed node. 7878 // 7879 TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, 7880 const TSourceLoc& loc, bool subset) 7881 { 7882 TIntermTyped* newNode; 7883 TOperator basicOp; 7884 7885 // 7886 // First, convert types as needed. 7887 // 7888 switch (op) { 7889 case EOpConstructVec2: 7890 case EOpConstructVec3: 7891 case EOpConstructVec4: 7892 case EOpConstructMat2x2: 7893 case EOpConstructMat2x3: 7894 case EOpConstructMat2x4: 7895 case EOpConstructMat3x2: 7896 case EOpConstructMat3x3: 7897 case EOpConstructMat3x4: 7898 case EOpConstructMat4x2: 7899 case EOpConstructMat4x3: 7900 case EOpConstructMat4x4: 7901 case EOpConstructFloat: 7902 basicOp = EOpConstructFloat; 7903 break; 7904 7905 case EOpConstructDVec2: 7906 case EOpConstructDVec3: 7907 case EOpConstructDVec4: 7908 case EOpConstructDMat2x2: 7909 case EOpConstructDMat2x3: 7910 case EOpConstructDMat2x4: 7911 case EOpConstructDMat3x2: 7912 case EOpConstructDMat3x3: 7913 case EOpConstructDMat3x4: 7914 case EOpConstructDMat4x2: 7915 case EOpConstructDMat4x3: 7916 case EOpConstructDMat4x4: 7917 case EOpConstructDouble: 7918 basicOp = EOpConstructDouble; 7919 break; 7920 7921 case EOpConstructIVec2: 7922 case EOpConstructIVec3: 7923 case EOpConstructIVec4: 7924 case EOpConstructIMat2x2: 7925 case EOpConstructIMat2x3: 7926 case EOpConstructIMat2x4: 7927 case EOpConstructIMat3x2: 7928 case EOpConstructIMat3x3: 7929 case EOpConstructIMat3x4: 7930 case EOpConstructIMat4x2: 7931 case EOpConstructIMat4x3: 7932 case EOpConstructIMat4x4: 7933 case EOpConstructInt: 7934 basicOp = EOpConstructInt; 7935 break; 7936 7937 case EOpConstructUVec2: 7938 case EOpConstructUVec3: 7939 case EOpConstructUVec4: 7940 case EOpConstructUMat2x2: 7941 case EOpConstructUMat2x3: 7942 case EOpConstructUMat2x4: 7943 case EOpConstructUMat3x2: 7944 case EOpConstructUMat3x3: 7945 case EOpConstructUMat3x4: 7946 case EOpConstructUMat4x2: 7947 case EOpConstructUMat4x3: 7948 case EOpConstructUMat4x4: 7949 case EOpConstructUint: 7950 basicOp = EOpConstructUint; 7951 break; 7952 7953 case EOpConstructBVec2: 7954 case EOpConstructBVec3: 7955 case EOpConstructBVec4: 7956 case EOpConstructBMat2x2: 7957 case EOpConstructBMat2x3: 7958 case EOpConstructBMat2x4: 7959 case EOpConstructBMat3x2: 7960 case EOpConstructBMat3x3: 7961 case EOpConstructBMat3x4: 7962 case EOpConstructBMat4x2: 7963 case EOpConstructBMat4x3: 7964 case EOpConstructBMat4x4: 7965 case EOpConstructBool: 7966 basicOp = EOpConstructBool; 7967 break; 7968 7969 default: 7970 error(loc, "unsupported construction", "", ""); 7971 7972 return nullptr; 7973 } 7974 newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); 7975 if (newNode == nullptr) { 7976 error(loc, "can't convert", "constructor", ""); 7977 return nullptr; 7978 } 7979 7980 // 7981 // Now, if there still isn't an operation to do the construction, and we need one, add one. 7982 // 7983 7984 // Otherwise, skip out early. 7985 if (subset || (newNode != node && newNode->getType() == type)) 7986 return newNode; 7987 7988 // setAggregateOperator will insert a new node for the constructor, as needed. 7989 return intermediate.setAggregateOperator(newNode, op, type, loc); 7990 } 7991 7992 // Convert the array in node to the requested type, which is also an array. 7993 // Returns nullptr on failure, otherwise returns aggregate holding the list of 7994 // elements needed to construct the array. 7995 TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type) 7996 { 7997 assert(node->isArray() && type.isArray()); 7998 if (node->getType().computeNumComponents() < type.computeNumComponents()) 7999 return nullptr; 8000 8001 // TODO: write an argument replicator, for the case the argument should not be 8002 // executed multiple times, yet multiple copies are needed. 8003 8004 TIntermTyped* constructee = node->getAsTyped(); 8005 // track where we are in consuming the argument 8006 int constructeeElement = 0; 8007 int constructeeComponent = 0; 8008 8009 // bump up to the next component to consume 8010 const auto getNextComponent = [&]() { 8011 TIntermTyped* component; 8012 component = handleBracketDereference(node->getLoc(), constructee, 8013 intermediate.addConstantUnion(constructeeElement, node->getLoc())); 8014 if (component->isVector()) 8015 component = handleBracketDereference(node->getLoc(), component, 8016 intermediate.addConstantUnion(constructeeComponent, node->getLoc())); 8017 // bump component pointer up 8018 ++constructeeComponent; 8019 if (constructeeComponent == constructee->getVectorSize()) { 8020 constructeeComponent = 0; 8021 ++constructeeElement; 8022 } 8023 return component; 8024 }; 8025 8026 // make one subnode per constructed array element 8027 TIntermAggregate* constructor = nullptr; 8028 TType derefType(type, 0); 8029 TType speculativeComponentType(derefType, 0); 8030 TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType; 8031 TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType); 8032 TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize()); 8033 for (int e = 0; e < type.getOuterArraySize(); ++e) { 8034 // construct an element 8035 TIntermTyped* elementArg; 8036 if (type.getVectorSize() == constructee->getVectorSize()) { 8037 // same element shape 8038 elementArg = handleBracketDereference(node->getLoc(), constructee, 8039 intermediate.addConstantUnion(e, node->getLoc())); 8040 } else { 8041 // mismatched element shapes 8042 if (type.getVectorSize() == 1) 8043 elementArg = getNextComponent(); 8044 else { 8045 // make a vector 8046 TIntermAggregate* elementConstructee = nullptr; 8047 for (int c = 0; c < type.getVectorSize(); ++c) 8048 elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent()); 8049 elementArg = addConstructor(node->getLoc(), elementConstructee, crossType); 8050 } 8051 } 8052 // convert basic types 8053 elementArg = intermediate.addConversion(componentOp, derefType, elementArg); 8054 if (elementArg == nullptr) 8055 return nullptr; 8056 // combine with top-level constructor 8057 constructor = intermediate.growAggregate(constructor, elementArg); 8058 } 8059 8060 return constructor; 8061 } 8062 8063 // This function tests for the type of the parameters to the structure or array constructor. Raises 8064 // an error message if the expected type does not match the parameter passed to the constructor. 8065 // 8066 // Returns nullptr for an error or the input node itself if the expected and the given parameter types match. 8067 // 8068 TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, 8069 const TSourceLoc& loc) 8070 { 8071 // Handle cases that map more 1:1 between constructor arguments and constructed. 8072 TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); 8073 if (converted == nullptr || converted->getType() != type) { 8074 error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, 8075 node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); 8076 8077 return nullptr; 8078 } 8079 8080 return converted; 8081 } 8082 8083 // 8084 // Do everything needed to add an interface block. 8085 // 8086 void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName, TArraySizes* arraySizes) 8087 { 8088 assert(type.getWritableStruct() != nullptr); 8089 8090 // Clean up top-level decorations that don't belong. 8091 switch (type.getQualifier().storage) { 8092 case EvqUniform: 8093 case EvqBuffer: 8094 correctUniform(type.getQualifier()); 8095 break; 8096 case EvqVaryingIn: 8097 correctInput(type.getQualifier()); 8098 break; 8099 case EvqVaryingOut: 8100 correctOutput(type.getQualifier()); 8101 break; 8102 default: 8103 break; 8104 } 8105 8106 TTypeList& typeList = *type.getWritableStruct(); 8107 // fix and check for member storage qualifiers and types that don't belong within a block 8108 for (unsigned int member = 0; member < typeList.size(); ++member) { 8109 TType& memberType = *typeList[member].type; 8110 TQualifier& memberQualifier = memberType.getQualifier(); 8111 const TSourceLoc& memberLoc = typeList[member].loc; 8112 globalQualifierFix(memberLoc, memberQualifier); 8113 memberQualifier.storage = type.getQualifier().storage; 8114 8115 if (memberType.isStruct()) { 8116 // clean up and pick up the right set of decorations 8117 auto it = ioTypeMap.find(memberType.getStruct()); 8118 switch (type.getQualifier().storage) { 8119 case EvqUniform: 8120 case EvqBuffer: 8121 correctUniform(type.getQualifier()); 8122 if (it != ioTypeMap.end() && it->second.uniform) 8123 memberType.setStruct(it->second.uniform); 8124 break; 8125 case EvqVaryingIn: 8126 correctInput(type.getQualifier()); 8127 if (it != ioTypeMap.end() && it->second.input) 8128 memberType.setStruct(it->second.input); 8129 break; 8130 case EvqVaryingOut: 8131 correctOutput(type.getQualifier()); 8132 if (it != ioTypeMap.end() && it->second.output) 8133 memberType.setStruct(it->second.output); 8134 break; 8135 default: 8136 break; 8137 } 8138 } 8139 } 8140 8141 // Make default block qualification, and adjust the member qualifications 8142 8143 TQualifier defaultQualification; 8144 switch (type.getQualifier().storage) { 8145 case EvqUniform: defaultQualification = globalUniformDefaults; break; 8146 case EvqBuffer: defaultQualification = globalBufferDefaults; break; 8147 case EvqVaryingIn: defaultQualification = globalInputDefaults; break; 8148 case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; 8149 default: defaultQualification.clear(); break; 8150 } 8151 8152 // Special case for "push_constant uniform", which has a default of std430, 8153 // contrary to normal uniform defaults, and can't have a default tracked for it. 8154 if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking()) 8155 type.getQualifier().layoutPacking = ElpStd430; 8156 8157 // fix and check for member layout qualifiers 8158 8159 mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true); 8160 8161 bool memberWithLocation = false; 8162 bool memberWithoutLocation = false; 8163 for (unsigned int member = 0; member < typeList.size(); ++member) { 8164 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 8165 const TSourceLoc& memberLoc = typeList[member].loc; 8166 if (memberQualifier.hasStream()) { 8167 if (defaultQualification.layoutStream != memberQualifier.layoutStream) 8168 error(memberLoc, "member cannot contradict block", "stream", ""); 8169 } 8170 8171 // "This includes a block's inheritance of the 8172 // current global default buffer, a block member's inheritance of the block's 8173 // buffer, and the requirement that any *xfb_buffer* declared on a block 8174 // member must match the buffer inherited from the block." 8175 if (memberQualifier.hasXfbBuffer()) { 8176 if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) 8177 error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); 8178 } 8179 8180 if (memberQualifier.hasLocation()) { 8181 switch (type.getQualifier().storage) { 8182 case EvqVaryingIn: 8183 case EvqVaryingOut: 8184 memberWithLocation = true; 8185 break; 8186 default: 8187 break; 8188 } 8189 } else 8190 memberWithoutLocation = true; 8191 8192 TQualifier newMemberQualification = defaultQualification; 8193 mergeQualifiers(newMemberQualification, memberQualifier); 8194 memberQualifier = newMemberQualification; 8195 } 8196 8197 // Process the members 8198 fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation); 8199 fixBlockXfbOffsets(type.getQualifier(), typeList); 8200 fixBlockUniformOffsets(type.getQualifier(), typeList); 8201 8202 // reverse merge, so that currentBlockQualifier now has all layout information 8203 // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) 8204 mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true); 8205 8206 // 8207 // Build and add the interface block as a new type named 'blockName' 8208 // 8209 8210 // Use the instance name as the interface name if one exists, else the block name. 8211 const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName(); 8212 8213 TType blockType(&typeList, interfaceName, type.getQualifier()); 8214 if (arraySizes) 8215 blockType.newArraySizes(*arraySizes); 8216 8217 // Add the variable, as anonymous or named instanceName. 8218 // Make an anonymous variable if no name was provided. 8219 if (instanceName == nullptr) 8220 instanceName = NewPoolTString(""); 8221 8222 TVariable& variable = *new TVariable(instanceName, blockType); 8223 if (! symbolTable.insert(variable)) { 8224 if (*instanceName == "") 8225 error(loc, "nameless block contains a member that already has a name at global scope", 8226 "" /* blockName->c_str() */, ""); 8227 else 8228 error(loc, "block instance name redefinition", variable.getName().c_str(), ""); 8229 8230 return; 8231 } 8232 8233 // Save it in the AST for linker use. 8234 trackLinkage(variable); 8235 } 8236 8237 // 8238 // "For a block, this process applies to the entire block, or until the first member 8239 // is reached that has a location layout qualifier. When a block member is declared with a location 8240 // qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level 8241 // declaration. Subsequent members are again assigned consecutive locations, based on the newest location, 8242 // until the next member declared with a location qualifier. The values used for locations do not have to be 8243 // declared in increasing order." 8244 void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) 8245 { 8246 // "If a block has no block-level location layout qualifier, it is required that either all or none of its members 8247 // have a location layout qualifier, or a compile-time error results." 8248 if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) 8249 error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); 8250 else { 8251 if (memberWithLocation) { 8252 // remove any block-level location and make it per *every* member 8253 int nextLocation = 0; // by the rule above, initial value is not relevant 8254 if (qualifier.hasAnyLocation()) { 8255 nextLocation = qualifier.layoutLocation; 8256 qualifier.layoutLocation = TQualifier::layoutLocationEnd; 8257 if (qualifier.hasComponent()) { 8258 // "It is a compile-time error to apply the *component* qualifier to a ... block" 8259 error(loc, "cannot apply to a block", "component", ""); 8260 } 8261 if (qualifier.hasIndex()) { 8262 error(loc, "cannot apply to a block", "index", ""); 8263 } 8264 } 8265 for (unsigned int member = 0; member < typeList.size(); ++member) { 8266 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 8267 const TSourceLoc& memberLoc = typeList[member].loc; 8268 if (! memberQualifier.hasLocation()) { 8269 if (nextLocation >= (int)TQualifier::layoutLocationEnd) 8270 error(memberLoc, "location is too large", "location", ""); 8271 memberQualifier.layoutLocation = nextLocation; 8272 memberQualifier.layoutComponent = 0; 8273 } 8274 nextLocation = memberQualifier.layoutLocation + 8275 intermediate.computeTypeLocationSize(*typeList[member].type); 8276 } 8277 } 8278 } 8279 } 8280 8281 void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& typeList) 8282 { 8283 // "If a block is qualified with xfb_offset, all its 8284 // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any 8285 // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer 8286 // offsets." 8287 8288 if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) 8289 return; 8290 8291 int nextOffset = qualifier.layoutXfbOffset; 8292 for (unsigned int member = 0; member < typeList.size(); ++member) { 8293 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 8294 bool containsDouble = false; 8295 int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, containsDouble); 8296 // see if we need to auto-assign an offset to this member 8297 if (! memberQualifier.hasXfbOffset()) { 8298 // "if applied to an aggregate containing a double, the offset must also be a multiple of 8" 8299 if (containsDouble) 8300 RoundToPow2(nextOffset, 8); 8301 memberQualifier.layoutXfbOffset = nextOffset; 8302 } else 8303 nextOffset = memberQualifier.layoutXfbOffset; 8304 nextOffset += memberSize; 8305 } 8306 8307 // The above gave all block members an offset, so we can take it off the block now, 8308 // which will avoid double counting the offset usage. 8309 qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; 8310 } 8311 8312 // Calculate and save the offset of each block member, using the recursively 8313 // defined block offset rules and the user-provided offset and align. 8314 // 8315 // Also, compute and save the total size of the block. For the block's size, arrayness 8316 // is not taken into account, as each element is backed by a separate buffer. 8317 // 8318 void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList) 8319 { 8320 if (! qualifier.isUniformOrBuffer()) 8321 return; 8322 if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430) 8323 return; 8324 8325 int offset = 0; 8326 int memberSize; 8327 for (unsigned int member = 0; member < typeList.size(); ++member) { 8328 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 8329 const TSourceLoc& memberLoc = typeList[member].loc; 8330 8331 // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." 8332 8333 // modify just the children's view of matrix layout, if there is one for this member 8334 TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; 8335 int dummyStride; 8336 int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride, 8337 qualifier.layoutPacking == ElpStd140, 8338 subMatrixLayout != ElmNone 8339 ? subMatrixLayout == ElmRowMajor 8340 : qualifier.layoutMatrix == ElmRowMajor); 8341 if (memberQualifier.hasOffset()) { 8342 // "The specified offset must be a multiple 8343 // of the base alignment of the type of the block member it qualifies, or a compile-time error results." 8344 if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) 8345 error(memberLoc, "must be a multiple of the member's alignment", "offset", ""); 8346 8347 // "The offset qualifier forces the qualified member to start at or after the specified 8348 // integral-constant expression, which will be its byte offset from the beginning of the buffer. 8349 // "The actual offset of a member is computed as 8350 // follows: If offset was declared, start with that offset, otherwise start with the next available offset." 8351 offset = std::max(offset, memberQualifier.layoutOffset); 8352 } 8353 8354 // "The actual alignment of a member will be the greater of the specified align alignment and the standard 8355 // (e.g., std140) base alignment for the member's type." 8356 if (memberQualifier.hasAlign()) 8357 memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); 8358 8359 // "If the resulting offset is not a multiple of the actual alignment, 8360 // increase it to the first offset that is a multiple of 8361 // the actual alignment." 8362 RoundToPow2(offset, memberAlignment); 8363 typeList[member].type->getQualifier().layoutOffset = offset; 8364 offset += memberSize; 8365 } 8366 } 8367 8368 // For an identifier that is already declared, add more qualification to it. 8369 void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) 8370 { 8371 TSymbol* symbol = symbolTable.find(identifier); 8372 if (symbol == nullptr) { 8373 error(loc, "identifier not previously declared", identifier.c_str(), ""); 8374 return; 8375 } 8376 if (symbol->getAsFunction()) { 8377 error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); 8378 return; 8379 } 8380 8381 if (qualifier.isAuxiliary() || 8382 qualifier.isMemory() || 8383 qualifier.isInterpolation() || 8384 qualifier.hasLayout() || 8385 qualifier.storage != EvqTemporary || 8386 qualifier.precision != EpqNone) { 8387 error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); 8388 return; 8389 } 8390 8391 // For read-only built-ins, add a new symbol for holding the modified qualifier. 8392 // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) 8393 if (symbol->isReadOnly()) 8394 symbol = symbolTable.copyUp(symbol); 8395 8396 if (qualifier.invariant) { 8397 if (intermediate.inIoAccessed(identifier)) 8398 error(loc, "cannot change qualification after use", "invariant", ""); 8399 symbol->getWritableType().getQualifier().invariant = true; 8400 } else if (qualifier.noContraction) { 8401 if (intermediate.inIoAccessed(identifier)) 8402 error(loc, "cannot change qualification after use", "precise", ""); 8403 symbol->getWritableType().getQualifier().noContraction = true; 8404 } else if (qualifier.specConstant) { 8405 symbol->getWritableType().getQualifier().makeSpecConstant(); 8406 if (qualifier.hasSpecConstantId()) 8407 symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; 8408 } else 8409 warn(loc, "unknown requalification", "", ""); 8410 } 8411 8412 void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) 8413 { 8414 for (unsigned int i = 0; i < identifiers.size(); ++i) 8415 addQualifierToExisting(loc, qualifier, *identifiers[i]); 8416 } 8417 8418 // 8419 // Update the intermediate for the given input geometry 8420 // 8421 bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) 8422 { 8423 switch (geometry) { 8424 case ElgPoints: // fall through 8425 case ElgLines: // ... 8426 case ElgTriangles: // ... 8427 case ElgLinesAdjacency: // ... 8428 case ElgTrianglesAdjacency: // ... 8429 if (! intermediate.setInputPrimitive(geometry)) { 8430 error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); 8431 return false; 8432 } 8433 break; 8434 8435 default: 8436 error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), ""); 8437 return false; 8438 } 8439 8440 return true; 8441 } 8442 8443 // 8444 // Update the intermediate for the given output geometry 8445 // 8446 bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) 8447 { 8448 switch (geometry) { 8449 case ElgPoints: 8450 case ElgLineStrip: 8451 case ElgTriangleStrip: 8452 if (! intermediate.setOutputPrimitive(geometry)) { 8453 error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); 8454 return false; 8455 } 8456 break; 8457 default: 8458 error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), ""); 8459 return false; 8460 } 8461 8462 return true; 8463 } 8464 8465 // 8466 // Selection hints 8467 // 8468 TSelectionControl HlslParseContext::handleSelectionControl(const TAttributeMap& attributes) const 8469 { 8470 if (attributes.contains(EatFlatten)) 8471 return ESelectionControlFlatten; 8472 else if (attributes.contains(EatBranch)) 8473 return ESelectionControlDontFlatten; 8474 else 8475 return ESelectionControlNone; 8476 } 8477 8478 // 8479 // Loop hints 8480 // 8481 TLoopControl HlslParseContext::handleLoopControl(const TAttributeMap& attributes) const 8482 { 8483 if (attributes.contains(EatUnroll)) 8484 return ELoopControlUnroll; 8485 else if (attributes.contains(EatLoop)) 8486 return ELoopControlDontUnroll; 8487 else 8488 return ELoopControlNone; 8489 } 8490 8491 // 8492 // Updating default qualifier for the case of a declaration with just a qualifier, 8493 // no type, block, or identifier. 8494 // 8495 void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) 8496 { 8497 if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { 8498 assert(language == EShLangTessControl || language == EShLangGeometry); 8499 // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; 8500 } 8501 if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { 8502 if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) 8503 error(loc, "cannot change previously set layout value", "invocations", ""); 8504 } 8505 if (publicType.shaderQualifiers.geometry != ElgNone) { 8506 if (publicType.qualifier.storage == EvqVaryingIn) { 8507 switch (publicType.shaderQualifiers.geometry) { 8508 case ElgPoints: 8509 case ElgLines: 8510 case ElgLinesAdjacency: 8511 case ElgTriangles: 8512 case ElgTrianglesAdjacency: 8513 case ElgQuads: 8514 case ElgIsolines: 8515 break; 8516 default: 8517 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), 8518 ""); 8519 } 8520 } else if (publicType.qualifier.storage == EvqVaryingOut) { 8521 handleOutputGeometry(loc, publicType.shaderQualifiers.geometry); 8522 } else 8523 error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), 8524 GetStorageQualifierString(publicType.qualifier.storage)); 8525 } 8526 if (publicType.shaderQualifiers.spacing != EvsNone) 8527 intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing); 8528 if (publicType.shaderQualifiers.order != EvoNone) 8529 intermediate.setVertexOrder(publicType.shaderQualifiers.order); 8530 if (publicType.shaderQualifiers.pointMode) 8531 intermediate.setPointMode(); 8532 for (int i = 0; i < 3; ++i) { 8533 if (publicType.shaderQualifiers.localSize[i] > 1) { 8534 int max = 0; 8535 switch (i) { 8536 case 0: max = resources.maxComputeWorkGroupSizeX; break; 8537 case 1: max = resources.maxComputeWorkGroupSizeY; break; 8538 case 2: max = resources.maxComputeWorkGroupSizeZ; break; 8539 default: break; 8540 } 8541 if (intermediate.getLocalSize(i) > (unsigned int)max) 8542 error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); 8543 8544 // Fix the existing constant gl_WorkGroupSize with this new information. 8545 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); 8546 workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); 8547 } 8548 if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { 8549 intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]); 8550 // Set the workgroup built-in variable as a specialization constant 8551 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); 8552 workGroupSize->getWritableType().getQualifier().specConstant = true; 8553 } 8554 } 8555 if (publicType.shaderQualifiers.earlyFragmentTests) 8556 intermediate.setEarlyFragmentTests(); 8557 8558 const TQualifier& qualifier = publicType.qualifier; 8559 8560 switch (qualifier.storage) { 8561 case EvqUniform: 8562 if (qualifier.hasMatrix()) 8563 globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; 8564 if (qualifier.hasPacking()) 8565 globalUniformDefaults.layoutPacking = qualifier.layoutPacking; 8566 break; 8567 case EvqBuffer: 8568 if (qualifier.hasMatrix()) 8569 globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; 8570 if (qualifier.hasPacking()) 8571 globalBufferDefaults.layoutPacking = qualifier.layoutPacking; 8572 break; 8573 case EvqVaryingIn: 8574 break; 8575 case EvqVaryingOut: 8576 if (qualifier.hasStream()) 8577 globalOutputDefaults.layoutStream = qualifier.layoutStream; 8578 if (qualifier.hasXfbBuffer()) 8579 globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; 8580 if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { 8581 if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) 8582 error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", 8583 qualifier.layoutXfbBuffer); 8584 } 8585 break; 8586 default: 8587 error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); 8588 return; 8589 } 8590 } 8591 8592 // 8593 // Take the sequence of statements that has been built up since the last case/default, 8594 // put it on the list of top-level nodes for the current (inner-most) switch statement, 8595 // and follow that by the case/default we are on now. (See switch topology comment on 8596 // TIntermSwitch.) 8597 // 8598 void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) 8599 { 8600 TIntermSequence* switchSequence = switchSequenceStack.back(); 8601 8602 if (statements) { 8603 statements->setOperator(EOpSequence); 8604 switchSequence->push_back(statements); 8605 } 8606 if (branchNode) { 8607 // check all previous cases for the same label (or both are 'default') 8608 for (unsigned int s = 0; s < switchSequence->size(); ++s) { 8609 TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); 8610 if (prevBranch) { 8611 TIntermTyped* prevExpression = prevBranch->getExpression(); 8612 TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); 8613 if (prevExpression == nullptr && newExpression == nullptr) 8614 error(branchNode->getLoc(), "duplicate label", "default", ""); 8615 else if (prevExpression != nullptr && 8616 newExpression != nullptr && 8617 prevExpression->getAsConstantUnion() && 8618 newExpression->getAsConstantUnion() && 8619 prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == 8620 newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) 8621 error(branchNode->getLoc(), "duplicated value", "case", ""); 8622 } 8623 } 8624 switchSequence->push_back(branchNode); 8625 } 8626 } 8627 8628 // 8629 // Turn the top-level node sequence built up of wrapupSwitchSubsequence 8630 // into a switch node. 8631 // 8632 TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, 8633 TIntermAggregate* lastStatements, TSelectionControl control) 8634 { 8635 wrapupSwitchSubsequence(lastStatements, nullptr); 8636 8637 if (expression == nullptr || 8638 (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || 8639 expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) 8640 error(loc, "condition must be a scalar integer expression", "switch", ""); 8641 8642 // If there is nothing to do, drop the switch but still execute the expression 8643 TIntermSequence* switchSequence = switchSequenceStack.back(); 8644 if (switchSequence->size() == 0) 8645 return expression; 8646 8647 if (lastStatements == nullptr) { 8648 // emulate a break for error recovery 8649 lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); 8650 lastStatements->setOperator(EOpSequence); 8651 switchSequence->push_back(lastStatements); 8652 } 8653 8654 TIntermAggregate* body = new TIntermAggregate(EOpSequence); 8655 body->getSequence() = *switchSequenceStack.back(); 8656 body->setLoc(loc); 8657 8658 TIntermSwitch* switchNode = new TIntermSwitch(expression, body); 8659 switchNode->setLoc(loc); 8660 switchNode->setSelectionControl(control); 8661 8662 return switchNode; 8663 } 8664 8665 // Make a new symbol-table level that is made out of the members of a structure. 8666 // This should be done as an anonymous struct (name is "") so that the symbol table 8667 // finds the members with no explicit reference to a 'this' variable. 8668 void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators) 8669 { 8670 // member variables 8671 TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct); 8672 symbolTable.pushThis(thisVariable); 8673 8674 // member functions 8675 for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) { 8676 // member should have a prefix matching currentTypePrefix.back() 8677 // but, symbol lookup within the class scope will just use the 8678 // unprefixed name. Hence, there are two: one fully prefixed and 8679 // one with no prefix. 8680 TFunction& member = *it->function->clone(); 8681 member.removePrefix(currentTypePrefix.back()); 8682 symbolTable.insert(member); 8683 } 8684 } 8685 8686 // Track levels of class/struct/namespace nesting with a prefix string using 8687 // the type names separated by the scoping operator. E.g., two levels 8688 // would look like: 8689 // 8690 // outer::inner 8691 // 8692 // The string is empty when at normal global level. 8693 // 8694 void HlslParseContext::pushNamespace(const TString& typeName) 8695 { 8696 // make new type prefix 8697 TString newPrefix; 8698 if (currentTypePrefix.size() > 0) 8699 newPrefix = currentTypePrefix.back(); 8700 newPrefix.append(typeName); 8701 newPrefix.append(scopeMangler); 8702 currentTypePrefix.push_back(newPrefix); 8703 } 8704 8705 // Opposite of pushNamespace(), see above 8706 void HlslParseContext::popNamespace() 8707 { 8708 currentTypePrefix.pop_back(); 8709 } 8710 8711 // Use the class/struct nesting string to create a global name for 8712 // a member of a class/struct. 8713 void HlslParseContext::getFullNamespaceName(const TString*& name) const 8714 { 8715 if (currentTypePrefix.size() == 0) 8716 return; 8717 8718 TString* fullName = NewPoolTString(currentTypePrefix.back().c_str()); 8719 fullName->append(*name); 8720 name = fullName; 8721 } 8722 8723 // Helper function to add the namespace scope mangling syntax to a string. 8724 void HlslParseContext::addScopeMangler(TString& name) 8725 { 8726 name.append(scopeMangler); 8727 } 8728 8729 // Potentially rename shader entry point function 8730 void HlslParseContext::renameShaderFunction(const TString*& name) const 8731 { 8732 // Replace the entry point name given in the shader with the real entry point name, 8733 // if there is a substitution. 8734 if (name != nullptr && *name == sourceEntryPointName) 8735 name = NewPoolTString(intermediate.getEntryPointName().c_str()); 8736 } 8737 8738 // Return true if this has uniform-interface like decorations. 8739 bool HlslParseContext::hasUniform(const TQualifier& qualifier) const 8740 { 8741 return qualifier.hasUniformLayout() || 8742 qualifier.layoutPushConstant; 8743 } 8744 8745 // Potentially not the opposite of hasUniform(), as if some characteristic is 8746 // ever used for more than one thing (e.g., uniform or input), hasUniform() should 8747 // say it exists, but clearUniform() should leave it in place. 8748 void HlslParseContext::clearUniform(TQualifier& qualifier) 8749 { 8750 qualifier.clearUniformLayout(); 8751 qualifier.layoutPushConstant = false; 8752 } 8753 8754 // Return false if builtIn by itself doesn't force this qualifier to be an input qualifier. 8755 bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const 8756 { 8757 switch (qualifier.builtIn) { 8758 case EbvPosition: 8759 case EbvPointSize: 8760 return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment; 8761 case EbvClipDistance: 8762 case EbvCullDistance: 8763 return language != EShLangVertex && language != EShLangCompute; 8764 case EbvFragCoord: 8765 case EbvFace: 8766 case EbvHelperInvocation: 8767 case EbvLayer: 8768 case EbvPointCoord: 8769 case EbvSampleId: 8770 case EbvSampleMask: 8771 case EbvSamplePosition: 8772 case EbvViewportIndex: 8773 return language == EShLangFragment; 8774 case EbvGlobalInvocationId: 8775 case EbvLocalInvocationIndex: 8776 case EbvLocalInvocationId: 8777 case EbvNumWorkGroups: 8778 case EbvWorkGroupId: 8779 case EbvWorkGroupSize: 8780 return language == EShLangCompute; 8781 case EbvInvocationId: 8782 return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry; 8783 case EbvPatchVertices: 8784 return language == EShLangTessControl || language == EShLangTessEvaluation; 8785 case EbvInstanceId: 8786 case EbvInstanceIndex: 8787 case EbvVertexId: 8788 case EbvVertexIndex: 8789 return language == EShLangVertex; 8790 case EbvPrimitiveId: 8791 return language == EShLangGeometry || language == EShLangFragment; 8792 case EbvTessLevelInner: 8793 case EbvTessLevelOuter: 8794 return language == EShLangTessEvaluation; 8795 case EbvTessCoord: 8796 return language == EShLangTessEvaluation; 8797 default: 8798 return false; 8799 } 8800 } 8801 8802 // Return true if there are decorations to preserve for input-like storage. 8803 bool HlslParseContext::hasInput(const TQualifier& qualifier) const 8804 { 8805 if (qualifier.hasAnyLocation()) 8806 return true; 8807 8808 if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample)) 8809 return true; 8810 8811 if (language == EShLangTessEvaluation && qualifier.patch) 8812 return true; 8813 8814 if (isInputBuiltIn(qualifier)) 8815 return true; 8816 8817 return false; 8818 } 8819 8820 // Return false if builtIn by itself doesn't force this qualifier to be an output qualifier. 8821 bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const 8822 { 8823 switch (qualifier.builtIn) { 8824 case EbvPosition: 8825 case EbvPointSize: 8826 case EbvClipVertex: 8827 case EbvClipDistance: 8828 case EbvCullDistance: 8829 return language != EShLangFragment && language != EShLangCompute; 8830 case EbvFragDepth: 8831 case EbvFragDepthGreater: 8832 case EbvFragDepthLesser: 8833 case EbvSampleMask: 8834 return language == EShLangFragment; 8835 case EbvLayer: 8836 case EbvViewportIndex: 8837 return language == EShLangGeometry; 8838 case EbvPrimitiveId: 8839 return language == EShLangGeometry || language == EShLangTessControl || language == EShLangTessEvaluation; 8840 case EbvTessLevelInner: 8841 case EbvTessLevelOuter: 8842 return language == EShLangTessControl; 8843 default: 8844 return false; 8845 } 8846 } 8847 8848 // Return true if there are decorations to preserve for output-like storage. 8849 bool HlslParseContext::hasOutput(const TQualifier& qualifier) const 8850 { 8851 if (qualifier.hasAnyLocation()) 8852 return true; 8853 8854 if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb()) 8855 return true; 8856 8857 if (language == EShLangTessControl && qualifier.patch) 8858 return true; 8859 8860 if (language == EShLangGeometry && qualifier.hasStream()) 8861 return true; 8862 8863 if (isOutputBuiltIn(qualifier)) 8864 return true; 8865 8866 return false; 8867 } 8868 8869 // Make the IO decorations etc. be appropriate only for an input interface. 8870 void HlslParseContext::correctInput(TQualifier& qualifier) 8871 { 8872 clearUniform(qualifier); 8873 if (language == EShLangVertex) 8874 qualifier.clearInterstage(); 8875 if (language != EShLangTessEvaluation) 8876 qualifier.patch = false; 8877 if (language != EShLangFragment) { 8878 qualifier.clearInterpolation(); 8879 qualifier.sample = false; 8880 } 8881 8882 qualifier.clearStreamLayout(); 8883 qualifier.clearXfbLayout(); 8884 8885 if (! isInputBuiltIn(qualifier)) 8886 qualifier.builtIn = EbvNone; 8887 } 8888 8889 // Make the IO decorations etc. be appropriate only for an output interface. 8890 void HlslParseContext::correctOutput(TQualifier& qualifier) 8891 { 8892 clearUniform(qualifier); 8893 if (language == EShLangFragment) 8894 qualifier.clearInterstage(); 8895 if (language != EShLangGeometry) 8896 qualifier.clearStreamLayout(); 8897 if (language == EShLangFragment) 8898 qualifier.clearXfbLayout(); 8899 if (language != EShLangTessControl) 8900 qualifier.patch = false; 8901 8902 switch (qualifier.builtIn) { 8903 case EbvFragDepthGreater: 8904 intermediate.setDepth(EldGreater); 8905 qualifier.builtIn = EbvFragDepth; 8906 break; 8907 case EbvFragDepthLesser: 8908 intermediate.setDepth(EldLess); 8909 qualifier.builtIn = EbvFragDepth; 8910 break; 8911 default: 8912 break; 8913 } 8914 8915 if (! isOutputBuiltIn(qualifier)) 8916 qualifier.builtIn = EbvNone; 8917 } 8918 8919 // Make the IO decorations etc. be appropriate only for uniform type interfaces. 8920 void HlslParseContext::correctUniform(TQualifier& qualifier) 8921 { 8922 if (qualifier.declaredBuiltIn == EbvNone) 8923 qualifier.declaredBuiltIn = qualifier.builtIn; 8924 8925 qualifier.builtIn = EbvNone; 8926 qualifier.clearInterstage(); 8927 qualifier.clearInterstageLayout(); 8928 } 8929 8930 // Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface. 8931 void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier) 8932 { 8933 clearUniform(qualifier); 8934 correctUniform(qualifier); 8935 } 8936 8937 8938 // Set texture return type. Returns success (not all types are valid). 8939 bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc) 8940 { 8941 // Seed the output with an invalid index. We will set it to a valid one if we can. 8942 sampler.structReturnIndex = TSampler::noReturnStruct; 8943 8944 // Arrays aren't supported. 8945 if (retType.isArray()) { 8946 error(loc, "Arrays not supported in texture template types", "", ""); 8947 return false; 8948 } 8949 8950 // If return type is a vector, remember the vector size in the sampler, and return. 8951 if (retType.isVector() || retType.isScalar()) { 8952 sampler.vectorSize = retType.getVectorSize(); 8953 return true; 8954 } 8955 8956 // If it wasn't a vector, it must be a struct meeting certain requirements. The requirements 8957 // are checked below: just check for struct-ness here. 8958 if (!retType.isStruct()) { 8959 error(loc, "Invalid texture template type", "", ""); 8960 return false; 8961 } 8962 8963 TTypeList* members = retType.getWritableStruct(); 8964 8965 // Check for too many or not enough structure members. 8966 if (members->size() > 4 || members->size() == 0) { 8967 error(loc, "Invalid member count in texture template structure", "", ""); 8968 return false; 8969 } 8970 8971 // Error checking: We must have <= 4 total components, all of the same basic type. 8972 unsigned totalComponents = 0; 8973 for (unsigned m = 0; m < members->size(); ++m) { 8974 // Check for bad member types 8975 if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) { 8976 error(loc, "Invalid texture template struct member type", "", ""); 8977 return false; 8978 } 8979 8980 const unsigned memberVectorSize = (*members)[m].type->getVectorSize(); 8981 totalComponents += memberVectorSize; 8982 8983 // too many total member components 8984 if (totalComponents > 4) { 8985 error(loc, "Too many components in texture template structure type", "", ""); 8986 return false; 8987 } 8988 8989 // All members must be of a common basic type 8990 if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) { 8991 error(loc, "Texture template structure members must same basic type", "", ""); 8992 return false; 8993 } 8994 } 8995 8996 // If the structure in the return type already exists in the table, we'll use it. Otherwise, we'll make 8997 // a new entry. This is a linear search, but it hardly ever happens, and the list cannot be very large. 8998 for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) { 8999 if (textureReturnStruct[idx] == members) { 9000 sampler.structReturnIndex = idx; 9001 return true; 9002 } 9003 } 9004 9005 // It wasn't found as an existing entry. See if we have room for a new one. 9006 if (textureReturnStruct.size() >= TSampler::structReturnSlots) { 9007 error(loc, "Texture template struct return slots exceeded", "", ""); 9008 return false; 9009 } 9010 9011 // Insert it in the vector that tracks struct return types. 9012 sampler.structReturnIndex = unsigned(textureReturnStruct.size()); 9013 textureReturnStruct.push_back(members); 9014 9015 // Success! 9016 return true; 9017 } 9018 9019 // Return the sampler return type in retType. 9020 void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const 9021 { 9022 if (sampler.hasReturnStruct()) { 9023 assert(textureReturnStruct.size() >= sampler.structReturnIndex); 9024 9025 // We land here if the texture return is a structure. 9026 TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex]; 9027 9028 const TType resultType(blockStruct, ""); 9029 retType.shallowCopy(resultType); 9030 } else { 9031 // We land here if the texture return is a vector or scalar. 9032 const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize()); 9033 retType.shallowCopy(resultType); 9034 } 9035 } 9036 9037 9038 // Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type 9039 TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const 9040 { 9041 const auto it = builtInTessLinkageSymbols.find(biType); 9042 if (it == builtInTessLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr 9043 return nullptr; 9044 9045 return intermediate.addSymbol(*it->second->getAsVariable()); 9046 } 9047 9048 // Finalization step: Add patch constant function invocation 9049 void HlslParseContext::addPatchConstantInvocation() 9050 { 9051 TSourceLoc loc; 9052 loc.init(); 9053 9054 // If there's no patch constant function, or we're not a HS, do nothing. 9055 if (patchConstantFunctionName.empty() || language != EShLangTessControl) 9056 return; 9057 9058 if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) { 9059 error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), ""); 9060 return; 9061 } 9062 9063 const TString mangledName = patchConstantFunctionName + "("; 9064 9065 // create list of PCF candidates 9066 TVector<const TFunction*> candidateList; 9067 bool builtIn; 9068 symbolTable.findFunctionNameList(mangledName, candidateList, builtIn); 9069 9070 // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not 9071 // allow any disambiguation of overloads. 9072 if (candidateList.empty()) { 9073 error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), ""); 9074 return; 9075 } 9076 9077 // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions, 9078 // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error 9079 // out if there is more than one candidate. 9080 if (candidateList.size() > 1) { 9081 error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), ""); 9082 return; 9083 } 9084 9085 // Look for built-in variables in a function's parameter list. 9086 const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) { 9087 for (int p=0; p<function.getParamCount(); ++p) { 9088 TStorageQualifier storage = function[p].type->getQualifier().storage; 9089 9090 if (storage == EvqConstReadOnly) // treated identically to input 9091 storage = EvqIn; 9092 9093 if (function[p].getDeclaredBuiltIn() != EbvNone) 9094 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage)); 9095 else 9096 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage)); 9097 } 9098 }; 9099 9100 // If we synthesize a built-in interface variable, we must add it to the linkage. 9101 const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) { 9102 if (name == nullptr) { 9103 error(loc, "unable to locate patch function parameter name", "", ""); 9104 return; 9105 } else { 9106 TVariable& variable = *new TVariable(name, type); 9107 if (! symbolTable.insert(variable)) { 9108 error(loc, "unable to declare patch constant function interface variable", name->c_str(), ""); 9109 return; 9110 } 9111 9112 globalQualifierFix(loc, variable.getWritableType().getQualifier()); 9113 9114 if (symbolNode != nullptr) 9115 *symbolNode = intermediate.addSymbol(variable); 9116 9117 trackLinkage(variable); 9118 } 9119 }; 9120 9121 const auto isOutputPatch = [this](TFunction& patchConstantFunction, int param) { 9122 const TType& type = *patchConstantFunction[param].type; 9123 const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn(); 9124 9125 return type.isArray() && !type.isRuntimeSizedArray() && biType == EbvOutputPatch; 9126 }; 9127 9128 // We will perform these steps. Each is in a scoped block for separation: they could 9129 // become separate functions to make addPatchConstantInvocation shorter. 9130 // 9131 // 1. Union the interfaces, and create built-ins for anything present in the PCF and 9132 // declared as a built-in variable that isn't present in the entry point's signature. 9133 // 9134 // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main, 9135 // or the ones we created. Matching is based on built-in type. We may use synthesized 9136 // variables from (1) above. 9137 // 9138 // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them. 9139 // 9140 // 3. Create a return sequence: copy the return value (if any) from the PCF to a 9141 // (non-sanitized) output variable. In case this may involve multiple copies, such as for 9142 // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple 9143 // indirections into a complex R-value coming from the call to the PCF. 9144 // 9145 // 4. Create a barrier. 9146 // 9147 // 5/5B. Call the PCF inside an if test for (invocation id == 0). 9148 9149 TFunction& patchConstantFunction = const_cast<TFunction&>(*candidateList[0]); 9150 const int pcfParamCount = patchConstantFunction.getParamCount(); 9151 TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); 9152 TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence(); 9153 9154 int outPatchParam = -1; // -1 means there isn't one. 9155 9156 // ================ Step 1A: Union Interfaces ================ 9157 // Our patch constant function. 9158 { 9159 std::set<tInterstageIoData> pcfBuiltIns; // patch constant function built-ins 9160 std::set<tInterstageIoData> epfBuiltIns; // entry point function built-ins 9161 9162 assert(entryPointFunction); 9163 assert(entryPointFunctionBody); 9164 9165 findBuiltIns(patchConstantFunction, pcfBuiltIns); 9166 findBuiltIns(*entryPointFunction, epfBuiltIns); 9167 9168 // Find the set of built-ins in the PCF that are not present in the entry point. 9169 std::set<tInterstageIoData> notInEntryPoint; 9170 9171 notInEntryPoint = pcfBuiltIns; 9172 9173 // std::set_difference not usable on unordered containers 9174 for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi) 9175 notInEntryPoint.erase(*bi); 9176 9177 // Now we'll add those to the entry and to the linkage. 9178 for (int p=0; p<pcfParamCount; ++p) { 9179 const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn(); 9180 TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage; 9181 9182 // Track whether there is an output patch param 9183 if (isOutputPatch(patchConstantFunction, p)) { 9184 if (outPatchParam >= 0) { 9185 // Presently we only support one per ctrl pt input. 9186 error(loc, "unimplemented: multiple output patches in patch constant function", "", ""); 9187 return; 9188 } 9189 outPatchParam = p; 9190 } 9191 9192 if (biType != EbvNone) { 9193 TType* paramType = patchConstantFunction[p].type->clone(); 9194 9195 if (storage == EvqConstReadOnly) // treated identically to input 9196 storage = EvqIn; 9197 9198 // Presently, the only non-built-in we support is InputPatch, which is treated as 9199 // a pseudo-built-in. 9200 if (biType == EbvInputPatch) { 9201 builtInTessLinkageSymbols[biType] = inputPatch; 9202 } else if (biType == EbvOutputPatch) { 9203 // Nothing... 9204 } else { 9205 // Use the original declaration type for the linkage 9206 paramType->getQualifier().builtIn = biType; 9207 9208 if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1) 9209 addToLinkage(*paramType, patchConstantFunction[p].name, nullptr); 9210 } 9211 } 9212 } 9213 9214 // If we didn't find it because the shader made one, add our own. 9215 if (invocationIdSym == nullptr) { 9216 TType invocationIdType(EbtUint, EvqIn, 1); 9217 TString* invocationIdName = NewPoolTString("InvocationId"); 9218 invocationIdType.getQualifier().builtIn = EbvInvocationId; 9219 addToLinkage(invocationIdType, invocationIdName, &invocationIdSym); 9220 } 9221 9222 assert(invocationIdSym); 9223 } 9224 9225 TIntermTyped* pcfArguments = nullptr; 9226 TVariable* perCtrlPtVar = nullptr; 9227 9228 // ================ Step 1B: Argument synthesis ================ 9229 // Create pcfArguments for synthesis of patchconstantfunction invocation 9230 // TODO: handle struct or array inputs 9231 { 9232 for (int p=0; p<pcfParamCount; ++p) { 9233 TIntermSymbol* inputArg = nullptr; 9234 9235 if (p == outPatchParam) { 9236 if (perCtrlPtVar == nullptr) { 9237 perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name, 9238 *patchConstantFunction[outPatchParam].type); 9239 9240 perCtrlPtVar->getWritableType().getQualifier().makeTemporary(); 9241 } 9242 inputArg = intermediate.addSymbol(*perCtrlPtVar, loc); 9243 } else { 9244 // find which built-in it is 9245 const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn(); 9246 9247 inputArg = findTessLinkageSymbol(biType); 9248 9249 if (inputArg == nullptr) { 9250 error(loc, "unable to find patch constant function built-in variable", "", ""); 9251 return; 9252 } 9253 } 9254 9255 if (pcfParamCount == 1) 9256 pcfArguments = inputArg; 9257 else 9258 pcfArguments = intermediate.growAggregate(pcfArguments, inputArg); 9259 } 9260 } 9261 9262 // ================ Step 2: Synthesize call to PCF ================ 9263 TIntermAggregate* pcfCallSequence = nullptr; 9264 TIntermTyped* pcfCall = nullptr; 9265 9266 { 9267 // Create a function call to the patchconstantfunction 9268 if (pcfArguments) 9269 addInputArgumentConversions(patchConstantFunction, pcfArguments); 9270 9271 // Synthetic call. 9272 pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc); 9273 pcfCall->getAsAggregate()->setUserDefined(); 9274 pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName()); 9275 intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(), 9276 patchConstantFunction.getMangledName()); 9277 9278 if (pcfCall->getAsAggregate()) { 9279 TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList(); 9280 for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) { 9281 TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage; 9282 qualifierList.push_back(qual); 9283 } 9284 pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator()); 9285 } 9286 } 9287 9288 // ================ Step 2B: Per Control Point synthesis ================ 9289 // If there is per control point data, we must either emulate that with multiple 9290 // invocations of the entry point to build up an array, or (TODO:) use a yet 9291 // unavailable extension to look across the SIMD lanes. This is the former 9292 // as a placeholder for the latter. 9293 if (outPatchParam >= 0) { 9294 // We must introduce a local temp variable of the type wanted by the PCF input. 9295 const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize(); 9296 9297 if (entryPointFunction->getType().getBasicType() == EbtVoid) { 9298 error(loc, "entry point must return a value for use with patch constant function", "", ""); 9299 return; 9300 } 9301 9302 // Create calls to wrapped main to fill in the array. We will substitute fixed values 9303 // of invocation ID when calling the wrapped main. 9304 9305 // This is the type of the each member of the per ctrl point array. 9306 const TType derefType(perCtrlPtVar->getType(), 0); 9307 9308 for (int cpt = 0; cpt < arraySize; ++cpt) { 9309 // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab 9310 // for this function. 9311 const TString origName = entryPointFunction->getName().substr(1); 9312 TFunction callee(&origName, TType(EbtVoid)); 9313 TIntermTyped* callingArgs = nullptr; 9314 9315 for (int i = 0; i < entryPointFunction->getParamCount(); i++) { 9316 TParameter& param = (*entryPointFunction)[i]; 9317 TType& paramType = *param.type; 9318 9319 if (paramType.getQualifier().isParamOutput()) { 9320 error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", ""); 9321 return; 9322 } 9323 9324 if (paramType.getQualifier().isParamInput()) { 9325 TIntermTyped* arg = nullptr; 9326 if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) { 9327 // substitute invocation ID with the array element ID 9328 arg = intermediate.addConstantUnion(cpt, loc); 9329 } else { 9330 TVariable* argVar = makeInternalVariable(*param.name, *param.type); 9331 argVar->getWritableType().getQualifier().makeTemporary(); 9332 arg = intermediate.addSymbol(*argVar); 9333 } 9334 9335 handleFunctionArgument(&callee, callingArgs, arg); 9336 } 9337 } 9338 9339 // Call and assign to per ctrl point variable 9340 currentCaller = intermediate.getEntryPointMangledName().c_str(); 9341 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); 9342 TIntermTyped* index = intermediate.addConstantUnion(cpt, loc); 9343 TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc); 9344 TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc); 9345 element->setType(derefType); 9346 element->setLoc(loc); 9347 9348 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, 9349 handleAssign(loc, EOpAssign, element, callReturn)); 9350 } 9351 } 9352 9353 // ================ Step 3: Create return Sequence ================ 9354 // Return sequence: copy PCF result to a temporary, then to shader output variable. 9355 if (pcfCall->getBasicType() != EbtVoid) { 9356 const TType* retType = &patchConstantFunction.getType(); // return type from the PCF 9357 TType outType; // output type that goes with the return type. 9358 outType.shallowCopy(*retType); 9359 9360 // substitute the output type 9361 const auto newLists = ioTypeMap.find(retType->getStruct()); 9362 if (newLists != ioTypeMap.end()) 9363 outType.setStruct(newLists->second.output); 9364 9365 // Substitute the top level type's built-in type 9366 if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone) 9367 outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType(); 9368 9369 outType.getQualifier().patch = true; // make it a per-patch variable 9370 9371 TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType); 9372 pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut; 9373 9374 if (pcfOutput->getType().containsBuiltIn()) 9375 split(*pcfOutput); 9376 9377 assignToInterface(*pcfOutput); 9378 9379 TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc); 9380 9381 // The call to the PCF is a complex R-value: we want to store it in a temp to avoid 9382 // repeated calls to the PCF: 9383 TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType); 9384 pcfCallResult->getWritableType().getQualifier().makeTemporary(); 9385 9386 TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc); 9387 TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall); 9388 TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym, 9389 intermediate.addSymbol(*pcfCallResult, loc)); 9390 9391 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign); 9392 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut); 9393 } else { 9394 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall); 9395 } 9396 9397 // ================ Step 4: Barrier ================ 9398 TIntermTyped* barrier = new TIntermAggregate(EOpBarrier); 9399 barrier->setLoc(loc); 9400 barrier->setType(TType(EbtVoid)); 9401 epBodySeq.insert(epBodySeq.end(), barrier); 9402 9403 // ================ Step 5: Test on invocation ID ================ 9404 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); 9405 TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool)); 9406 9407 9408 // ================ Step 5B: Create if statement on Invocation ID == 0 ================ 9409 intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc); 9410 TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr); 9411 invocationIdTest->setLoc(loc); 9412 9413 // add our test sequence before the return. 9414 epBodySeq.insert(epBodySeq.end(), invocationIdTest); 9415 } 9416 9417 // Finalization step: remove unused buffer blocks from linkage (we don't know until the 9418 // shader is entirely compiled) 9419 void HlslParseContext::removeUnusedStructBufferCounters() 9420 { 9421 const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(), 9422 [this](const TSymbol* sym) { 9423 const auto sbcIt = structBufferCounter.find(sym->getName()); 9424 return sbcIt != structBufferCounter.end() && !sbcIt->second; 9425 }); 9426 9427 linkageSymbols.erase(endIt, linkageSymbols.end()); 9428 } 9429 9430 // post-processing 9431 void HlslParseContext::finish() 9432 { 9433 // Error check: There was a dangling .mips operator. These are not nested constructs in the grammar, so 9434 // cannot be detected there. This is not strictly needed in a non-validating parser; it's just helpful. 9435 if (! mipsOperatorMipArg.empty()) { 9436 error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", ""); 9437 } 9438 9439 removeUnusedStructBufferCounters(); 9440 addPatchConstantInvocation(); 9441 9442 TParseContextBase::finish(); 9443 } 9444 9445 } // end namespace glslang 9446