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