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 41 #include "../glslang/MachineIndependent/Scan.h" 42 #include "../glslang/MachineIndependent/preprocessor/PpContext.h" 43 44 #include "../glslang/OSDependent/osinclude.h" 45 46 #include <stdarg.h> 47 #include <algorithm> 48 49 namespace glslang { 50 51 HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool /*parsingBuiltins*/, 52 int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, 53 bool forwardCompatible, EShMessages messages) : 54 TParseContextBase(symbolTable, interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages), 55 contextPragma(true, false), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0), 56 postMainReturn(false), 57 limits(resources.limits), 58 afterEOF(false) 59 { 60 // ensure we always have a linkage node, even if empty, to simplify tree topology algorithms 61 linkage = new TIntermAggregate; 62 63 globalUniformDefaults.clear(); 64 globalUniformDefaults.layoutMatrix = ElmColumnMajor; 65 globalUniformDefaults.layoutPacking = ElpStd140; 66 67 globalBufferDefaults.clear(); 68 globalBufferDefaults.layoutMatrix = ElmColumnMajor; 69 globalBufferDefaults.layoutPacking = ElpStd430; 70 71 globalInputDefaults.clear(); 72 globalOutputDefaults.clear(); 73 74 // "Shaders in the transform 75 // feedback capturing mode have an initial global default of 76 // layout(xfb_buffer = 0) out;" 77 if (language == EShLangVertex || 78 language == EShLangTessControl || 79 language == EShLangTessEvaluation || 80 language == EShLangGeometry) 81 globalOutputDefaults.layoutXfbBuffer = 0; 82 83 if (language == EShLangGeometry) 84 globalOutputDefaults.layoutStream = 0; 85 } 86 87 HlslParseContext::~HlslParseContext() 88 { 89 } 90 91 void HlslParseContext::setLimits(const TBuiltInResource& r) 92 { 93 resources = r; 94 intermediate.setLimits(resources); 95 } 96 97 // 98 // Parse an array of strings using the parser in HlslRules. 99 // 100 // Returns true for successful acceptance of the shader, false if any errors. 101 // 102 bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) 103 { 104 currentScanner = &input; 105 ppContext.setInput(input, versionWillBeError); 106 107 HlslScanContext::fillInKeywordMap(); // TODO: right place, and include the delete too 108 109 HlslScanContext scanContext(*this, ppContext); 110 HlslGrammar grammar(scanContext, *this); 111 if (! grammar.parse()) 112 printf("HLSL translation failed.\n"); 113 114 return numErrors == 0; 115 } 116 117 void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) 118 { 119 if (pragmaCallback) 120 pragmaCallback(loc.line, tokens); 121 122 if (tokens.size() == 0) 123 return; 124 } 125 126 // 127 // Look at a '.' field selector string and change it into offsets 128 // for a vector or scalar 129 // 130 // Returns true if there is no error. 131 // 132 bool HlslParseContext::parseVectorFields(const TSourceLoc& loc, const TString& compString, int vecSize, TVectorFields& fields) 133 { 134 fields.num = (int)compString.size(); 135 if (fields.num > 4) { 136 error(loc, "illegal vector field selection", compString.c_str(), ""); 137 return false; 138 } 139 140 enum { 141 exyzw, 142 ergba, 143 estpq, 144 } fieldSet[4]; 145 146 for (int i = 0; i < fields.num; ++i) { 147 switch (compString[i]) { 148 case 'x': 149 fields.offsets[i] = 0; 150 fieldSet[i] = exyzw; 151 break; 152 case 'r': 153 fields.offsets[i] = 0; 154 fieldSet[i] = ergba; 155 break; 156 case 's': 157 fields.offsets[i] = 0; 158 fieldSet[i] = estpq; 159 break; 160 case 'y': 161 fields.offsets[i] = 1; 162 fieldSet[i] = exyzw; 163 break; 164 case 'g': 165 fields.offsets[i] = 1; 166 fieldSet[i] = ergba; 167 break; 168 case 't': 169 fields.offsets[i] = 1; 170 fieldSet[i] = estpq; 171 break; 172 case 'z': 173 fields.offsets[i] = 2; 174 fieldSet[i] = exyzw; 175 break; 176 case 'b': 177 fields.offsets[i] = 2; 178 fieldSet[i] = ergba; 179 break; 180 case 'p': 181 fields.offsets[i] = 2; 182 fieldSet[i] = estpq; 183 break; 184 185 case 'w': 186 fields.offsets[i] = 3; 187 fieldSet[i] = exyzw; 188 break; 189 case 'a': 190 fields.offsets[i] = 3; 191 fieldSet[i] = ergba; 192 break; 193 case 'q': 194 fields.offsets[i] = 3; 195 fieldSet[i] = estpq; 196 break; 197 default: 198 error(loc, "illegal vector field selection", compString.c_str(), ""); 199 return false; 200 } 201 } 202 203 for (int i = 0; i < fields.num; ++i) { 204 if (fields.offsets[i] >= vecSize) { 205 error(loc, "vector field selection out of range", compString.c_str(), ""); 206 return false; 207 } 208 209 if (i > 0) { 210 if (fieldSet[i] != fieldSet[i - 1]) { 211 error(loc, "illegal - vector component fields not from the same set", compString.c_str(), ""); 212 return false; 213 } 214 } 215 } 216 217 return true; 218 } 219 220 // 221 // Used to output syntax, parsing, and semantic errors. 222 // 223 224 void HlslParseContext::outputMessage(const TSourceLoc& loc, const char* szReason, 225 const char* szToken, 226 const char* szExtraInfoFormat, 227 TPrefixType prefix, va_list args) 228 { 229 const int maxSize = MaxTokenLength + 200; 230 char szExtraInfo[maxSize]; 231 232 safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args); 233 234 infoSink.info.prefix(prefix); 235 infoSink.info.location(loc); 236 infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n"; 237 238 if (prefix == EPrefixError) { 239 ++numErrors; 240 } 241 } 242 243 void C_DECL HlslParseContext::error(const TSourceLoc& loc, const char* szReason, const char* szToken, 244 const char* szExtraInfoFormat, ...) 245 { 246 if (messages & EShMsgOnlyPreprocessor) 247 return; 248 va_list args; 249 va_start(args, szExtraInfoFormat); 250 outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); 251 va_end(args); 252 } 253 254 void C_DECL HlslParseContext::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, 255 const char* szExtraInfoFormat, ...) 256 { 257 if (suppressWarnings()) 258 return; 259 va_list args; 260 va_start(args, szExtraInfoFormat); 261 outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); 262 va_end(args); 263 } 264 265 void C_DECL HlslParseContext::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken, 266 const char* szExtraInfoFormat, ...) 267 { 268 va_list args; 269 va_start(args, szExtraInfoFormat); 270 outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); 271 va_end(args); 272 } 273 274 void C_DECL HlslParseContext::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, 275 const char* szExtraInfoFormat, ...) 276 { 277 va_list args; 278 va_start(args, szExtraInfoFormat); 279 outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); 280 va_end(args); 281 } 282 283 // 284 // Handle seeing a variable identifier in the grammar. 285 // 286 TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string) 287 { 288 if (symbol == nullptr) 289 symbol = symbolTable.find(*string); 290 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { 291 error(loc, "expected symbol, not user-defined type", string->c_str(), ""); 292 return nullptr; 293 } 294 295 // Error check for requiring specific extensions present. 296 if (symbol && symbol->getNumExtensions()) 297 requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); 298 299 if (symbol && symbol->isReadOnly()) { 300 // All shared things containing an implicitly sized array must be copied up 301 // on first use, so that all future references will share its array structure, 302 // so that editing the implicit size will effect all nodes consuming it, 303 // and so that editing the implicit size won't change the shared one. 304 // 305 // If this is a variable or a block, check it and all it contains, but if this 306 // is a member of an anonymous block, check the whole block, as the whole block 307 // will need to be copied up if it contains an implicitly-sized array. 308 if (symbol->getType().containsImplicitlySizedArray() || (symbol->getAsAnonMember() && symbol->getAsAnonMember()->getAnonContainer().getType().containsImplicitlySizedArray())) 309 makeEditable(symbol); 310 } 311 312 const TVariable* variable; 313 const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; 314 TIntermTyped* node = nullptr; 315 if (anon) { 316 // It was a member of an anonymous container. 317 318 // Create a subtree for its dereference. 319 variable = anon->getAnonContainer().getAsVariable(); 320 TIntermTyped* container = intermediate.addSymbol(*variable, loc); 321 TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); 322 node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); 323 324 node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); 325 if (node->getType().hiddenMember()) 326 error(loc, "member of nameless block was not redeclared", string->c_str(), ""); 327 } else { 328 // Not a member of an anonymous container. 329 330 // The symbol table search was done in the lexical phase. 331 // See if it was a variable. 332 variable = symbol ? symbol->getAsVariable() : nullptr; 333 if (variable) { 334 if ((variable->getType().getBasicType() == EbtBlock || 335 variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) { 336 error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); 337 variable = nullptr; 338 } 339 } else { 340 if (symbol) 341 error(loc, "variable name expected", string->c_str(), ""); 342 } 343 344 // Recovery, if it wasn't found or was not a variable. 345 if (! variable) 346 variable = new TVariable(string, TType(EbtVoid)); 347 348 if (variable->getType().getQualifier().isFrontEndConstant()) 349 node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); 350 else 351 node = intermediate.addSymbol(*variable, loc); 352 } 353 354 if (variable->getType().getQualifier().isIo()) 355 intermediate.addIoAccessed(*string); 356 357 return node; 358 } 359 360 // 361 // Handle seeing a base[index] dereference in the grammar. 362 // 363 TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) 364 { 365 TIntermTyped* result = nullptr; 366 367 int indexValue = 0; 368 if (index->getQualifier().storage == EvqConst) { 369 indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); 370 checkIndex(loc, base->getType(), indexValue); 371 } 372 373 variableCheck(base); 374 if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) { 375 if (base->getAsSymbolNode()) 376 error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); 377 else 378 error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); 379 } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) 380 return intermediate.foldDereference(base, indexValue, loc); 381 else { 382 // at least one of base and index is variable... 383 384 if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) 385 handleIoResizeArrayAccess(loc, base); 386 387 if (index->getQualifier().storage == EvqConst) { 388 if (base->getType().isImplicitlySizedArray()) 389 updateImplicitArraySize(loc, base, indexValue); 390 result = intermediate.addIndex(EOpIndexDirect, base, index, loc); 391 } else { 392 result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); 393 } 394 } 395 396 if (result == nullptr) { 397 // Insert dummy error-recovery result 398 result = intermediate.addConstantUnion(0.0, EbtFloat, loc); 399 } else { 400 // Insert valid dereferenced result 401 TType newType(base->getType(), 0); // dereferenced type 402 if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) 403 newType.getQualifier().storage = EvqConst; 404 else 405 newType.getQualifier().storage = EvqTemporary; 406 result->setType(newType); 407 } 408 409 return result; 410 } 411 412 void HlslParseContext::checkIndex(const TSourceLoc& /*loc*/, const TType& /*type*/, int& /*index*/) 413 { 414 // HLSL todo: any rules for index fixups? 415 } 416 417 // Make a shared symbol have a non-shared version that can be edited by the current 418 // compile, such that editing its type will not change the shared version and will 419 // effect all nodes sharing it. 420 void HlslParseContext::makeEditable(TSymbol*& symbol) 421 { 422 // copyUp() does a deep copy of the type. 423 symbol = symbolTable.copyUp(symbol); 424 425 // Also, see if it's tied to IO resizing 426 if (isIoResizeArray(symbol->getType())) 427 ioArraySymbolResizeList.push_back(symbol); 428 429 // Also, save it in the AST for linker use. 430 intermediate.addSymbolLinkageNode(linkage, *symbol); 431 } 432 433 TVariable* HlslParseContext::getEditableVariable(const char* name) 434 { 435 bool builtIn; 436 TSymbol* symbol = symbolTable.find(name, &builtIn); 437 if (builtIn) 438 makeEditable(symbol); 439 440 return symbol->getAsVariable(); 441 } 442 443 // Return true if this is a geometry shader input array or tessellation control output array. 444 bool HlslParseContext::isIoResizeArray(const TType& type) const 445 { 446 return type.isArray() && 447 ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || 448 (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && ! type.getQualifier().patch)); 449 } 450 451 // If an array is not isIoResizeArray() but is an io array, make sure it has the right size 452 void HlslParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) 453 { 454 if (! type.isArray() || type.getQualifier().patch || symbolTable.atBuiltInLevel()) 455 return; 456 457 assert(! isIoResizeArray(type)); 458 459 if (type.getQualifier().storage != EvqVaryingIn || type.getQualifier().patch) 460 return; 461 462 if (language == EShLangTessControl || language == EShLangTessEvaluation) { 463 if (type.getOuterArraySize() != resources.maxPatchVertices) { 464 if (type.isExplicitlySizedArray()) 465 error(loc, "tessellation input array size must be gl_MaxPatchVertices or implicitly sized", "[]", ""); 466 type.changeOuterArraySize(resources.maxPatchVertices); 467 } 468 } 469 } 470 471 // Handle a dereference of a geometry shader input array or tessellation control output array. 472 // See ioArraySymbolResizeList comment in ParseHelper.h. 473 // 474 void HlslParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base) 475 { 476 TIntermSymbol* symbolNode = base->getAsSymbolNode(); 477 assert(symbolNode); 478 if (! symbolNode) 479 return; 480 481 // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing) 482 if (symbolNode->getType().isImplicitlySizedArray()) { 483 int newSize = getIoArrayImplicitSize(); 484 if (newSize > 0) 485 symbolNode->getWritableType().changeOuterArraySize(newSize); 486 } 487 } 488 489 // If there has been an input primitive declaration (geometry shader) or an output 490 // number of vertices declaration(tessellation shader), make sure all input array types 491 // match it in size. Types come either from nodes in the AST or symbols in the 492 // symbol table. 493 // 494 // Types without an array size will be given one. 495 // Types already having a size that is wrong will get an error. 496 // 497 void HlslParseContext::checkIoArraysConsistency(const TSourceLoc& loc, bool tailOnly) 498 { 499 int requiredSize = getIoArrayImplicitSize(); 500 if (requiredSize == 0) 501 return; 502 503 const char* feature; 504 if (language == EShLangGeometry) 505 feature = TQualifier::getGeometryString(intermediate.getInputPrimitive()); 506 else if (language == EShLangTessControl) 507 feature = "vertices"; 508 else 509 feature = "unknown"; 510 511 if (tailOnly) { 512 checkIoArrayConsistency(loc, requiredSize, feature, ioArraySymbolResizeList.back()->getWritableType(), ioArraySymbolResizeList.back()->getName()); 513 return; 514 } 515 516 for (size_t i = 0; i < ioArraySymbolResizeList.size(); ++i) 517 checkIoArrayConsistency(loc, requiredSize, feature, ioArraySymbolResizeList[i]->getWritableType(), ioArraySymbolResizeList[i]->getName()); 518 } 519 520 int HlslParseContext::getIoArrayImplicitSize() const 521 { 522 if (language == EShLangGeometry) 523 return TQualifier::mapGeometryToSize(intermediate.getInputPrimitive()); 524 else if (language == EShLangTessControl) 525 return intermediate.getVertices() != TQualifier::layoutNotSet ? intermediate.getVertices() : 0; 526 else 527 return 0; 528 } 529 530 void HlslParseContext::checkIoArrayConsistency(const TSourceLoc& /*loc*/, int requiredSize, const char* /*feature*/, TType& type, const TString& /*name*/) 531 { 532 if (type.isImplicitlySizedArray()) 533 type.changeOuterArraySize(requiredSize); 534 } 535 536 // Handle seeing a binary node with a math operation. 537 TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) 538 { 539 TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc); 540 if (! result) 541 binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); 542 543 return result; 544 } 545 546 // Handle seeing a unary node with a math operation. 547 TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) 548 { 549 TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc); 550 551 if (result) 552 return result; 553 else 554 unaryOpError(loc, str, childNode->getCompleteString()); 555 556 return childNode; 557 } 558 559 // 560 // Handle seeing a base.field dereference in the grammar. 561 // 562 TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) 563 { 564 variableCheck(base); 565 566 // 567 // methods can't be resolved until we later see the function-calling syntax. 568 // Save away the name in the AST for now. Processing is completed in 569 // handleLengthMethod(), etc. 570 // 571 if (field == "length") { 572 return intermediate.addMethod(base, TType(EbtInt), &field, loc); 573 } else if (field == "CalculateLevelOfDetail" || 574 field == "CalculateLevelOfDetailUnclamped" || 575 field == "Gather" || 576 field == "GetDimensions" || 577 field == "GetSamplePosition" || 578 field == "Load" || 579 field == "Sample" || 580 field == "SampleBias" || 581 field == "SampleCmp" || 582 field == "SampleCmpLevelZero" || 583 field == "SampleGrad" || 584 field == "SampleLevel") { 585 // If it's not a method on a sampler object, we fall through in case it is a struct member. 586 if (base->getType().getBasicType() == EbtSampler) { 587 const TSampler& texType = base->getType().getSampler(); 588 if (! texType.isPureSampler()) { 589 const int vecSize = texType.isShadow() ? 1 : 4; 590 return intermediate.addMethod(base, TType(texType.type, EvqTemporary, vecSize), &field, loc); 591 } 592 } 593 } 594 595 // It's not .length() if we get to here. 596 597 if (base->isArray()) { 598 error(loc, "cannot apply to an array:", ".", field.c_str()); 599 600 return base; 601 } 602 603 // It's neither an array nor .length() if we get here, 604 // leaving swizzles and struct/block dereferences. 605 606 TIntermTyped* result = base; 607 if (base->isVector() || base->isScalar()) { 608 TVectorFields fields; 609 if (! parseVectorFields(loc, field, base->getVectorSize(), fields)) { 610 fields.num = 1; 611 fields.offsets[0] = 0; 612 } 613 614 if (base->isScalar()) { 615 if (fields.num == 1) 616 return result; 617 else { 618 TType type(base->getBasicType(), EvqTemporary, fields.num); 619 return addConstructor(loc, base, type, mapTypeToConstructorOp(type)); 620 } 621 } 622 623 if (base->getType().getQualifier().isFrontEndConstant()) 624 result = intermediate.foldSwizzle(base, fields, loc); 625 else { 626 if (fields.num == 1) { 627 TIntermTyped* index = intermediate.addConstantUnion(fields.offsets[0], loc); 628 result = intermediate.addIndex(EOpIndexDirect, base, index, loc); 629 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision)); 630 } else { 631 TString vectorString = field; 632 TIntermTyped* index = intermediate.addSwizzle(fields, loc); 633 result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); 634 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, (int)vectorString.size())); 635 } 636 } 637 } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { 638 const TTypeList* fields = base->getType().getStruct(); 639 bool fieldFound = false; 640 int member; 641 for (member = 0; member < (int)fields->size(); ++member) { 642 if ((*fields)[member].type->getFieldName() == field) { 643 fieldFound = true; 644 break; 645 } 646 } 647 if (fieldFound) { 648 if (base->getType().getQualifier().storage == EvqConst) 649 result = intermediate.foldDereference(base, member, loc); 650 else { 651 TIntermTyped* index = intermediate.addConstantUnion(member, loc); 652 result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); 653 result->setType(*(*fields)[member].type); 654 } 655 } else 656 error(loc, "no such field in structure", field.c_str(), ""); 657 } else 658 error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); 659 660 return result; 661 } 662 663 // 664 // Handle seeing a function declarator in the grammar. This is the precursor 665 // to recognizing a function prototype or function definition. 666 // 667 TFunction* HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) 668 { 669 // 670 // Multiple declarations of the same function name are allowed. 671 // 672 // If this is a definition, the definition production code will check for redefinitions 673 // (we don't know at this point if it's a definition or not). 674 // 675 bool builtIn; 676 TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); 677 const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; 678 679 if (prototype) { 680 // All built-in functions are defined, even though they don't have a body. 681 // Count their prototype as a definition instead. 682 if (symbolTable.atBuiltInLevel()) 683 function.setDefined(); 684 else { 685 if (prevDec && ! builtIn) 686 symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const 687 function.setPrototyped(); 688 } 689 } 690 691 // This insert won't actually insert it if it's a duplicate signature, but it will still check for 692 // other forms of name collisions. 693 if (! symbolTable.insert(function)) 694 error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); 695 696 // 697 // If this is a redeclaration, it could also be a definition, 698 // in which case, we need to use the parameter names from this one, and not the one that's 699 // being redeclared. So, pass back this declaration, not the one in the symbol table. 700 // 701 return &function; 702 } 703 704 // 705 // Handle seeing the function prototype in front of a function definition in the grammar. 706 // The body is handled after this function returns. 707 // 708 TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function) 709 { 710 currentCaller = function.getMangledName(); 711 TSymbol* symbol = symbolTable.find(function.getMangledName()); 712 TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; 713 714 if (! prevDec) 715 error(loc, "can't find function", function.getName().c_str(), ""); 716 // Note: 'prevDec' could be 'function' if this is the first time we've seen function 717 // as it would have just been put in the symbol table. Otherwise, we're looking up 718 // an earlier occurrence. 719 720 if (prevDec && prevDec->isDefined()) { 721 // Then this function already has a body. 722 error(loc, "function already has a body", function.getName().c_str(), ""); 723 } 724 if (prevDec && ! prevDec->isDefined()) { 725 prevDec->setDefined(); 726 727 // Remember the return type for later checking for RETURN statements. 728 currentFunctionType = &(prevDec->getType()); 729 } else 730 currentFunctionType = new TType(EbtVoid); 731 functionReturnsValue = false; 732 733 inEntrypoint = (function.getName() == intermediate.getEntryPoint().c_str()); 734 if (inEntrypoint) { 735 // parameters are actually shader-level inputs 736 for (int i = 0; i < function.getParamCount(); i++) 737 function[i].type->getQualifier().storage = EvqVaryingIn; 738 } 739 740 // 741 // New symbol table scope for body of function plus its arguments 742 // 743 pushScope(); 744 745 // 746 // Insert parameters into the symbol table. 747 // If the parameter has no name, it's not an error, just don't insert it 748 // (could be used for unused args). 749 // 750 // Also, accumulate the list of parameters into the HIL, so lower level code 751 // knows where to find parameters. 752 // 753 TIntermAggregate* paramNodes = new TIntermAggregate; 754 for (int i = 0; i < function.getParamCount(); i++) { 755 TParameter& param = function[i]; 756 if (param.name != nullptr) { 757 TVariable *variable = new TVariable(param.name, *param.type); 758 759 // Insert the parameters with name in the symbol table. 760 if (! symbolTable.insert(*variable)) 761 error(loc, "redefinition", variable->getName().c_str(), ""); 762 else { 763 // Transfer ownership of name pointer to symbol table. 764 param.name = nullptr; 765 766 // Add the parameter to the HIL 767 paramNodes = intermediate.growAggregate(paramNodes, 768 intermediate.addSymbol(*variable, loc), 769 loc); 770 } 771 } else 772 paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); 773 } 774 intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); 775 loopNestingLevel = 0; 776 controlFlowNestingLevel = 0; 777 postMainReturn = false; 778 779 return paramNodes; 780 } 781 782 void HlslParseContext::handleFunctionArgument(TFunction* function, TIntermTyped*& arguments, TIntermTyped* newArg) 783 { 784 TParameter param = { 0, new TType }; 785 param.type->shallowCopy(newArg->getType()); 786 function->addParameter(param); 787 if (arguments) 788 arguments = intermediate.growAggregate(arguments, newArg); 789 else 790 arguments = newArg; 791 } 792 793 // 794 // HLSL atomic operations have slightly different arguments than 795 // GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic. 796 // This provides the post-decomposition equivalent opcode. 797 // 798 TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage) 799 { 800 switch (op) { 801 case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd; 802 case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd; 803 case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap; 804 case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax; 805 case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin; 806 case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr; 807 case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor; 808 case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange; 809 case EOpInterlockedCompareStore: // TODO: ... 810 default: 811 error(loc, "unknown atomic operation", "unknown op", ""); 812 return EOpNull; 813 } 814 } 815 816 // 817 // Create a combined sampler/texture from separate sampler and texture. 818 // 819 TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler) 820 { 821 TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler); 822 823 txcombine->getSequence().push_back(argTex); 824 txcombine->getSequence().push_back(argSampler); 825 826 TSampler samplerType = argTex->getType().getSampler(); 827 samplerType.combined = true; 828 829 txcombine->setType(TType(samplerType, EvqTemporary)); 830 txcombine->setLoc(loc); 831 832 return txcombine; 833 } 834 835 // 836 // Decompose DX9 and DX10 sample intrinsics & object methods into AST 837 // 838 void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 839 { 840 if (!node || !node->getAsOperator()) 841 return; 842 843 const TOperator op = node->getAsOperator()->getOp(); 844 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; 845 846 switch (op) { 847 // **** DX9 intrinsics: **** 848 case EOpTexture: 849 { 850 // Texture with ddx & ddy is really gradient form in HLSL 851 if (argAggregate->getSequence().size() == 4) { 852 node->getAsAggregate()->setOperator(EOpTextureGrad); 853 break; 854 } 855 856 break; 857 } 858 859 case EOpTextureBias: 860 { 861 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler 862 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord 863 864 // HLSL puts bias in W component of coordinate. We extract it and add it to 865 // the argument list, instead 866 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); 867 TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); 868 869 TOperator constructOp = EOpNull; 870 switch (arg0->getType().getSampler().dim) { 871 case Esd1D: constructOp = EOpConstructFloat; break; // 1D 872 case Esd2D: constructOp = EOpConstructVec2; break; // 2D 873 case Esd3D: constructOp = EOpConstructVec3; break; // 3D 874 case EsdCube: constructOp = EOpConstructVec3; break; // also 3D 875 default: break; 876 } 877 878 TIntermAggregate* constructCoord = new TIntermAggregate(constructOp); 879 constructCoord->getSequence().push_back(arg1); 880 constructCoord->setLoc(loc); 881 882 TIntermAggregate* tex = new TIntermAggregate(EOpTexture); 883 tex->getSequence().push_back(arg0); // sampler 884 tex->getSequence().push_back(constructCoord); // coordinate 885 tex->getSequence().push_back(bias); // bias 886 tex->setLoc(loc); 887 node = tex; 888 889 break; 890 } 891 892 // **** DX10 methods: **** 893 case EOpMethodSample: // fall through 894 case EOpMethodSampleBias: // ... 895 { 896 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 897 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 898 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 899 TIntermTyped* argBias = nullptr; 900 TIntermTyped* argOffset = nullptr; 901 902 int nextArg = 3; 903 904 if (op == EOpMethodSampleBias) // SampleBias has a bias arg 905 argBias = argAggregate->getSequence()[nextArg++]->getAsTyped(); 906 907 TOperator textureOp = EOpTexture; 908 909 if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form 910 textureOp = EOpTextureOffset; 911 argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped(); 912 } 913 914 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 915 916 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 917 txsample->getSequence().push_back(txcombine); 918 txsample->getSequence().push_back(argCoord); 919 920 if (argBias != nullptr) 921 txsample->getSequence().push_back(argBias); 922 923 if (argOffset != nullptr) 924 txsample->getSequence().push_back(argOffset); 925 926 txsample->setType(node->getType()); 927 txsample->setLoc(loc); 928 node = txsample; 929 930 break; 931 } 932 933 case EOpMethodSampleGrad: // ... 934 { 935 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 936 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); 937 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); 938 TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped(); 939 TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped(); 940 TIntermTyped* argOffset = nullptr; 941 942 TOperator textureOp = EOpTextureGrad; 943 944 if (argAggregate->getSequence().size() == 6) { // last parameter is offset form 945 textureOp = EOpTextureGradOffset; 946 argOffset = argAggregate->getSequence()[5]->getAsTyped(); 947 } 948 949 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); 950 951 TIntermAggregate* txsample = new TIntermAggregate(textureOp); 952 txsample->getSequence().push_back(txcombine); 953 txsample->getSequence().push_back(argCoord); 954 txsample->getSequence().push_back(argDDX); 955 txsample->getSequence().push_back(argDDY); 956 957 if (argOffset != nullptr) 958 txsample->getSequence().push_back(argOffset); 959 960 txsample->setType(node->getType()); 961 txsample->setLoc(loc); 962 node = txsample; 963 964 break; 965 } 966 967 case EOpMethodGetDimensions: 968 { 969 // AST returns a vector of results, which we break apart component-wise into 970 // separate values to assign to the HLSL method's outputs, ala: 971 // tx . GetDimensions(width, height); 972 // float2 sizeQueryTemp = EOpTextureQuerySize 973 // width = sizeQueryTemp.X; 974 // height = sizeQueryTemp.Y; 975 976 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); 977 const TType& texType = argTex->getType(); 978 979 assert(texType.getBasicType() == EbtSampler); 980 981 const TSampler& texSampler = texType.getSampler(); 982 const TSamplerDim dim = texSampler.dim; 983 const int numArgs = argAggregate->getSequence().size(); 984 985 int numDims = 0; 986 987 switch (dim) { 988 case Esd1D: numDims = 1; break; // W 989 case Esd2D: numDims = 2; break; // W, H 990 case Esd3D: numDims = 3; break; // W, H, D 991 case EsdCube: numDims = 2; break; // W, H (cube) 992 default: 993 assert(0 && "unhandled texture dimension"); 994 } 995 996 // Arrayed adds another dimension for the number of array elements 997 if (texSampler.isArrayed()) 998 ++numDims; 999 1000 // Establish whether we're querying mip levels 1001 const bool mipQuery = numArgs > (numDims + 1); 1002 1003 // AST assumes integer return. Will be converted to float if required. 1004 TIntermAggregate* sizeQuery = new TIntermAggregate(EOpTextureQuerySize); 1005 sizeQuery->getSequence().push_back(argTex); 1006 // If we're querying an explicit LOD, add the LOD, which is always arg #1 1007 if (mipQuery) { 1008 TIntermTyped* queryLod = argAggregate->getSequence()[1]->getAsTyped(); 1009 sizeQuery->getSequence().push_back(queryLod); 1010 } 1011 sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims)); 1012 sizeQuery->setLoc(loc); 1013 1014 // Return value from size query 1015 TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType()); 1016 tempArg->getWritableType().getQualifier().makeTemporary(); 1017 TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc); 1018 1019 TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign, sizeQueryReturn, sizeQuery, loc); 1020 1021 // Compound statement for assigning outputs 1022 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc); 1023 // Index of first output parameter 1024 const int outParamBase = mipQuery ? 2 : 1; 1025 1026 for (int compNum = 0; compNum < numDims; ++compNum) { 1027 TIntermTyped* indexedOut = nullptr; 1028 1029 if (numDims > 1) { 1030 TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true); 1031 indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc); 1032 indexedOut->setType(TType(EbtUint, EvqTemporary, 1)); 1033 indexedOut->setLoc(loc); 1034 } else { 1035 indexedOut = sizeQueryReturn; 1036 } 1037 1038 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped(); 1039 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc); 1040 1041 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 1042 } 1043 1044 // handle mip level parameter 1045 if (mipQuery) { 1046 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); 1047 1048 TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels); 1049 levelsQuery->getSequence().push_back(argTex); 1050 levelsQuery->setType(TType(EbtUint, EvqTemporary, 1)); 1051 levelsQuery->setLoc(loc); 1052 1053 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc); 1054 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); 1055 } 1056 1057 compoundStatement->setOperator(EOpSequence); 1058 compoundStatement->setLoc(loc); 1059 compoundStatement->setType(TType(EbtVoid)); 1060 1061 node = compoundStatement; 1062 1063 break; 1064 } 1065 1066 default: 1067 break; // most pass through unchanged 1068 } 1069 } 1070 1071 // 1072 // Optionally decompose intrinsics to AST opcodes. 1073 // 1074 void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) 1075 { 1076 // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST 1077 // opcodes for compatibility with existing software stacks. 1078 static const bool decomposeHlslIntrinsics = true; 1079 1080 if (!decomposeHlslIntrinsics || !node || !node->getAsOperator()) 1081 return; 1082 1083 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; 1084 TIntermUnary* fnUnary = node->getAsUnaryNode(); 1085 const TOperator op = node->getAsOperator()->getOp(); 1086 1087 switch (op) { 1088 case EOpGenMul: 1089 { 1090 // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul 1091 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 1092 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 1093 1094 if (arg0->isVector() && arg1->isVector()) { // vec * vec 1095 node->getAsAggregate()->setOperator(EOpDot); 1096 } else { 1097 node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1); 1098 } 1099 1100 break; 1101 } 1102 1103 case EOpRcp: 1104 { 1105 // rcp(a) -> 1 / a 1106 TIntermTyped* arg0 = fnUnary->getOperand(); 1107 TBasicType type0 = arg0->getBasicType(); 1108 TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true); 1109 node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0); 1110 1111 break; 1112 } 1113 1114 case EOpSaturate: 1115 { 1116 // saturate(a) -> clamp(a,0,1) 1117 TIntermTyped* arg0 = fnUnary->getOperand(); 1118 TBasicType type0 = arg0->getBasicType(); 1119 TIntermAggregate* clamp = new TIntermAggregate(EOpClamp); 1120 1121 clamp->getSequence().push_back(arg0); 1122 clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true)); 1123 clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true)); 1124 clamp->setLoc(loc); 1125 clamp->setType(node->getType()); 1126 clamp->getWritableType().getQualifier().makeTemporary(); 1127 node = clamp; 1128 1129 break; 1130 } 1131 1132 case EOpSinCos: 1133 { 1134 // sincos(a,b,c) -> b = sin(a), c = cos(a) 1135 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 1136 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 1137 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); 1138 1139 TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0); 1140 TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0); 1141 TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc); 1142 TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc); 1143 1144 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc); 1145 compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign); 1146 compoundStatement->setOperator(EOpSequence); 1147 compoundStatement->setLoc(loc); 1148 compoundStatement->setType(TType(EbtVoid)); 1149 1150 node = compoundStatement; 1151 1152 break; 1153 } 1154 1155 case EOpClip: 1156 { 1157 // clip(a) -> if (any(a<0)) discard; 1158 TIntermTyped* arg0 = fnUnary->getOperand(); 1159 TBasicType type0 = arg0->getBasicType(); 1160 TIntermTyped* compareNode = nullptr; 1161 1162 // For non-scalars: per experiment with FXC compiler, discard if any component < 0. 1163 if (!arg0->isScalar()) { 1164 // component-wise compare: a < 0 1165 TIntermAggregate* less = new TIntermAggregate(EOpLessThan); 1166 less->getSequence().push_back(arg0); 1167 less->setLoc(loc); 1168 1169 // make vec or mat of bool matching dimensions of input 1170 less->setType(TType(EbtBool, EvqTemporary, 1171 arg0->getType().getVectorSize(), 1172 arg0->getType().getMatrixCols(), 1173 arg0->getType().getMatrixRows(), 1174 arg0->getType().isVector())); 1175 1176 // calculate # of components for comparison const 1177 const int constComponentCount = 1178 std::max(arg0->getType().getVectorSize(), 1) * 1179 std::max(arg0->getType().getMatrixCols(), 1) * 1180 std::max(arg0->getType().getMatrixRows(), 1); 1181 1182 TConstUnion zero; 1183 zero.setDConst(0.0); 1184 TConstUnionArray zeros(constComponentCount, zero); 1185 1186 less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true)); 1187 1188 compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool)); 1189 } else { 1190 TIntermTyped* zero = intermediate.addConstantUnion(0, type0, loc, true); 1191 compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero); 1192 } 1193 1194 TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc); 1195 1196 node = new TIntermSelection(compareNode, killNode, nullptr); 1197 node->setLoc(loc); 1198 1199 break; 1200 } 1201 1202 case EOpLog10: 1203 { 1204 // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10)) 1205 TIntermTyped* arg0 = fnUnary->getOperand(); 1206 TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0); 1207 TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true); 1208 1209 node = handleBinaryMath(loc, "mul", EOpMul, log2, base); 1210 1211 break; 1212 } 1213 1214 case EOpDst: 1215 { 1216 // dest.x = 1; 1217 // dest.y = src0.y * src1.y; 1218 // dest.z = src0.z; 1219 // dest.w = src1.w; 1220 1221 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 1222 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 1223 1224 TIntermTyped* y = intermediate.addConstantUnion(1, loc, true); 1225 TIntermTyped* z = intermediate.addConstantUnion(2, loc, true); 1226 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); 1227 1228 TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc); 1229 TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc); 1230 TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc); 1231 TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); 1232 1233 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); 1234 1235 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 1236 dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y)); 1237 dst->getSequence().push_back(src0z); 1238 dst->getSequence().push_back(src1w); 1239 dst->setType(TType(EbtFloat, EvqTemporary, 4)); 1240 dst->setLoc(loc); 1241 node = dst; 1242 1243 break; 1244 } 1245 1246 case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value 1247 case EOpInterlockedMin: // ... 1248 case EOpInterlockedMax: // ... 1249 case EOpInterlockedAnd: // ... 1250 case EOpInterlockedOr: // ... 1251 case EOpInterlockedXor: // ... 1252 case EOpInterlockedExchange: // always has output arg 1253 { 1254 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 1255 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 1256 1257 const bool isImage = arg0->getType().isImage(); 1258 const TOperator atomicOp = mapAtomicOp(loc, op, isImage); 1259 1260 if (argAggregate->getSequence().size() > 2) { 1261 // optional output param is present. return value goes to arg2. 1262 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); 1263 1264 TIntermAggregate* atomic = new TIntermAggregate(atomicOp); 1265 atomic->getSequence().push_back(arg0); 1266 atomic->getSequence().push_back(arg1); 1267 atomic->setLoc(loc); 1268 atomic->setType(arg0->getType()); 1269 atomic->getWritableType().getQualifier().makeTemporary(); 1270 1271 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); 1272 } else { 1273 // Set the matching operator. Since output is absent, this is all we need to do. 1274 node->getAsAggregate()->setOperator(atomicOp); 1275 } 1276 1277 break; 1278 } 1279 1280 case EOpInterlockedCompareExchange: 1281 { 1282 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest 1283 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp 1284 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value 1285 TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig 1286 1287 const bool isImage = arg0->getType().isImage(); 1288 TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage)); 1289 atomic->getSequence().push_back(arg0); 1290 atomic->getSequence().push_back(arg1); 1291 atomic->getSequence().push_back(arg2); 1292 atomic->setLoc(loc); 1293 atomic->setType(arg2->getType()); 1294 atomic->getWritableType().getQualifier().makeTemporary(); 1295 1296 node = intermediate.addAssign(EOpAssign, arg3, atomic, loc); 1297 1298 break; 1299 } 1300 1301 case EOpEvaluateAttributeSnapped: 1302 { 1303 // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels 1304 // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y: 1305 // iU = (iU<<28)>>28 1306 // fU = ((float)iU)/16 1307 // Targets might handle this natively, in which case they can disable 1308 // decompositions. 1309 1310 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value 1311 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset 1312 1313 TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true); 1314 TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift, 1315 handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28), 1316 i28); 1317 1318 TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true); 1319 TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul, 1320 intermediate.addConversion(EOpConstructFloat, 1321 TType(EbtFloat, EvqTemporary, 2), iU), 1322 recip16); 1323 1324 TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset); 1325 interp->getSequence().push_back(arg0); 1326 interp->getSequence().push_back(floatOffset); 1327 interp->setLoc(loc); 1328 interp->setType(arg0->getType()); 1329 interp->getWritableType().getQualifier().makeTemporary(); 1330 1331 node = interp; 1332 1333 break; 1334 } 1335 1336 case EOpLit: 1337 { 1338 TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped(); 1339 TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped(); 1340 TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped(); 1341 1342 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); 1343 1344 // Ambient 1345 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 1346 1347 // Diffuse: 1348 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); 1349 TIntermAggregate* diffuse = new TIntermAggregate(EOpMax); 1350 diffuse->getSequence().push_back(n_dot_l); 1351 diffuse->getSequence().push_back(zero); 1352 diffuse->setLoc(loc); 1353 diffuse->setType(TType(EbtFloat)); 1354 dst->getSequence().push_back(diffuse); 1355 1356 // Specular: 1357 TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin); 1358 min_ndot->getSequence().push_back(n_dot_l); 1359 min_ndot->getSequence().push_back(n_dot_h); 1360 min_ndot->setLoc(loc); 1361 min_ndot->setType(TType(EbtFloat)); 1362 1363 TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero); 1364 TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m 1365 1366 dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc)); 1367 1368 // One: 1369 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); 1370 1371 dst->setLoc(loc); 1372 dst->setType(TType(EbtFloat, EvqTemporary, 4)); 1373 node = dst; 1374 break; 1375 } 1376 1377 case EOpAsDouble: 1378 { 1379 // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must 1380 // first construct a uint64. 1381 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); 1382 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); 1383 1384 if (arg0->getType().isVector()) { // TODO: ... 1385 error(loc, "double2 conversion not implemented", "asdouble", ""); 1386 break; 1387 } 1388 1389 TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2); 1390 1391 uint64->getSequence().push_back(arg0); 1392 uint64->getSequence().push_back(arg1); 1393 uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2 1394 uint64->setLoc(loc); 1395 1396 // bitcast uint2 to a double 1397 TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble); 1398 convert->getAsUnaryNode()->setOperand(uint64); 1399 convert->setLoc(loc); 1400 convert->setType(TType(EbtDouble, EvqTemporary)); 1401 node = convert; 1402 1403 break; 1404 } 1405 1406 case EOpF16tof32: 1407 case EOpF32tof16: 1408 { 1409 // Temporary until decomposition is available. 1410 error(loc, "unimplemented intrinsic: handle natively", "f32tof16", ""); 1411 break; 1412 } 1413 1414 default: 1415 break; // most pass through unchanged 1416 } 1417 } 1418 1419 // 1420 // Handle seeing function call syntax in the grammar, which could be any of 1421 // - .length() method 1422 // - constructor 1423 // - a call to a built-in function mapped to an operator 1424 // - a call to a built-in function that will remain a function call (e.g., texturing) 1425 // - user function 1426 // - subroutine call (not implemented yet) 1427 // 1428 TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) 1429 { 1430 TIntermTyped* result = nullptr; 1431 1432 TOperator op = function->getBuiltInOp(); 1433 if (op == EOpArrayLength) 1434 result = handleLengthMethod(loc, function, arguments); 1435 else if (op != EOpNull) { 1436 // 1437 // Then this should be a constructor. 1438 // Don't go through the symbol table for constructors. 1439 // Their parameters will be verified algorithmically. 1440 // 1441 TType type(EbtVoid); // use this to get the type back 1442 if (! constructorError(loc, arguments, *function, op, type)) { 1443 // 1444 // It's a constructor, of type 'type'. 1445 // 1446 result = addConstructor(loc, arguments, type, op); 1447 if (result == nullptr) 1448 error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); 1449 } 1450 } else { 1451 // 1452 // Find it in the symbol table. 1453 // 1454 const TFunction* fnCandidate; 1455 bool builtIn; 1456 fnCandidate = findFunction(loc, *function, builtIn); 1457 if (fnCandidate) { 1458 // This is a declared function that might map to 1459 // - a built-in operator, 1460 // - a built-in function not mapped to an operator, or 1461 // - a user function. 1462 1463 // Error check for a function requiring specific extensions present. 1464 if (builtIn && fnCandidate->getNumExtensions()) 1465 requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); 1466 1467 if (arguments) { 1468 // Make sure qualifications work for these arguments. 1469 //TIntermAggregate* aggregate = arguments->getAsAggregate(); 1470 //for (int i = 0; i < fnCandidate->getParamCount(); ++i) { 1471 // // At this early point there is a slight ambiguity between whether an aggregate 'arguments' 1472 // // is the single argument itself or its children are the arguments. Only one argument 1473 // // means take 'arguments' itself as the one argument. 1474 // TIntermNode* arg = fnCandidate->getParamCount() == 1 ? arguments : (aggregate ? aggregate->getSequence()[i] : arguments); 1475 // TQualifier& formalQualifier = (*fnCandidate)[i].type->getQualifier(); 1476 // TQualifier& argQualifier = arg->getAsTyped()->getQualifier(); 1477 //} 1478 1479 // Convert 'in' arguments 1480 addInputArgumentConversions(*fnCandidate, arguments); // arguments may be modified if it's just a single argument node 1481 } 1482 1483 op = fnCandidate->getBuiltInOp(); 1484 if (builtIn && op != EOpNull) { 1485 // A function call mapped to a built-in operation. 1486 result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, fnCandidate->getType()); 1487 if (result == nullptr) { 1488 error(arguments->getLoc(), " wrong operand type", "Internal Error", 1489 "built in unary operator function. Type: %s", 1490 static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str()); 1491 } else if (result->getAsOperator()) { 1492 builtInOpCheck(loc, *fnCandidate, *result->getAsOperator()); 1493 } 1494 } else { 1495 // This is a function call not mapped to built-in operator. 1496 // It could still be a built-in function, but only if PureOperatorBuiltins == false. 1497 result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); 1498 TIntermAggregate* call = result->getAsAggregate(); 1499 call->setName(fnCandidate->getMangledName()); 1500 1501 // this is how we know whether the given function is a built-in function or a user-defined function 1502 // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also 1503 // if builtIn == true, it's definitely a built-in function with EOpNull 1504 if (! builtIn) { 1505 call->setUserDefined(); 1506 intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); 1507 } 1508 } 1509 1510 // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. 1511 // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. 1512 // Also, build the qualifier list for user function calls, which are always called with an aggregate. 1513 if (result->getAsAggregate()) { 1514 TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); 1515 for (int i = 0; i < fnCandidate->getParamCount(); ++i) { 1516 TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; 1517 qualifierList.push_back(qual); 1518 } 1519 result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate()); 1520 } 1521 1522 decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions 1523 decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions 1524 } 1525 } 1526 1527 // generic error recovery 1528 // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades 1529 if (result == nullptr) 1530 result = intermediate.addConstantUnion(0.0, EbtFloat, loc); 1531 1532 return result; 1533 } 1534 1535 // Finish processing object.length(). This started earlier in handleDotDereference(), where 1536 // the ".length" part was recognized and semantically checked, and finished here where the 1537 // function syntax "()" is recognized. 1538 // 1539 // Return resulting tree node. 1540 TIntermTyped* HlslParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode) 1541 { 1542 int length = 0; 1543 1544 if (function->getParamCount() > 0) 1545 error(loc, "method does not accept any arguments", function->getName().c_str(), ""); 1546 else { 1547 const TType& type = intermNode->getAsTyped()->getType(); 1548 if (type.isArray()) { 1549 if (type.isRuntimeSizedArray()) { 1550 // Create a unary op and let the back end handle it 1551 return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); 1552 } else if (type.isImplicitlySizedArray()) { 1553 if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) { 1554 // We could be between a layout declaration that gives a built-in io array implicit size and 1555 // a user redeclaration of that array, meaning we have to substitute its implicit size here 1556 // without actually redeclaring the array. (It is an error to use a member before the 1557 // redeclaration, but not an error to use the array name itself.) 1558 const TString& name = intermNode->getAsSymbolNode()->getName(); 1559 if (name == "gl_in" || name == "gl_out") 1560 length = getIoArrayImplicitSize(); 1561 } 1562 if (length == 0) { 1563 if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) 1564 error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); 1565 else 1566 error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); 1567 } 1568 } else 1569 length = type.getOuterArraySize(); 1570 } else if (type.isMatrix()) 1571 length = type.getMatrixCols(); 1572 else if (type.isVector()) 1573 length = type.getVectorSize(); 1574 else { 1575 // we should not get here, because earlier semantic checking should have prevented this path 1576 error(loc, ".length()", "unexpected use of .length()", ""); 1577 } 1578 } 1579 1580 if (length == 0) 1581 length = 1; 1582 1583 return intermediate.addConstantUnion(length, loc); 1584 } 1585 1586 // 1587 // Add any needed implicit conversions for function-call arguments to input parameters. 1588 // 1589 void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const 1590 { 1591 TIntermAggregate* aggregate = arguments->getAsAggregate(); 1592 1593 // Process each argument's conversion 1594 for (int i = 0; i < function.getParamCount(); ++i) { 1595 // At this early point there is a slight ambiguity between whether an aggregate 'arguments' 1596 // is the single argument itself or its children are the arguments. Only one argument 1597 // means take 'arguments' itself as the one argument. 1598 TIntermTyped* arg = function.getParamCount() == 1 ? arguments->getAsTyped() : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped()); 1599 if (*function[i].type != arg->getType()) { 1600 if (function[i].type->getQualifier().isParamInput()) { 1601 // In-qualified arguments just need an extra node added above the argument to 1602 // convert to the correct type. 1603 arg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg); 1604 if (arg) { 1605 if (function.getParamCount() == 1) 1606 arguments = arg; 1607 else { 1608 if (aggregate) 1609 aggregate->getSequence()[i] = arg; 1610 else 1611 arguments = arg; 1612 } 1613 } 1614 } 1615 } 1616 } 1617 } 1618 1619 // 1620 // Add any needed implicit output conversions for function-call arguments. This 1621 // can require a new tree topology, complicated further by whether the function 1622 // has a return value. 1623 // 1624 // Returns a node of a subtree that evaluates to the return value of the function. 1625 // 1626 TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const 1627 { 1628 TIntermSequence& arguments = intermNode.getSequence(); 1629 1630 // Will there be any output conversions? 1631 bool outputConversions = false; 1632 for (int i = 0; i < function.getParamCount(); ++i) { 1633 if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().storage == EvqOut) { 1634 outputConversions = true; 1635 break; 1636 } 1637 } 1638 1639 if (! outputConversions) 1640 return &intermNode; 1641 1642 // Setup for the new tree, if needed: 1643 // 1644 // Output conversions need a different tree topology. 1645 // Out-qualified arguments need a temporary of the correct type, with the call 1646 // followed by an assignment of the temporary to the original argument: 1647 // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) 1648 // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) 1649 // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. 1650 TIntermTyped* conversionTree = nullptr; 1651 TVariable* tempRet = nullptr; 1652 if (intermNode.getBasicType() != EbtVoid) { 1653 // do the "tempRet = function(...), " bit from above 1654 tempRet = makeInternalVariable("tempReturn", intermNode.getType()); 1655 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); 1656 conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc()); 1657 } else 1658 conversionTree = &intermNode; 1659 1660 conversionTree = intermediate.makeAggregate(conversionTree); 1661 1662 // Process each argument's conversion 1663 for (int i = 0; i < function.getParamCount(); ++i) { 1664 if (*function[i].type != arguments[i]->getAsTyped()->getType()) { 1665 if (function[i].type->getQualifier().isParamOutput()) { 1666 // Out-qualified arguments need to use the topology set up above. 1667 // do the " ...(tempArg, ...), arg = tempArg" bit from above 1668 TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type); 1669 tempArg->getWritableType().getQualifier().makeTemporary(); 1670 TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc()); 1671 TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc()); 1672 conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); 1673 // replace the argument with another node for the same tempArg variable 1674 arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc()); 1675 } 1676 } 1677 } 1678 1679 // Finalize the tree topology (see bigger comment above). 1680 if (tempRet) { 1681 // do the "..., tempRet" bit from above 1682 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); 1683 conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc()); 1684 } 1685 conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); 1686 1687 return conversionTree; 1688 } 1689 1690 // 1691 // Do additional checking of built-in function calls that is not caught 1692 // by normal semantic checks on argument type, extension tagging, etc. 1693 // 1694 // Assumes there has been a semantically correct match to a built-in function prototype. 1695 // 1696 void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) 1697 { 1698 // Set up convenience accessors to the argument(s). There is almost always 1699 // multiple arguments for the cases below, but when there might be one, 1700 // check the unaryArg first. 1701 const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference 1702 const TIntermTyped* unaryArg = nullptr; 1703 const TIntermTyped* arg0 = nullptr; 1704 if (callNode.getAsAggregate()) { 1705 argp = &callNode.getAsAggregate()->getSequence(); 1706 if (argp->size() > 0) 1707 arg0 = (*argp)[0]->getAsTyped(); 1708 } else { 1709 assert(callNode.getAsUnaryNode()); 1710 unaryArg = callNode.getAsUnaryNode()->getOperand(); 1711 arg0 = unaryArg; 1712 } 1713 const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr 1714 1715 // built-in texturing functions get their return value precision from the precision of the sampler 1716 if (fnCandidate.getType().getQualifier().precision == EpqNone && 1717 fnCandidate.getParamCount() > 0 && fnCandidate[0].type->getBasicType() == EbtSampler) 1718 callNode.getQualifier().precision = arg0->getQualifier().precision; 1719 1720 switch (callNode.getOp()) { 1721 case EOpTextureGather: 1722 case EOpTextureGatherOffset: 1723 case EOpTextureGatherOffsets: 1724 { 1725 // Figure out which variants are allowed by what extensions, 1726 // and what arguments must be constant for which situations. 1727 1728 TString featureString = fnCandidate.getName() + "(...)"; 1729 const char* feature = featureString.c_str(); 1730 int compArg = -1; // track which argument, if any, is the constant component argument 1731 switch (callNode.getOp()) { 1732 case EOpTextureGather: 1733 // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, 1734 // otherwise, need GL_ARB_texture_gather. 1735 if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { 1736 if (! fnCandidate[0].type->getSampler().shadow) 1737 compArg = 2; 1738 } 1739 break; 1740 case EOpTextureGatherOffset: 1741 // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument 1742 if (! fnCandidate[0].type->getSampler().shadow) 1743 compArg = 3; 1744 break; 1745 case EOpTextureGatherOffsets: 1746 if (! fnCandidate[0].type->getSampler().shadow) 1747 compArg = 3; 1748 break; 1749 default: 1750 break; 1751 } 1752 1753 if (compArg > 0 && compArg < fnCandidate.getParamCount()) { 1754 if (aggArgs[compArg]->getAsConstantUnion()) { 1755 int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); 1756 if (value < 0 || value > 3) 1757 error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); 1758 } else 1759 error(loc, "must be a compile-time constant:", feature, "component argument"); 1760 } 1761 1762 break; 1763 } 1764 1765 case EOpTextureOffset: 1766 case EOpTextureFetchOffset: 1767 case EOpTextureProjOffset: 1768 case EOpTextureLodOffset: 1769 case EOpTextureProjLodOffset: 1770 case EOpTextureGradOffset: 1771 case EOpTextureProjGradOffset: 1772 { 1773 // Handle texture-offset limits checking 1774 // Pick which argument has to hold constant offsets 1775 int arg = -1; 1776 switch (callNode.getOp()) { 1777 case EOpTextureOffset: arg = 2; break; 1778 case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break; 1779 case EOpTextureProjOffset: arg = 2; break; 1780 case EOpTextureLodOffset: arg = 3; break; 1781 case EOpTextureProjLodOffset: arg = 3; break; 1782 case EOpTextureGradOffset: arg = 4; break; 1783 case EOpTextureProjGradOffset: arg = 4; break; 1784 default: 1785 assert(0); 1786 break; 1787 } 1788 1789 if (arg > 0) { 1790 if (! aggArgs[arg]->getAsConstantUnion()) 1791 error(loc, "argument must be compile-time constant", "texel offset", ""); 1792 else { 1793 const TType& type = aggArgs[arg]->getAsTyped()->getType(); 1794 for (int c = 0; c < type.getVectorSize(); ++c) { 1795 int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); 1796 if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) 1797 error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); 1798 } 1799 } 1800 } 1801 1802 break; 1803 } 1804 1805 case EOpTextureQuerySamples: 1806 case EOpImageQuerySamples: 1807 break; 1808 1809 case EOpImageAtomicAdd: 1810 case EOpImageAtomicMin: 1811 case EOpImageAtomicMax: 1812 case EOpImageAtomicAnd: 1813 case EOpImageAtomicOr: 1814 case EOpImageAtomicXor: 1815 case EOpImageAtomicExchange: 1816 case EOpImageAtomicCompSwap: 1817 break; 1818 1819 case EOpInterpolateAtCentroid: 1820 case EOpInterpolateAtSample: 1821 case EOpInterpolateAtOffset: 1822 // "For the interpolateAt* functions, the call will return a precision 1823 // qualification matching the precision of the 'interpolant' argument to 1824 // the function call." 1825 callNode.getQualifier().precision = arg0->getQualifier().precision; 1826 1827 // Make sure the first argument is an interpolant, or an array element of an interpolant 1828 if (arg0->getType().getQualifier().storage != EvqVaryingIn) { 1829 // It might still be an array element. 1830 // 1831 // We could check more, but the semantics of the first argument are already met; the 1832 // only way to turn an array into a float/vec* is array dereference and swizzle. 1833 // 1834 // ES and desktop 4.3 and earlier: swizzles may not be used 1835 // desktop 4.4 and later: swizzles may be used 1836 const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true); 1837 if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) 1838 error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), ""); 1839 } 1840 break; 1841 1842 default: 1843 break; 1844 } 1845 } 1846 1847 // 1848 // Handle seeing a built-in constructor in a grammar production. 1849 // 1850 TFunction* HlslParseContext::handleConstructorCall(const TSourceLoc& loc, const TType& type) 1851 { 1852 TOperator op = mapTypeToConstructorOp(type); 1853 1854 if (op == EOpNull) { 1855 error(loc, "cannot construct this type", type.getBasicString(), ""); 1856 return nullptr; 1857 } 1858 1859 TString empty(""); 1860 1861 return new TFunction(&empty, type, op); 1862 } 1863 1864 // 1865 // Handle seeing a "COLON semantic" at the end of a type declaration, 1866 // by updating the type according to the semantic. 1867 // 1868 void HlslParseContext::handleSemantic(TType& type, const TString& semantic) 1869 { 1870 // TODO: need to know if it's an input or an output 1871 // The following sketches what needs to be done, but can't be right 1872 // without taking into account stage and input/output. 1873 1874 if (semantic == "PSIZE") 1875 type.getQualifier().builtIn = EbvPointSize; 1876 else if (semantic == "POSITION") 1877 type.getQualifier().builtIn = EbvPosition; 1878 else if (semantic == "FOG") 1879 type.getQualifier().builtIn = EbvFogFragCoord; 1880 else if (semantic == "DEPTH" || semantic == "SV_Depth") 1881 type.getQualifier().builtIn = EbvFragDepth; 1882 else if (semantic == "VFACE" || semantic == "SV_IsFrontFace") 1883 type.getQualifier().builtIn = EbvFace; 1884 else if (semantic == "VPOS" || semantic == "SV_Position") 1885 type.getQualifier().builtIn = EbvFragCoord; 1886 else if (semantic == "SV_ClipDistance") 1887 type.getQualifier().builtIn = EbvClipDistance; 1888 else if (semantic == "SV_CullDistance") 1889 type.getQualifier().builtIn = EbvCullDistance; 1890 else if (semantic == "SV_VertexID") 1891 type.getQualifier().builtIn = EbvVertexId; 1892 else if (semantic == "SV_ViewportArrayIndex") 1893 type.getQualifier().builtIn = EbvViewportIndex; 1894 } 1895 1896 // 1897 // Given a type, find what operation would fully construct it. 1898 // 1899 TOperator HlslParseContext::mapTypeToConstructorOp(const TType& type) const 1900 { 1901 TOperator op = EOpNull; 1902 1903 switch (type.getBasicType()) { 1904 case EbtStruct: 1905 op = EOpConstructStruct; 1906 break; 1907 case EbtSampler: 1908 if (type.getSampler().combined) 1909 op = EOpConstructTextureSampler; 1910 break; 1911 case EbtFloat: 1912 if (type.isMatrix()) { 1913 switch (type.getMatrixCols()) { 1914 case 2: 1915 switch (type.getMatrixRows()) { 1916 case 2: op = EOpConstructMat2x2; break; 1917 case 3: op = EOpConstructMat2x3; break; 1918 case 4: op = EOpConstructMat2x4; break; 1919 default: break; // some compilers want this 1920 } 1921 break; 1922 case 3: 1923 switch (type.getMatrixRows()) { 1924 case 2: op = EOpConstructMat3x2; break; 1925 case 3: op = EOpConstructMat3x3; break; 1926 case 4: op = EOpConstructMat3x4; break; 1927 default: break; // some compilers want this 1928 } 1929 break; 1930 case 4: 1931 switch (type.getMatrixRows()) { 1932 case 2: op = EOpConstructMat4x2; break; 1933 case 3: op = EOpConstructMat4x3; break; 1934 case 4: op = EOpConstructMat4x4; break; 1935 default: break; // some compilers want this 1936 } 1937 break; 1938 default: break; // some compilers want this 1939 } 1940 } else { 1941 switch (type.getVectorSize()) { 1942 case 1: op = EOpConstructFloat; break; 1943 case 2: op = EOpConstructVec2; break; 1944 case 3: op = EOpConstructVec3; break; 1945 case 4: op = EOpConstructVec4; break; 1946 default: break; // some compilers want this 1947 } 1948 } 1949 break; 1950 case EbtDouble: 1951 if (type.getMatrixCols()) { 1952 switch (type.getMatrixCols()) { 1953 case 2: 1954 switch (type.getMatrixRows()) { 1955 case 2: op = EOpConstructDMat2x2; break; 1956 case 3: op = EOpConstructDMat2x3; break; 1957 case 4: op = EOpConstructDMat2x4; break; 1958 default: break; // some compilers want this 1959 } 1960 break; 1961 case 3: 1962 switch (type.getMatrixRows()) { 1963 case 2: op = EOpConstructDMat3x2; break; 1964 case 3: op = EOpConstructDMat3x3; break; 1965 case 4: op = EOpConstructDMat3x4; break; 1966 default: break; // some compilers want this 1967 } 1968 break; 1969 case 4: 1970 switch (type.getMatrixRows()) { 1971 case 2: op = EOpConstructDMat4x2; break; 1972 case 3: op = EOpConstructDMat4x3; break; 1973 case 4: op = EOpConstructDMat4x4; break; 1974 default: break; // some compilers want this 1975 } 1976 break; 1977 } 1978 } else { 1979 switch (type.getVectorSize()) { 1980 case 1: op = EOpConstructDouble; break; 1981 case 2: op = EOpConstructDVec2; break; 1982 case 3: op = EOpConstructDVec3; break; 1983 case 4: op = EOpConstructDVec4; break; 1984 default: break; // some compilers want this 1985 } 1986 } 1987 break; 1988 case EbtInt: 1989 switch (type.getVectorSize()) { 1990 case 1: op = EOpConstructInt; break; 1991 case 2: op = EOpConstructIVec2; break; 1992 case 3: op = EOpConstructIVec3; break; 1993 case 4: op = EOpConstructIVec4; break; 1994 default: break; // some compilers want this 1995 } 1996 break; 1997 case EbtUint: 1998 switch (type.getVectorSize()) { 1999 case 1: op = EOpConstructUint; break; 2000 case 2: op = EOpConstructUVec2; break; 2001 case 3: op = EOpConstructUVec3; break; 2002 case 4: op = EOpConstructUVec4; break; 2003 default: break; // some compilers want this 2004 } 2005 break; 2006 case EbtBool: 2007 switch (type.getVectorSize()) { 2008 case 1: op = EOpConstructBool; break; 2009 case 2: op = EOpConstructBVec2; break; 2010 case 3: op = EOpConstructBVec3; break; 2011 case 4: op = EOpConstructBVec4; break; 2012 default: break; // some compilers want this 2013 } 2014 break; 2015 default: 2016 break; 2017 } 2018 2019 return op; 2020 } 2021 2022 // 2023 // Same error message for all places assignments don't work. 2024 // 2025 void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) 2026 { 2027 error(loc, "", op, "cannot convert from '%s' to '%s'", 2028 right.c_str(), left.c_str()); 2029 } 2030 2031 // 2032 // Same error message for all places unary operations don't work. 2033 // 2034 void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) 2035 { 2036 error(loc, " wrong operand type", op, 2037 "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", 2038 op, operand.c_str()); 2039 } 2040 2041 // 2042 // Same error message for all binary operations don't work. 2043 // 2044 void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) 2045 { 2046 error(loc, " wrong operand types:", op, 2047 "no operation '%s' exists that takes a left-hand operand of type '%s' and " 2048 "a right operand of type '%s' (or there is no acceptable conversion)", 2049 op, left.c_str(), right.c_str()); 2050 } 2051 2052 // 2053 // A basic type of EbtVoid is a key that the name string was seen in the source, but 2054 // it was not found as a variable in the symbol table. If so, give the error 2055 // message and insert a dummy variable in the symbol table to prevent future errors. 2056 // 2057 void HlslParseContext::variableCheck(TIntermTyped*& nodePtr) 2058 { 2059 TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); 2060 if (! symbol) 2061 return; 2062 2063 if (symbol->getType().getBasicType() == EbtVoid) { 2064 error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), ""); 2065 2066 // Add to symbol table to prevent future error messages on the same name 2067 if (symbol->getName().size() > 0) { 2068 TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); 2069 symbolTable.insert(*fakeVariable); 2070 2071 // substitute a symbol node for this new variable 2072 nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); 2073 } 2074 } 2075 } 2076 2077 // 2078 // Both test, and if necessary spit out an error, to see if the node is really 2079 // a constant. 2080 // 2081 void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token) 2082 { 2083 if (node->getQualifier().storage != EvqConst) 2084 error(node->getLoc(), "constant expression required", token, ""); 2085 } 2086 2087 // 2088 // Both test, and if necessary spit out an error, to see if the node is really 2089 // an integer. 2090 // 2091 void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token) 2092 { 2093 if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) 2094 return; 2095 2096 error(node->getLoc(), "scalar integer expression required", token, ""); 2097 } 2098 2099 // 2100 // Both test, and if necessary spit out an error, to see if we are currently 2101 // globally scoped. 2102 // 2103 void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token) 2104 { 2105 if (! symbolTable.atGlobalLevel()) 2106 error(loc, "not allowed in nested scope", token, ""); 2107 } 2108 2109 2110 bool HlslParseContext::builtInName(const TString& /*identifier*/) 2111 { 2112 return false; 2113 } 2114 2115 // 2116 // Make sure there is enough data and not too many arguments provided to the 2117 // constructor to build something of the type of the constructor. Also returns 2118 // the type of the constructor. 2119 // 2120 // Returns true if there was an error in construction. 2121 // 2122 bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* /*node*/, TFunction& function, 2123 TOperator op, TType& type) 2124 { 2125 type.shallowCopy(function.getType()); 2126 2127 bool constructingMatrix = false; 2128 switch (op) { 2129 case EOpConstructTextureSampler: 2130 return constructorTextureSamplerError(loc, function); 2131 case EOpConstructMat2x2: 2132 case EOpConstructMat2x3: 2133 case EOpConstructMat2x4: 2134 case EOpConstructMat3x2: 2135 case EOpConstructMat3x3: 2136 case EOpConstructMat3x4: 2137 case EOpConstructMat4x2: 2138 case EOpConstructMat4x3: 2139 case EOpConstructMat4x4: 2140 case EOpConstructDMat2x2: 2141 case EOpConstructDMat2x3: 2142 case EOpConstructDMat2x4: 2143 case EOpConstructDMat3x2: 2144 case EOpConstructDMat3x3: 2145 case EOpConstructDMat3x4: 2146 case EOpConstructDMat4x2: 2147 case EOpConstructDMat4x3: 2148 case EOpConstructDMat4x4: 2149 constructingMatrix = true; 2150 break; 2151 default: 2152 break; 2153 } 2154 2155 // 2156 // Walk the arguments for first-pass checks and collection of information. 2157 // 2158 2159 int size = 0; 2160 bool constType = true; 2161 bool full = false; 2162 bool overFull = false; 2163 bool matrixInMatrix = false; 2164 bool arrayArg = false; 2165 for (int arg = 0; arg < function.getParamCount(); ++arg) { 2166 if (function[arg].type->isArray()) { 2167 if (! function[arg].type->isExplicitlySizedArray()) { 2168 // Can't construct from an unsized array. 2169 error(loc, "array argument must be sized", "constructor", ""); 2170 return true; 2171 } 2172 arrayArg = true; 2173 } 2174 if (constructingMatrix && function[arg].type->isMatrix()) 2175 matrixInMatrix = true; 2176 2177 // 'full' will go to true when enough args have been seen. If we loop 2178 // again, there is an extra argument. 2179 if (full) { 2180 // For vectors and matrices, it's okay to have too many components 2181 // available, but not okay to have unused arguments. 2182 overFull = true; 2183 } 2184 2185 size += function[arg].type->computeNumComponents(); 2186 if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) 2187 full = true; 2188 2189 if (function[arg].type->getQualifier().storage != EvqConst) 2190 constType = false; 2191 } 2192 2193 if (constType) 2194 type.getQualifier().storage = EvqConst; 2195 2196 if (type.isArray()) { 2197 if (function.getParamCount() == 0) { 2198 error(loc, "array constructor must have at least one argument", "constructor", ""); 2199 return true; 2200 } 2201 2202 if (type.isImplicitlySizedArray()) { 2203 // auto adapt the constructor type to the number of arguments 2204 type.changeOuterArraySize(function.getParamCount()); 2205 } else if (type.getOuterArraySize() != function.getParamCount()) { 2206 error(loc, "array constructor needs one argument per array element", "constructor", ""); 2207 return true; 2208 } 2209 2210 if (type.isArrayOfArrays()) { 2211 // Types have to match, but we're still making the type. 2212 // Finish making the type, and the comparison is done later 2213 // when checking for conversion. 2214 TArraySizes& arraySizes = type.getArraySizes(); 2215 2216 // At least the dimensionalities have to match. 2217 if (! function[0].type->isArray() || arraySizes.getNumDims() != function[0].type->getArraySizes().getNumDims() + 1) { 2218 error(loc, "array constructor argument not correct type to construct array element", "constructior", ""); 2219 return true; 2220 } 2221 2222 if (arraySizes.isInnerImplicit()) { 2223 // "Arrays of arrays ..., and the size for any dimension is optional" 2224 // That means we need to adopt (from the first argument) the other array sizes into the type. 2225 for (int d = 1; d < arraySizes.getNumDims(); ++d) { 2226 if (arraySizes.getDimSize(d) == UnsizedArraySize) { 2227 arraySizes.setDimSize(d, function[0].type->getArraySizes().getDimSize(d - 1)); 2228 } 2229 } 2230 } 2231 } 2232 } 2233 2234 if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { 2235 error(loc, "constructing non-array constituent from array argument", "constructor", ""); 2236 return true; 2237 } 2238 2239 if (matrixInMatrix && ! type.isArray()) { 2240 return false; 2241 } 2242 2243 if (overFull) { 2244 error(loc, "too many arguments", "constructor", ""); 2245 return true; 2246 } 2247 2248 if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) { 2249 error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); 2250 return true; 2251 } 2252 2253 if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || 2254 (op == EOpConstructStruct && size < type.computeNumComponents())) { 2255 error(loc, "not enough data provided for construction", "constructor", ""); 2256 return true; 2257 } 2258 2259 // TIntermTyped* typed = node->getAsTyped(); 2260 2261 return false; 2262 } 2263 2264 // Verify all the correct semantics for constructing a combined texture/sampler. 2265 // Return true if the semantics are incorrect. 2266 bool HlslParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) 2267 { 2268 TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change 2269 const char* token = constructorName.c_str(); 2270 2271 // exactly two arguments needed 2272 if (function.getParamCount() != 2) { 2273 error(loc, "sampler-constructor requires two arguments", token, ""); 2274 return true; 2275 } 2276 2277 // For now, not allowing arrayed constructors, the rest of this function 2278 // is set up to allow them, if this test is removed: 2279 if (function.getType().isArray()) { 2280 error(loc, "sampler-constructor cannot make an array of samplers", token, ""); 2281 return true; 2282 } 2283 2284 // first argument 2285 // * the constructor's first argument must be a texture type 2286 // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array) 2287 // of the texture type must match that of the constructed sampler type 2288 // (that is, the suffixes of the type of the first argument and the 2289 // type of the constructor will be spelled the same way) 2290 if (function[0].type->getBasicType() != EbtSampler || 2291 ! function[0].type->getSampler().isTexture() || 2292 function[0].type->isArray()) { 2293 error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, ""); 2294 return true; 2295 } 2296 // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() 2297 TSampler texture = function.getType().getSampler(); 2298 texture.combined = false; 2299 texture.shadow = false; 2300 if (texture != function[0].type->getSampler()) { 2301 error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, ""); 2302 return true; 2303 } 2304 2305 // second argument 2306 // * the constructor's second argument must be a scalar of type 2307 // *sampler* or *samplerShadow* 2308 // * the presence or absence of depth comparison (Shadow) must match 2309 // between the constructed sampler type and the type of the second argument 2310 if (function[1].type->getBasicType() != EbtSampler || 2311 ! function[1].type->getSampler().isPureSampler() || 2312 function[1].type->isArray()) { 2313 error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, ""); 2314 return true; 2315 } 2316 if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) { 2317 error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow", token, ""); 2318 return true; 2319 } 2320 2321 return false; 2322 } 2323 2324 // Checks to see if a void variable has been declared and raise an error message for such a case 2325 // 2326 // returns true in case of an error 2327 // 2328 bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) 2329 { 2330 if (basicType == EbtVoid) { 2331 error(loc, "illegal use of type 'void'", identifier.c_str(), ""); 2332 return true; 2333 } 2334 2335 return false; 2336 } 2337 2338 // Checks to see if the node (for the expression) contains a scalar boolean expression or not 2339 void HlslParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type) 2340 { 2341 if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) 2342 error(loc, "boolean expression expected", "", ""); 2343 } 2344 2345 // 2346 // Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. 2347 // 2348 void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier) 2349 { 2350 // move from parameter/unknown qualifiers to pipeline in/out qualifiers 2351 switch (qualifier.storage) { 2352 case EvqIn: 2353 qualifier.storage = EvqVaryingIn; 2354 break; 2355 case EvqOut: 2356 qualifier.storage = EvqVaryingOut; 2357 break; 2358 default: 2359 break; 2360 } 2361 } 2362 2363 // 2364 // Merge characteristics of the 'src' qualifier into the 'dst'. 2365 // If there is duplication, issue error messages, unless 'force' 2366 // is specified, which means to just override default settings. 2367 // 2368 // Also, when force is false, it will be assumed that 'src' follows 2369 // 'dst', for the purpose of error checking order for versions 2370 // that require specific orderings of qualifiers. 2371 // 2372 void HlslParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force) 2373 { 2374 // Storage qualification 2375 if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) 2376 dst.storage = src.storage; 2377 else if ((dst.storage == EvqIn && src.storage == EvqOut) || 2378 (dst.storage == EvqOut && src.storage == EvqIn)) 2379 dst.storage = EvqInOut; 2380 else if ((dst.storage == EvqIn && src.storage == EvqConst) || 2381 (dst.storage == EvqConst && src.storage == EvqIn)) 2382 dst.storage = EvqConstReadOnly; 2383 else if (src.storage != EvqTemporary && src.storage != EvqGlobal) 2384 error(loc, "too many storage qualifiers", GetStorageQualifierString(src.storage), ""); 2385 2386 // Precision qualifiers 2387 if (dst.precision == EpqNone || (force && src.precision != EpqNone)) 2388 dst.precision = src.precision; 2389 2390 // Layout qualifiers 2391 mergeObjectLayoutQualifiers(dst, src, false); 2392 2393 // individual qualifiers 2394 bool repeated = false; 2395 #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; 2396 MERGE_SINGLETON(invariant); 2397 MERGE_SINGLETON(noContraction); 2398 MERGE_SINGLETON(centroid); 2399 MERGE_SINGLETON(smooth); 2400 MERGE_SINGLETON(flat); 2401 MERGE_SINGLETON(nopersp); 2402 MERGE_SINGLETON(patch); 2403 MERGE_SINGLETON(sample); 2404 MERGE_SINGLETON(coherent); 2405 MERGE_SINGLETON(volatil); 2406 MERGE_SINGLETON(restrict); 2407 MERGE_SINGLETON(readonly); 2408 MERGE_SINGLETON(writeonly); 2409 MERGE_SINGLETON(specConstant); 2410 } 2411 2412 // used to flatten the sampler type space into a single dimension 2413 // correlates with the declaration of defaultSamplerPrecision[] 2414 int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler) 2415 { 2416 int arrayIndex = sampler.arrayed ? 1 : 0; 2417 int shadowIndex = sampler.shadow ? 1 : 0; 2418 int externalIndex = sampler.external ? 1 : 0; 2419 2420 return EsdNumDims * (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim; 2421 } 2422 2423 // 2424 // Do size checking for an array type's size. 2425 // 2426 void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair) 2427 { 2428 bool isConst = false; 2429 sizePair.size = 1; 2430 sizePair.node = nullptr; 2431 2432 TIntermConstantUnion* constant = expr->getAsConstantUnion(); 2433 if (constant) { 2434 // handle true (non-specialization) constant 2435 sizePair.size = constant->getConstArray()[0].getIConst(); 2436 isConst = true; 2437 } else { 2438 // see if it's a specialization constant instead 2439 if (expr->getQualifier().isSpecConstant()) { 2440 isConst = true; 2441 sizePair.node = expr; 2442 TIntermSymbol* symbol = expr->getAsSymbolNode(); 2443 if (symbol && symbol->getConstArray().size() > 0) 2444 sizePair.size = symbol->getConstArray()[0].getIConst(); 2445 } 2446 } 2447 2448 if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { 2449 error(loc, "array size must be a constant integer expression", "", ""); 2450 return; 2451 } 2452 2453 if (sizePair.size <= 0) { 2454 error(loc, "array size must be a positive integer", "", ""); 2455 return; 2456 } 2457 } 2458 2459 // 2460 // Require array to be completely sized 2461 // 2462 void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) 2463 { 2464 if (arraySizes.isImplicit()) 2465 error(loc, "array size required", "", ""); 2466 } 2467 2468 void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) 2469 { 2470 const TTypeList& structure = *type.getStruct(); 2471 for (int m = 0; m < (int)structure.size(); ++m) { 2472 const TType& member = *structure[m].type; 2473 if (member.isArray()) 2474 arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); 2475 } 2476 } 2477 2478 // Merge array dimensions listed in 'sizes' onto the type's array dimensions. 2479 // 2480 // From the spec: "vec4[2] a[3]; // size-3 array of size-2 array of vec4" 2481 // 2482 // That means, the 'sizes' go in front of the 'type' as outermost sizes. 2483 // 'type' is the type part of the declaration (to the left) 2484 // 'sizes' is the arrayness tagged on the identifier (to the right) 2485 // 2486 void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes) 2487 { 2488 if (sizes) 2489 type.addArrayOuterSizes(*sizes); 2490 } 2491 2492 // 2493 // Do all the semantic checking for declaring or redeclaring an array, with and 2494 // without a size, and make the right changes to the symbol table. 2495 // 2496 void HlslParseContext::declareArray(const TSourceLoc& loc, TString& identifier, const TType& type, TSymbol*& symbol, bool& newDeclaration) 2497 { 2498 if (! symbol) { 2499 bool currentScope; 2500 symbol = symbolTable.find(identifier, nullptr, ¤tScope); 2501 2502 if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { 2503 // bad shader (errors already reported) trying to redeclare a built-in name as an array 2504 return; 2505 } 2506 if (symbol == nullptr || ! currentScope) { 2507 // 2508 // Successfully process a new definition. 2509 // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) 2510 // 2511 symbol = new TVariable(&identifier, type); 2512 symbolTable.insert(*symbol); 2513 newDeclaration = true; 2514 2515 if (! symbolTable.atBuiltInLevel()) { 2516 if (isIoResizeArray(type)) { 2517 ioArraySymbolResizeList.push_back(symbol); 2518 checkIoArraysConsistency(loc, true); 2519 } else 2520 fixIoArraySize(loc, symbol->getWritableType()); 2521 } 2522 2523 return; 2524 } 2525 if (symbol->getAsAnonMember()) { 2526 error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); 2527 symbol = nullptr; 2528 return; 2529 } 2530 } 2531 2532 // 2533 // Process a redeclaration. 2534 // 2535 2536 if (! symbol) { 2537 error(loc, "array variable name expected", identifier.c_str(), ""); 2538 return; 2539 } 2540 2541 // redeclareBuiltinVariable() should have already done the copyUp() 2542 TType& existingType = symbol->getWritableType(); 2543 2544 2545 if (existingType.isExplicitlySizedArray()) { 2546 // be more lenient for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size 2547 if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize())) 2548 error(loc, "redeclaration of array with size", identifier.c_str(), ""); 2549 return; 2550 } 2551 2552 existingType.updateArraySizes(type); 2553 2554 if (isIoResizeArray(type)) 2555 checkIoArraysConsistency(loc); 2556 } 2557 2558 void HlslParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *node, int index) 2559 { 2560 // maybe there is nothing to do... 2561 TIntermTyped* typedNode = node->getAsTyped(); 2562 if (typedNode->getType().getImplicitArraySize() > index) 2563 return; 2564 2565 // something to do... 2566 2567 // Figure out what symbol to lookup, as we will use its type to edit for the size change, 2568 // as that type will be shared through shallow copies for future references. 2569 TSymbol* symbol = nullptr; 2570 int blockIndex = -1; 2571 const TString* lookupName = nullptr; 2572 if (node->getAsSymbolNode()) 2573 lookupName = &node->getAsSymbolNode()->getName(); 2574 else if (node->getAsBinaryNode()) { 2575 const TIntermBinary* deref = node->getAsBinaryNode(); 2576 // This has to be the result of a block dereference, unless it's bad shader code 2577 // If it's a uniform block, then an error will be issued elsewhere, but 2578 // return early now to avoid crashing later in this function. 2579 if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock || 2580 deref->getLeft()->getType().getQualifier().storage == EvqUniform || 2581 deref->getRight()->getAsConstantUnion() == nullptr) 2582 return; 2583 2584 blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); 2585 2586 lookupName = &deref->getLeft()->getAsSymbolNode()->getName(); 2587 if (IsAnonymous(*lookupName)) 2588 lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName(); 2589 } 2590 2591 // Lookup the symbol, should only fail if shader code is incorrect 2592 symbol = symbolTable.find(*lookupName); 2593 if (symbol == nullptr) 2594 return; 2595 2596 if (symbol->getAsFunction()) { 2597 error(loc, "array variable name expected", symbol->getName().c_str(), ""); 2598 return; 2599 } 2600 2601 symbol->getWritableType().setImplicitArraySize(index + 1); 2602 } 2603 2604 // 2605 // See if the identifier is a built-in symbol that can be redeclared, and if so, 2606 // copy the symbol table's read-only built-in variable to the current 2607 // global level, where it can be modified based on the passed in type. 2608 // 2609 // Returns nullptr if no redeclaration took place; meaning a normal declaration still 2610 // needs to occur for it, not necessarily an error. 2611 // 2612 // Returns a redeclared and type-modified variable if a redeclared occurred. 2613 // 2614 TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier, 2615 const TQualifier& /*qualifier*/, 2616 const TShaderQualifiers& /*publicType*/, bool& /*newDeclaration*/) 2617 { 2618 if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) 2619 return nullptr; 2620 2621 return nullptr; 2622 } 2623 2624 // 2625 // Either redeclare the requested block, or give an error message why it can't be done. 2626 // 2627 // TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size 2628 void HlslParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes) 2629 { 2630 // Redeclaring a built-in block... 2631 2632 // Blocks with instance names are easy to find, lookup the instance name, 2633 // Anonymous blocks need to be found via a member. 2634 bool builtIn; 2635 TSymbol* block; 2636 if (instanceName) 2637 block = symbolTable.find(*instanceName, &builtIn); 2638 else 2639 block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn); 2640 2641 // If the block was not found, this must be a version/profile/stage 2642 // that doesn't have it, or the instance name is wrong. 2643 const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str(); 2644 if (! block) { 2645 error(loc, "no declaration found for redeclaration", errorName, ""); 2646 return; 2647 } 2648 // Built-in blocks cannot be redeclared more than once, which if happened, 2649 // we'd be finding the already redeclared one here, rather than the built in. 2650 if (! builtIn) { 2651 error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), ""); 2652 return; 2653 } 2654 2655 // Copy the block to make a writable version, to insert into the block table after editing. 2656 block = symbolTable.copyUpDeferredInsert(block); 2657 2658 if (block->getType().getBasicType() != EbtBlock) { 2659 error(loc, "cannot redeclare a non block as a block", errorName, ""); 2660 return; 2661 } 2662 2663 // Edit and error check the container against the redeclaration 2664 // - remove unused members 2665 // - ensure remaining qualifiers/types match 2666 TType& type = block->getWritableType(); 2667 TTypeList::iterator member = type.getWritableStruct()->begin(); 2668 size_t numOriginalMembersFound = 0; 2669 while (member != type.getStruct()->end()) { 2670 // look for match 2671 bool found = false; 2672 TTypeList::const_iterator newMember; 2673 TSourceLoc memberLoc; 2674 memberLoc.init(); 2675 for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) { 2676 if (member->type->getFieldName() == newMember->type->getFieldName()) { 2677 found = true; 2678 memberLoc = newMember->loc; 2679 break; 2680 } 2681 } 2682 2683 if (found) { 2684 ++numOriginalMembersFound; 2685 // - ensure match between redeclared members' types 2686 // - check for things that can't be changed 2687 // - update things that can be changed 2688 TType& oldType = *member->type; 2689 const TType& newType = *newMember->type; 2690 if (! newType.sameElementType(oldType)) 2691 error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), ""); 2692 if (oldType.isArray() != newType.isArray()) 2693 error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); 2694 else if (! oldType.sameArrayness(newType) && oldType.isExplicitlySizedArray()) 2695 error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), ""); 2696 if (newType.getQualifier().isMemory()) 2697 error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); 2698 if (newType.getQualifier().hasLayout()) 2699 error(memberLoc, "cannot add layout to redeclared block member", member->type->getFieldName().c_str(), ""); 2700 if (newType.getQualifier().patch) 2701 error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), ""); 2702 oldType.getQualifier().centroid = newType.getQualifier().centroid; 2703 oldType.getQualifier().sample = newType.getQualifier().sample; 2704 oldType.getQualifier().invariant = newType.getQualifier().invariant; 2705 oldType.getQualifier().noContraction = newType.getQualifier().noContraction; 2706 oldType.getQualifier().smooth = newType.getQualifier().smooth; 2707 oldType.getQualifier().flat = newType.getQualifier().flat; 2708 oldType.getQualifier().nopersp = newType.getQualifier().nopersp; 2709 2710 // go to next member 2711 ++member; 2712 } else { 2713 // For missing members of anonymous blocks that have been redeclared, 2714 // hide the original (shared) declaration. 2715 // Instance-named blocks can just have the member removed. 2716 if (instanceName) 2717 member = type.getWritableStruct()->erase(member); 2718 else { 2719 member->type->hideMember(); 2720 ++member; 2721 } 2722 } 2723 } 2724 2725 if (numOriginalMembersFound < newTypeList.size()) 2726 error(loc, "block redeclaration has extra members", blockName.c_str(), ""); 2727 if (type.isArray() != (arraySizes != nullptr)) 2728 error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), ""); 2729 else if (type.isArray()) { 2730 if (type.isExplicitlySizedArray() && arraySizes->getOuterSize() == UnsizedArraySize) 2731 error(loc, "block already declared with size, can't redeclare as implicitly-sized", blockName.c_str(), ""); 2732 else if (type.isExplicitlySizedArray() && type.getArraySizes() != *arraySizes) 2733 error(loc, "cannot change array size of redeclared block", blockName.c_str(), ""); 2734 else if (type.isImplicitlySizedArray() && arraySizes->getOuterSize() != UnsizedArraySize) 2735 type.changeOuterArraySize(arraySizes->getOuterSize()); 2736 } 2737 2738 symbolTable.insert(*block); 2739 2740 // Tracking for implicit sizing of array 2741 if (isIoResizeArray(block->getType())) { 2742 ioArraySymbolResizeList.push_back(block); 2743 checkIoArraysConsistency(loc, true); 2744 } else if (block->getType().isArray()) 2745 fixIoArraySize(loc, block->getWritableType()); 2746 2747 // Save it in the AST for linker use. 2748 intermediate.addSymbolLinkageNode(linkage, *block); 2749 } 2750 2751 void HlslParseContext::paramFix(TType& type) 2752 { 2753 switch (type.getQualifier().storage) { 2754 case EvqConst: 2755 type.getQualifier().storage = EvqConstReadOnly; 2756 break; 2757 case EvqGlobal: 2758 case EvqTemporary: 2759 type.getQualifier().storage = EvqIn; 2760 break; 2761 default: 2762 break; 2763 } 2764 } 2765 2766 void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) 2767 { 2768 if (type.containsSpecializationSize()) 2769 error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); 2770 } 2771 2772 // 2773 // Layout qualifier stuff. 2774 // 2775 2776 // Put the id's layout qualification into the public type, for qualifiers not having a number set. 2777 // This is before we know any type information for error checking. 2778 void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id) 2779 { 2780 std::transform(id.begin(), id.end(), id.begin(), ::tolower); 2781 2782 if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { 2783 publicType.qualifier.layoutMatrix = ElmColumnMajor; 2784 return; 2785 } 2786 if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { 2787 publicType.qualifier.layoutMatrix = ElmRowMajor; 2788 return; 2789 } 2790 if (id == "push_constant") { 2791 requireVulkan(loc, "push_constant"); 2792 publicType.qualifier.layoutPushConstant = true; 2793 return; 2794 } 2795 if (language == EShLangGeometry || language == EShLangTessEvaluation) { 2796 if (id == TQualifier::getGeometryString(ElgTriangles)) { 2797 publicType.shaderQualifiers.geometry = ElgTriangles; 2798 return; 2799 } 2800 if (language == EShLangGeometry) { 2801 if (id == TQualifier::getGeometryString(ElgPoints)) { 2802 publicType.shaderQualifiers.geometry = ElgPoints; 2803 return; 2804 } 2805 if (id == TQualifier::getGeometryString(ElgLineStrip)) { 2806 publicType.shaderQualifiers.geometry = ElgLineStrip; 2807 return; 2808 } 2809 if (id == TQualifier::getGeometryString(ElgLines)) { 2810 publicType.shaderQualifiers.geometry = ElgLines; 2811 return; 2812 } 2813 if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { 2814 publicType.shaderQualifiers.geometry = ElgLinesAdjacency; 2815 return; 2816 } 2817 if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { 2818 publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; 2819 return; 2820 } 2821 if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { 2822 publicType.shaderQualifiers.geometry = ElgTriangleStrip; 2823 return; 2824 } 2825 } else { 2826 assert(language == EShLangTessEvaluation); 2827 2828 // input primitive 2829 if (id == TQualifier::getGeometryString(ElgTriangles)) { 2830 publicType.shaderQualifiers.geometry = ElgTriangles; 2831 return; 2832 } 2833 if (id == TQualifier::getGeometryString(ElgQuads)) { 2834 publicType.shaderQualifiers.geometry = ElgQuads; 2835 return; 2836 } 2837 if (id == TQualifier::getGeometryString(ElgIsolines)) { 2838 publicType.shaderQualifiers.geometry = ElgIsolines; 2839 return; 2840 } 2841 2842 // vertex spacing 2843 if (id == TQualifier::getVertexSpacingString(EvsEqual)) { 2844 publicType.shaderQualifiers.spacing = EvsEqual; 2845 return; 2846 } 2847 if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { 2848 publicType.shaderQualifiers.spacing = EvsFractionalEven; 2849 return; 2850 } 2851 if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { 2852 publicType.shaderQualifiers.spacing = EvsFractionalOdd; 2853 return; 2854 } 2855 2856 // triangle order 2857 if (id == TQualifier::getVertexOrderString(EvoCw)) { 2858 publicType.shaderQualifiers.order = EvoCw; 2859 return; 2860 } 2861 if (id == TQualifier::getVertexOrderString(EvoCcw)) { 2862 publicType.shaderQualifiers.order = EvoCcw; 2863 return; 2864 } 2865 2866 // point mode 2867 if (id == "point_mode") { 2868 publicType.shaderQualifiers.pointMode = true; 2869 return; 2870 } 2871 } 2872 } 2873 if (language == EShLangFragment) { 2874 if (id == "origin_upper_left") { 2875 publicType.shaderQualifiers.originUpperLeft = true; 2876 return; 2877 } 2878 if (id == "pixel_center_integer") { 2879 publicType.shaderQualifiers.pixelCenterInteger = true; 2880 return; 2881 } 2882 if (id == "early_fragment_tests") { 2883 publicType.shaderQualifiers.earlyFragmentTests = true; 2884 return; 2885 } 2886 for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) { 2887 if (id == TQualifier::getLayoutDepthString(depth)) { 2888 publicType.shaderQualifiers.layoutDepth = depth; 2889 return; 2890 } 2891 } 2892 if (id.compare(0, 13, "blend_support") == 0) { 2893 bool found = false; 2894 for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { 2895 if (id == TQualifier::getBlendEquationString(be)) { 2896 requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation"); 2897 intermediate.addBlendEquation(be); 2898 publicType.shaderQualifiers.blendEquation = true; 2899 found = true; 2900 break; 2901 } 2902 } 2903 if (! found) 2904 error(loc, "unknown blend equation", "blend_support", ""); 2905 return; 2906 } 2907 } 2908 error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); 2909 } 2910 2911 // Put the id's layout qualifier value into the public type, for qualifiers having a number set. 2912 // This is before we know any type information for error checking. 2913 void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node) 2914 { 2915 const char* feature = "layout-id value"; 2916 //const char* nonLiteralFeature = "non-literal layout-id value"; 2917 2918 integerCheck(node, feature); 2919 const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); 2920 int value = 0; 2921 if (constUnion) { 2922 value = constUnion->getConstArray()[0].getIConst(); 2923 } 2924 2925 std::transform(id.begin(), id.end(), id.begin(), ::tolower); 2926 2927 if (id == "offset") { 2928 publicType.qualifier.layoutOffset = value; 2929 return; 2930 } else if (id == "align") { 2931 // "The specified alignment must be a power of 2, or a compile-time error results." 2932 if (! IsPow2(value)) 2933 error(loc, "must be a power of 2", "align", ""); 2934 else 2935 publicType.qualifier.layoutAlign = value; 2936 return; 2937 } else if (id == "location") { 2938 if ((unsigned int)value >= TQualifier::layoutLocationEnd) 2939 error(loc, "location is too large", id.c_str(), ""); 2940 else 2941 publicType.qualifier.layoutLocation = value; 2942 return; 2943 } else if (id == "set") { 2944 if ((unsigned int)value >= TQualifier::layoutSetEnd) 2945 error(loc, "set is too large", id.c_str(), ""); 2946 else 2947 publicType.qualifier.layoutSet = value; 2948 return; 2949 } else if (id == "binding") { 2950 if ((unsigned int)value >= TQualifier::layoutBindingEnd) 2951 error(loc, "binding is too large", id.c_str(), ""); 2952 else 2953 publicType.qualifier.layoutBinding = value; 2954 return; 2955 } else if (id == "component") { 2956 if ((unsigned)value >= TQualifier::layoutComponentEnd) 2957 error(loc, "component is too large", id.c_str(), ""); 2958 else 2959 publicType.qualifier.layoutComponent = value; 2960 return; 2961 } else if (id.compare(0, 4, "xfb_") == 0) { 2962 // "Any shader making any static use (after preprocessing) of any of these 2963 // *xfb_* qualifiers will cause the shader to be in a transform feedback 2964 // capturing mode and hence responsible for describing the transform feedback 2965 // setup." 2966 intermediate.setXfbMode(); 2967 if (id == "xfb_buffer") { 2968 // "It is a compile-time error to specify an *xfb_buffer* that is greater than 2969 // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." 2970 if (value >= resources.maxTransformFeedbackBuffers) 2971 error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers); 2972 if (value >= (int)TQualifier::layoutXfbBufferEnd) 2973 error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1); 2974 else 2975 publicType.qualifier.layoutXfbBuffer = value; 2976 return; 2977 } else if (id == "xfb_offset") { 2978 if (value >= (int)TQualifier::layoutXfbOffsetEnd) 2979 error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1); 2980 else 2981 publicType.qualifier.layoutXfbOffset = value; 2982 return; 2983 } else if (id == "xfb_stride") { 2984 // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the 2985 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." 2986 if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) 2987 error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", resources.maxTransformFeedbackInterleavedComponents); 2988 else if (value >= (int)TQualifier::layoutXfbStrideEnd) 2989 error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1); 2990 if (value < (int)TQualifier::layoutXfbStrideEnd) 2991 publicType.qualifier.layoutXfbStride = value; 2992 return; 2993 } 2994 } 2995 2996 if (id == "input_attachment_index") { 2997 requireVulkan(loc, "input_attachment_index"); 2998 if (value >= (int)TQualifier::layoutAttachmentEnd) 2999 error(loc, "attachment index is too large", id.c_str(), ""); 3000 else 3001 publicType.qualifier.layoutAttachment = value; 3002 return; 3003 } 3004 if (id == "constant_id") { 3005 requireSpv(loc, "constant_id"); 3006 if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { 3007 error(loc, "specialization-constant id is too large", id.c_str(), ""); 3008 } else { 3009 publicType.qualifier.layoutSpecConstantId = value; 3010 publicType.qualifier.specConstant = true; 3011 if (! intermediate.addUsedConstantId(value)) 3012 error(loc, "specialization-constant id already used", id.c_str(), ""); 3013 } 3014 return; 3015 } 3016 3017 switch (language) { 3018 case EShLangVertex: 3019 break; 3020 3021 case EShLangTessControl: 3022 if (id == "vertices") { 3023 if (value == 0) 3024 error(loc, "must be greater than 0", "vertices", ""); 3025 else 3026 publicType.shaderQualifiers.vertices = value; 3027 return; 3028 } 3029 break; 3030 3031 case EShLangTessEvaluation: 3032 break; 3033 3034 case EShLangGeometry: 3035 if (id == "invocations") { 3036 if (value == 0) 3037 error(loc, "must be at least 1", "invocations", ""); 3038 else 3039 publicType.shaderQualifiers.invocations = value; 3040 return; 3041 } 3042 if (id == "max_vertices") { 3043 publicType.shaderQualifiers.vertices = value; 3044 if (value > resources.maxGeometryOutputVertices) 3045 error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); 3046 return; 3047 } 3048 if (id == "stream") { 3049 publicType.qualifier.layoutStream = value; 3050 return; 3051 } 3052 break; 3053 3054 case EShLangFragment: 3055 if (id == "index") { 3056 publicType.qualifier.layoutIndex = value; 3057 return; 3058 } 3059 break; 3060 3061 case EShLangCompute: 3062 if (id.compare(0, 11, "local_size_") == 0) { 3063 if (id == "local_size_x") { 3064 publicType.shaderQualifiers.localSize[0] = value; 3065 return; 3066 } 3067 if (id == "local_size_y") { 3068 publicType.shaderQualifiers.localSize[1] = value; 3069 return; 3070 } 3071 if (id == "local_size_z") { 3072 publicType.shaderQualifiers.localSize[2] = value; 3073 return; 3074 } 3075 if (spvVersion.spv != 0) { 3076 if (id == "local_size_x_id") { 3077 publicType.shaderQualifiers.localSizeSpecId[0] = value; 3078 return; 3079 } 3080 if (id == "local_size_y_id") { 3081 publicType.shaderQualifiers.localSizeSpecId[1] = value; 3082 return; 3083 } 3084 if (id == "local_size_z_id") { 3085 publicType.shaderQualifiers.localSizeSpecId[2] = value; 3086 return; 3087 } 3088 } 3089 } 3090 break; 3091 3092 default: 3093 break; 3094 } 3095 3096 error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); 3097 } 3098 3099 // Merge any layout qualifier information from src into dst, leaving everything else in dst alone 3100 // 3101 // "More than one layout qualifier may appear in a single declaration. 3102 // Additionally, the same layout-qualifier-name can occur multiple times 3103 // within a layout qualifier or across multiple layout qualifiers in the 3104 // same declaration. When the same layout-qualifier-name occurs 3105 // multiple times, in a single declaration, the last occurrence overrides 3106 // the former occurrence(s). Further, if such a layout-qualifier-name 3107 // will effect subsequent declarations or other observable behavior, it 3108 // is only the last occurrence that will have any effect, behaving as if 3109 // the earlier occurrence(s) within the declaration are not present. 3110 // This is also true for overriding layout-qualifier-names, where one 3111 // overrides the other (e.g., row_major vs. column_major); only the last 3112 // occurrence has any effect." 3113 // 3114 void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) 3115 { 3116 if (src.hasMatrix()) 3117 dst.layoutMatrix = src.layoutMatrix; 3118 if (src.hasPacking()) 3119 dst.layoutPacking = src.layoutPacking; 3120 3121 if (src.hasStream()) 3122 dst.layoutStream = src.layoutStream; 3123 3124 if (src.hasFormat()) 3125 dst.layoutFormat = src.layoutFormat; 3126 3127 if (src.hasXfbBuffer()) 3128 dst.layoutXfbBuffer = src.layoutXfbBuffer; 3129 3130 if (src.hasAlign()) 3131 dst.layoutAlign = src.layoutAlign; 3132 3133 if (! inheritOnly) { 3134 if (src.hasLocation()) 3135 dst.layoutLocation = src.layoutLocation; 3136 if (src.hasComponent()) 3137 dst.layoutComponent = src.layoutComponent; 3138 if (src.hasIndex()) 3139 dst.layoutIndex = src.layoutIndex; 3140 3141 if (src.hasOffset()) 3142 dst.layoutOffset = src.layoutOffset; 3143 3144 if (src.hasSet()) 3145 dst.layoutSet = src.layoutSet; 3146 if (src.layoutBinding != TQualifier::layoutBindingEnd) 3147 dst.layoutBinding = src.layoutBinding; 3148 3149 if (src.hasXfbStride()) 3150 dst.layoutXfbStride = src.layoutXfbStride; 3151 if (src.hasXfbOffset()) 3152 dst.layoutXfbOffset = src.layoutXfbOffset; 3153 if (src.hasAttachment()) 3154 dst.layoutAttachment = src.layoutAttachment; 3155 if (src.hasSpecConstantId()) 3156 dst.layoutSpecConstantId = src.layoutSpecConstantId; 3157 3158 if (src.layoutPushConstant) 3159 dst.layoutPushConstant = true; 3160 } 3161 } 3162 3163 // 3164 // Look up a function name in the symbol table, and make sure it is a function. 3165 // 3166 // Return the function symbol if found, otherwise nullptr. 3167 // 3168 const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) 3169 { 3170 // const TFunction* function = nullptr; 3171 3172 if (symbolTable.isFunctionNameVariable(call.getName())) { 3173 error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); 3174 return nullptr; 3175 } 3176 3177 // first, look for an exact match 3178 TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); 3179 if (symbol) 3180 return symbol->getAsFunction(); 3181 3182 // exact match not found, look through a list of overloaded functions of the same name 3183 3184 const TFunction* candidate = nullptr; 3185 TVector<TFunction*> candidateList; 3186 symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); 3187 3188 for (TVector<TFunction*>::const_iterator it = candidateList.begin(); it != candidateList.end(); ++it) { 3189 const TFunction& function = *(*it); 3190 3191 // to even be a potential match, number of arguments has to match 3192 if (call.getParamCount() != function.getParamCount()) 3193 continue; 3194 3195 bool possibleMatch = true; 3196 for (int i = 0; i < function.getParamCount(); ++i) { 3197 // same types is easy 3198 if (*function[i].type == *call[i].type) 3199 continue; 3200 3201 // We have a mismatch in type, see if it is implicitly convertible 3202 3203 if (function[i].type->isArray() || call[i].type->isArray() || 3204 ! function[i].type->sameElementShape(*call[i].type)) 3205 possibleMatch = false; 3206 else { 3207 // do direction-specific checks for conversion of basic type 3208 if (function[i].type->getQualifier().isParamInput()) { 3209 if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType())) 3210 possibleMatch = false; 3211 } 3212 if (function[i].type->getQualifier().isParamOutput()) { 3213 if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType())) 3214 possibleMatch = false; 3215 } 3216 } 3217 if (! possibleMatch) 3218 break; 3219 } 3220 if (possibleMatch) { 3221 if (candidate) { 3222 // our second match, meaning ambiguity 3223 error(loc, "ambiguous function signature match: multiple signatures match under implicit type conversion", call.getName().c_str(), ""); 3224 } else 3225 candidate = &function; 3226 } 3227 } 3228 3229 if (candidate == nullptr) 3230 error(loc, "no matching overloaded function found", call.getName().c_str(), ""); 3231 3232 return candidate; 3233 } 3234 3235 // 3236 // Do everything necessary to handle a typedef declaration, for a single symbol. 3237 // 3238 // 'parseType' is the type part of the declaration (to the left) 3239 // 'arraySizes' is the arrayness tagged on the identifier (to the right) 3240 // 3241 void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier, const TType& parseType, TArraySizes* arraySizes) 3242 { 3243 TType type; 3244 type.deepCopy(parseType); 3245 3246 // Arrayness is potentially coming both from the type and from the 3247 // variable: "int[] a[];" or just one or the other. 3248 // Merge it all to the type, so all arrayness is part of the type. 3249 arrayDimMerge(type, arraySizes); 3250 3251 TVariable* typeSymbol = new TVariable(&identifier, type, true); 3252 if (! symbolTable.insert(*typeSymbol)) 3253 error(loc, "name already defined", "typedef", identifier.c_str()); 3254 } 3255 3256 // 3257 // Do everything necessary to handle a variable (non-block) declaration. 3258 // Either redeclaring a variable, or making a new one, updating the symbol 3259 // table, and all error checking. 3260 // 3261 // Returns a subtree node that computes an initializer, if needed. 3262 // Returns nullptr if there is no code to execute for initialization. 3263 // 3264 // 'parseType' is the type part of the declaration (to the left) 3265 // 'arraySizes' is the arrayness tagged on the identifier (to the right) 3266 // 3267 TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TType& parseType, TArraySizes* arraySizes, TIntermTyped* initializer) 3268 { 3269 TType type; 3270 type.shallowCopy(parseType); 3271 if (type.isImplicitlySizedArray()) { 3272 // Because "int[] a = int[2](...), b = int[3](...)" makes two arrays a and b 3273 // of different sizes, for this case sharing the shallow copy of arrayness 3274 // with the parseType oversubscribes it, so get a deep copy of the arrayness. 3275 type.newArraySizes(*parseType.getArraySizes()); 3276 } 3277 3278 if (voidErrorCheck(loc, identifier, type.getBasicType())) 3279 return nullptr; 3280 3281 // Check for redeclaration of built-ins and/or attempting to declare a reserved name 3282 bool newDeclaration = false; // true if a new entry gets added to the symbol table 3283 TSymbol* symbol = nullptr; // = redeclareBuiltinVariable(loc, identifier, type.getQualifier(), parseType.shaderQualifiers, newDeclaration); 3284 3285 inheritGlobalDefaults(type.getQualifier()); 3286 3287 // Declare the variable 3288 if (arraySizes || type.isArray()) { 3289 // Arrayness is potentially coming both from the type and from the 3290 // variable: "int[] a[];" or just one or the other. 3291 // Merge it all to the type, so all arrayness is part of the type. 3292 arrayDimMerge(type, arraySizes); 3293 declareArray(loc, identifier, type, symbol, newDeclaration); 3294 } else { 3295 // non-array case 3296 if (! symbol) 3297 symbol = declareNonArray(loc, identifier, type, newDeclaration); 3298 else if (type != symbol->getType()) 3299 error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); 3300 } 3301 3302 if (! symbol) 3303 return nullptr; 3304 3305 // Deal with initializer 3306 TIntermNode* initNode = nullptr; 3307 if (symbol && initializer) { 3308 TVariable* variable = symbol->getAsVariable(); 3309 if (! variable) { 3310 error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); 3311 return nullptr; 3312 } 3313 initNode = executeInitializer(loc, initializer, variable); 3314 } 3315 3316 // see if it's a linker-level object to track 3317 if (newDeclaration && symbolTable.atGlobalLevel()) 3318 intermediate.addSymbolLinkageNode(linkage, *symbol); 3319 3320 return initNode; 3321 } 3322 3323 // Pick up global defaults from the provide global defaults into dst. 3324 void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const 3325 { 3326 if (dst.storage == EvqVaryingOut) { 3327 if (! dst.hasStream() && language == EShLangGeometry) 3328 dst.layoutStream = globalOutputDefaults.layoutStream; 3329 if (! dst.hasXfbBuffer()) 3330 dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; 3331 } 3332 } 3333 3334 // 3335 // Make an internal-only variable whose name is for debug purposes only 3336 // and won't be searched for. Callers will only use the return value to use 3337 // the variable, not the name to look it up. It is okay if the name 3338 // is the same as other names; there won't be any conflict. 3339 // 3340 TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const 3341 { 3342 TString* nameString = new TString(name); 3343 TVariable* variable = new TVariable(nameString, type); 3344 symbolTable.makeInternalVariable(*variable); 3345 3346 return variable; 3347 } 3348 3349 // 3350 // Declare a non-array variable, the main point being there is no redeclaration 3351 // for resizing allowed. 3352 // 3353 // Return the successfully declared variable. 3354 // 3355 TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, TString& identifier, TType& type, bool& newDeclaration) 3356 { 3357 // make a new variable 3358 TVariable* variable = new TVariable(&identifier, type); 3359 3360 // add variable to symbol table 3361 if (! symbolTable.insert(*variable)) { 3362 error(loc, "redefinition", variable->getName().c_str(), ""); 3363 return nullptr; 3364 } else { 3365 newDeclaration = true; 3366 return variable; 3367 } 3368 } 3369 3370 // 3371 // Handle all types of initializers from the grammar. 3372 // 3373 // Returning nullptr just means there is no code to execute to handle the 3374 // initializer, which will, for example, be the case for constant initializers. 3375 // 3376 TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) 3377 { 3378 // 3379 // Identifier must be of type constant, a global, or a temporary, and 3380 // starting at version 120, desktop allows uniforms to have initializers. 3381 // 3382 TStorageQualifier qualifier = variable->getType().getQualifier().storage; 3383 3384 // 3385 // If the initializer was from braces { ... }, we convert the whole subtree to a 3386 // constructor-style subtree, allowing the rest of the code to operate 3387 // identically for both kinds of initializers. 3388 // 3389 initializer = convertInitializerList(loc, variable->getType(), initializer); 3390 if (! initializer) { 3391 // error recovery; don't leave const without constant values 3392 if (qualifier == EvqConst) 3393 variable->getWritableType().getQualifier().storage = EvqTemporary; 3394 return nullptr; 3395 } 3396 3397 // Fix outer arrayness if variable is unsized, getting size from the initializer 3398 if (initializer->getType().isExplicitlySizedArray() && 3399 variable->getType().isImplicitlySizedArray()) 3400 variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); 3401 3402 // Inner arrayness can also get set by an initializer 3403 if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && 3404 initializer->getType().getArraySizes()->getNumDims() == 3405 variable->getType().getArraySizes()->getNumDims()) { 3406 // adopt unsized sizes from the initializer's sizes 3407 for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { 3408 if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) 3409 variable->getWritableType().getArraySizes().setDimSize(d, initializer->getType().getArraySizes()->getDimSize(d)); 3410 } 3411 } 3412 3413 // Uniform and global consts require a constant initializer 3414 if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) { 3415 error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); 3416 variable->getWritableType().getQualifier().storage = EvqTemporary; 3417 return nullptr; 3418 } 3419 if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) { 3420 error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); 3421 variable->getWritableType().getQualifier().storage = EvqTemporary; 3422 return nullptr; 3423 } 3424 3425 // Const variables require a constant initializer, depending on version 3426 if (qualifier == EvqConst) { 3427 if (initializer->getType().getQualifier().storage != EvqConst) { 3428 variable->getWritableType().getQualifier().storage = EvqConstReadOnly; 3429 qualifier = EvqConstReadOnly; 3430 } 3431 } 3432 3433 if (qualifier == EvqConst || qualifier == EvqUniform) { 3434 // Compile-time tagging of the variable with its constant value... 3435 3436 initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); 3437 if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) { 3438 error(loc, "non-matching or non-convertible constant type for const initializer", 3439 variable->getType().getStorageQualifierString(), ""); 3440 variable->getWritableType().getQualifier().storage = EvqTemporary; 3441 return nullptr; 3442 } 3443 3444 variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); 3445 } else { 3446 // normal assigning of a value to a variable... 3447 specializationCheck(loc, initializer->getType(), "initializer"); 3448 TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); 3449 TIntermNode* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc); 3450 if (! initNode) 3451 assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); 3452 3453 return initNode; 3454 } 3455 3456 return nullptr; 3457 } 3458 3459 // 3460 // Reprocess any initializer-list { ... } parts of the initializer. 3461 // Need to hierarchically assign correct types and implicit 3462 // conversions. Will do this mimicking the same process used for 3463 // creating a constructor-style initializer, ensuring we get the 3464 // same form. 3465 // 3466 TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer) 3467 { 3468 // Will operate recursively. Once a subtree is found that is constructor style, 3469 // everything below it is already good: Only the "top part" of the initializer 3470 // can be an initializer list, where "top part" can extend for several (or all) levels. 3471 3472 // see if we have bottomed out in the tree within the initializer-list part 3473 TIntermAggregate* initList = initializer->getAsAggregate(); 3474 if (! initList || initList->getOp() != EOpNull) 3475 return initializer; 3476 3477 // Of the initializer-list set of nodes, need to process bottom up, 3478 // so recurse deep, then process on the way up. 3479 3480 // Go down the tree here... 3481 if (type.isArray()) { 3482 // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. 3483 // Later on, initializer execution code will deal with array size logic. 3484 TType arrayType; 3485 arrayType.shallowCopy(type); // sharing struct stuff is fine 3486 arrayType.newArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below 3487 3488 // edit array sizes to fill in unsized dimensions 3489 arrayType.changeOuterArraySize((int)initList->getSequence().size()); 3490 TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); 3491 if (arrayType.isArrayOfArrays() && firstInit->getType().isArray() && 3492 arrayType.getArraySizes().getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { 3493 for (int d = 1; d < arrayType.getArraySizes().getNumDims(); ++d) { 3494 if (arrayType.getArraySizes().getDimSize(d) == UnsizedArraySize) 3495 arrayType.getArraySizes().setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); 3496 } 3497 } 3498 3499 TType elementType(arrayType, 0); // dereferenced type 3500 for (size_t i = 0; i < initList->getSequence().size(); ++i) { 3501 initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped()); 3502 if (initList->getSequence()[i] == nullptr) 3503 return nullptr; 3504 } 3505 3506 return addConstructor(loc, initList, arrayType, mapTypeToConstructorOp(arrayType)); 3507 } else if (type.isStruct()) { 3508 if (type.getStruct()->size() != initList->getSequence().size()) { 3509 error(loc, "wrong number of structure members", "initializer list", ""); 3510 return nullptr; 3511 } 3512 for (size_t i = 0; i < type.getStruct()->size(); ++i) { 3513 initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped()); 3514 if (initList->getSequence()[i] == nullptr) 3515 return nullptr; 3516 } 3517 } else if (type.isMatrix()) { 3518 if (type.getMatrixCols() != (int)initList->getSequence().size()) { 3519 error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); 3520 return nullptr; 3521 } 3522 TType vectorType(type, 0); // dereferenced type 3523 for (int i = 0; i < type.getMatrixCols(); ++i) { 3524 initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped()); 3525 if (initList->getSequence()[i] == nullptr) 3526 return nullptr; 3527 } 3528 } else if (type.isVector()) { 3529 if (type.getVectorSize() != (int)initList->getSequence().size()) { 3530 error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str()); 3531 return nullptr; 3532 } 3533 } else { 3534 error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); 3535 return nullptr; 3536 } 3537 3538 // now that the subtree is processed, process this node 3539 return addConstructor(loc, initList, type, mapTypeToConstructorOp(type)); 3540 } 3541 3542 // 3543 // Test for the correctness of the parameters passed to various constructor functions 3544 // and also convert them to the right data type, if allowed and required. 3545 // 3546 // Returns nullptr for an error or the constructed node (aggregate or typed) for no error. 3547 // 3548 TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type, TOperator op) 3549 { 3550 if (node == nullptr || node->getAsTyped() == nullptr) 3551 return nullptr; 3552 3553 TIntermAggregate* aggrNode = node->getAsAggregate(); 3554 3555 // Combined texture-sampler constructors are completely semantic checked 3556 // in constructorTextureSamplerError() 3557 if (op == EOpConstructTextureSampler) 3558 return intermediate.setAggregateOperator(aggrNode, op, type, loc); 3559 3560 TTypeList::const_iterator memberTypes; 3561 if (op == EOpConstructStruct) 3562 memberTypes = type.getStruct()->begin(); 3563 3564 TType elementType; 3565 if (type.isArray()) { 3566 TType dereferenced(type, 0); 3567 elementType.shallowCopy(dereferenced); 3568 } else 3569 elementType.shallowCopy(type); 3570 3571 bool singleArg; 3572 if (aggrNode) { 3573 if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1) 3574 singleArg = true; 3575 else 3576 singleArg = false; 3577 } else 3578 singleArg = true; 3579 3580 TIntermTyped *newNode; 3581 if (singleArg) { 3582 // If structure constructor or array constructor is being called 3583 // for only one parameter inside the structure, we need to call constructAggregate function once. 3584 if (type.isArray()) 3585 newNode = constructAggregate(node, elementType, 1, node->getLoc()); 3586 else if (op == EOpConstructStruct) 3587 newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); 3588 else 3589 newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false); 3590 3591 if (newNode && (type.isArray() || op == EOpConstructStruct)) 3592 newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); 3593 3594 return newNode; 3595 } 3596 3597 // 3598 // Handle list of arguments. 3599 // 3600 TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor 3601 // if the structure constructor contains more than one parameter, then construct 3602 // each parameter 3603 3604 int paramCount = 0; // keeps a track of the constructor parameter number being checked 3605 3606 // for each parameter to the constructor call, check to see if the right type is passed or convert them 3607 // to the right type if possible (and allowed). 3608 // for structure constructors, just check if the right type is passed, no conversion is allowed. 3609 3610 for (TIntermSequence::iterator p = sequenceVector.begin(); 3611 p != sequenceVector.end(); p++, paramCount++) { 3612 if (type.isArray()) 3613 newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc()); 3614 else if (op == EOpConstructStruct) 3615 newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc()); 3616 else 3617 newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); 3618 3619 if (newNode) 3620 *p = newNode; 3621 else 3622 return nullptr; 3623 } 3624 3625 TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); 3626 3627 return constructor; 3628 } 3629 3630 // Function for constructor implementation. Calls addUnaryMath with appropriate EOp value 3631 // for the parameter to the constructor (passed to this function). Essentially, it converts 3632 // the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a 3633 // float, then float is converted to int. 3634 // 3635 // Returns nullptr for an error or the constructed node. 3636 // 3637 TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, bool subset) 3638 { 3639 TIntermTyped* newNode; 3640 TOperator basicOp; 3641 3642 // 3643 // First, convert types as needed. 3644 // 3645 switch (op) { 3646 case EOpConstructVec2: 3647 case EOpConstructVec3: 3648 case EOpConstructVec4: 3649 case EOpConstructMat2x2: 3650 case EOpConstructMat2x3: 3651 case EOpConstructMat2x4: 3652 case EOpConstructMat3x2: 3653 case EOpConstructMat3x3: 3654 case EOpConstructMat3x4: 3655 case EOpConstructMat4x2: 3656 case EOpConstructMat4x3: 3657 case EOpConstructMat4x4: 3658 case EOpConstructFloat: 3659 basicOp = EOpConstructFloat; 3660 break; 3661 3662 case EOpConstructDVec2: 3663 case EOpConstructDVec3: 3664 case EOpConstructDVec4: 3665 case EOpConstructDMat2x2: 3666 case EOpConstructDMat2x3: 3667 case EOpConstructDMat2x4: 3668 case EOpConstructDMat3x2: 3669 case EOpConstructDMat3x3: 3670 case EOpConstructDMat3x4: 3671 case EOpConstructDMat4x2: 3672 case EOpConstructDMat4x3: 3673 case EOpConstructDMat4x4: 3674 case EOpConstructDouble: 3675 basicOp = EOpConstructDouble; 3676 break; 3677 3678 case EOpConstructIVec2: 3679 case EOpConstructIVec3: 3680 case EOpConstructIVec4: 3681 case EOpConstructInt: 3682 basicOp = EOpConstructInt; 3683 break; 3684 3685 case EOpConstructUVec2: 3686 case EOpConstructUVec3: 3687 case EOpConstructUVec4: 3688 case EOpConstructUint: 3689 basicOp = EOpConstructUint; 3690 break; 3691 3692 case EOpConstructBVec2: 3693 case EOpConstructBVec3: 3694 case EOpConstructBVec4: 3695 case EOpConstructBool: 3696 basicOp = EOpConstructBool; 3697 break; 3698 3699 default: 3700 error(loc, "unsupported construction", "", ""); 3701 3702 return nullptr; 3703 } 3704 newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); 3705 if (newNode == nullptr) { 3706 error(loc, "can't convert", "constructor", ""); 3707 return nullptr; 3708 } 3709 3710 // 3711 // Now, if there still isn't an operation to do the construction, and we need one, add one. 3712 // 3713 3714 // Otherwise, skip out early. 3715 if (subset || (newNode != node && newNode->getType() == type)) 3716 return newNode; 3717 3718 // setAggregateOperator will insert a new node for the constructor, as needed. 3719 return intermediate.setAggregateOperator(newNode, op, type, loc); 3720 } 3721 3722 // This function tests for the type of the parameters to the structure or array constructor. Raises 3723 // an error message if the expected type does not match the parameter passed to the constructor. 3724 // 3725 // Returns nullptr for an error or the input node itself if the expected and the given parameter types match. 3726 // 3727 TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) 3728 { 3729 TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); 3730 if (! converted || converted->getType() != type) { 3731 error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, 3732 node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); 3733 3734 return nullptr; 3735 } 3736 3737 return converted; 3738 } 3739 3740 // 3741 // Do everything needed to add an interface block. 3742 // 3743 void HlslParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, TArraySizes* arraySizes) 3744 { 3745 // fix and check for member storage qualifiers and types that don't belong within a block 3746 for (unsigned int member = 0; member < typeList.size(); ++member) { 3747 TType& memberType = *typeList[member].type; 3748 TQualifier& memberQualifier = memberType.getQualifier(); 3749 const TSourceLoc& memberLoc = typeList[member].loc; 3750 globalQualifierFix(memberLoc, memberQualifier); 3751 memberQualifier.storage = currentBlockQualifier.storage; 3752 } 3753 3754 // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will 3755 // do all the rest. 3756 if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) { 3757 redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes); 3758 return; 3759 } 3760 3761 // Make default block qualification, and adjust the member qualifications 3762 3763 TQualifier defaultQualification; 3764 switch (currentBlockQualifier.storage) { 3765 case EvqUniform: defaultQualification = globalUniformDefaults; break; 3766 case EvqBuffer: defaultQualification = globalBufferDefaults; break; 3767 case EvqVaryingIn: defaultQualification = globalInputDefaults; break; 3768 case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; 3769 default: defaultQualification.clear(); break; 3770 } 3771 3772 // Special case for "push_constant uniform", which has a default of std430, 3773 // contrary to normal uniform defaults, and can't have a default tracked for it. 3774 if (currentBlockQualifier.layoutPushConstant && ! currentBlockQualifier.hasPacking()) 3775 currentBlockQualifier.layoutPacking = ElpStd430; 3776 3777 // fix and check for member layout qualifiers 3778 3779 mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true); 3780 3781 bool memberWithLocation = false; 3782 bool memberWithoutLocation = false; 3783 for (unsigned int member = 0; member < typeList.size(); ++member) { 3784 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 3785 const TSourceLoc& memberLoc = typeList[member].loc; 3786 if (memberQualifier.hasStream()) { 3787 if (defaultQualification.layoutStream != memberQualifier.layoutStream) 3788 error(memberLoc, "member cannot contradict block", "stream", ""); 3789 } 3790 3791 // "This includes a block's inheritance of the 3792 // current global default buffer, a block member's inheritance of the block's 3793 // buffer, and the requirement that any *xfb_buffer* declared on a block 3794 // member must match the buffer inherited from the block." 3795 if (memberQualifier.hasXfbBuffer()) { 3796 if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) 3797 error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); 3798 } 3799 3800 if (memberQualifier.hasPacking()) 3801 error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), ""); 3802 if (memberQualifier.hasLocation()) { 3803 switch (currentBlockQualifier.storage) { 3804 case EvqVaryingIn: 3805 case EvqVaryingOut: 3806 memberWithLocation = true; 3807 break; 3808 default: 3809 break; 3810 } 3811 } else 3812 memberWithoutLocation = true; 3813 if (memberQualifier.hasAlign()) { 3814 if (defaultQualification.layoutPacking != ElpStd140 && defaultQualification.layoutPacking != ElpStd430) 3815 error(memberLoc, "can only be used with std140 or std430 layout packing", "align", ""); 3816 } 3817 3818 TQualifier newMemberQualification = defaultQualification; 3819 mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false); 3820 memberQualifier = newMemberQualification; 3821 } 3822 3823 // Process the members 3824 fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation); 3825 fixBlockXfbOffsets(currentBlockQualifier, typeList); 3826 fixBlockUniformOffsets(currentBlockQualifier, typeList); 3827 3828 // reverse merge, so that currentBlockQualifier now has all layout information 3829 // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) 3830 mergeObjectLayoutQualifiers(currentBlockQualifier, defaultQualification, true); 3831 3832 // 3833 // Build and add the interface block as a new type named 'blockName' 3834 // 3835 3836 TType blockType(&typeList, *blockName, currentBlockQualifier); 3837 if (arraySizes) 3838 blockType.newArraySizes(*arraySizes); 3839 3840 // 3841 // Don't make a user-defined type out of block name; that will cause an error 3842 // if the same block name gets reused in a different interface. 3843 // 3844 // "Block names have no other use within a shader 3845 // beyond interface matching; it is a compile-time error to use a block name at global scope for anything 3846 // other than as a block name (e.g., use of a block name for a global variable name or function name is 3847 // currently reserved)." 3848 // 3849 // Use the symbol table to prevent normal reuse of the block's name, as a variable entry, 3850 // whose type is EbtBlock, but without all the structure; that will come from the type 3851 // the instances point to. 3852 // 3853 TType blockNameType(EbtBlock, blockType.getQualifier().storage); 3854 TVariable* blockNameVar = new TVariable(blockName, blockNameType); 3855 if (! symbolTable.insert(*blockNameVar)) { 3856 TSymbol* existingName = symbolTable.find(*blockName); 3857 if (existingName->getType().getBasicType() == EbtBlock) { 3858 if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { 3859 error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString()); 3860 return; 3861 } 3862 } else { 3863 error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); 3864 return; 3865 } 3866 } 3867 3868 // Add the variable, as anonymous or named instanceName. 3869 // Make an anonymous variable if no name was provided. 3870 if (! instanceName) 3871 instanceName = NewPoolTString(""); 3872 3873 TVariable& variable = *new TVariable(instanceName, blockType); 3874 if (! symbolTable.insert(variable)) { 3875 if (*instanceName == "") 3876 error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), ""); 3877 else 3878 error(loc, "block instance name redefinition", variable.getName().c_str(), ""); 3879 3880 return; 3881 } 3882 3883 if (isIoResizeArray(blockType)) { 3884 ioArraySymbolResizeList.push_back(&variable); 3885 checkIoArraysConsistency(loc, true); 3886 } else 3887 fixIoArraySize(loc, variable.getWritableType()); 3888 3889 // Save it in the AST for linker use. 3890 intermediate.addSymbolLinkageNode(linkage, variable); 3891 } 3892 3893 // 3894 // "For a block, this process applies to the entire block, or until the first member 3895 // is reached that has a location layout qualifier. When a block member is declared with a location 3896 // qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level 3897 // declaration. Subsequent members are again assigned consecutive locations, based on the newest location, 3898 // until the next member declared with a location qualifier. The values used for locations do not have to be 3899 // declared in increasing order." 3900 void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) 3901 { 3902 // "If a block has no block-level location layout qualifier, it is required that either all or none of its members 3903 // have a location layout qualifier, or a compile-time error results." 3904 if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) 3905 error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); 3906 else { 3907 if (memberWithLocation) { 3908 // remove any block-level location and make it per *every* member 3909 int nextLocation = 0; // by the rule above, initial value is not relevant 3910 if (qualifier.hasAnyLocation()) { 3911 nextLocation = qualifier.layoutLocation; 3912 qualifier.layoutLocation = TQualifier::layoutLocationEnd; 3913 if (qualifier.hasComponent()) { 3914 // "It is a compile-time error to apply the *component* qualifier to a ... block" 3915 error(loc, "cannot apply to a block", "component", ""); 3916 } 3917 if (qualifier.hasIndex()) { 3918 error(loc, "cannot apply to a block", "index", ""); 3919 } 3920 } 3921 for (unsigned int member = 0; member < typeList.size(); ++member) { 3922 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 3923 const TSourceLoc& memberLoc = typeList[member].loc; 3924 if (! memberQualifier.hasLocation()) { 3925 if (nextLocation >= (int)TQualifier::layoutLocationEnd) 3926 error(memberLoc, "location is too large", "location", ""); 3927 memberQualifier.layoutLocation = nextLocation; 3928 memberQualifier.layoutComponent = 0; 3929 } 3930 nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize(*typeList[member].type); 3931 } 3932 } 3933 } 3934 } 3935 3936 void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& typeList) 3937 { 3938 // "If a block is qualified with xfb_offset, all its 3939 // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any 3940 // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer 3941 // offsets." 3942 3943 if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) 3944 return; 3945 3946 int nextOffset = qualifier.layoutXfbOffset; 3947 for (unsigned int member = 0; member < typeList.size(); ++member) { 3948 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 3949 bool containsDouble = false; 3950 int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, containsDouble); 3951 // see if we need to auto-assign an offset to this member 3952 if (! memberQualifier.hasXfbOffset()) { 3953 // "if applied to an aggregate containing a double, the offset must also be a multiple of 8" 3954 if (containsDouble) 3955 RoundToPow2(nextOffset, 8); 3956 memberQualifier.layoutXfbOffset = nextOffset; 3957 } else 3958 nextOffset = memberQualifier.layoutXfbOffset; 3959 nextOffset += memberSize; 3960 } 3961 3962 // The above gave all block members an offset, so we can take it off the block now, 3963 // which will avoid double counting the offset usage. 3964 qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; 3965 } 3966 3967 // Calculate and save the offset of each block member, using the recursively 3968 // defined block offset rules and the user-provided offset and align. 3969 // 3970 // Also, compute and save the total size of the block. For the block's size, arrayness 3971 // is not taken into account, as each element is backed by a separate buffer. 3972 // 3973 void HlslParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList) 3974 { 3975 if (! qualifier.isUniformOrBuffer()) 3976 return; 3977 if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430) 3978 return; 3979 3980 int offset = 0; 3981 int memberSize; 3982 for (unsigned int member = 0; member < typeList.size(); ++member) { 3983 TQualifier& memberQualifier = typeList[member].type->getQualifier(); 3984 const TSourceLoc& memberLoc = typeList[member].loc; 3985 3986 // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." 3987 3988 // modify just the children's view of matrix layout, if there is one for this member 3989 TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; 3990 int dummyStride; 3991 int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking == ElpStd140, 3992 subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor); 3993 if (memberQualifier.hasOffset()) { 3994 // "The specified offset must be a multiple 3995 // of the base alignment of the type of the block member it qualifies, or a compile-time error results." 3996 if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) 3997 error(memberLoc, "must be a multiple of the member's alignment", "offset", ""); 3998 3999 // "It is a compile-time error to specify an offset that is smaller than the offset of the previous 4000 // member in the block or that lies within the previous member of the block" 4001 if (memberQualifier.layoutOffset < offset) 4002 error(memberLoc, "cannot lie in previous members", "offset", ""); 4003 4004 // "The offset qualifier forces the qualified member to start at or after the specified 4005 // integral-constant expression, which will be its byte offset from the beginning of the buffer. 4006 // "The actual offset of a member is computed as 4007 // follows: If offset was declared, start with that offset, otherwise start with the next available offset." 4008 offset = std::max(offset, memberQualifier.layoutOffset); 4009 } 4010 4011 // "The actual alignment of a member will be the greater of the specified align alignment and the standard 4012 // (e.g., std140) base alignment for the member's type." 4013 if (memberQualifier.hasAlign()) 4014 memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); 4015 4016 // "If the resulting offset is not a multiple of the actual alignment, 4017 // increase it to the first offset that is a multiple of 4018 // the actual alignment." 4019 RoundToPow2(offset, memberAlignment); 4020 typeList[member].type->getQualifier().layoutOffset = offset; 4021 offset += memberSize; 4022 } 4023 } 4024 4025 // For an identifier that is already declared, add more qualification to it. 4026 void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) 4027 { 4028 TSymbol* symbol = symbolTable.find(identifier); 4029 if (! symbol) { 4030 error(loc, "identifier not previously declared", identifier.c_str(), ""); 4031 return; 4032 } 4033 if (symbol->getAsFunction()) { 4034 error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); 4035 return; 4036 } 4037 4038 if (qualifier.isAuxiliary() || 4039 qualifier.isMemory() || 4040 qualifier.isInterpolation() || 4041 qualifier.hasLayout() || 4042 qualifier.storage != EvqTemporary || 4043 qualifier.precision != EpqNone) { 4044 error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); 4045 return; 4046 } 4047 4048 // For read-only built-ins, add a new symbol for holding the modified qualifier. 4049 // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) 4050 if (symbol->isReadOnly()) 4051 symbol = symbolTable.copyUp(symbol); 4052 4053 if (qualifier.invariant) { 4054 if (intermediate.inIoAccessed(identifier)) 4055 error(loc, "cannot change qualification after use", "invariant", ""); 4056 symbol->getWritableType().getQualifier().invariant = true; 4057 } else if (qualifier.noContraction) { 4058 if (intermediate.inIoAccessed(identifier)) 4059 error(loc, "cannot change qualification after use", "precise", ""); 4060 symbol->getWritableType().getQualifier().noContraction = true; 4061 } else if (qualifier.specConstant) { 4062 symbol->getWritableType().getQualifier().makeSpecConstant(); 4063 if (qualifier.hasSpecConstantId()) 4064 symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; 4065 } else 4066 warn(loc, "unknown requalification", "", ""); 4067 } 4068 4069 void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) 4070 { 4071 for (unsigned int i = 0; i < identifiers.size(); ++i) 4072 addQualifierToExisting(loc, qualifier, *identifiers[i]); 4073 } 4074 4075 // 4076 // Updating default qualifier for the case of a declaration with just a qualifier, 4077 // no type, block, or identifier. 4078 // 4079 void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) 4080 { 4081 if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { 4082 assert(language == EShLangTessControl || language == EShLangGeometry); 4083 // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; 4084 4085 if (language == EShLangTessControl) 4086 checkIoArraysConsistency(loc); 4087 } 4088 if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { 4089 if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) 4090 error(loc, "cannot change previously set layout value", "invocations", ""); 4091 } 4092 if (publicType.shaderQualifiers.geometry != ElgNone) { 4093 if (publicType.qualifier.storage == EvqVaryingIn) { 4094 switch (publicType.shaderQualifiers.geometry) { 4095 case ElgPoints: 4096 case ElgLines: 4097 case ElgLinesAdjacency: 4098 case ElgTriangles: 4099 case ElgTrianglesAdjacency: 4100 case ElgQuads: 4101 case ElgIsolines: 4102 if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { 4103 if (language == EShLangGeometry) 4104 checkIoArraysConsistency(loc); 4105 } else 4106 error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); 4107 break; 4108 default: 4109 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); 4110 } 4111 } else if (publicType.qualifier.storage == EvqVaryingOut) { 4112 switch (publicType.shaderQualifiers.geometry) { 4113 case ElgPoints: 4114 case ElgLineStrip: 4115 case ElgTriangleStrip: 4116 if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry)) 4117 error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); 4118 break; 4119 default: 4120 error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); 4121 } 4122 } else 4123 error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage)); 4124 } 4125 if (publicType.shaderQualifiers.spacing != EvsNone) 4126 intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing); 4127 if (publicType.shaderQualifiers.order != EvoNone) 4128 intermediate.setVertexOrder(publicType.shaderQualifiers.order); 4129 if (publicType.shaderQualifiers.pointMode) 4130 intermediate.setPointMode(); 4131 for (int i = 0; i < 3; ++i) { 4132 if (publicType.shaderQualifiers.localSize[i] > 1) { 4133 int max = 0; 4134 switch (i) { 4135 case 0: max = resources.maxComputeWorkGroupSizeX; break; 4136 case 1: max = resources.maxComputeWorkGroupSizeY; break; 4137 case 2: max = resources.maxComputeWorkGroupSizeZ; break; 4138 default: break; 4139 } 4140 if (intermediate.getLocalSize(i) > (unsigned int)max) 4141 error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); 4142 4143 // Fix the existing constant gl_WorkGroupSize with this new information. 4144 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); 4145 workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); 4146 } 4147 if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { 4148 intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]); 4149 // Set the workgroup built-in variable as a specialization constant 4150 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); 4151 workGroupSize->getWritableType().getQualifier().specConstant = true; 4152 } 4153 } 4154 if (publicType.shaderQualifiers.earlyFragmentTests) 4155 intermediate.setEarlyFragmentTests(); 4156 4157 const TQualifier& qualifier = publicType.qualifier; 4158 4159 switch (qualifier.storage) { 4160 case EvqUniform: 4161 if (qualifier.hasMatrix()) 4162 globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; 4163 if (qualifier.hasPacking()) 4164 globalUniformDefaults.layoutPacking = qualifier.layoutPacking; 4165 break; 4166 case EvqBuffer: 4167 if (qualifier.hasMatrix()) 4168 globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; 4169 if (qualifier.hasPacking()) 4170 globalBufferDefaults.layoutPacking = qualifier.layoutPacking; 4171 break; 4172 case EvqVaryingIn: 4173 break; 4174 case EvqVaryingOut: 4175 if (qualifier.hasStream()) 4176 globalOutputDefaults.layoutStream = qualifier.layoutStream; 4177 if (qualifier.hasXfbBuffer()) 4178 globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; 4179 if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { 4180 if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) 4181 error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); 4182 } 4183 break; 4184 default: 4185 error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); 4186 return; 4187 } 4188 } 4189 4190 // 4191 // Take the sequence of statements that has been built up since the last case/default, 4192 // put it on the list of top-level nodes for the current (inner-most) switch statement, 4193 // and follow that by the case/default we are on now. (See switch topology comment on 4194 // TIntermSwitch.) 4195 // 4196 void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) 4197 { 4198 TIntermSequence* switchSequence = switchSequenceStack.back(); 4199 4200 if (statements) { 4201 statements->setOperator(EOpSequence); 4202 switchSequence->push_back(statements); 4203 } 4204 if (branchNode) { 4205 // check all previous cases for the same label (or both are 'default') 4206 for (unsigned int s = 0; s < switchSequence->size(); ++s) { 4207 TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); 4208 if (prevBranch) { 4209 TIntermTyped* prevExpression = prevBranch->getExpression(); 4210 TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); 4211 if (prevExpression == nullptr && newExpression == nullptr) 4212 error(branchNode->getLoc(), "duplicate label", "default", ""); 4213 else if (prevExpression != nullptr && 4214 newExpression != nullptr && 4215 prevExpression->getAsConstantUnion() && 4216 newExpression->getAsConstantUnion() && 4217 prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == 4218 newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) 4219 error(branchNode->getLoc(), "duplicated value", "case", ""); 4220 } 4221 } 4222 switchSequence->push_back(branchNode); 4223 } 4224 } 4225 4226 // 4227 // Turn the top-level node sequence built up of wrapupSwitchSubsequence 4228 // into a switch node. 4229 // 4230 TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements) 4231 { 4232 wrapupSwitchSubsequence(lastStatements, nullptr); 4233 4234 if (expression == nullptr || 4235 (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || 4236 expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) 4237 error(loc, "condition must be a scalar integer expression", "switch", ""); 4238 4239 // If there is nothing to do, drop the switch but still execute the expression 4240 TIntermSequence* switchSequence = switchSequenceStack.back(); 4241 if (switchSequence->size() == 0) 4242 return expression; 4243 4244 if (lastStatements == nullptr) { 4245 // emulate a break for error recovery 4246 lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); 4247 lastStatements->setOperator(EOpSequence); 4248 switchSequence->push_back(lastStatements); 4249 } 4250 4251 TIntermAggregate* body = new TIntermAggregate(EOpSequence); 4252 body->getSequence() = *switchSequenceStack.back(); 4253 body->setLoc(loc); 4254 4255 TIntermSwitch* switchNode = new TIntermSwitch(expression, body); 4256 switchNode->setLoc(loc); 4257 4258 return switchNode; 4259 } 4260 4261 } // end namespace glslang 4262