Coverage Report

Created: 2025-10-10 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/glslang/HLSL/hlslParseHelper.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2017-2018 Google, Inc.
3
// Copyright (C) 2017 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 "../Include/Common.h"
43
#include "../MachineIndependent/Scan.h"
44
#include "../MachineIndependent/preprocessor/PpContext.h"
45
46
#include <algorithm>
47
#include <functional>
48
#include <cctype>
49
#include <array>
50
#include <set>
51
52
namespace glslang {
53
54
HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
55
                                   int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
56
                                   TInfoSink& infoSink,
57
                                   const TString sourceEntryPointName,
58
                                   bool forwardCompatible, EShMessages messages) :
59
31.6k
    TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink,
60
31.6k
                      forwardCompatible, messages, &sourceEntryPointName),
61
31.6k
    annotationNestingLevel(0),
62
31.6k
    inputPatch(nullptr),
63
31.6k
    nextInLocation(0), nextOutLocation(0),
64
31.6k
    entryPointFunction(nullptr),
65
31.6k
    entryPointFunctionBody(nullptr),
66
31.6k
    gsStreamOutput(nullptr),
67
31.6k
    clipDistanceOutput(nullptr),
68
31.6k
    cullDistanceOutput(nullptr),
69
31.6k
    clipDistanceInput(nullptr),
70
31.6k
    cullDistanceInput(nullptr),
71
31.6k
    parsingEntrypointParameters(false)
72
31.6k
{
73
31.6k
    globalUniformDefaults.clear();
74
31.6k
    globalUniformDefaults.layoutMatrix = ElmRowMajor;
75
31.6k
    globalUniformDefaults.layoutPacking = ElpStd140;
76
77
31.6k
    globalBufferDefaults.clear();
78
31.6k
    globalBufferDefaults.layoutMatrix = ElmRowMajor;
79
31.6k
    globalBufferDefaults.layoutPacking = ElpStd430;
80
81
31.6k
    globalInputDefaults.clear();
82
31.6k
    globalOutputDefaults.clear();
83
84
31.6k
    clipSemanticNSizeIn.fill(0);
85
31.6k
    cullSemanticNSizeIn.fill(0);
86
31.6k
    clipSemanticNSizeOut.fill(0);
87
31.6k
    cullSemanticNSizeOut.fill(0);
88
89
    // "Shaders in the transform
90
    // feedback capturing mode have an initial global default of
91
    //     layout(xfb_buffer = 0) out;"
92
31.6k
    if (language == EShLangVertex ||
93
24.0k
        language == EShLangTessControl ||
94
20.9k
        language == EShLangTessEvaluation ||
95
19.1k
        language == EShLangGeometry)
96
14.2k
        globalOutputDefaults.layoutXfbBuffer = 0;
97
98
31.6k
    if (language == EShLangGeometry)
99
1.72k
        globalOutputDefaults.layoutStream = 0;
100
31.6k
}
101
102
HlslParseContext::~HlslParseContext()
103
31.6k
{
104
31.6k
}
105
106
void HlslParseContext::initializeExtensionBehavior()
107
4.35k
{
108
4.35k
    TParseContextBase::initializeExtensionBehavior();
109
110
    // HLSL allows #line by default.
111
4.35k
    extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
112
4.35k
}
113
114
void HlslParseContext::setLimits(const TBuiltInResource& r)
115
4.35k
{
116
4.35k
    resources = r;
117
4.35k
    intermediate.setLimits(resources);
118
4.35k
}
119
120
//
121
// Parse an array of strings using the parser in HlslRules.
122
//
123
// Returns true for successful acceptance of the shader, false if any errors.
124
//
125
bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
126
3.73k
{
127
3.73k
    currentScanner = &input;
128
3.73k
    ppContext.setInput(input, versionWillBeError);
129
130
3.73k
    HlslScanContext scanContext(*this, ppContext);
131
3.73k
    HlslGrammar grammar(scanContext, *this);
132
3.73k
    if (!grammar.parse()) {
133
        // Print a message formated such that if you click on the message it will take you right to
134
        // the line through most UIs.
135
1.97k
        const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
136
1.97k
        infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column
137
1.97k
                      << ", HLSL parsing failed.\n";
138
1.97k
        ++numErrors;
139
1.97k
        return false;
140
1.97k
    }
141
142
1.75k
    finish();
143
144
1.75k
    return numErrors == 0;
145
3.73k
}
146
147
//
148
// Return true if this l-value node should be converted in some manner.
149
// For instance: turning a load aggregate into a store in an l-value.
150
//
151
bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
152
2.14k
{
153
2.14k
    if (node == nullptr || node->getAsTyped() == nullptr)
154
8
        return false;
155
156
2.13k
    const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
157
2.13k
    const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
158
159
    // If it's a swizzled/indexed aggregate, look at the left node instead.
160
2.13k
    if (lhsAsBinary != nullptr &&
161
470
        (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
162
210
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
163
2.13k
    if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
164
164
        return true;
165
166
1.97k
    return false;
167
2.13k
}
168
169
void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName,
170
                                              TTypeList* newTypeList)
171
2.36k
{
172
2.36k
    newTypeList = nullptr;
173
2.36k
    correctUniform(memberType.getQualifier());
174
2.36k
    if (memberType.isStruct()) {
175
36
        auto it = ioTypeMap.find(memberType.getStruct());
176
36
        if (it != ioTypeMap.end() && it->second.uniform)
177
0
            newTypeList = it->second.uniform;
178
36
    }
179
2.36k
    TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
180
2.36k
}
181
182
//
183
// Return a TLayoutFormat corresponding to the given texture type.
184
//
185
TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
186
87.6k
{
187
87.6k
    if (txType.isStruct()) {
188
        // TODO: implement.
189
0
        error(loc, "unimplemented: structure type in image or buffer", "", "");
190
0
        return ElfNone;
191
0
    }
192
193
87.6k
    const int components = txType.getVectorSize();
194
87.6k
    const TBasicType txBasicType = txType.getBasicType();
195
196
87.6k
    const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
197
87.6k
        if (intermediate.getNoStorageFormat())
198
0
            return ElfNone;
199
200
87.6k
        return components == 1 ? v1 :
201
87.6k
               components == 2 ? v2 : v4;
202
87.6k
    };
203
204
87.6k
    switch (txBasicType) {
205
29.4k
    case EbtFloat: return selectFormat(ElfR32f,  ElfRg32f,  ElfRgba32f);
206
29.1k
    case EbtInt:   return selectFormat(ElfR32i,  ElfRg32i,  ElfRgba32i);
207
29.1k
    case EbtUint:  return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
208
0
    default:
209
0
        error(loc, "unknown basic type in image format", "", "");
210
0
        return ElfNone;
211
87.6k
    }
212
87.6k
}
213
214
//
215
// Both test and if necessary, spit out an error, to see if the node is really
216
// an l-value that can be operated on this way.
217
//
218
// Returns true if there was an error.
219
//
220
bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
221
1.10k
{
222
1.10k
    if (shouldConvertLValue(node)) {
223
        // if we're writing to a texture, it must be an RW form.
224
225
0
        TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
226
0
        TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
227
228
0
        if (!object->getType().getSampler().isImage()) {
229
0
            error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
230
0
            return true;
231
0
        }
232
0
    }
233
234
    // We tolerate samplers as l-values, even though they are nominally
235
    // illegal, because we expect a later optimization to eliminate them.
236
1.10k
    if (node->getType().getBasicType() == EbtSampler) {
237
2
        intermediate.setNeedsLegalization();
238
2
        return false;
239
2
    }
240
241
    // Let the base class check errors
242
1.09k
    return TParseContextBase::lValueErrorCheck(loc, op, node);
243
1.10k
}
244
245
//
246
// This function handles l-value conversions and verifications.  It uses, but is not synonymous
247
// with lValueErrorCheck.  That function accepts an l-value directly, while this one must be
248
// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
249
// series of other image operations.
250
//
251
// Most things are passed through unmodified, except for error checking.
252
//
253
TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
254
1.10k
{
255
1.10k
    if (node == nullptr)
256
100
        return nullptr;
257
258
1.00k
    TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
259
1.00k
    TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
260
1.00k
    TIntermAggregate* sequence = nullptr;
261
262
1.00k
    TIntermTyped* lhs = nodeAsUnary  ? nodeAsUnary->getOperand() :
263
1.00k
                        nodeAsBinary ? nodeAsBinary->getLeft() :
264
846
                        nullptr;
265
266
    // Early bail out if there is no conversion to apply
267
1.00k
    if (!shouldConvertLValue(lhs)) {
268
864
        if (lhs != nullptr)
269
856
            if (lValueErrorCheck(loc, op, lhs))
270
18
                return nullptr;
271
846
        return node;
272
864
    }
273
274
    // *** If we get here, we're going to apply some conversion to an l-value.
275
276
    // Helper to create a load.
277
140
    const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
278
42
        TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
279
42
        loadOp->setLoc(loc);
280
42
        loadOp->getSequence().push_back(object);
281
42
        loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
282
42
        loadOp->setType(derefType);
283
284
42
        sequence = intermediate.growAggregate(sequence,
285
42
                                              intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
286
42
                                              loc);
287
42
    };
288
289
    // Helper to create a store.
290
140
    const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
291
140
        TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
292
140
        storeOp->getSequence().push_back(object);
293
140
        storeOp->getSequence().push_back(coord);
294
140
        storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
295
140
        storeOp->setLoc(loc);
296
140
        storeOp->setType(TType(EbtVoid));
297
298
140
        sequence = intermediate.growAggregate(sequence, storeOp);
299
140
    };
300
301
    // Helper to create an assign.
302
154
    const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
303
154
        sequence = intermediate.growAggregate(sequence,
304
154
                                              intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
305
154
                                              loc);
306
154
    };
307
308
    // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
309
140
    const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
310
        // Add a trailing use of the temp, so the sequence returns the proper value.
311
140
        sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
312
140
        sequence->setOperator(EOpSequence);
313
140
        sequence->setLoc(loc);
314
140
        sequence->setType(derefType);
315
316
140
        return sequence;
317
140
    };
318
319
    // Helper to add unary op
320
140
    const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
321
0
        sequence = intermediate.growAggregate(sequence,
322
0
                                              intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
323
0
                                                                        rhsTmp->getType()),
324
0
                                              loc);
325
0
    };
326
327
    // Return true if swizzle or index writes all components of the given variable.
328
140
    const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
329
112
        if (swizzle == nullptr)  // not a swizzle or index
330
90
            return true;
331
332
        // Track which components are being set.
333
22
        std::array<bool, 4> compIsSet;
334
22
        compIsSet.fill(false);
335
336
22
        const TIntermConstantUnion* asConst     = swizzle->getRight()->getAsConstantUnion();
337
22
        const TIntermAggregate*     asAggregate = swizzle->getRight()->getAsAggregate();
338
339
        // This could be either a direct index, or a swizzle.
340
22
        if (asConst) {
341
0
            compIsSet[asConst->getConstArray()[0].getIConst()] = true;
342
22
        } else if (asAggregate) {
343
22
            const TIntermSequence& seq = asAggregate->getSequence();
344
88
            for (int comp=0; comp<int(seq.size()); ++comp)
345
66
                compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
346
22
        } else {
347
0
            assert(0);
348
0
        }
349
350
        // Return true if all components are being set by the index or swizzle
351
22
        return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
352
66
                           [](bool isSet) { return isSet; } );
353
112
    };
354
355
    // Create swizzle matching input swizzle
356
140
    const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
357
112
        if (swizzle)
358
22
            return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
359
90
        else
360
90
            return var;
361
112
    };
362
363
140
    TIntermBinary*    lhsAsBinary    = lhs->getAsBinaryNode();
364
140
    TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
365
140
    bool lhsIsSwizzle = false;
366
367
    // If it's a swizzled L-value, remember the swizzle, and use the LHS.
368
140
    if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
369
22
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
370
22
        lhsIsSwizzle = true;
371
22
    }
372
373
140
    TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
374
140
    TIntermTyped* coord  = lhsAsAggregate->getSequence()[1]->getAsTyped();
375
376
140
    const TSampler& texSampler = object->getType().getSampler();
377
378
140
    TType objDerefType;
379
140
    getTextureReturnType(texSampler, objDerefType);
380
381
140
    if (nodeAsBinary) {
382
140
        TIntermTyped* rhs = nodeAsBinary->getRight();
383
140
        const TOperator assignOp = nodeAsBinary->getOp();
384
385
140
        bool isModifyOp = false;
386
387
140
        switch (assignOp) {
388
6
        case EOpAddAssign:
389
12
        case EOpSubAssign:
390
12
        case EOpMulAssign:
391
12
        case EOpVectorTimesMatrixAssign:
392
18
        case EOpVectorTimesScalarAssign:
393
18
        case EOpMatrixTimesScalarAssign:
394
18
        case EOpMatrixTimesMatrixAssign:
395
22
        case EOpDivAssign:
396
26
        case EOpModAssign:
397
30
        case EOpAndAssign:
398
34
        case EOpInclusiveOrAssign:
399
34
        case EOpExclusiveOrAssign:
400
38
        case EOpLeftShiftAssign:
401
42
        case EOpRightShiftAssign:
402
42
            isModifyOp = true;
403
42
            [[fallthrough]];
404
140
        case EOpAssign:
405
140
            {
406
                // Since this is an lvalue, we'll convert an image load to a sequence like this
407
                // (to still provide the value):
408
                //   OpSequence
409
                //      OpImageStore(object, lhs, rhs)
410
                //      rhs
411
                // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS,
412
                // so we'll convert instead to this:
413
                //   OpSequence
414
                //      rhsTmp = rhs
415
                //      OpImageStore(object, coord, rhsTmp)
416
                //      rhsTmp
417
                // If this is a read-modify-write op, like +=, we issue:
418
                //   OpSequence
419
                //      coordtmp = load's param1
420
                //      rhsTmp = OpImageLoad(object, coordTmp)
421
                //      rhsTmp op= rhs
422
                //      OpImageStore(object, coordTmp, rhsTmp)
423
                //      rhsTmp
424
                //
425
                // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
426
                //    ...
427
                //    rhsTmp.some_swizzle = ...
428
                // For partial writes, an error is generated.
429
430
140
                TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
431
140
                TIntermTyped* coordTmp = coord;
432
433
140
                if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
434
112
                    rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
435
436
                    // Partial updates not yet supported
437
112
                    if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
438
0
                        error(loc, "unimplemented: partial image updates", "", "");
439
0
                    }
440
441
                    // Assign storeTemp = rhs
442
112
                    if (isModifyOp) {
443
                        // We have to make a temp var for the coordinate, to avoid evaluating it twice.
444
42
                        coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
445
42
                        makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
446
42
                        makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
447
42
                    }
448
449
                    // rhsTmp op= rhs.
450
112
                    makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
451
112
                }
452
453
140
                makeStore(object, coordTmp, rhsTmp);         // add a store
454
140
                return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
455
42
            }
456
457
0
        default:
458
0
            break;
459
140
        }
460
140
    }
461
462
0
    if (nodeAsUnary) {
463
0
        const TOperator assignOp = nodeAsUnary->getOp();
464
465
0
        switch (assignOp) {
466
0
        case EOpPreIncrement:
467
0
        case EOpPreDecrement:
468
0
            {
469
                // We turn this into:
470
                //   OpSequence
471
                //      coordtmp = load's param1
472
                //      rhsTmp = OpImageLoad(object, coordTmp)
473
                //      rhsTmp op
474
                //      OpImageStore(object, coordTmp, rhsTmp)
475
                //      rhsTmp
476
477
0
                TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
478
0
                TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
479
480
0
                makeBinary(EOpAssign, coordTmp, coord);           // coordtmp = load[param1]
481
0
                makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
482
0
                makeUnary(assignOp, rhsTmp);                      // op rhsTmp
483
0
                makeStore(object, coordTmp, rhsTmp);              // OpImageStore(object, coordTmp, rhsTmp)
484
0
                return finishSequence(rhsTmp, objDerefType);      // return rhsTmp from sequence
485
0
            }
486
487
0
        case EOpPostIncrement:
488
0
        case EOpPostDecrement:
489
0
            {
490
                // We turn this into:
491
                //   OpSequence
492
                //      coordtmp = load's param1
493
                //      rhsTmp1 = OpImageLoad(object, coordTmp)
494
                //      rhsTmp2 = rhsTmp1
495
                //      rhsTmp2 op
496
                //      OpImageStore(object, coordTmp, rhsTmp2)
497
                //      rhsTmp1 (pre-op value)
498
0
                TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre",  objDerefType);
499
0
                TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
500
0
                TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
501
502
0
                makeBinary(EOpAssign, coordTmp, coord);            // coordtmp = load[param1]
503
0
                makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
504
0
                makeBinary(EOpAssign, rhsTmp2, rhsTmp1);           // rhsTmp2 = rhsTmp1
505
0
                makeUnary(assignOp, rhsTmp2);                      // rhsTmp op
506
0
                makeStore(object, coordTmp, rhsTmp2);              // OpImageStore(object, coordTmp, rhsTmp2)
507
0
                return finishSequence(rhsTmp1, objDerefType);      // return rhsTmp from sequence
508
0
            }
509
510
0
        default:
511
0
            break;
512
0
        }
513
0
    }
514
515
0
    if (lhs)
516
0
        if (lValueErrorCheck(loc, op, lhs))
517
0
            return nullptr;
518
519
0
    return node;
520
0
}
521
522
void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
523
196
{
524
196
    if (pragmaCallback)
525
140
        pragmaCallback(loc.line, tokens);
526
527
196
    if (tokens.size() == 0)
528
12
        return;
529
530
    // These pragmas are case insensitive in HLSL, so we'll compare in lower case.
531
184
    TVector<TString> lowerTokens = tokens;
532
533
2.45k
    for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it)
534
2.26k
        std::transform(it->begin(), it->end(), it->begin(), ::tolower);
535
536
    // Handle pack_matrix
537
184
    if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") {
538
        // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense.
539
        // Row major becomes column major and vice versa.
540
541
56
        if (lowerTokens[2] == "row_major") {
542
21
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor;
543
35
        } else if (lowerTokens[2] == "column_major") {
544
13
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
545
22
        } else {
546
            // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major)
547
22
            warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), "");
548
22
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
549
22
        }
550
56
        return;
551
56
    }
552
553
    // Handle once
554
128
    if (lowerTokens[0] == "once") {
555
0
        warn(loc, "not implemented", "#pragma once", "");
556
0
        return;
557
0
    }
558
128
}
559
560
//
561
// Look at a '.' matrix selector string and change it into components
562
// for a matrix. There are two types:
563
//
564
//   _21    second row, first column (one based)
565
//   _m21   third row, second column (zero based)
566
//
567
// Returns true if there is no error.
568
//
569
bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
570
                                                  TSwizzleSelectors<TMatrixSelector>& components)
571
10
{
572
10
    int startPos[MaxSwizzleSelectors];
573
10
    int numComps = 0;
574
10
    TString compString = fields;
575
576
    // Find where each component starts,
577
    // recording the first character position after the '_'.
578
54
    for (size_t c = 0; c < compString.size(); ++c) {
579
44
        if (compString[c] == '_') {
580
0
            if (numComps >= MaxSwizzleSelectors) {
581
0
                error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
582
0
                return false;
583
0
            }
584
0
            if (c > compString.size() - 3 ||
585
0
                    ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) {
586
0
                error(loc, "matrix component swizzle missing", compString.c_str(), "");
587
0
                return false;
588
0
            }
589
0
            startPos[numComps++] = (int)c + 1;
590
0
        }
591
44
    }
592
593
    // Process each component
594
10
    for (int i = 0; i < numComps; ++i) {
595
0
        int pos = startPos[i];
596
0
        int bias = -1;
597
0
        if (compString[pos] == 'm' || compString[pos] == 'M') {
598
0
            bias = 0;
599
0
            ++pos;
600
0
        }
601
0
        TMatrixSelector comp;
602
0
        comp.coord1 = compString[pos+0] - '0' + bias;
603
0
        comp.coord2 = compString[pos+1] - '0' + bias;
604
0
        if (comp.coord1 < 0 || comp.coord1 >= cols) {
605
0
            error(loc, "matrix row component out of range", compString.c_str(), "");
606
0
            return false;
607
0
        }
608
0
        if (comp.coord2 < 0 || comp.coord2 >= rows) {
609
0
            error(loc, "matrix column component out of range", compString.c_str(), "");
610
0
            return false;
611
0
        }
612
0
        components.push_back(comp);
613
0
    }
614
615
10
    return true;
616
10
}
617
618
// If the 'comps' express a column of a matrix,
619
// return the column.  Column means the first coords all match.
620
//
621
// Otherwise, return -1.
622
//
623
int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
624
10
{
625
10
    int col = -1;
626
627
    // right number of comps?
628
10
    if (selector.size() != rows)
629
10
        return -1;
630
631
    // all comps in the same column?
632
    // rows in order?
633
0
    col = selector[0].coord1;
634
0
    for (int i = 0; i < rows; ++i) {
635
0
        if (col != selector[i].coord1)
636
0
            return -1;
637
0
        if (i != selector[i].coord2)
638
0
            return -1;
639
0
    }
640
641
0
    return col;
642
0
}
643
644
//
645
// Handle seeing a variable identifier in the grammar.
646
//
647
TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string)
648
22.2k
{
649
22.2k
    int thisDepth;
650
22.2k
    TSymbol* symbol = symbolTable.find(*string, thisDepth);
651
22.2k
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
652
2
        error(loc, "expected symbol, not user-defined type", string->c_str(), "");
653
2
        return nullptr;
654
2
    }
655
656
22.2k
    const TVariable* variable = nullptr;
657
22.2k
    const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
658
22.2k
    TIntermTyped* node = nullptr;
659
22.2k
    if (anon) {
660
        // It was a member of an anonymous container, which could be a 'this' structure.
661
662
        // Create a subtree for its dereference.
663
1.86k
        if (thisDepth > 0) {
664
2
            variable = getImplicitThis(thisDepth);
665
2
            if (variable == nullptr)
666
0
                error(loc, "cannot access member variables (static member function?)", "this", "");
667
2
        }
668
1.86k
        if (variable == nullptr)
669
1.86k
            variable = anon->getAnonContainer().getAsVariable();
670
671
1.86k
        TIntermTyped* container = intermediate.addSymbol(*variable, loc);
672
1.86k
        TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
673
1.86k
        node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
674
675
1.86k
        node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
676
1.86k
        if (node->getType().hiddenMember())
677
18
            error(loc, "member of nameless block was not redeclared", string->c_str(), "");
678
20.3k
    } else {
679
        // Not a member of an anonymous container.
680
681
        // The symbol table search was done in the lexical phase.
682
        // See if it was a variable.
683
20.3k
        variable = symbol ? symbol->getAsVariable() : nullptr;
684
20.3k
        if (variable) {
685
18.0k
            if ((variable->getType().getBasicType() == EbtBlock ||
686
17.5k
                variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
687
0
                error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
688
0
                variable = nullptr;
689
0
            }
690
18.0k
        } else {
691
2.35k
            if (symbol)
692
0
                error(loc, "variable name expected", string->c_str(), "");
693
2.35k
        }
694
695
        // Recovery, if it wasn't found or was not a variable.
696
20.3k
        if (variable == nullptr) {
697
2.35k
            error(loc, "unknown variable", string->c_str(), "");
698
2.35k
            variable = new TVariable(string, TType(EbtVoid));
699
2.35k
        }
700
701
20.3k
        if (variable->getType().getQualifier().isFrontEndConstant())
702
634
            node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
703
19.7k
        else
704
19.7k
            node = intermediate.addSymbol(*variable, loc);
705
20.3k
    }
706
707
22.2k
    if (variable->getType().getQualifier().isIo())
708
4.01k
        intermediate.addIoAccessed(*string);
709
710
22.2k
    return node;
711
22.2k
}
712
713
//
714
// Handle operator[] on any objects it applies to.  Currently:
715
//    Textures
716
//    Buffers
717
//
718
TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
719
734
{
720
    // handle r-value operator[] on textures and images.  l-values will be processed later.
721
734
    if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
722
302
        const TSampler& sampler = base->getType().getSampler();
723
302
        if (sampler.isImage() || sampler.isTexture()) {
724
302
            if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) {
725
                // The first operator[] to a .mips[] sequence is the mip level.  We'll remember it.
726
34
                mipsOperatorMipArg.back().mipLevel = index;
727
34
                return base;  // next [] index is to the same base.
728
268
            } else {
729
268
                TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
730
731
268
                TType sampReturnType;
732
268
                getTextureReturnType(sampler, sampReturnType);
733
734
268
                load->setType(sampReturnType);
735
268
                load->setLoc(loc);
736
268
                load->getSequence().push_back(base);
737
268
                load->getSequence().push_back(index);
738
739
                // Textures need a MIP.  If we saw one go by, use it.  Otherwise, use zero.
740
268
                if (sampler.isTexture()) {
741
30
                    if (! mipsOperatorMipArg.empty()) {
742
24
                        load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel);
743
24
                        mipsOperatorMipArg.pop_back();
744
24
                    } else {
745
6
                        load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
746
6
                    }
747
30
                }
748
749
268
                return load;
750
268
            }
751
302
        }
752
302
    }
753
754
    // Handle operator[] on structured buffers: this indexes into the array element of the buffer.
755
    // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
756
432
    TIntermTyped* sbArray = indexStructBufferContent(loc, base);
757
432
    if (sbArray != nullptr) {
758
        // Now we'll apply the [] index to that array
759
64
        const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
760
761
64
        TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
762
64
        const TType derefType(sbArray->getType(), 0);
763
64
        element->setType(derefType);
764
64
        return element;
765
64
    }
766
767
368
    return nullptr;
768
432
}
769
770
//
771
// Cast index value to a uint if it isn't already (for operator[], load indexes, etc)
772
TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index)
773
994
{
774
994
    const TBasicType indexBasicType = index->getType().getBasicType();
775
994
    const int vecSize = index->getType().getVectorSize();
776
777
    // We can use int types directly as the index
778
994
    if (indexBasicType == EbtInt || indexBasicType == EbtUint ||
779
34
        indexBasicType == EbtInt64 || indexBasicType == EbtUint64)
780
960
        return index;
781
782
    // Cast index to unsigned integer if it isn't one.
783
34
    return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index);
784
994
}
785
786
//
787
// Handle seeing a base[index] dereference in the grammar.
788
//
789
TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
790
740
{
791
740
    index = makeIntegerIndex(index);
792
793
740
    if (index == nullptr) {
794
6
        error(loc, " unknown index type ", "", "");
795
6
        return nullptr;
796
6
    }
797
798
734
    TIntermTyped* result = handleBracketOperator(loc, base, index);
799
800
734
    if (result != nullptr)
801
366
        return result;  // it was handled as an operator[]
802
803
368
    bool flattened = false;
804
368
    int indexValue = 0;
805
368
    if (index->getQualifier().isFrontEndConstant())
806
254
        indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
807
808
368
    variableCheck(base);
809
368
    if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
810
110
        if (base->getAsSymbolNode())
811
82
            error(loc, " left of '[' is not of type array, matrix, or vector ",
812
82
                  base->getAsSymbolNode()->getName().c_str(), "");
813
28
        else
814
28
            error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
815
258
    } else if (base->getType().getQualifier().isFrontEndConstant() &&
816
30
               index->getQualifier().isFrontEndConstant()) {
817
        // both base and index are front-end constants
818
24
        checkIndex(loc, base->getType(), indexValue);
819
24
        return intermediate.foldDereference(base, indexValue, loc);
820
234
    } else {
821
        // at least one of base and index is variable...
822
823
234
        if (index->getQualifier().isFrontEndConstant())
824
140
            checkIndex(loc, base->getType(), indexValue);
825
826
234
        if (base->getType().isScalarOrVec1())
827
0
            result = base;
828
234
        else if (base->getAsSymbolNode() && wasFlattened(base)) {
829
0
            if (index->getQualifier().storage != EvqConst)
830
0
                error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
831
832
0
            result = flattenAccess(base, indexValue);
833
0
            flattened = (result != base);
834
234
        } else {
835
234
            if (index->getQualifier().isFrontEndConstant()) {
836
140
                if (base->getType().isUnsizedArray())
837
0
                    base->getWritableType().updateImplicitArraySize(indexValue + 1);
838
140
                else
839
140
                    checkIndex(loc, base->getType(), indexValue);
840
140
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
841
140
            } else
842
94
                result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
843
234
        }
844
234
    }
845
846
344
    if (result == nullptr) {
847
        // Insert dummy error-recovery result
848
110
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
849
234
    } else {
850
        // If the array reference was flattened, it has the correct type.  E.g, if it was
851
        // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
852
        // In that case, we preserve the qualifiers.
853
234
        if (!flattened) {
854
            // Insert valid dereferenced result
855
234
            TType newType(base->getType(), 0);  // dereferenced type
856
234
            if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
857
6
                newType.getQualifier().storage = EvqConst;
858
228
            else
859
228
                newType.getQualifier().storage = EvqTemporary;
860
234
            result->setType(newType);
861
234
        }
862
234
    }
863
864
344
    return result;
865
368
}
866
867
// Handle seeing a binary node with a math operation.
868
TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op,
869
                                                 TIntermTyped* left, TIntermTyped* right)
870
402
{
871
402
    TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
872
402
    if (result == nullptr)
873
20
        binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
874
875
402
    return result;
876
402
}
877
878
// Handle seeing a unary node with a math operation.
879
TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op,
880
                                                TIntermTyped* childNode)
881
320
{
882
320
    TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
883
884
320
    if (result)
885
320
        return result;
886
0
    else
887
0
        unaryOpError(loc, str, childNode->getCompleteString());
888
889
0
    return childNode;
890
320
}
891
//
892
// Return true if the name is a struct buffer method
893
//
894
bool HlslParseContext::isStructBufferMethod(const TString& name) const
895
664
{
896
664
    return
897
664
        name == "GetDimensions"              ||
898
652
        name == "Load"                       ||
899
620
        name == "Load2"                      ||
900
608
        name == "Load3"                      ||
901
596
        name == "Load4"                      ||
902
588
        name == "Store"                      ||
903
578
        name == "Store2"                     ||
904
570
        name == "Store3"                     ||
905
558
        name == "Store4"                     ||
906
548
        name == "InterlockedAdd"             ||
907
428
        name == "InterlockedAnd"             ||
908
312
        name == "InterlockedCompareExchange" ||
909
264
        name == "InterlockedCompareStore"    ||
910
260
        name == "InterlockedExchange"        ||
911
242
        name == "InterlockedMax"             ||
912
226
        name == "InterlockedMin"             ||
913
198
        name == "InterlockedOr"              ||
914
162
        name == "InterlockedXor"             ||
915
126
        name == "IncrementCounter"           ||
916
110
        name == "DecrementCounter"           ||
917
96
        name == "Append"                     ||
918
80
        name == "Consume";
919
664
}
920
921
//
922
// Handle seeing a base.field dereference in the grammar, where 'field' is a
923
// swizzle or member variable.
924
//
925
TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
926
890
{
927
890
    variableCheck(base);
928
929
890
    if (base->isArray()) {
930
4
        error(loc, "cannot apply to an array:", ".", field.c_str());
931
4
        return base;
932
4
    }
933
934
886
    TIntermTyped* result = base;
935
936
886
    if (base->getType().getBasicType() == EbtSampler) {
937
        // Handle .mips[mipid][pos] operation on textures
938
84
        const TSampler& sampler = base->getType().getSampler();
939
84
        if (sampler.isTexture() && field == "mips") {
940
            // Push a null to signify that we expect a mip level under operator[] next.
941
34
            mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr));
942
            // Keep 'result' pointing to 'base', since we expect an operator[] to go by next.
943
50
        } else {
944
50
            if (field == "mips")
945
0
                error(loc, "unexpected texture type for .mips[][] operator:",
946
0
                      base->getType().getCompleteString().c_str(), "");
947
50
            else
948
50
                error(loc, "unexpected operator on texture type:", field.c_str(),
949
50
                      base->getType().getCompleteString().c_str());
950
50
        }
951
802
    } else if (base->isVector() || base->isScalar()) {
952
488
        TSwizzleSelectors<TVectorSelector> selectors;
953
488
        parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
954
955
488
        if (base->isScalar()) {
956
40
            if (selectors.size() == 1)
957
40
                return result;
958
0
            else {
959
0
                TType type(base->getBasicType(), EvqTemporary, selectors.size());
960
0
                return addConstructor(loc, base, type);
961
0
            }
962
40
        }
963
        // Use EOpIndexDirect (below) with vec1.x so that it remains l-value (Test/hlsl.swizzle.vec1.comp)
964
448
        if (base->getVectorSize() == 1 && selectors.size() > 1) {
965
6
            TType scalarType(base->getBasicType(), EvqTemporary, 1);
966
6
            TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
967
6
            return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
968
6
        }
969
970
442
        if (base->getType().getQualifier().isFrontEndConstant())
971
2
            result = intermediate.foldSwizzle(base, selectors, loc);
972
440
        else {
973
440
            if (selectors.size() == 1) {
974
238
                TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
975
238
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
976
238
                result->setType(TType(base->getBasicType(), EvqTemporary));
977
238
            } else {
978
202
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
979
202
                result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
980
202
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
981
202
                                selectors.size()));
982
202
            }
983
440
        }
984
442
    } else if (base->isMatrix()) {
985
10
        TSwizzleSelectors<TMatrixSelector> selectors;
986
10
        if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
987
0
            return result;
988
989
10
        if (selectors.size() == 1) {
990
            // Representable by m[c][r]
991
0
            if (base->getType().getQualifier().isFrontEndConstant()) {
992
0
                result = intermediate.foldDereference(base, selectors[0].coord1, loc);
993
0
                result = intermediate.foldDereference(result, selectors[0].coord2, loc);
994
0
            } else {
995
0
                result = intermediate.addIndex(EOpIndexDirect, base,
996
0
                                               intermediate.addConstantUnion(selectors[0].coord1, loc),
997
0
                                               loc);
998
0
                TType dereferencedCol(base->getType(), 0);
999
0
                result->setType(dereferencedCol);
1000
0
                result = intermediate.addIndex(EOpIndexDirect, result,
1001
0
                                               intermediate.addConstantUnion(selectors[0].coord2, loc),
1002
0
                                               loc);
1003
0
                TType dereferenced(dereferencedCol, 0);
1004
0
                result->setType(dereferenced);
1005
0
            }
1006
10
        } else {
1007
10
            int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
1008
10
            if (column >= 0) {
1009
                // Representable by m[c]
1010
0
                if (base->getType().getQualifier().isFrontEndConstant())
1011
0
                    result = intermediate.foldDereference(base, column, loc);
1012
0
                else {
1013
0
                    result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc),
1014
0
                                                   loc);
1015
0
                    TType dereferenced(base->getType(), 0);
1016
0
                    result->setType(dereferenced);
1017
0
                }
1018
10
            } else {
1019
                // general case, not a column, not a single component
1020
10
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
1021
10
                result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
1022
10
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
1023
10
                                      selectors.size()));
1024
10
           }
1025
10
        }
1026
304
    } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
1027
304
        const TTypeList* fields = base->getType().getStruct();
1028
304
        bool fieldFound = false;
1029
304
        int member;
1030
554
        for (member = 0; member < (int)fields->size(); ++member) {
1031
508
            if ((*fields)[member].type->getFieldName() == field) {
1032
258
                fieldFound = true;
1033
258
                break;
1034
258
            }
1035
508
        }
1036
304
        if (fieldFound) {
1037
258
            if (base->getAsSymbolNode() && wasFlattened(base)) {
1038
0
                result = flattenAccess(base, member);
1039
258
            } else {
1040
258
                if (base->getType().getQualifier().storage == EvqConst)
1041
10
                    result = intermediate.foldDereference(base, member, loc);
1042
248
                else {
1043
248
                    TIntermTyped* index = intermediate.addConstantUnion(member, loc);
1044
248
                    result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
1045
248
                    result->setType(*(*fields)[member].type);
1046
248
                }
1047
258
            }
1048
258
        } else
1049
46
            error(loc, "no such field in structure", field.c_str(), "");
1050
304
    } else
1051
0
        error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
1052
1053
840
    return result;
1054
886
}
1055
1056
//
1057
// Return true if the field should be treated as a built-in method.
1058
// Return false otherwise.
1059
//
1060
bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
1061
1.39k
{
1062
1.39k
    if (base == nullptr)
1063
0
        return false;
1064
1065
1.39k
    variableCheck(base);
1066
1067
1.39k
    if (base->getType().getBasicType() == EbtSampler) {
1068
976
        return true;
1069
976
    } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
1070
308
        return true;
1071
308
    } else if (field == "Append" ||
1072
100
               field == "RestartStrip") {
1073
        // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
1074
        // the code is around in the shader source.
1075
12
        return true;
1076
12
    } else
1077
96
        return false;
1078
1.39k
}
1079
1080
// Independently establish a built-in that is a member of a structure.
1081
// 'arraySizes' are what's desired for the independent built-in, whatever
1082
// the higher-level source/expression of them was.
1083
void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
1084
                                    const TQualifier& outerQualifier)
1085
0
{
1086
    // Because of arrays of structs, we might be asked more than once,
1087
    // but the arraySizes passed in should have captured the whole thing
1088
    // the first time.
1089
    // However, clip/cull rely on multiple updates.
1090
0
    if (!isClipOrCullDistance(memberType))
1091
0
        if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) !=
1092
0
            splitBuiltIns.end())
1093
0
            return;
1094
1095
0
    TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType);
1096
1097
0
    if (arraySizes != nullptr && !memberType.isArray())
1098
0
        ioVar->getWritableType().copyArraySizes(*arraySizes);
1099
1100
0
    splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
1101
0
    if (!isClipOrCullDistance(ioVar->getType()))
1102
0
        trackLinkage(*ioVar);
1103
1104
    // Merge qualifier from the user structure
1105
0
    mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
1106
1107
    // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the
1108
    // shader declared them).  This is done after mergeQualifiers(), in case fixBuiltInIoType looks
1109
    // at the qualifier to determine e.g, in or out qualifications.
1110
0
    fixBuiltInIoType(ioVar->getWritableType());
1111
1112
    // But, not location, we're losing that
1113
0
    ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1114
0
}
1115
1116
// Split a type into
1117
//   1. a struct of non-I/O members
1118
//   2. a collection of independent I/O variables
1119
void HlslParseContext::split(const TVariable& variable)
1120
0
{
1121
    // Create a new variable:
1122
0
    const TType& clonedType = *variable.getType().clone();
1123
0
    const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
1124
0
    splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
1125
0
}
1126
1127
// Recursive implementation of split().
1128
// Returns reference to the modified type.
1129
const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
1130
0
{
1131
0
    if (type.isStruct()) {
1132
0
        TTypeList* userStructure = type.getWritableStruct();
1133
0
        for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
1134
0
            if (ioType->type->isBuiltIn()) {
1135
                // move out the built-in
1136
0
                splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
1137
0
                ioType = userStructure->erase(ioType);
1138
0
            } else {
1139
0
                split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier);
1140
0
                ++ioType;
1141
0
            }
1142
0
        }
1143
0
    }
1144
1145
0
    return type;
1146
0
}
1147
1148
// Is this an aggregate that should be flattened?
1149
// Can be applied to intermediate levels of type in a hierarchy.
1150
// Some things like flattening uniform arrays are only about the top level
1151
// of the aggregate, triggered on 'topLevel'.
1152
bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const
1153
36.9k
{
1154
36.9k
    switch (qualifier) {
1155
30
    case EvqVaryingIn:
1156
1.56k
    case EvqVaryingOut:
1157
1.56k
        return type.isStruct() || type.isArray();
1158
4.67k
    case EvqUniform:
1159
4.67k
        return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) ||
1160
4.67k
               (type.isStruct() && type.containsOpaque());
1161
30.7k
    default:
1162
30.7k
        return false;
1163
36.9k
    };
1164
0
}
1165
1166
// Top level variable flattening: construct data
1167
void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed)
1168
364
{
1169
364
    const TType& type = variable.getType();
1170
1171
    // If it's a standalone built-in, there is nothing to flatten
1172
364
    if (type.isBuiltIn() && !type.isStruct())
1173
0
        return;
1174
1175
1176
364
    auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
1177
364
                                                  TFlattenData(type.getQualifier().layoutBinding,
1178
364
                                                               type.getQualifier().layoutLocation)));
1179
1180
364
    if (type.isStruct() && type.getStruct()->size()==0)
1181
2
        return;
1182
    // if flattening arrayed io struct, array each member of dereferenced type
1183
362
    if (arrayed) {
1184
152
        const TType dereferencedType(type, 0);
1185
152
        flatten(variable, dereferencedType, entry.first->second, variable.getName(), linkage,
1186
152
                type.getQualifier(), type.getArraySizes());
1187
210
    } else {
1188
210
        flatten(variable, type, entry.first->second, variable.getName(), linkage,
1189
210
                type.getQualifier(), nullptr);
1190
210
    }
1191
362
}
1192
1193
// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1194
//
1195
// This is mutually recursive with flattenStruct and flattenArray.
1196
// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1197
// members, and later on, we want to turn a path through the tree structure into a final
1198
// location in this linear sequence.
1199
//
1200
// If the tree was N-ary, that can be directly calculated.  However, we are dealing with
1201
// arbitrary numbers - perhaps a struct of 7 members containing an array of 3.  Thus, we must
1202
// build a data structure to allow the sequence of bracket and dot operators on arrays and
1203
// structs to arrive at the proper member.
1204
//
1205
// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1206
// The leaves are the indexes into the flattened member array.
1207
// Each level will have the next location for the Nth item stored sequentially, so for instance:
1208
//
1209
// struct { float2 a[2]; int b; float4 c[3] };
1210
//
1211
// This will produce the following flattened tree:
1212
// Pos: 0  1   2    3  4    5  6   7     8   9  10   11  12 13
1213
//     (3, 7,  8,   5, 6,   0, 1,  2,   11, 12, 13,   3,  4, 5}
1214
//
1215
// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1216
//   (0+2) = 8  -->  (8+1) = 12 -->   12 = 4
1217
//
1218
// so the 4th flattened member in traversal order is ours.
1219
//
1220
int HlslParseContext::flatten(const TVariable& variable, const TType& type,
1221
                              TFlattenData& flattenData, TString name, bool linkage,
1222
                              const TQualifier& outerQualifier,
1223
                              const TArraySizes* builtInArraySizes)
1224
362
{
1225
    // If something is an arrayed struct, the array flattener will recursively call flatten()
1226
    // to then flatten the struct, so this is an "if else": we don't do both.
1227
362
    if (type.isArray())
1228
0
        return flattenArray(variable, type, flattenData, name, linkage, outerQualifier);
1229
362
    else if (type.isStruct())
1230
362
        return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes);
1231
0
    else {
1232
0
        assert(0); // should never happen
1233
0
        return -1;
1234
0
    }
1235
362
}
1236
1237
// Add a single flattened member to the flattened data being tracked for the composite
1238
// Returns true for the final flattening level.
1239
int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData,
1240
                                         const TString& memberName, bool linkage,
1241
                                         const TQualifier& outerQualifier,
1242
                                         const TArraySizes* builtInArraySizes)
1243
474
{
1244
474
    if (!shouldFlatten(type, outerQualifier.storage, false)) {
1245
        // This is as far as we flatten.  Insert the variable.
1246
474
        TVariable* memberVariable = makeInternalVariable(memberName, type);
1247
474
        mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1248
1249
474
        if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
1250
0
            memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1251
1252
474
        if (memberVariable->getType().isBuiltIn()) {
1253
            // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number)
1254
0
            memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1255
474
        } else {
1256
            // inherited locations must be auto bumped, not replicated
1257
474
            if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
1258
0
                memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
1259
0
                flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language);
1260
0
                nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
1261
0
            }
1262
474
        }
1263
1264
        // Only propagate arraysizes here for arrayed io
1265
474
        if (variable.getType().getQualifier().isArrayedIo(language) && builtInArraySizes != nullptr)
1266
162
            memberVariable->getWritableType().copyArraySizes(*builtInArraySizes);
1267
1268
474
        flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
1269
474
        flattenData.members.push_back(memberVariable);
1270
1271
474
        if (linkage)
1272
60
            trackLinkage(*memberVariable);
1273
1274
474
        return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference
1275
474
    } else {
1276
        // Further recursion required
1277
0
        return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes);
1278
0
    }
1279
474
}
1280
1281
// Figure out the mapping between an aggregate's top members and an
1282
// equivalent set of individual variables.
1283
//
1284
// Assumes shouldFlatten() or equivalent was called first.
1285
int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type,
1286
                                    TFlattenData& flattenData, TString name, bool linkage,
1287
                                    const TQualifier& outerQualifier,
1288
                                    const TArraySizes* builtInArraySizes)
1289
362
{
1290
362
    assert(type.isStruct());
1291
1292
362
    auto members = *type.getStruct();
1293
1294
    // Reserve space for this tree level.
1295
362
    int start = static_cast<int>(flattenData.offsets.size());
1296
362
    int pos = start;
1297
362
    flattenData.offsets.resize(int(pos + members.size()), -1);
1298
1299
836
    for (int member = 0; member < (int)members.size(); ++member) {
1300
474
        TType& dereferencedType = *members[member].type;
1301
474
        if (dereferencedType.isBuiltIn())
1302
0
            splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier);
1303
474
        else {
1304
474
            const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1305
474
                                                name + "." + dereferencedType.getFieldName(),
1306
474
                                                linkage, outerQualifier,
1307
474
                                                builtInArraySizes == nullptr && dereferencedType.isArray()
1308
474
                                                                       ? dereferencedType.getArraySizes()
1309
474
                                                                       : builtInArraySizes);
1310
474
            flattenData.offsets[pos++] = mpos;
1311
474
        }
1312
474
    }
1313
1314
362
    return start;
1315
362
}
1316
1317
// Figure out mapping between an array's members and an
1318
// equivalent set of individual variables.
1319
//
1320
// Assumes shouldFlatten() or equivalent was called first.
1321
int HlslParseContext::flattenArray(const TVariable& variable, const TType& type,
1322
                                   TFlattenData& flattenData, TString name, bool linkage,
1323
                                   const TQualifier& outerQualifier)
1324
0
{
1325
0
    assert(type.isSizedArray());
1326
1327
0
    const int size = type.getOuterArraySize();
1328
0
    const TType dereferencedType(type, 0);
1329
1330
0
    if (name.empty())
1331
0
        name = variable.getName();
1332
1333
    // Reserve space for this tree level.
1334
0
    int start = static_cast<int>(flattenData.offsets.size());
1335
0
    int pos   = start;
1336
0
    flattenData.offsets.resize(int(pos + size), -1);
1337
1338
0
    for (int element=0; element < size; ++element) {
1339
0
        char elementNumBuf[20];  // sufficient for MAXINT
1340
0
        snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
1341
0
        const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1342
0
                                            name + elementNumBuf, linkage, outerQualifier,
1343
0
                                            type.getArraySizes());
1344
1345
0
        flattenData.offsets[pos++] = mpos;
1346
0
    }
1347
1348
0
    return start;
1349
0
}
1350
1351
// Return true if we have flattened this node.
1352
bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1353
47.2k
{
1354
47.2k
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1355
27.7k
           wasFlattened(node->getAsSymbolNode()->getId());
1356
47.2k
}
1357
1358
// Return true if we have split this structure
1359
bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1360
34.0k
{
1361
34.0k
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1362
16.9k
           wasSplit(node->getAsSymbolNode()->getId());
1363
34.0k
}
1364
1365
// Turn an access into an aggregate that was flattened to instead be
1366
// an access to the individual variable the member was flattened to.
1367
// Assumes wasFlattened() or equivalent was called first.
1368
TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
1369
0
{
1370
0
    const TType dereferencedType(base->getType(), member);  // dereferenced type
1371
0
    const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
1372
0
    TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage,
1373
0
                                            dereferencedType, symbolNode.getFlattenSubset());
1374
1375
0
    return flattened ? flattened : base;
1376
0
}
1377
TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage,
1378
    const TType& dereferencedType, int subset)
1379
0
{
1380
0
    const auto flattenData = flattenMap.find(uniqueId);
1381
1382
0
    if (flattenData == flattenMap.end())
1383
0
        return nullptr;
1384
1385
    // Calculate new cumulative offset from the packed tree
1386
0
    int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member];
1387
1388
0
    TIntermSymbol* subsetSymbol;
1389
0
    if (!shouldFlatten(dereferencedType, outerStorage, false)) {
1390
        // Finished flattening: create symbol for variable
1391
0
        member = flattenData->second.offsets[newSubset];
1392
0
        const TVariable* memberVariable = flattenData->second.members[member];
1393
0
        subsetSymbol = intermediate.addSymbol(*memberVariable);
1394
0
        subsetSymbol->setFlattenSubset(-1);
1395
0
    } else {
1396
1397
        // If this is not the final flattening, accumulate the position and return
1398
        // an object of the partially dereferenced type.
1399
0
        subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", getLanguage(), dereferencedType);
1400
0
        subsetSymbol->setFlattenSubset(newSubset);
1401
0
    }
1402
1403
0
    return subsetSymbol;
1404
0
}
1405
1406
// For finding where the first leaf is in a subtree of a multi-level aggregate
1407
// that is just getting a subset assigned. Follows the same logic as flattenAccess,
1408
// but logically going down the "left-most" tree branch each step of the way.
1409
//
1410
// Returns the offset into the first leaf of the subset.
1411
int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const
1412
652
{
1413
652
    const TIntermSymbol* sym = node.getAsSymbolNode();
1414
652
    if (sym == nullptr)
1415
460
        return 0;
1416
192
    if (!sym->isArray() && !sym->isStruct())
1417
0
        return 0;
1418
192
    int subset = sym->getFlattenSubset();
1419
192
    if (subset == -1)
1420
192
        return 0;
1421
1422
    // Getting this far means a partial aggregate is identified by the flatten subset.
1423
    // Find the first leaf of the subset.
1424
1425
0
    const auto flattenData = flattenMap.find(sym->getId());
1426
0
    if (flattenData == flattenMap.end())
1427
0
        return 0;
1428
1429
0
    return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets);
1430
1431
0
    do {
1432
0
        subset = flattenData->second.offsets[subset];
1433
0
    } while (true);
1434
0
}
1435
// Recursively do the desent
1436
int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const
1437
0
{
1438
0
    if (!type.isArray() && !type.isStruct())
1439
0
        return offsets[subset];
1440
0
    TType derefType(type, 0);
1441
0
    return findSubtreeOffset(derefType, offsets[subset], offsets);
1442
0
}
1443
1444
// Find and return the split IO TVariable for id, or nullptr if none.
1445
TVariable* HlslParseContext::getSplitNonIoVar(long long id) const
1446
0
{
1447
0
    const auto splitNonIoVar = splitNonIoVars.find(id);
1448
0
    if (splitNonIoVar == splitNonIoVars.end())
1449
0
        return nullptr;
1450
1451
0
    return splitNonIoVar->second;
1452
0
}
1453
1454
// Pass through to base class after remembering built-in mappings.
1455
void HlslParseContext::trackLinkage(TSymbol& symbol)
1456
8.44k
{
1457
8.44k
    TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
1458
1459
8.44k
    if (biType != EbvNone)
1460
408
        builtInTessLinkageSymbols[biType] = symbol.clone();
1461
1462
8.44k
    TParseContextBase::trackLinkage(symbol);
1463
8.44k
}
1464
1465
1466
// Returns true if the built-in is a clip or cull distance variable.
1467
bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn)
1468
36.0k
{
1469
36.0k
    return builtIn == EbvClipDistance || builtIn == EbvCullDistance;
1470
36.0k
}
1471
1472
// Some types require fixed array sizes in SPIR-V, but can be scalars or
1473
// arrays of sizes SPIR-V doesn't allow.  For example, tessellation factors.
1474
// This creates the right size.  A conversion is performed when the internal
1475
// type is copied to or from the external type.  This corrects the externally
1476
// facing input or output type to abide downstream semantics.
1477
void HlslParseContext::fixBuiltInIoType(TType& type)
1478
1.43k
{
1479
1.43k
    int requiredArraySize = 0;
1480
1.43k
    int requiredVectorSize = 0;
1481
1482
1.43k
    switch (type.getQualifier().builtIn) {
1483
0
    case EbvTessLevelOuter: requiredArraySize = 4; break;
1484
0
    case EbvTessLevelInner: requiredArraySize = 2; break;
1485
1486
0
    case EbvSampleMask:
1487
0
        {
1488
            // Promote scalar to array of size 1.  Leave existing arrays alone.
1489
0
            if (!type.isArray())
1490
0
                requiredArraySize = 1;
1491
0
            break;
1492
0
        }
1493
1494
0
    case EbvWorkGroupId:        requiredVectorSize = 3; break;
1495
0
    case EbvGlobalInvocationId: requiredVectorSize = 3; break;
1496
0
    case EbvLocalInvocationId:  requiredVectorSize = 3; break;
1497
0
    case EbvTessCoord:          requiredVectorSize = 3; break;
1498
1499
1.43k
    default:
1500
1.43k
        if (isClipOrCullDistance(type)) {
1501
24
            const int loc = type.getQualifier().layoutLocation;
1502
1503
24
            if (type.getQualifier().builtIn == EbvClipDistance) {
1504
16
                if (type.getQualifier().storage == EvqVaryingIn)
1505
16
                    clipSemanticNSizeIn[loc] = type.getVectorSize();
1506
0
                else
1507
0
                    clipSemanticNSizeOut[loc] = type.getVectorSize();
1508
16
            } else {
1509
8
                if (type.getQualifier().storage == EvqVaryingIn)
1510
8
                    cullSemanticNSizeIn[loc] = type.getVectorSize();
1511
0
                else
1512
0
                    cullSemanticNSizeOut[loc] = type.getVectorSize();
1513
8
            }
1514
24
        }
1515
1516
1.43k
        return;
1517
1.43k
    }
1518
1519
    // Alter or set vector size as needed.
1520
0
    if (requiredVectorSize > 0) {
1521
0
        TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize);
1522
0
        newType.getQualifier() = type.getQualifier();
1523
1524
0
        type.shallowCopy(newType);
1525
0
    }
1526
1527
    // Alter or set array size as needed.
1528
0
    if (requiredArraySize > 0) {
1529
0
        if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) {
1530
0
            TArraySizes* arraySizes = new TArraySizes;
1531
0
            arraySizes->addInnerSize(requiredArraySize);
1532
0
            type.transferArraySizes(arraySizes);
1533
0
        }
1534
0
    }
1535
0
}
1536
1537
// Variables that correspond to the user-interface in and out of a stage
1538
// (not the built-in interface) are
1539
//  - assigned locations
1540
//  - registered as a linkage node (part of the stage's external interface).
1541
// Assumes it is called in the order in which locations should be assigned.
1542
void HlslParseContext::assignToInterface(TVariable& variable)
1543
952
{
1544
1.04k
    const auto assignLocation = [&](TVariable& variable) {
1545
1.04k
        TType& type = variable.getWritableType();
1546
1.04k
        if (!type.isStruct() || type.getStruct()->size() > 0) {
1547
1.04k
            TQualifier& qualifier = type.getQualifier();
1548
1.04k
            if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1549
1.04k
                if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) {
1550
                    // Strip off the outer array dimension for those having an extra one.
1551
952
                    int size;
1552
952
                    if (type.isArray() && qualifier.isArrayedIo(language)) {
1553
470
                        TType elementType(type, 0);
1554
470
                        size = intermediate.computeTypeLocationSize(elementType, language);
1555
470
                    } else
1556
482
                        size = intermediate.computeTypeLocationSize(type, language);
1557
1558
952
                    if (qualifier.storage == EvqVaryingIn) {
1559
90
                        variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
1560
90
                        nextInLocation += size;
1561
862
                    } else {
1562
862
                        variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
1563
862
                        nextOutLocation += size;
1564
862
                    }
1565
952
                }
1566
1.04k
                trackLinkage(variable);
1567
1.04k
            }
1568
1.04k
        }
1569
1.04k
    };
1570
1571
952
    if (wasFlattened(variable.getUniqueId())) {
1572
326
        auto& memberList = flattenMap[variable.getUniqueId()].members;
1573
740
        for (auto member = memberList.begin(); member != memberList.end(); ++member)
1574
414
            assignLocation(**member);
1575
626
    } else if (wasSplit(variable.getUniqueId())) {
1576
0
        TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId());
1577
0
        assignLocation(*splitIoVar);
1578
626
    } else {
1579
626
        assignLocation(variable);
1580
626
    }
1581
952
}
1582
1583
//
1584
// Handle seeing a function declarator in the grammar.  This is the precursor
1585
// to recognizing a function prototype or function definition.
1586
//
1587
void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
1588
6.12M
{
1589
    //
1590
    // Multiple declarations of the same function name are allowed.
1591
    //
1592
    // If this is a definition, the definition production code will check for redefinitions
1593
    // (we don't know at this point if it's a definition or not).
1594
    //
1595
6.12M
    bool builtIn;
1596
6.12M
    TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1597
6.12M
    const TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1598
1599
6.12M
    if (prototype) {
1600
        // All built-in functions are defined, even though they don't have a body.
1601
        // Count their prototype as a definition instead.
1602
6.12M
        if (symbolTable.atBuiltInLevel())
1603
6.12M
            function.setDefined();
1604
100
        else {
1605
100
            if (prevDec && ! builtIn)
1606
18
                symbol->getAsFunction()->setPrototyped();  // need a writable one, but like having prevDec as a const
1607
100
            function.setPrototyped();
1608
100
        }
1609
6.12M
    }
1610
1611
    // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1612
    // other forms of name collisions.
1613
6.12M
    if (! symbolTable.insert(function))
1614
0
        error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
1615
6.12M
}
1616
1617
// For struct buffers with counters, we must pass the counter buffer as hidden parameter.
1618
// This adds the hidden parameter to the parameter list in 'paramNodes' if needed.
1619
// Otherwise, it's a no-op
1620
void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param,
1621
                                                         TIntermAggregate*& paramNodes)
1622
2.72k
{
1623
2.72k
    if (! hasStructBuffCounter(*param.type))
1624
2.71k
        return;
1625
1626
18
    const TString counterBlockName(intermediate.addCounterBufferName(*param.name));
1627
1628
18
    TType counterType;
1629
18
    counterBufferType(loc, counterType);
1630
18
    TVariable *variable = makeInternalVariable(counterBlockName, counterType);
1631
1632
18
    if (! symbolTable.insert(*variable))
1633
0
        error(loc, "redefinition", variable->getName().c_str(), "");
1634
1635
18
    paramNodes = intermediate.growAggregate(paramNodes,
1636
18
                                            intermediate.addSymbol(*variable, loc),
1637
18
                                            loc);
1638
18
}
1639
1640
//
1641
// Handle seeing the function prototype in front of a function definition in the grammar.
1642
// The body is handled after this function returns.
1643
//
1644
// Returns an aggregate of parameter-symbol nodes.
1645
//
1646
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
1647
                                                             const TAttributes& attributes,
1648
                                                             TIntermNode*& entryPointTree)
1649
2.17k
{
1650
2.17k
    currentCaller = function.getMangledName();
1651
2.17k
    TSymbol* symbol = symbolTable.find(function.getMangledName());
1652
2.17k
    TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1653
1654
2.17k
    if (prevDec == nullptr)
1655
0
        error(loc, "can't find function", function.getName().c_str(), "");
1656
    // Note:  'prevDec' could be 'function' if this is the first time we've seen function
1657
    // as it would have just been put in the symbol table.  Otherwise, we're looking up
1658
    // an earlier occurrence.
1659
1660
2.17k
    if (prevDec && prevDec->isDefined()) {
1661
        // Then this function already has a body.
1662
124
        error(loc, "function already has a body", function.getName().c_str(), "");
1663
124
    }
1664
2.17k
    if (prevDec && ! prevDec->isDefined()) {
1665
2.05k
        prevDec->setDefined();
1666
1667
        // Remember the return type for later checking for RETURN statements.
1668
2.05k
        currentFunctionType = &(prevDec->getType());
1669
2.05k
    } else
1670
124
        currentFunctionType = new TType(EbtVoid);
1671
2.17k
    functionReturnsValue = false;
1672
1673
    // Entry points need different I/O and other handling, transform it so the
1674
    // rest of this function doesn't care.
1675
2.17k
    entryPointTree = transformEntryPoint(loc, function, attributes);
1676
1677
    //
1678
    // New symbol table scope for body of function plus its arguments
1679
    //
1680
2.17k
    pushScope();
1681
1682
    //
1683
    // Insert parameters into the symbol table.
1684
    // If the parameter has no name, it's not an error, just don't insert it
1685
    // (could be used for unused args).
1686
    //
1687
    // Also, accumulate the list of parameters into the AST, so lower level code
1688
    // knows where to find parameters.
1689
    //
1690
2.17k
    TIntermAggregate* paramNodes = new TIntermAggregate;
1691
4.93k
    for (int i = 0; i < function.getParamCount(); i++) {
1692
2.75k
        TParameter& param = function[i];
1693
2.75k
        if (param.name != nullptr) {
1694
2.72k
            TVariable *variable = new TVariable(param.name, *param.type);
1695
1696
2.72k
            if (i == 0 && function.hasImplicitThis()) {
1697
                // Anonymous 'this' members are already in a symbol-table level,
1698
                // and we need to know what function parameter to map them to.
1699
24
                symbolTable.makeInternalVariable(*variable);
1700
24
                pushImplicitThis(variable);
1701
24
            }
1702
1703
            // Insert the parameters with name in the symbol table.
1704
2.72k
            if (! symbolTable.insert(*variable))
1705
18
                error(loc, "redefinition", variable->getName().c_str(), "");
1706
1707
            // Add parameters to the AST list.
1708
2.72k
            if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) {
1709
                // Expand the AST parameter nodes (but not the name mangling or symbol table view)
1710
                // for structures that need to be flattened.
1711
0
                flatten(*variable, false);
1712
0
                const TTypeList* structure = variable->getType().getStruct();
1713
0
                for (int mem = 0; mem < (int)structure->size(); ++mem) {
1714
0
                    paramNodes = intermediate.growAggregate(paramNodes,
1715
0
                                                            flattenAccess(variable->getUniqueId(), mem,
1716
0
                                                                          variable->getType().getQualifier().storage,
1717
0
                                                                          *(*structure)[mem].type),
1718
0
                                                            loc);
1719
0
                }
1720
2.72k
            } else {
1721
                // Add the parameter to the AST
1722
2.72k
                paramNodes = intermediate.growAggregate(paramNodes,
1723
2.72k
                                                        intermediate.addSymbol(*variable, loc),
1724
2.72k
                                                        loc);
1725
2.72k
            }
1726
1727
            // Add hidden AST parameter for struct buffer counters, if needed.
1728
2.72k
            addStructBufferHiddenCounterParam(loc, param, paramNodes);
1729
2.72k
        } else
1730
24
            paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
1731
2.75k
    }
1732
2.17k
    if (function.hasIllegalImplicitThis())
1733
4
        pushImplicitThis(nullptr);
1734
1735
2.17k
    intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1736
2.17k
    loopNestingLevel = 0;
1737
2.17k
    controlFlowNestingLevel = 0;
1738
2.17k
    postEntryPointReturn = false;
1739
1740
2.17k
    return paramNodes;
1741
2.17k
}
1742
1743
// Handle all [attrib] attribute for the shader entry point
1744
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
1745
838
{
1746
868
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1747
30
        switch (it->name) {
1748
0
        case EatNumThreads:
1749
0
        {
1750
0
            const TIntermSequence& sequence = it->args->getSequence();
1751
0
            for (int lid = 0; lid < int(sequence.size()); ++lid)
1752
0
                intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1753
0
            break;
1754
0
        }
1755
0
        case EatInstance: 
1756
0
        {
1757
0
            int invocations;
1758
1759
0
            if (!it->getInt(invocations)) {
1760
0
                error(loc, "invalid instance", "", "");
1761
0
            } else {
1762
0
                if (!intermediate.setInvocations(invocations))
1763
0
                    error(loc, "cannot change previously set instance attribute", "", "");
1764
0
            }
1765
0
            break;
1766
0
        }
1767
0
        case EatMaxVertexCount:
1768
0
        {
1769
0
            int maxVertexCount;
1770
1771
0
            if (! it->getInt(maxVertexCount)) {
1772
0
                error(loc, "invalid maxvertexcount", "", "");
1773
0
            } else {
1774
0
                if (! intermediate.setVertices(maxVertexCount))
1775
0
                    error(loc, "cannot change previously set maxvertexcount attribute", "", "");
1776
0
            }
1777
0
            break;
1778
0
        }
1779
0
        case EatPatchConstantFunc:
1780
0
        {
1781
0
            TString pcfName;
1782
0
            if (! it->getString(pcfName, 0, false)) {
1783
0
                error(loc, "invalid patch constant function", "", "");
1784
0
            } else {
1785
0
                patchConstantFunctionName = pcfName;
1786
0
            }
1787
0
            break;
1788
0
        }
1789
0
        case EatDomain:
1790
0
        {
1791
            // Handle [domain("...")]
1792
0
            TString domainStr;
1793
0
            if (! it->getString(domainStr)) {
1794
0
                error(loc, "invalid domain", "", "");
1795
0
            } else {
1796
0
                TLayoutGeometry domain = ElgNone;
1797
1798
0
                if (domainStr == "tri") {
1799
0
                    domain = ElgTriangles;
1800
0
                } else if (domainStr == "quad") {
1801
0
                    domain = ElgQuads;
1802
0
                } else if (domainStr == "isoline") {
1803
0
                    domain = ElgIsolines;
1804
0
                } else {
1805
0
                    error(loc, "unsupported domain type", domainStr.c_str(), "");
1806
0
                }
1807
1808
0
                if (language == EShLangTessEvaluation) {
1809
0
                    if (! intermediate.setInputPrimitive(domain))
1810
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1811
0
                } else {
1812
0
                    if (! intermediate.setOutputPrimitive(domain))
1813
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1814
0
                }
1815
0
            }
1816
0
            break;
1817
0
        }
1818
0
        case EatOutputTopology:
1819
0
        {
1820
            // Handle [outputtopology("...")]
1821
0
            TString topologyStr;
1822
0
            if (! it->getString(topologyStr)) {
1823
0
                error(loc, "invalid outputtopology", "", "");
1824
0
            } else {
1825
0
                TVertexOrder vertexOrder = EvoNone;
1826
0
                TLayoutGeometry primitive = ElgNone;
1827
1828
0
                if (topologyStr == "point") {
1829
0
                    intermediate.setPointMode();
1830
0
                } else if (topologyStr == "line") {
1831
0
                    primitive = ElgIsolines;
1832
0
                } else if (topologyStr == "triangle_cw") {
1833
0
                    vertexOrder = EvoCw;
1834
0
                    primitive = ElgTriangles;
1835
0
                } else if (topologyStr == "triangle_ccw") {
1836
0
                    vertexOrder = EvoCcw;
1837
0
                    primitive = ElgTriangles;
1838
0
                } else {
1839
0
                    error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
1840
0
                }
1841
1842
0
                if (vertexOrder != EvoNone) {
1843
0
                    if (! intermediate.setVertexOrder(vertexOrder)) {
1844
0
                        error(loc, "cannot change previously set outputtopology",
1845
0
                              TQualifier::getVertexOrderString(vertexOrder), "");
1846
0
                    }
1847
0
                }
1848
0
                if (primitive != ElgNone)
1849
0
                    intermediate.setOutputPrimitive(primitive);
1850
0
            }
1851
0
            break;
1852
0
        }
1853
0
        case EatPartitioning:
1854
0
        {
1855
            // Handle [partitioning("...")]
1856
0
            TString partitionStr;
1857
0
            if (! it->getString(partitionStr)) {
1858
0
                error(loc, "invalid partitioning", "", "");
1859
0
            } else {
1860
0
                TVertexSpacing partitioning = EvsNone;
1861
1862
0
                if (partitionStr == "integer") {
1863
0
                    partitioning = EvsEqual;
1864
0
                } else if (partitionStr == "fractional_even") {
1865
0
                    partitioning = EvsFractionalEven;
1866
0
                } else if (partitionStr == "fractional_odd") {
1867
0
                    partitioning = EvsFractionalOdd;
1868
                    //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
1869
0
                } else {
1870
0
                    error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
1871
0
                }
1872
1873
0
                if (! intermediate.setVertexSpacing(partitioning))
1874
0
                    error(loc, "cannot change previously set partitioning",
1875
0
                          TQualifier::getVertexSpacingString(partitioning), "");
1876
0
            }
1877
0
            break;
1878
0
        }
1879
0
        case EatOutputControlPoints:
1880
0
        {
1881
            // Handle [outputcontrolpoints("...")]
1882
0
            int ctrlPoints;
1883
0
            if (! it->getInt(ctrlPoints)) {
1884
0
                error(loc, "invalid outputcontrolpoints", "", "");
1885
0
            } else {
1886
0
                if (! intermediate.setVertices(ctrlPoints)) {
1887
0
                    error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
1888
0
                }
1889
0
            }
1890
0
            break;
1891
0
        }
1892
0
        case EatEarlyDepthStencil:
1893
0
            intermediate.setEarlyFragmentTests();
1894
0
            break;
1895
0
        case EatBuiltIn:
1896
30
        case EatLocation:
1897
            // tolerate these because of dual use of entrypoint and type attributes
1898
30
            break;
1899
0
        default:
1900
0
            warn(loc, "attribute does not apply to entry point", "", "");
1901
0
            break;
1902
30
        }
1903
30
    }
1904
838
}
1905
1906
// Update the given type with any type-like attribute information in the
1907
// attributes.
1908
void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
1909
    bool allowEntry)
1910
19.9M
{
1911
19.9M
    if (attributes.size() == 0)
1912
19.9M
        return;
1913
1914
446
    int value;
1915
446
    TString builtInString;
1916
892
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1917
446
        switch (it->name) {
1918
80
        case EatLocation:
1919
            // location
1920
80
            if (it->getInt(value))
1921
78
                type.getQualifier().layoutLocation = value;
1922
2
            else
1923
2
                error(loc, "needs a literal integer", "location", "");
1924
80
            break;
1925
204
        case EatBinding:
1926
            // binding
1927
204
            if (it->getInt(value)) {
1928
184
                type.getQualifier().layoutBinding = value;
1929
184
                type.getQualifier().layoutSet = 0;
1930
184
            } else
1931
20
                error(loc, "needs a literal integer", "binding", "");
1932
            // set
1933
204
            if (it->getInt(value, 1))
1934
92
                type.getQualifier().layoutSet = value;
1935
204
            break;
1936
0
        case EatGlobalBinding:
1937
            // global cbuffer binding
1938
0
            if (it->getInt(value))
1939
0
                globalUniformBinding = value;
1940
0
            else
1941
0
                error(loc, "needs a literal integer", "global binding", "");
1942
            // global cbuffer set
1943
0
            if (it->getInt(value, 1))
1944
0
                globalUniformSet = value;
1945
0
            break;
1946
58
        case EatInputAttachment:
1947
            // input attachment
1948
58
            if (it->getInt(value))
1949
56
                type.getQualifier().layoutAttachment = value;
1950
2
            else
1951
2
                error(loc, "needs a literal integer", "input attachment", "");
1952
58
            break;
1953
0
        case EatBuiltIn:
1954
            // PointSize built-in
1955
0
            if (it->getString(builtInString, 0, false)) {
1956
0
                if (builtInString == "PointSize")
1957
0
                    type.getQualifier().builtIn = EbvPointSize;
1958
0
            }
1959
0
            break;
1960
32
        case EatPushConstant:
1961
            // push_constant
1962
32
            type.getQualifier().layoutPushConstant = true;
1963
32
            break;
1964
58
        case EatConstantId:
1965
            // specialization constant
1966
58
            if (type.getQualifier().storage != EvqConst) {
1967
0
                error(loc, "needs a const type", "constant_id", "");
1968
0
                break;
1969
0
            }
1970
58
            if (it->getInt(value)) {
1971
58
                TSourceLoc loc;
1972
58
                loc.init();
1973
58
                setSpecConstantId(loc, type.getQualifier(), value);
1974
58
            }
1975
58
            break;
1976
1977
        // image formats
1978
0
        case EatFormatRgba32f:      type.getQualifier().layoutFormat = ElfRgba32f;      break;
1979
0
        case EatFormatRgba16f:      type.getQualifier().layoutFormat = ElfRgba16f;      break;
1980
0
        case EatFormatR32f:         type.getQualifier().layoutFormat = ElfR32f;         break;
1981
0
        case EatFormatRgba8:        type.getQualifier().layoutFormat = ElfRgba8;        break;
1982
0
        case EatFormatRgba8Snorm:   type.getQualifier().layoutFormat = ElfRgba8Snorm;   break;
1983
0
        case EatFormatRg32f:        type.getQualifier().layoutFormat = ElfRg32f;        break;
1984
0
        case EatFormatRg16f:        type.getQualifier().layoutFormat = ElfRg16f;        break;
1985
0
        case EatFormatR11fG11fB10f: type.getQualifier().layoutFormat = ElfR11fG11fB10f; break;
1986
0
        case EatFormatR16f:         type.getQualifier().layoutFormat = ElfR16f;         break;
1987
0
        case EatFormatRgba16:       type.getQualifier().layoutFormat = ElfRgba16;       break;
1988
0
        case EatFormatRgb10A2:      type.getQualifier().layoutFormat = ElfRgb10A2;      break;
1989
0
        case EatFormatRg16:         type.getQualifier().layoutFormat = ElfRg16;         break;
1990
0
        case EatFormatRg8:          type.getQualifier().layoutFormat = ElfRg8;          break;
1991
0
        case EatFormatR16:          type.getQualifier().layoutFormat = ElfR16;          break;
1992
0
        case EatFormatR8:           type.getQualifier().layoutFormat = ElfR8;           break;
1993
0
        case EatFormatRgba16Snorm:  type.getQualifier().layoutFormat = ElfRgba16Snorm;  break;
1994
0
        case EatFormatRg16Snorm:    type.getQualifier().layoutFormat = ElfRg16Snorm;    break;
1995
0
        case EatFormatRg8Snorm:     type.getQualifier().layoutFormat = ElfRg8Snorm;     break;
1996
0
        case EatFormatR16Snorm:     type.getQualifier().layoutFormat = ElfR16Snorm;     break;
1997
0
        case EatFormatR8Snorm:      type.getQualifier().layoutFormat = ElfR8Snorm;      break;
1998
0
        case EatFormatRgba32i:      type.getQualifier().layoutFormat = ElfRgba32i;      break;
1999
0
        case EatFormatRgba16i:      type.getQualifier().layoutFormat = ElfRgba16i;      break;
2000
0
        case EatFormatRgba8i:       type.getQualifier().layoutFormat = ElfRgba8i;       break;
2001
0
        case EatFormatR32i:         type.getQualifier().layoutFormat = ElfR32i;         break;
2002
0
        case EatFormatRg32i:        type.getQualifier().layoutFormat = ElfRg32i;        break;
2003
0
        case EatFormatRg16i:        type.getQualifier().layoutFormat = ElfRg16i;        break;
2004
0
        case EatFormatRg8i:         type.getQualifier().layoutFormat = ElfRg8i;         break;
2005
0
        case EatFormatR16i:         type.getQualifier().layoutFormat = ElfR16i;         break;
2006
0
        case EatFormatR8i:          type.getQualifier().layoutFormat = ElfR8i;          break;
2007
0
        case EatFormatRgba32ui:     type.getQualifier().layoutFormat = ElfRgba32ui;     break;
2008
0
        case EatFormatRgba16ui:     type.getQualifier().layoutFormat = ElfRgba16ui;     break;
2009
0
        case EatFormatRgba8ui:      type.getQualifier().layoutFormat = ElfRgba8ui;      break;
2010
0
        case EatFormatR32ui:        type.getQualifier().layoutFormat = ElfR32ui;        break;
2011
0
        case EatFormatRgb10a2ui:    type.getQualifier().layoutFormat = ElfRgb10a2ui;    break;
2012
0
        case EatFormatRg32ui:       type.getQualifier().layoutFormat = ElfRg32ui;       break;
2013
0
        case EatFormatRg16ui:       type.getQualifier().layoutFormat = ElfRg16ui;       break;
2014
0
        case EatFormatRg8ui:        type.getQualifier().layoutFormat = ElfRg8ui;        break;
2015
0
        case EatFormatR16ui:        type.getQualifier().layoutFormat = ElfR16ui;        break;
2016
0
        case EatFormatR8ui:         type.getQualifier().layoutFormat = ElfR8ui;         break;
2017
0
        case EatFormatUnknown:      type.getQualifier().layoutFormat = ElfNone;         break;
2018
2019
0
        case EatNonWritable:  type.getQualifier().readonly = true;   break;
2020
0
        case EatNonReadable:  type.getQualifier().writeonly = true;  break;
2021
2022
14
        default:
2023
14
            if (! allowEntry)
2024
2
                warn(loc, "attribute does not apply to a type", "", "");
2025
14
            break;
2026
446
        }
2027
446
    }
2028
446
}
2029
2030
//
2031
// Do all special handling for the entry point, including wrapping
2032
// the shader's entry point with the official entry point that will call it.
2033
//
2034
// The following:
2035
//
2036
//    retType shaderEntryPoint(args...) // shader declared entry point
2037
//    { body }
2038
//
2039
// Becomes
2040
//
2041
//    out retType ret;
2042
//    in iargs<that are input>...;
2043
//    out oargs<that are output> ...;
2044
//
2045
//    void shaderEntryPoint()    // synthesized, but official, entry point
2046
//    {
2047
//        args<that are input> = iargs...;
2048
//        ret = @shaderEntryPoint(args...);
2049
//        oargs = args<that are output>...;
2050
//    }
2051
//    retType @shaderEntryPoint(args...)
2052
//    { body }
2053
//
2054
// The symbol table will still map the original entry point name to the
2055
// the modified function and its new name:
2056
//
2057
//    symbol table:  shaderEntryPoint  ->   @shaderEntryPoint
2058
//
2059
// Returns nullptr if no entry-point tree was built, otherwise, returns
2060
// a subtree that creates the entry point.
2061
//
2062
TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
2063
                                                   const TAttributes& attributes)
2064
2.17k
{
2065
    // Return true if this is a tessellation patch constant function input to a domain shader.
2066
2.17k
    const auto isDsPcfInput = [this](const TType& type) {
2067
182
        return language == EShLangTessEvaluation &&
2068
4
        type.contains([](const TType* t) {
2069
4
                return t->getQualifier().builtIn == EbvTessLevelOuter ||
2070
4
                       t->getQualifier().builtIn == EbvTessLevelInner;
2071
4
            });
2072
182
    };
2073
2074
    // if we aren't in the entry point, fix the IO as such and exit
2075
2.17k
    if (! isEntrypointName(userFunction.getName())) {
2076
1.34k
        remapNonEntryPointIO(userFunction);
2077
1.34k
        return nullptr;
2078
1.34k
    }
2079
2080
838
    entryPointFunction = &userFunction; // needed in finish()
2081
2082
    // Handle entry point attributes
2083
838
    handleEntryPointAttributes(loc, attributes);
2084
2085
    // entry point logic...
2086
2087
    // Move parameters and return value to shader in/out
2088
838
    TVariable* entryPointOutput; // gets created in remapEntryPointIO
2089
838
    TVector<TVariable*> inputs;
2090
838
    TVector<TVariable*> outputs;
2091
838
    remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
2092
2093
    // Further this return/in/out transform by flattening, splitting, and assigning locations
2094
976
    const auto makeVariableInOut = [&](TVariable& variable) {
2095
976
        if (variable.getType().isStruct()) {
2096
326
            bool arrayed = variable.getType().getQualifier().isArrayedIo(language);
2097
326
            flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */, arrayed);
2098
326
        }
2099
        // TODO: flatten arrays too
2100
        // TODO: flatten everything in I/O
2101
        // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed
2102
2103
        // For clip and cull distance, multiple output variables potentially get merged
2104
        // into one in assignClipCullDistance.  That code in assignClipCullDistance
2105
        // handles the interface logic, so we avoid it here in that case.
2106
976
        if (!isClipOrCullDistance(variable.getType()))
2107
952
            assignToInterface(variable);
2108
976
    };
2109
838
    if (entryPointOutput != nullptr)
2110
780
        makeVariableInOut(*entryPointOutput);
2111
1.01k
    for (auto it = inputs.begin(); it != inputs.end(); ++it)
2112
180
        if (!isDsPcfInput((*it)->getType()))  // wait until the end for PCF input (see comment below)
2113
180
            makeVariableInOut(*(*it));
2114
854
    for (auto it = outputs.begin(); it != outputs.end(); ++it)
2115
16
        makeVariableInOut(*(*it));
2116
2117
    // In the domain shader, PCF input must be at the end of the linkage.  That's because in the
2118
    // hull shader there is no ordering: the output comes from the separate PCF, which does not
2119
    // participate in the argument list.  That is always put at the end of the HS linkage, so the
2120
    // input side of the DS must match.  The argument may be in any position in the DS argument list
2121
    // however, so this ensures the linkage is built in the correct order regardless of argument order.
2122
838
    if (language == EShLangTessEvaluation) {
2123
12
        for (auto it = inputs.begin(); it != inputs.end(); ++it)
2124
2
            if (isDsPcfInput((*it)->getType()))
2125
0
                makeVariableInOut(*(*it));
2126
10
    }
2127
2128
    // Add uniform parameters to the $Global uniform block.
2129
838
    TVector<TVariable*> opaque_uniforms;
2130
1.03k
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2131
196
        TType& paramType = *userFunction[i].type;
2132
196
        TString& paramName = *userFunction[i].name;
2133
196
        if (paramType.getQualifier().storage == EvqUniform) {
2134
0
            if (!paramType.containsOpaque()) {
2135
                // Add it to the global uniform block.
2136
0
                growGlobalUniformBlock(loc, paramType, paramName);
2137
0
            } else {
2138
                // Declare it as a separate variable.
2139
0
                TVariable *var = makeInternalVariable(paramName.c_str(), paramType);
2140
0
                opaque_uniforms.push_back(var);
2141
0
            }
2142
0
        }
2143
196
    }
2144
2145
    // Synthesize the call
2146
2147
838
    pushScope(); // matches the one in handleFunctionBody()
2148
2149
    // new signature
2150
838
    TType voidType(EbtVoid);
2151
838
    TFunction synthEntryPoint(&userFunction.getName(), voidType);
2152
838
    TIntermAggregate* synthParams = new TIntermAggregate();
2153
838
    intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
2154
838
    intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
2155
838
    intermediate.incrementEntryPointCount();
2156
838
    TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
2157
2158
    // change original name
2159
838
    userFunction.addPrefix("@");                         // change the name in the function, but not in the symbol table
2160
2161
    // Copy inputs (shader-in -> calling arg), while building up the call node
2162
838
    TVector<TVariable*> argVars;
2163
838
    TIntermAggregate* synthBody = new TIntermAggregate();
2164
838
    auto inputIt = inputs.begin();
2165
838
    auto opaqueUniformIt = opaque_uniforms.begin();
2166
838
    TIntermTyped* callingArgs = nullptr;
2167
2168
1.03k
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2169
196
        TParameter& param = userFunction[i];
2170
196
        argVars.push_back(makeInternalVariable(*param.name, *param.type));
2171
196
        argVars.back()->getWritableType().getQualifier().makeTemporary();
2172
2173
        // Track the input patch, which is the only non-builtin supported by hull shader PCF.
2174
196
        if (param.getDeclaredBuiltIn() == EbvInputPatch)
2175
0
            inputPatch = argVars.back();
2176
2177
196
        TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
2178
196
        handleFunctionArgument(&callee, callingArgs, arg);
2179
196
        if (param.type->getQualifier().isParamInput()) {
2180
180
            TIntermTyped* input = intermediate.addSymbol(**inputIt);
2181
180
            if (input->getType().getQualifier().builtIn == EbvFragCoord && intermediate.getDxPositionW()) {
2182
                // Replace FragCoord W with reciprocal
2183
0
                auto pos_xyz = handleDotDereference(loc, input, "xyz");
2184
0
                auto pos_w   = handleDotDereference(loc, input, "w");
2185
0
                auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2186
0
                auto recip_w = intermediate.addBinaryMath(EOpDiv, one, pos_w, loc);
2187
0
                TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
2188
0
                dst->getSequence().push_back(pos_xyz);
2189
0
                dst->getSequence().push_back(recip_w);
2190
0
                dst->setType(TType(EbtFloat, EvqTemporary, 4));
2191
0
                dst->setLoc(loc);
2192
0
                input = dst;
2193
0
            }
2194
180
            intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, input));
2195
180
            inputIt++;
2196
180
        }
2197
196
        if (param.type->getQualifier().storage == EvqUniform) {
2198
0
            if (!param.type->containsOpaque()) {
2199
                // Look it up in the $Global uniform block.
2200
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2201
0
                                                                   handleVariable(loc, param.name)));
2202
0
            } else {
2203
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2204
0
                                                                   intermediate.addSymbol(**opaqueUniformIt)));
2205
0
                ++opaqueUniformIt;
2206
0
            }
2207
0
        }
2208
196
    }
2209
2210
    // Call
2211
838
    currentCaller = synthEntryPoint.getMangledName();
2212
838
    TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
2213
838
    currentCaller = userFunction.getMangledName();
2214
2215
    // Return value
2216
838
    if (entryPointOutput) {
2217
780
        TIntermTyped* returnAssign;
2218
2219
        // For hull shaders, the wrapped entry point return value is written to
2220
        // an array element as indexed by invocation ID, which we might have to make up.
2221
        // This is required to match SPIR-V semantics.
2222
780
        if (language == EShLangTessControl) {
2223
460
            TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
2224
2225
            // If there is no user declared invocation ID, we must make one.
2226
460
            if (invocationIdSym == nullptr) {
2227
384
                TType invocationIdType(EbtUint, EvqIn, 1);
2228
384
                TString* invocationIdName = NewPoolTString("InvocationId");
2229
384
                invocationIdType.getQualifier().builtIn = EbvInvocationId;
2230
2231
384
                TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
2232
2233
384
                globalQualifierFix(loc, variable->getWritableType().getQualifier());
2234
384
                trackLinkage(*variable);
2235
2236
384
                invocationIdSym = intermediate.addSymbol(*variable);
2237
384
            }
2238
2239
460
            TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
2240
460
                                                          invocationIdSym, loc);
2241
2242
            // Set the type of the array element being dereferenced
2243
460
            const TType derefElementType(entryPointOutput->getType(), 0);
2244
460
            element->setType(derefElementType);
2245
2246
460
            returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
2247
460
        } else {
2248
320
            returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
2249
320
        }
2250
780
        intermediate.growAggregate(synthBody, returnAssign);
2251
780
    } else
2252
58
        intermediate.growAggregate(synthBody, callReturn);
2253
2254
    // Output copies
2255
838
    auto outputIt = outputs.begin();
2256
1.03k
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2257
196
        TParameter& param = userFunction[i];
2258
2259
        // GS outputs are via emit, so we do not copy them here.
2260
196
        if (param.type->getQualifier().isParamOutput()) {
2261
16
            if (param.getDeclaredBuiltIn() == EbvGsOutputStream) {
2262
                // GS output stream does not assign outputs here: it's the Append() method
2263
                // which writes to the output, probably multiple times separated by Emit.
2264
                // We merely remember the output to use, here.
2265
0
                gsStreamOutput = *outputIt;
2266
16
            } else {
2267
16
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
2268
16
                                                                   intermediate.addSymbol(**outputIt),
2269
16
                                                                   intermediate.addSymbol(*argVars[i])));
2270
16
            }
2271
2272
16
            outputIt++;
2273
16
        }
2274
196
    }
2275
2276
    // Put the pieces together to form a full function subtree
2277
    // for the synthesized entry point.
2278
838
    synthBody->setOperator(EOpSequence);
2279
838
    TIntermNode* synthFunctionDef = synthParams;
2280
838
    handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
2281
2282
838
    entryPointFunctionBody = synthBody;
2283
2284
838
    return synthFunctionDef;
2285
2.17k
}
2286
2287
void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody,
2288
                                          TIntermNode*& node)
2289
1.84k
{
2290
1.84k
    node = intermediate.growAggregate(node, functionBody);
2291
1.84k
    intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
2292
1.84k
    node->getAsAggregate()->setName(function.getMangledName().c_str());
2293
2294
1.84k
    popScope();
2295
1.84k
    if (function.hasImplicitThis())
2296
18
        popImplicitThis();
2297
2298
1.84k
    if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
2299
48
        error(loc, "function does not return a value:", "", function.getName().c_str());
2300
1.84k
}
2301
2302
// AST I/O is done through shader globals declared in the 'in' or 'out'
2303
// storage class.  An HLSL entry point has a return value, input parameters
2304
// and output parameters.  These need to get remapped to the AST I/O.
2305
void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
2306
    TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
2307
838
{
2308
    // We might have in input structure type with no decorations that caused it
2309
    // to look like an input type, yet it has (e.g.) interpolation types that
2310
    // must be modified that turn it into an input type.
2311
    // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
2312
838
    const auto synthesizeEditedInput = [this](TType& type) {
2313
        // True if a type needs to be 'flat'
2314
180
        const auto needsFlat = [](const TType& type) {
2315
2
            return type.containsBasicType(EbtInt) ||
2316
2
                    type.containsBasicType(EbtUint) ||
2317
0
                    type.containsBasicType(EbtInt64) ||
2318
0
                    type.containsBasicType(EbtUint64) ||
2319
0
                    type.containsBasicType(EbtBool) ||
2320
0
                    type.containsBasicType(EbtDouble);
2321
2
        };
2322
2323
180
        if (language == EShLangFragment && needsFlat(type)) {
2324
2
            if (type.isStruct()) {
2325
0
                TTypeList* finalList = nullptr;
2326
0
                auto it = ioTypeMap.find(type.getStruct());
2327
0
                if (it == ioTypeMap.end() || it->second.input == nullptr) {
2328
                    // Getting here means we have no input struct, but we need one.
2329
0
                    auto list = new TTypeList;
2330
0
                    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
2331
0
                        TType* newType = new TType;
2332
0
                        newType->shallowCopy(*member->type);
2333
0
                        TTypeLoc typeLoc = { newType, member->loc };
2334
0
                        list->push_back(typeLoc);
2335
0
                    }
2336
                    // install the new input type
2337
0
                    if (it == ioTypeMap.end()) {
2338
0
                        tIoKinds newLists = { list, nullptr, nullptr };
2339
0
                        ioTypeMap[type.getStruct()] = newLists;
2340
0
                    } else
2341
0
                        it->second.input = list;
2342
0
                    finalList = list;
2343
0
                } else
2344
0
                    finalList = it->second.input;
2345
                // edit for 'flat'
2346
0
                for (auto member = finalList->begin(); member != finalList->end(); ++member) {
2347
0
                    if (needsFlat(*member->type)) {
2348
0
                        member->type->getQualifier().clearInterpolation();
2349
0
                        member->type->getQualifier().flat = true;
2350
0
                    }
2351
0
                }
2352
2
            } else {
2353
2
                type.getQualifier().clearInterpolation();
2354
2
                type.getQualifier().flat = true;
2355
2
            }
2356
2
        }
2357
180
    };
2358
2359
    // Do the actual work to make a type be a shader input or output variable,
2360
    // and clear the original to be non-IO (for use as a normal function parameter/return).
2361
1.43k
    const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
2362
1.43k
        TVariable* ioVariable = makeInternalVariable(name, type);
2363
1.43k
        clearUniformInputOutput(type.getQualifier());
2364
1.43k
        if (type.isStruct()) {
2365
478
            auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
2366
478
            if (newLists != ioTypeMap.end()) {
2367
4
                if (storage == EvqVaryingIn && newLists->second.input)
2368
0
                    ioVariable->getWritableType().setStruct(newLists->second.input);
2369
4
                else if (storage == EvqVaryingOut && newLists->second.output)
2370
4
                    ioVariable->getWritableType().setStruct(newLists->second.output);
2371
4
            }
2372
478
        }
2373
1.43k
        if (storage == EvqVaryingIn) {
2374
180
            correctInput(ioVariable->getWritableType().getQualifier());
2375
180
            if (language == EShLangTessEvaluation)
2376
2
                if (!ioVariable->getType().isArray())
2377
2
                    ioVariable->getWritableType().getQualifier().patch = true;
2378
1.25k
        } else {
2379
1.25k
            correctOutput(ioVariable->getWritableType().getQualifier());
2380
1.25k
        }
2381
1.43k
        ioVariable->getWritableType().getQualifier().storage = storage;
2382
2383
1.43k
        fixBuiltInIoType(ioVariable->getWritableType());
2384
2385
1.43k
        return ioVariable;
2386
1.43k
    };
2387
2388
    // return value is actually a shader-scoped output (out)
2389
838
    if (function.getType().getBasicType() == EbtVoid) {
2390
58
        returnValue = nullptr;
2391
780
    } else {
2392
780
        if (language == EShLangTessControl) {
2393
            // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
2394
            // array in SPIR-V semantics.  We'll write to it indexed by invocation ID.
2395
2396
460
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2397
2398
460
            TType outputType;
2399
460
            outputType.shallowCopy(function.getType());
2400
2401
            // vertices has necessarily already been set when handling entry point attributes.
2402
460
            TArraySizes* arraySizes = new TArraySizes;
2403
460
            arraySizes->addInnerSize(intermediate.getVertices());
2404
460
            outputType.transferArraySizes(arraySizes);
2405
2406
460
            clearUniformInputOutput(function.getWritableType().getQualifier());
2407
460
            returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
2408
460
        } else {
2409
320
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2410
320
        }
2411
780
    }
2412
2413
    // parameters are actually shader-scoped inputs and outputs (in or out)
2414
1.03k
    for (int i = 0; i < function.getParamCount(); i++) {
2415
196
        TType& paramType = *function[i].type;
2416
196
        if (paramType.getQualifier().isParamInput()) {
2417
180
            synthesizeEditedInput(paramType);
2418
180
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
2419
180
            inputs.push_back(argAsGlobal);
2420
180
        }
2421
196
        if (paramType.getQualifier().isParamOutput()) {
2422
16
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
2423
16
            outputs.push_back(argAsGlobal);
2424
16
        }
2425
196
    }
2426
838
}
2427
2428
// An HLSL function that looks like an entry point, but is not,
2429
// declares entry point IO built-ins, but these have to be undone.
2430
void HlslParseContext::remapNonEntryPointIO(TFunction& function)
2431
1.34k
{
2432
    // return value
2433
1.34k
    if (function.getType().getBasicType() != EbtVoid)
2434
974
        clearUniformInputOutput(function.getWritableType().getQualifier());
2435
2436
    // parameters.
2437
    // References to structuredbuffer types are left unmodified
2438
3.89k
    for (int i = 0; i < function.getParamCount(); i++)
2439
2.55k
        if (!isReference(*function[i].type))
2440
2.53k
            clearUniformInputOutput(function[i].type->getQualifier());
2441
1.34k
}
2442
2443
TIntermNode* HlslParseContext::handleDeclare(const TSourceLoc& loc, TIntermTyped* var)
2444
0
{
2445
0
    return intermediate.addUnaryNode(EOpDeclare, var, loc, TType(EbtVoid));
2446
0
}
2447
2448
// Handle function returns, including type conversions to the function return type
2449
// if necessary.
2450
TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
2451
908
{
2452
908
    functionReturnsValue = true;
2453
2454
908
    if (currentFunctionType->getBasicType() == EbtVoid) {
2455
100
        error(loc, "void function cannot return a value", "return", "");
2456
100
        return intermediate.addBranch(EOpReturn, loc);
2457
808
    } else if (*currentFunctionType != value->getType()) {
2458
230
        value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
2459
230
        if (value && *currentFunctionType != value->getType())
2460
130
            value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value);
2461
230
        if (value == nullptr || *currentFunctionType != value->getType()) {
2462
88
            error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
2463
88
            return value;
2464
88
        }
2465
230
    }
2466
2467
720
    return intermediate.addBranch(EOpReturn, value, loc);
2468
908
}
2469
2470
void HlslParseContext::handleFunctionArgument(TFunction* function,
2471
                                              TIntermTyped*& arguments, TIntermTyped* newArg)
2472
62.3k
{
2473
62.3k
    TParameter param = { nullptr, new TType, nullptr };
2474
62.3k
    param.type->shallowCopy(newArg->getType());
2475
2476
62.3k
    function->addParameter(param);
2477
62.3k
    if (arguments)
2478
42.1k
        arguments = intermediate.growAggregate(arguments, newArg);
2479
20.1k
    else
2480
20.1k
        arguments = newArg;
2481
62.3k
}
2482
2483
// FragCoord may require special loading: we can optionally reciprocate W.
2484
TIntermTyped* HlslParseContext::assignFromFragCoord(const TSourceLoc& loc, TOperator op,
2485
                                                    TIntermTyped* left, TIntermTyped* right)
2486
0
{
2487
    // If we are not asked for reciprocal W, use a plain old assign.
2488
0
    if (!intermediate.getDxPositionW())
2489
0
        return intermediate.addAssign(op, left, right, loc);
2490
2491
    // If we get here, we should reciprocate W.
2492
0
    TIntermAggregate* assignList = nullptr;
2493
2494
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2495
0
    TVariable* rhsTempVar = nullptr;
2496
0
    rhsTempVar = makeInternalVariable("@fragcoord", right->getType());
2497
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2498
2499
0
    {
2500
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2501
0
        assignList = intermediate.growAggregate(assignList,
2502
0
            intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2503
0
    }
2504
2505
    // tmp.w = 1.0 / tmp.w
2506
0
    {
2507
0
        const int W = 3;
2508
2509
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2510
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2511
0
        TIntermTyped* index = intermediate.addConstantUnion(W, loc);
2512
2513
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2514
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2515
2516
0
        const TType derefType(right->getType(), 0);
2517
2518
0
        lhsElement->setType(derefType);
2519
0
        rhsElement->setType(derefType);
2520
2521
0
        auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2522
0
        auto recip_w = intermediate.addBinaryMath(EOpDiv, one, rhsElement, loc);
2523
2524
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, recip_w, loc));
2525
0
    }
2526
2527
    // Assign the rhs temp (now with W reciprocal) to the final output
2528
0
    {
2529
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2530
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2531
0
    }
2532
2533
0
    assert(assignList != nullptr);
2534
0
    assignList->setOperator(EOpSequence);
2535
2536
0
    return assignList;
2537
0
}
2538
2539
// Position may require special handling: we can optionally invert Y.
2540
// See: https://github.com/KhronosGroup/glslang/issues/1173
2541
//      https://github.com/KhronosGroup/glslang/issues/494
2542
TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
2543
                                               TIntermTyped* left, TIntermTyped* right)
2544
0
{
2545
    // If we are not asked for Y inversion, use a plain old assign.
2546
0
    if (!intermediate.getInvertY())
2547
0
        return intermediate.addAssign(op, left, right, loc);
2548
2549
    // If we get here, we should invert Y.
2550
0
    TIntermAggregate* assignList = nullptr;
2551
2552
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2553
0
    TVariable* rhsTempVar = nullptr;
2554
0
    rhsTempVar = makeInternalVariable("@position", right->getType());
2555
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2556
2557
0
    {
2558
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2559
0
        assignList = intermediate.growAggregate(assignList,
2560
0
                                                intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2561
0
    }
2562
2563
    // pos.y = -pos.y
2564
0
    {
2565
0
        const int Y = 1;
2566
2567
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2568
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2569
0
        TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
2570
2571
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2572
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2573
2574
0
        const TType derefType(right->getType(), 0);
2575
2576
0
        lhsElement->setType(derefType);
2577
0
        rhsElement->setType(derefType);
2578
2579
0
        TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
2580
2581
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
2582
0
    }
2583
2584
    // Assign the rhs temp (now with Y inversion) to the final output
2585
0
    {
2586
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2587
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2588
0
    }
2589
2590
0
    assert(assignList != nullptr);
2591
0
    assignList->setOperator(EOpSequence);
2592
2593
0
    return assignList;
2594
0
}
2595
2596
// Clip and cull distance require special handling due to a semantic mismatch.  In HLSL,
2597
// these can be float scalar, float vector, or arrays of float scalar or float vector.
2598
// In SPIR-V, they are arrays of scalar floats in all cases.  We must copy individual components
2599
// (e.g, both x and y components of a float2) out into the destination float array.
2600
//
2601
// The values are assigned to sequential members of the output array.  The inner dimension
2602
// is vector components.  The outer dimension is array elements.
2603
TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId,
2604
                                                           TIntermTyped* left, TIntermTyped* right)
2605
24
{
2606
24
    switch (language) {
2607
0
    case EShLangFragment:
2608
0
    case EShLangVertex:
2609
0
    case EShLangGeometry:
2610
0
        break;
2611
24
    default:
2612
24
        error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", "");
2613
24
        return nullptr;
2614
24
    }
2615
2616
0
    TVariable** clipCullVar = nullptr;
2617
2618
    // Figure out if we are assigning to, or from, clip or cull distance.
2619
0
    const bool isOutput = isClipOrCullDistance(left->getType());
2620
2621
    // This is the rvalue or lvalue holding the clip or cull distance.
2622
0
    TIntermTyped* clipCullNode = isOutput ? left : right;
2623
    // This is the value going into or out of the clip or cull distance.
2624
0
    TIntermTyped* internalNode = isOutput ? right : left;
2625
2626
0
    const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn;
2627
2628
0
    decltype(clipSemanticNSizeIn)* semanticNSize = nullptr;
2629
2630
    // Refer to either the clip or the cull distance, depending on semantic.
2631
0
    switch (builtInType) {
2632
0
    case EbvClipDistance:
2633
0
        clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput;
2634
0
        semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn;
2635
0
        break;
2636
0
    case EbvCullDistance:
2637
0
        clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput;
2638
0
        semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn;
2639
0
        break;
2640
2641
    // called invalidly: we expected a clip or a cull distance.
2642
    // static compile time problem: should not happen.
2643
0
    default: assert(0); return nullptr;
2644
0
    }
2645
2646
    // This is the offset in the destination array of a given semantic's data
2647
0
    std::array<int, maxClipCullRegs> semanticOffset;
2648
2649
    // Calculate offset of variable of semantic N in destination array
2650
0
    int arrayLoc = 0;
2651
0
    int vecItems = 0;
2652
2653
0
    for (int x = 0; x < maxClipCullRegs; ++x) {
2654
        // See if we overflowed the vec4 packing
2655
0
        if ((vecItems + (*semanticNSize)[x]) > 4) {
2656
0
            arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4
2657
0
            vecItems = 0;
2658
0
        }
2659
2660
0
        semanticOffset[x] = arrayLoc;
2661
0
        vecItems += (*semanticNSize)[x];
2662
0
        arrayLoc += (*semanticNSize)[x];
2663
0
    }
2664
2665
2666
    // It can have up to 2 array dimensions (in the case of geometry shader inputs)
2667
0
    const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes();
2668
0
    const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0;
2669
    // vector sizes:
2670
0
    const int internalVectorSize = internalNode->getType().getVectorSize();
2671
    // array sizes, or 1 if it's not an array:
2672
0
    const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1);
2673
0
    const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1);
2674
2675
    // The created type may be an array of arrays, e.g, for geometry shader inputs.
2676
0
    const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput);
2677
2678
    // If we haven't created the output already, create it now.
2679
0
    if (*clipCullVar == nullptr) {
2680
        // ClipDistance and CullDistance are handled specially in the entry point input/output copy
2681
        // algorithm, because they may need to be unpacked from components of vectors (or a scalar)
2682
        // into a float array, or vice versa.  Here, we make the array the right size and type,
2683
        // which depends on the incoming data, which has several potential dimensions:
2684
        //    * Semantic ID
2685
        //    * vector size
2686
        //    * array size
2687
        // Of those, semantic ID and array size cannot appear simultaneously.
2688
        //
2689
        // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two
2690
        // array dimensions.  The shader's declaration may have one or two array dimensions.  One is always
2691
        // the geometry's dimension.
2692
2693
0
        const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed;
2694
2695
0
        const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1);
2696
0
        const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1;
2697
2698
0
        TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1);
2699
0
        clipCullType.getQualifier() = clipCullNode->getType().getQualifier();
2700
2701
        // Create required array dimension
2702
0
        TArraySizes* arraySizes = new TArraySizes;
2703
0
        if (isImplicitlyArrayed)
2704
0
            arraySizes->addInnerSize(requiredOuterArraySize);
2705
0
        arraySizes->addInnerSize(requiredInnerArraySize);
2706
0
        clipCullType.transferArraySizes(arraySizes);
2707
2708
        // Obtain symbol name: we'll use that for the symbol we introduce.
2709
0
        TIntermSymbol* sym = clipCullNode->getAsSymbolNode();
2710
0
        assert(sym != nullptr);
2711
2712
        // We are moving the semantic ID from the layout location, so it is no longer needed or
2713
        // desired there.
2714
0
        clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
2715
2716
        // Create variable and track its linkage
2717
0
        *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType);
2718
2719
0
        trackLinkage(**clipCullVar);
2720
0
    }
2721
2722
    // Create symbol for the clip or cull variable.
2723
0
    TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar);
2724
2725
    // vector sizes:
2726
0
    const int clipCullVectorSize = clipCullSym->getType().getVectorSize();
2727
2728
    // array sizes, or 1 if it's not an array:
2729
0
    const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes();
2730
0
    const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1;
2731
0
    const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0);
2732
2733
    // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics.
2734
    // fixBuiltInIoType() should have handled that upstream.
2735
0
    assert(clipCullSym->getType().isArray());
2736
0
    assert(clipCullSym->getType().getVectorSize() == 1);
2737
0
    assert(clipCullSym->getType().getBasicType() == EbtFloat);
2738
2739
    // We may be creating multiple sub-assignments.  This is an aggregate to hold them.
2740
    // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
2741
0
    TIntermAggregate* assignList = nullptr;
2742
2743
    // Holds individual component assignments as we make them.
2744
0
    TIntermTyped* clipCullAssign = nullptr;
2745
2746
    // If the types are homomorphic, use a simple assign.  No need to mess about with
2747
    // individual components.
2748
0
    if (clipCullSym->getType().isArray() == internalNode->getType().isArray() &&
2749
0
        clipCullInnerArraySize == internalInnerArraySize &&
2750
0
        clipCullOuterArraySize == internalOuterArraySize &&
2751
0
        clipCullVectorSize == internalVectorSize) {
2752
2753
0
        if (isOutput)
2754
0
            clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc);
2755
0
        else
2756
0
            clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc);
2757
2758
0
        assignList = intermediate.growAggregate(assignList, clipCullAssign);
2759
0
        assignList->setOperator(EOpSequence);
2760
2761
0
        return assignList;
2762
0
    }
2763
2764
    // We are going to copy each component of the internal (per array element if indicated) to sequential
2765
    // array elements of the clipCullSym.  This tracks the lhs element we're writing to as we go along.
2766
    // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above.
2767
0
    int clipCullInnerArrayPos = semanticOffset[semanticId];
2768
0
    int clipCullOuterArrayPos = 0;
2769
2770
    // Lambda to add an index to a node, set the type of the result, and return the new node.
2771
0
    const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* {
2772
0
        const TType derefType(node->getType(), 0);
2773
0
        node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc);
2774
0
        node->setType(derefType);
2775
0
        return node;
2776
0
    };
2777
2778
    // Loop through every component of every element of the internal, and copy to or from the matching external.
2779
0
    for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) {
2780
0
        for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) {
2781
0
            for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) {
2782
                // clip/cull array member to read from / write to:
2783
0
                TIntermTyped* clipCullMember = clipCullSym;
2784
2785
                // If implicitly arrayed, there is an outer array dimension involved
2786
0
                if (isImplicitlyArrayed)
2787
0
                    clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos);
2788
2789
                // Index into proper array position for clip cull member
2790
0
                clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++);
2791
2792
                // if needed, start over with next outer array slice.
2793
0
                if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) {
2794
0
                    clipCullInnerArrayPos = semanticOffset[semanticId];
2795
0
                    ++clipCullOuterArrayPos;
2796
0
                }
2797
2798
                // internal member to read from / write to:
2799
0
                TIntermTyped* internalMember = internalNode;
2800
2801
                // If internal node has outer array dimension, index appropriately.
2802
0
                if (internalArrayDims > 1)
2803
0
                    internalMember = addIndex(internalMember, internalOuterArrayPos);
2804
2805
                // If internal node has inner array dimension, index appropriately.
2806
0
                if (internalArrayDims > 0)
2807
0
                    internalMember = addIndex(internalMember, internalInnerArrayPos);
2808
2809
                // If internal node is a vector, extract the component of interest.
2810
0
                if (internalNode->getType().isVector())
2811
0
                    internalMember = addIndex(internalMember, internalComponent);
2812
2813
                // Create an assignment: output from internal to clip cull, or input from clip cull to internal.
2814
0
                if (isOutput)
2815
0
                    clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc);
2816
0
                else
2817
0
                    clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc);
2818
2819
                // Track assignment in the sequence.
2820
0
                assignList = intermediate.growAggregate(assignList, clipCullAssign);
2821
0
            }
2822
0
        }
2823
0
    }
2824
2825
0
    assert(assignList != nullptr);
2826
0
    assignList->setOperator(EOpSequence);
2827
2828
0
    return assignList;
2829
0
}
2830
2831
// Some simple source assignments need to be flattened to a sequence
2832
// of AST assignments. Catch these and flatten, otherwise, pass through
2833
// to intermediate.addAssign().
2834
//
2835
// Also, assignment to matrix swizzles requires multiple component assignments,
2836
// intercept those as well.
2837
TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
2838
                                             TIntermTyped* right)
2839
16.7k
{
2840
16.7k
    if (left == nullptr || right == nullptr)
2841
0
        return nullptr;
2842
2843
    // writing to opaques will require fixing transforms
2844
16.7k
    if (left->getType().containsOpaque())
2845
6
        intermediate.setNeedsLegalization();
2846
2847
16.7k
    if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
2848
8
        return handleAssignToMatrixSwizzle(loc, op, left, right);
2849
2850
    // Return true if the given node is an index operation into a split variable.
2851
33.4k
    const auto indexesSplit = [this](const TIntermTyped* node) -> bool {
2852
33.4k
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2853
2854
33.4k
        if (binaryNode == nullptr)
2855
32.2k
            return false;
2856
2857
1.16k
        return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) &&
2858
548
               wasSplit(binaryNode->getLeft());
2859
33.4k
    };
2860
2861
    // Return symbol if node is symbol or index ref
2862
33.4k
    const auto getSymbol = [](const TIntermTyped* node) -> const TIntermSymbol* {
2863
33.4k
        const TIntermSymbol* symbolNode = node->getAsSymbolNode();
2864
33.4k
        if (symbolNode != nullptr)
2865
16.4k
            return symbolNode;
2866
2867
17.0k
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2868
17.0k
        if (binaryNode != nullptr && (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect))
2869
548
            return binaryNode->getLeft()->getAsSymbolNode();
2870
2871
16.4k
        return nullptr;
2872
17.0k
    };
2873
2874
    // Return true if this stage assigns clip position with potentially inverted Y
2875
16.7k
    const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
2876
16.7k
        return node->getType().getQualifier().builtIn == EbvPosition &&
2877
0
               (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
2878
16.7k
    };
2879
2880
16.7k
    const TIntermSymbol* leftSymbol = getSymbol(left);
2881
16.7k
    const TIntermSymbol* rightSymbol = getSymbol(right);
2882
2883
16.7k
    const bool isSplitLeft    = wasSplit(left) || indexesSplit(left);
2884
16.7k
    const bool isSplitRight   = wasSplit(right) || indexesSplit(right);
2885
2886
16.7k
    const bool isFlattenLeft  = wasFlattened(leftSymbol);
2887
16.7k
    const bool isFlattenRight = wasFlattened(rightSymbol);
2888
2889
    // OK to do a single assign if neither side is split or flattened.  Otherwise,
2890
    // fall through to a member-wise copy.
2891
16.7k
    if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) {
2892
        // Clip and cull distance requires more processing.  See comment above assignClipCullDistance.
2893
16.4k
        if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) {
2894
24
            const bool isOutput = isClipOrCullDistance(left->getType());
2895
2896
24
            const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
2897
24
            return assignClipCullDistance(loc, op, semanticId, left, right);
2898
16.3k
        } else if (assignsClipPos(left)) {
2899
            // Position can require special handling: see comment above assignPosition
2900
0
            return assignPosition(loc, op, left, right);
2901
16.3k
        } else if (left->getQualifier().builtIn == EbvSampleMask) {
2902
            // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars
2903
            // in the shader.  Copy the scalar RHS into the LHS array element zero, if that happens.
2904
0
            if (left->isArray() && !right->isArray()) {
2905
0
                const TType derefType(left->getType(), 0);
2906
0
                left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc);
2907
0
                left->setType(derefType);
2908
                // Fall through to add assign.
2909
0
            }
2910
0
        }
2911
2912
16.3k
        return intermediate.addAssign(op, left, right, loc);
2913
16.4k
    }
2914
2915
326
    TIntermAggregate* assignList = nullptr;
2916
326
    const TVector<TVariable*>* leftVariables = nullptr;
2917
326
    const TVector<TVariable*>* rightVariables = nullptr;
2918
2919
    // A temporary to store the right node's value, so we don't keep indirecting into it
2920
    // if it's not a simple symbol.
2921
326
    TVariable* rhsTempVar = nullptr;
2922
2923
    // If the RHS is a simple symbol node, we'll copy it for each member.
2924
326
    TIntermSymbol* cloneSymNode = nullptr;
2925
2926
326
    int memberCount = 0;
2927
2928
    // Track how many items there are to copy.
2929
326
    if (left->getType().isStruct())
2930
326
        memberCount = (int)left->getType().getStruct()->size();
2931
326
    if (left->getType().isArray())
2932
0
        memberCount = left->getType().getCumulativeArraySize();
2933
2934
326
    if (isFlattenLeft)
2935
320
        leftVariables = &flattenMap.find(leftSymbol->getId())->second.members;
2936
2937
326
    if (isFlattenRight) {
2938
6
        rightVariables = &flattenMap.find(rightSymbol->getId())->second.members;
2939
320
    } else {
2940
        // The RHS is not flattened.  There are several cases:
2941
        // 1. 1 item to copy:  Use the RHS directly.
2942
        // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
2943
        // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member.
2944
2945
320
        if (memberCount <= 1) {
2946
            // case 1: we'll use the symbol directly below.  Nothing to do.
2947
234
        } else {
2948
86
            if (right->getAsSymbolNode() != nullptr) {
2949
                // case 2: we'll copy the symbol per iteration below.
2950
0
                cloneSymNode = right->getAsSymbolNode();
2951
86
            } else {
2952
                // case 3: assign to a temp, and indirect into that.
2953
86
                rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
2954
86
                rhsTempVar->getWritableType().getQualifier().makeTemporary();
2955
86
                TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
2956
2957
                // Add this to the aggregate being built.
2958
86
                assignList = intermediate.growAggregate(assignList,
2959
86
                                                        intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
2960
86
            }
2961
86
        }
2962
320
    }
2963
2964
    // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in
2965
    // variables, which is awkward when copying between split and unsplit structures.  This variable tracks
2966
    // array indirections so they can be percolated from outer structs to inner variables.
2967
326
    std::vector <int> arrayElement;
2968
2969
326
    TStorageQualifier leftStorage = left->getType().getQualifier().storage;
2970
326
    TStorageQualifier rightStorage = right->getType().getQualifier().storage;
2971
2972
326
    int leftOffsetStart = findSubtreeOffset(*left);
2973
326
    int rightOffsetStart = findSubtreeOffset(*right);
2974
326
    int leftOffset = leftOffsetStart;
2975
326
    int rightOffset = rightOffsetStart;
2976
2977
326
    const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember,
2978
326
                               bool flattened)
2979
828
                           -> TIntermTyped * {
2980
828
        const bool split     = isLeft ? isSplitLeft   : isSplitRight;
2981
2982
828
        TIntermTyped* subTree;
2983
828
        const TType derefType(type, member);
2984
828
        const TVariable* builtInVar = nullptr;
2985
828
        if ((flattened || split) && derefType.isBuiltIn()) {
2986
0
            auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData(
2987
0
                                                   derefType.getQualifier().builtIn,
2988
0
                                                   isLeft ? leftStorage : rightStorage));
2989
0
            if (splitPair != splitBuiltIns.end())
2990
0
                builtInVar = splitPair->second;
2991
0
        }
2992
828
        if (builtInVar != nullptr) {
2993
            // copy from interstage IO built-in if needed
2994
0
            subTree = intermediate.addSymbol(*builtInVar);
2995
2996
0
            if (subTree->getType().isArray()) {
2997
                // Arrayness of builtIn symbols isn't handled by the normal recursion:
2998
                // it's been extracted and moved to the built-in.
2999
0
                if (!arrayElement.empty()) {
3000
0
                    const TType splitDerefType(subTree->getType(), arrayElement.back());
3001
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3002
0
                                                    intermediate.addConstantUnion(arrayElement.back(), loc), loc);
3003
0
                    subTree->setType(splitDerefType);
3004
0
                } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
3005
                    // This might also be a stage with arrayed outputs, in which case there's an index
3006
                    // operation we should transfer to the output builtin.
3007
3008
0
                    const TType splitDerefType(subTree->getType(), 0);
3009
0
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3010
0
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3011
0
                    subTree->setType(splitDerefType);
3012
0
                }
3013
0
            }
3014
828
        } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
3015
414
            if (isLeft) {
3016
                // offset will cycle through variables for arrayed io
3017
406
                if (leftOffset >= static_cast<int>(leftVariables->size()))
3018
0
                    leftOffset = leftOffsetStart;
3019
406
                subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]);
3020
406
            } else {
3021
                // offset will cycle through variables for arrayed io
3022
8
                if (rightOffset >= static_cast<int>(rightVariables->size()))
3023
0
                    rightOffset = rightOffsetStart;
3024
8
                subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]);
3025
8
            }
3026
3027
            // arrayed io
3028
414
            if (subTree->getType().isArray()) {
3029
162
                if (!arrayElement.empty()) {
3030
0
                    const TType derefType(subTree->getType(), arrayElement.front());
3031
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3032
0
                                                    intermediate.addConstantUnion(arrayElement.front(), loc), loc);
3033
0
                    subTree->setType(derefType);
3034
162
                } else {
3035
                    // There's an index operation we should transfer to the output builtin.
3036
162
                    assert(splitNode->getAsOperator() != nullptr &&
3037
162
                           splitNode->getAsOperator()->getOp() == EOpIndexIndirect);
3038
162
                    const TType splitDerefType(subTree->getType(), 0);
3039
162
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3040
162
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3041
162
                    subTree->setType(splitDerefType);
3042
162
                }
3043
162
            }
3044
414
        } else {
3045
            // Index operator if it's an aggregate, else EOpNull
3046
414
            const TOperator accessOp = type.isArray()  ? EOpIndexDirect
3047
414
                                     : type.isStruct() ? EOpIndexDirectStruct
3048
414
                                     : EOpNull;
3049
414
            if (accessOp == EOpNull) {
3050
0
                subTree = splitNode;
3051
414
            } else {
3052
414
                subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc),
3053
414
                                                loc);
3054
414
                const TType splitDerefType(splitNode->getType(), splitMember);
3055
414
                subTree->setType(splitDerefType);
3056
414
            }
3057
414
        }
3058
3059
828
        return subTree;
3060
828
    };
3061
3062
    // Use the proper RHS node: a new symbol from a TVariable, copy
3063
    // of an TIntermSymbol node, or sometimes the right node directly.
3064
326
    right = rhsTempVar != nullptr   ? intermediate.addSymbol(*rhsTempVar, loc) :
3065
326
            cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) :
3066
240
            right;
3067
3068
    // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
3069
    // whole thing.  So, we'll resort to an explicit type via std::function.
3070
326
    const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3071
326
                             bool topLevel)>
3072
326
    traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3073
740
                   bool topLevel) -> void {
3074
        // If we get here, we are assigning to or from a whole array or struct that must be
3075
        // flattened, so have to do member-by-member assignment:
3076
3077
740
        bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel);
3078
740
        bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel);
3079
3080
740
        if ((left->getType().isArray() || right->getType().isArray()) &&
3081
0
              (shouldFlattenSubsetLeft  || isSplitLeft ||
3082
0
               shouldFlattenSubsetRight || isSplitRight)) {
3083
0
            const int elementsL = left->getType().isArray()  ? left->getType().getOuterArraySize()  : 1;
3084
0
            const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
3085
3086
            // The arrays might not be the same size,
3087
            // e.g., if the size has been forced for EbvTessLevelInner/Outer.
3088
0
            const int elementsToCopy = std::min(elementsL, elementsR);
3089
3090
            // array case
3091
0
            for (int element = 0; element < elementsToCopy; ++element) {
3092
0
                arrayElement.push_back(element);
3093
3094
                // Add a new AST symbol node if we have a temp variable holding a complex RHS.
3095
0
                TIntermTyped* subLeft  = getMember(true,  left->getType(),  element, left, element,
3096
0
                                                   shouldFlattenSubsetLeft);
3097
0
                TIntermTyped* subRight = getMember(false, right->getType(), element, right, element,
3098
0
                                                   shouldFlattenSubsetRight);
3099
3100
0
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  element, splitLeft,
3101
0
                                                                       element, shouldFlattenSubsetLeft)
3102
0
                                                           : subLeft;
3103
0
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight,
3104
0
                                                                       element, shouldFlattenSubsetRight)
3105
0
                                                           : subRight;
3106
3107
0
                traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3108
3109
0
                arrayElement.pop_back();
3110
0
            }
3111
740
        } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft  || isSplitLeft ||
3112
326
                                                  shouldFlattenSubsetRight || isSplitRight)) {
3113
            // struct case
3114
326
            const auto& membersL = *left->getType().getStruct();
3115
326
            const auto& membersR = *right->getType().getStruct();
3116
3117
            // These track the members in the split structures corresponding to the same in the unsplit structures,
3118
            // which we traverse in parallel.
3119
326
            int memberL = 0;
3120
326
            int memberR = 0;
3121
3122
            // Handle empty structure assignment
3123
326
            if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
3124
2
                assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3125
3126
740
            for (int member = 0; member < int(membersL.size()); ++member) {
3127
414
                const TType& typeL = *membersL[member].type;
3128
414
                const TType& typeR = *membersR[member].type;
3129
3130
414
                TIntermTyped* subLeft  = getMember(true,  left->getType(), member, left, member,
3131
414
                                                   shouldFlattenSubsetLeft);
3132
414
                TIntermTyped* subRight = getMember(false, right->getType(), member, right, member,
3133
414
                                                   shouldFlattenSubsetRight);
3134
3135
                // If there is no splitting, use the same values to avoid inefficiency.
3136
414
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  member, splitLeft,
3137
0
                                                                       memberL, shouldFlattenSubsetLeft)
3138
414
                                                           : subLeft;
3139
414
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight,
3140
0
                                                                       memberR, shouldFlattenSubsetRight)
3141
414
                                                           : subRight;
3142
3143
414
                if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) {
3144
                    // Clip and cull distance built-in assignment is complex in its own right, and is handled in
3145
                    // a separate function dedicated to that task.  See comment above assignClipCullDistance;
3146
3147
0
                    const bool isOutput = isClipOrCullDistance(subSplitLeft->getType());
3148
3149
                    // Since all clip/cull semantics boil down to the same built-in type, we need to get the
3150
                    // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping.
3151
0
                    const TType derefType((isOutput ? left : right)->getType(), member);
3152
0
                    const int semanticId = derefType.getQualifier().layoutLocation;
3153
3154
0
                    TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId,
3155
0
                                                                              subSplitLeft, subSplitRight);
3156
3157
0
                    assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
3158
414
                } else if (subSplitRight->getType().getQualifier().builtIn == EbvFragCoord) {
3159
                    // FragCoord can require special handling: see comment above assignFromFragCoord
3160
0
                    TIntermTyped* fragCoordAssign = assignFromFragCoord(loc, op, subSplitLeft, subSplitRight);
3161
0
                    assignList = intermediate.growAggregate(assignList, fragCoordAssign, loc);
3162
414
                } else if (assignsClipPos(subSplitLeft)) {
3163
                    // Position can require special handling: see comment above assignPosition
3164
0
                    TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
3165
0
                    assignList = intermediate.growAggregate(assignList, positionAssign, loc);
3166
414
                } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
3167
0
                           !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
3168
                    // If this is the final flattening (no nested types below to flatten)
3169
                    // we'll copy the member, else recurse into the type hierarchy.
3170
                    // However, if splitting the struct, that means we can copy a whole
3171
                    // subtree here IFF it does not itself contain any interstage built-in
3172
                    // IO variables, so we only have to recurse into it if there's something
3173
                    // for splitting to do.  That can save a lot of AST verbosity for
3174
                    // a bunch of memberwise copies.
3175
3176
0
                    assignList = intermediate.growAggregate(assignList,
3177
0
                                                            intermediate.addAssign(op, subSplitLeft, subSplitRight, loc),
3178
0
                                                            loc);
3179
414
                } else {
3180
414
                    traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3181
414
                }
3182
3183
414
                memberL += (typeL.isBuiltIn() ? 0 : 1);
3184
414
                memberR += (typeR.isBuiltIn() ? 0 : 1);
3185
414
            }
3186
414
        } else {
3187
            // Member copy
3188
414
            assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3189
414
        }
3190
3191
740
    };
3192
3193
326
    TIntermTyped* splitLeft  = left;
3194
326
    TIntermTyped* splitRight = right;
3195
3196
    // If either left or right was a split structure, we must read or write it, but still have to
3197
    // parallel-recurse through the unsplit structure to identify the built-in IO vars.
3198
    // The left can be either a symbol, or an index into a symbol (e.g, array reference)
3199
326
    if (isSplitLeft) {
3200
0
        if (indexesSplit(left)) {
3201
            // Index case: Refer to the indexed symbol, if the left is an index operator.
3202
0
            const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3203
3204
0
            TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3205
3206
0
            splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo,
3207
0
                                              left->getAsBinaryNode()->getRight(), loc);
3208
3209
0
            const TType derefType(splitLeftNonIo->getType(), 0);
3210
0
            splitLeft->setType(derefType);
3211
0
        } else {
3212
            // Symbol case: otherwise, if not indexed, we have the symbol directly.
3213
0
            const TIntermSymbol* symNode = left->getAsSymbolNode();
3214
0
            splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3215
0
        }
3216
0
    }
3217
3218
326
    if (isSplitRight)
3219
0
        splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc);
3220
3221
    // This makes the whole assignment, recursing through subtypes as needed.
3222
326
    traverse(left, right, splitLeft, splitRight, true);
3223
3224
326
    assert(assignList != nullptr);
3225
326
    assignList->setOperator(EOpSequence);
3226
3227
326
    return assignList;
3228
16.7k
}
3229
3230
// An assignment to matrix swizzle must be decomposed into individual assignments.
3231
// These must be selected component-wise from the RHS and stored component-wise
3232
// into the LHS.
3233
TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
3234
                                                            TIntermTyped* right)
3235
8
{
3236
8
    assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
3237
3238
8
    if (op != EOpAssign)
3239
0
        error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
3240
3241
    // isolate the matrix and swizzle nodes
3242
8
    TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
3243
8
    const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
3244
3245
    // if the RHS isn't already a simple vector, let's store into one
3246
8
    TIntermSymbol* vector = right->getAsSymbolNode();
3247
8
    TIntermTyped* vectorAssign = nullptr;
3248
8
    if (vector == nullptr) {
3249
        // create a new intermediate vector variable to assign to
3250
8
        TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
3251
8
        vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
3252
3253
        // assign the right to the new vector
3254
8
        vectorAssign = handleAssign(loc, op, vector, right);
3255
8
    }
3256
3257
    // Assign the vector components to the matrix components.
3258
    // Store this as a sequence, so a single aggregate node represents this
3259
    // entire operation.
3260
8
    TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
3261
8
    TType columnType(matrix->getType(), 0);
3262
8
    TType componentType(columnType, 0);
3263
8
    TType indexType(EbtInt);
3264
8
    for (int i = 0; i < (int)swizzle.size(); i += 2) {
3265
        // the right component, single index into the RHS vector
3266
0
        TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
3267
0
                                    intermediate.addConstantUnion(i/2, loc), loc);
3268
3269
        // the left component, double index into the LHS matrix
3270
0
        TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
3271
0
                                    intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
3272
0
                                                                  indexType, loc),
3273
0
                                    loc);
3274
0
        leftComp->setType(columnType);
3275
0
        leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
3276
0
                                    intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
3277
0
                                                                  indexType, loc),
3278
0
                                    loc);
3279
0
        leftComp->setType(componentType);
3280
3281
        // Add the assignment to the aggregate
3282
0
        result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
3283
0
    }
3284
3285
8
    result->setOp(EOpSequence);
3286
3287
8
    return result;
3288
8
}
3289
3290
//
3291
// HLSL atomic operations have slightly different arguments than
3292
// GLSL/AST/SPIRV.  The semantics are converted below in decomposeIntrinsic.
3293
// This provides the post-decomposition equivalent opcode.
3294
//
3295
TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
3296
202
{
3297
202
    switch (op) {
3298
60
    case EOpInterlockedAdd:             return isImage ? EOpImageAtomicAdd      : EOpAtomicAdd;
3299
56
    case EOpInterlockedAnd:             return isImage ? EOpImageAtomicAnd      : EOpAtomicAnd;
3300
22
    case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
3301
6
    case EOpInterlockedMax:             return isImage ? EOpImageAtomicMax      : EOpAtomicMax;
3302
14
    case EOpInterlockedMin:             return isImage ? EOpImageAtomicMin      : EOpAtomicMin;
3303
18
    case EOpInterlockedOr:              return isImage ? EOpImageAtomicOr       : EOpAtomicOr;
3304
18
    case EOpInterlockedXor:             return isImage ? EOpImageAtomicXor      : EOpAtomicXor;
3305
8
    case EOpInterlockedExchange:        return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
3306
0
    case EOpInterlockedCompareStore:  // TODO: ...
3307
0
    default:
3308
0
        error(loc, "unknown atomic operation", "unknown op", "");
3309
0
        return EOpNull;
3310
202
    }
3311
202
}
3312
3313
//
3314
// Create a combined sampler/texture from separate sampler and texture.
3315
//
3316
TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex,
3317
                                                                TIntermTyped* argSampler)
3318
306
{
3319
306
    TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
3320
3321
306
    txcombine->getSequence().push_back(argTex);
3322
306
    txcombine->getSequence().push_back(argSampler);
3323
3324
306
    TSampler samplerType = argTex->getType().getSampler();
3325
306
    samplerType.combined = true;
3326
3327
    // TODO:
3328
    // This block exists until the spec no longer requires shadow modes on texture objects.
3329
    // It can be deleted after that, along with the shadowTextureVariant member.
3330
306
    {
3331
306
        const bool shadowMode = argSampler->getType().getSampler().shadow;
3332
3333
306
        TIntermSymbol* texSymbol = argTex->getAsSymbolNode();
3334
3335
306
        if (texSymbol == nullptr)
3336
14
            texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3337
3338
306
        if (texSymbol == nullptr) {
3339
0
            error(loc, "unable to find texture symbol", "", "");
3340
0
            return nullptr;
3341
0
        }
3342
3343
        // This forces the texture's shadow state to be the sampler's
3344
        // shadow state.  This depends on downstream optimization to
3345
        // DCE one variant in [shadow, nonshadow] if both are present,
3346
        // or the SPIR-V module would be invalid.
3347
306
        long long newId = texSymbol->getId();
3348
3349
        // Check to see if this texture has been given a shadow mode already.
3350
        // If so, look up the one we already have.
3351
306
        const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId());
3352
3353
306
        if (textureShadowEntry != textureShadowVariant.end())
3354
6
            newId = textureShadowEntry->second->get(shadowMode);
3355
300
        else
3356
300
            textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1);
3357
3358
        // Sometimes we have to create another symbol (if this texture has been seen before,
3359
        // and we haven't created the form for this shadow mode).
3360
306
        if (newId == -1) {
3361
4
            TType texType;
3362
4
            texType.shallowCopy(argTex->getType());
3363
4
            texType.getSampler().shadow = shadowMode;  // set appropriate shadow mode.
3364
4
            globalQualifierFix(loc, texType.getQualifier());
3365
3366
4
            TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType);
3367
3368
4
            trackLinkage(*newTexture);
3369
3370
4
            newId = newTexture->getUniqueId();
3371
4
        }
3372
3373
306
        assert(newId != -1);
3374
3375
306
        if (textureShadowVariant.find(newId) == textureShadowVariant.end())
3376
4
            textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()];
3377
3378
306
        textureShadowVariant[newId]->set(shadowMode, newId);
3379
3380
        // Remember this shadow mode in the texture and the merged type.
3381
306
        argTex->getWritableType().getSampler().shadow = shadowMode;
3382
306
        samplerType.shadow = shadowMode;
3383
3384
306
        texSymbol->switchId(newId);
3385
306
    }
3386
3387
0
    txcombine->setType(TType(samplerType, EvqTemporary));
3388
306
    txcombine->setLoc(loc);
3389
3390
306
    return txcombine;
3391
306
}
3392
3393
// Return true if this a buffer type that has an associated counter buffer.
3394
bool HlslParseContext::hasStructBuffCounter(const TType& type) const
3395
9.85k
{
3396
9.85k
    switch (type.getQualifier().declaredBuiltIn) {
3397
76
    case EbvAppendConsume:       // fall through...
3398
180
    case EbvRWStructuredBuffer:  // ...
3399
180
        return true;
3400
9.67k
    default:
3401
9.67k
        return false; // the other structuredbuffer types do not have a counter.
3402
9.85k
    }
3403
9.85k
}
3404
3405
void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type)
3406
176
{
3407
    // Counter type
3408
176
    TType* counterType = new TType(EbtUint, EvqBuffer);
3409
176
    counterType->setFieldName(intermediate.implicitCounterName);
3410
3411
176
    TTypeList* blockStruct = new TTypeList;
3412
176
    TTypeLoc  member = { counterType, loc };
3413
176
    blockStruct->push_back(member);
3414
3415
176
    TType blockType(blockStruct, "", counterType->getQualifier());
3416
176
    blockType.getQualifier().storage = EvqBuffer;
3417
3418
176
    type.shallowCopy(blockType);
3419
176
    shareStructBufferType(type);
3420
176
}
3421
3422
// declare counter for a structured buffer type
3423
void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
3424
414
{
3425
    // Bail out if not a struct buffer
3426
414
    if (! isStructBufferType(bufferType))
3427
34
        return;
3428
3429
380
    if (! hasStructBuffCounter(bufferType))
3430
224
        return;
3431
3432
156
    TType blockType;
3433
156
    counterBufferType(loc, blockType);
3434
3435
156
    TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str());
3436
3437
    // Counter buffer is not yet in use
3438
156
    structBufferCounter[*blockName] = false;
3439
3440
156
    shareStructBufferType(blockType);
3441
156
    declareBlock(loc, blockType, blockName);
3442
156
}
3443
3444
// return the counter that goes with a given structuredbuffer
3445
TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
3446
28
{
3447
    // Bail out if not a struct buffer
3448
28
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
3449
0
        return nullptr;
3450
3451
28
    const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName()));
3452
3453
    // Mark the counter as being used
3454
28
    structBufferCounter[counterBlockName] = true;
3455
3456
28
    TIntermTyped* counterVar = handleVariable(loc, &counterBlockName);  // find the block structure
3457
28
    TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
3458
3459
28
    TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
3460
28
    counterMember->setType(TType(EbtUint));
3461
28
    return counterMember;
3462
28
}
3463
3464
//
3465
// Decompose structure buffer methods into AST
3466
//
3467
void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3468
12.4k
{
3469
12.4k
    if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
3470
910
        return;
3471
3472
11.4k
    const TOperator op  = node->getAsOperator()->getOp();
3473
11.4k
    TIntermAggregate* argAggregate = arguments->getAsAggregate();
3474
3475
    // Buffer is the object upon which method is called, so always arg 0
3476
11.4k
    TIntermTyped* bufferObj = nullptr;
3477
3478
    // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
3479
11.4k
    if (argAggregate) {
3480
3.92k
        if (argAggregate->getSequence().empty())
3481
0
            return;
3482
3.92k
        if (argAggregate->getSequence()[0])
3483
3.92k
            bufferObj = argAggregate->getSequence()[0]->getAsTyped();
3484
7.56k
    } else {
3485
7.56k
        bufferObj = arguments->getAsSymbolNode();
3486
7.56k
    }
3487
3488
11.4k
    if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr)
3489
2.13k
        return;
3490
3491
    // Some methods require a hidden internal counter, obtained via getStructBufferCounter().
3492
    // This lambda adds something to it and returns the old value.
3493
9.35k
    const auto incDecCounter = [&](int incval) -> TIntermTyped* {
3494
28
        TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast<unsigned int>(incval), loc, true);
3495
28
        TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
3496
3497
28
        if (counter == nullptr)
3498
0
            return nullptr;
3499
3500
28
        TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
3501
28
        counterIncrement->setType(TType(EbtUint, EvqTemporary));
3502
28
        counterIncrement->setLoc(loc);
3503
28
        counterIncrement->getSequence().push_back(counter);
3504
28
        counterIncrement->getSequence().push_back(incrementValue);
3505
3506
28
        return counterIncrement;
3507
28
    };
3508
3509
    // Index to obtain the runtime sized array out of the buffer.
3510
9.35k
    TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
3511
9.35k
    if (argArray == nullptr)
3512
9.06k
        return;  // It might not be a struct buffer method.
3513
3514
294
    switch (op) {
3515
16
    case EOpMethodLoad:
3516
16
        {
3517
16
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3518
3519
16
            const TType& bufferType = bufferObj->getType();
3520
3521
16
            const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn;
3522
3523
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3524
            // buffer then, but that's what it calls itself.
3525
16
            const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer   ||
3526
12
                                              builtInType == EbvRWByteAddressBuffer);
3527
3528
3529
16
            if (isByteAddressBuffer)
3530
16
                argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex,
3531
16
                                                      intermediate.addConstantUnion(2, loc, true),
3532
16
                                                      loc, TType(EbtInt));
3533
3534
            // Index into the array to find the item being loaded.
3535
16
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3536
3537
16
            node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3538
3539
16
            const TType derefType(argArray->getType(), 0);
3540
16
            node->setType(derefType);
3541
16
        }
3542
3543
16
        break;
3544
3545
6
    case EOpMethodLoad2:
3546
12
    case EOpMethodLoad3:
3547
16
    case EOpMethodLoad4:
3548
16
        {
3549
16
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3550
3551
16
            TOperator constructOp = EOpNull;
3552
16
            int size = 0;
3553
3554
16
            switch (op) {
3555
6
            case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
3556
6
            case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
3557
4
            case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
3558
0
            default: assert(0);
3559
16
            }
3560
3561
16
            TIntermTyped* body = nullptr;
3562
3563
            // First, we'll store the address in a variable to avoid multiple shifts
3564
            // (we must convert the byte address to an item address)
3565
16
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3566
16
                                                                   intermediate.addConstantUnion(2, loc, true),
3567
16
                                                                   loc, TType(EbtInt));
3568
3569
16
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3570
16
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3571
3572
16
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3573
3574
16
            TIntermTyped* vec = nullptr;
3575
3576
            // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
3577
            // address conversion.
3578
62
            for (int idx=0; idx<size; ++idx) {
3579
46
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3580
3581
                // add index offset
3582
46
                if (idx != 0)
3583
30
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx,
3584
30
                                                           intermediate.addConstantUnion(idx, loc, true),
3585
30
                                                           loc, TType(EbtInt));
3586
3587
46
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3588
46
                                                                                        : EOpIndexIndirect;
3589
3590
46
                TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3591
3592
46
                TType derefType(argArray->getType(), 0);
3593
46
                derefType.getQualifier().makeTemporary();
3594
46
                indexVal->setType(derefType);
3595
3596
46
                vec = intermediate.growAggregate(vec, indexVal);
3597
46
            }
3598
3599
16
            vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
3600
16
            vec->getAsAggregate()->setOperator(constructOp);
3601
3602
16
            body = intermediate.growAggregate(body, vec);
3603
16
            body->setType(vec->getType());
3604
16
            body->getAsAggregate()->setOperator(EOpSequence);
3605
3606
16
            node = body;
3607
16
        }
3608
3609
0
        break;
3610
3611
4
    case EOpMethodStore:
3612
8
    case EOpMethodStore2:
3613
14
    case EOpMethodStore3:
3614
18
    case EOpMethodStore4:
3615
18
        {
3616
18
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3617
18
            TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped();  // value
3618
3619
            // Index into the array to find the item being loaded.
3620
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3621
            // buffer then, but that's what it calls itself).
3622
3623
18
            int size = 0;
3624
3625
18
            switch (op) {
3626
4
            case EOpMethodStore:  size = 1; break;
3627
4
            case EOpMethodStore2: size = 2; break;
3628
6
            case EOpMethodStore3: size = 3; break;
3629
4
            case EOpMethodStore4: size = 4; break;
3630
0
            default: assert(0);
3631
18
            }
3632
3633
18
            TIntermAggregate* body = nullptr;
3634
3635
            // First, we'll store the address in a variable to avoid multiple shifts
3636
            // (we must convert the byte address to an item address)
3637
18
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3638
18
                                                                   intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
3639
3640
18
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3641
18
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3642
3643
18
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3644
3645
64
            for (int idx=0; idx<size; ++idx) {
3646
46
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3647
46
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3648
3649
                // add index offset
3650
46
                if (idx != 0)
3651
28
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
3652
3653
46
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3654
46
                                                                                        : EOpIndexIndirect;
3655
3656
46
                TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3657
46
                const TType derefType(argArray->getType(), 0);
3658
46
                lValue->setType(derefType);
3659
3660
46
                TIntermTyped* rValue;
3661
46
                if (size == 1) {
3662
4
                    rValue = argValue;
3663
42
                } else {
3664
42
                    rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
3665
42
                    const TType indexType(argValue->getType(), 0);
3666
42
                    rValue->setType(indexType);
3667
42
                }
3668
3669
46
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3670
3671
46
                body = intermediate.growAggregate(body, assign);
3672
46
            }
3673
3674
18
            body->setOperator(EOpSequence);
3675
18
            node = body;
3676
18
        }
3677
3678
0
        break;
3679
3680
6
    case EOpMethodGetDimensions:
3681
6
        {
3682
6
            const int numArgs = (int)argAggregate->getSequence().size();
3683
6
            TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped();  // out num items
3684
6
            TIntermTyped* argStride   = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr;  // out stride
3685
3686
6
            TIntermAggregate* body = nullptr;
3687
3688
            // Length output:
3689
6
            if (argArray->getType().isSizedArray()) {
3690
0
                const int length = argArray->getType().getOuterArraySize();
3691
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems,
3692
0
                                                              intermediate.addConstantUnion(length, loc, true), loc);
3693
0
                body = intermediate.growAggregate(body, assign, loc);
3694
6
            } else {
3695
6
                TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
3696
6
                                                                               argNumItems->getType());
3697
6
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
3698
6
                body = intermediate.growAggregate(body, assign, loc);
3699
6
            }
3700
3701
            // Stride output:
3702
6
            if (argStride != nullptr) {
3703
0
                int size;
3704
0
                int stride;
3705
0
                intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking,
3706
0
                                                argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
3707
3708
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride,
3709
0
                                                              intermediate.addConstantUnion(stride, loc, true), loc);
3710
3711
0
                body = intermediate.growAggregate(body, assign);
3712
0
            }
3713
3714
6
            body->setOperator(EOpSequence);
3715
6
            node = body;
3716
6
        }
3717
3718
6
        break;
3719
3720
60
    case EOpInterlockedAdd:
3721
116
    case EOpInterlockedAnd:
3722
124
    case EOpInterlockedExchange:
3723
130
    case EOpInterlockedMax:
3724
144
    case EOpInterlockedMin:
3725
162
    case EOpInterlockedOr:
3726
180
    case EOpInterlockedXor:
3727
202
    case EOpInterlockedCompareExchange:
3728
204
    case EOpInterlockedCompareStore:
3729
204
        {
3730
            // We'll replace the first argument with the block dereference, and let
3731
            // downstream decomposition handle the rest.
3732
3733
204
            TIntermSequence& sequence = argAggregate->getSequence();
3734
3735
204
            TIntermTyped* argIndex     = makeIntegerIndex(sequence[1]->getAsTyped());  // index
3736
204
            argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
3737
204
                                                  loc, TType(EbtInt));
3738
3739
204
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3740
204
            TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3741
3742
204
            const TType derefType(argArray->getType(), 0);
3743
204
            element->setType(derefType);
3744
3745
            // Replace the numeric byte offset parameter with array reference.
3746
204
            sequence[1] = element;
3747
204
            sequence.erase(sequence.begin(), sequence.begin()+1);
3748
204
        }
3749
204
        break;
3750
3751
8
    case EOpMethodIncrementCounter:
3752
8
        {
3753
8
            node = incDecCounter(1);
3754
8
            break;
3755
202
        }
3756
3757
6
    case EOpMethodDecrementCounter:
3758
6
        {
3759
6
            TIntermTyped* preIncValue = incDecCounter(-1); // result is original value
3760
6
            node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc,
3761
6
                                              preIncValue->getType());
3762
6
            break;
3763
202
        }
3764
3765
8
    case EOpMethodAppend:
3766
8
        {
3767
8
            TIntermTyped* oldCounter = incDecCounter(1);
3768
3769
8
            TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc);
3770
8
            TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped();
3771
3772
8
            const TType derefType(argArray->getType(), 0);
3773
8
            lValue->setType(derefType);
3774
3775
8
            node = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3776
3777
8
            break;
3778
202
        }
3779
3780
6
    case EOpMethodConsume:
3781
6
        {
3782
6
            TIntermTyped* oldCounter = incDecCounter(-1);
3783
3784
6
            TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter,
3785
6
                                                                  intermediate.addConstantUnion(-1, loc, true), loc,
3786
6
                                                                  oldCounter->getType());
3787
3788
6
            node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc);
3789
3790
6
            const TType derefType(argArray->getType(), 0);
3791
6
            node->setType(derefType);
3792
3793
6
            break;
3794
202
        }
3795
3796
6
    default:
3797
6
        break; // most pass through unchanged
3798
294
    }
3799
294
}
3800
3801
// Create array of standard sample positions for given sample count.
3802
// TODO: remove when a real method to query sample pos exists in SPIR-V.
3803
TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
3804
0
{
3805
0
    struct tSamplePos { float x, y; };
3806
3807
0
    static const tSamplePos pos1[] = {
3808
0
        { 0.0/16.0,  0.0/16.0 },
3809
0
    };
3810
3811
    // standard sample positions for 2, 4, 8, and 16 samples.
3812
0
    static const tSamplePos pos2[] = {
3813
0
        { 4.0/16.0,  4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
3814
0
    };
3815
3816
0
    static const tSamplePos pos4[] = {
3817
0
        {-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 },
3818
0
    };
3819
3820
0
    static const tSamplePos pos8[] = {
3821
0
        { 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 },
3822
0
        {-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 },
3823
0
    };
3824
3825
0
    static const tSamplePos pos16[] = {
3826
0
        { 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 },
3827
0
        {-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 },
3828
0
        {-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 },
3829
0
        {-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 },
3830
0
    };
3831
3832
0
    const tSamplePos* sampleLoc = nullptr;
3833
0
    int numSamples = count;
3834
3835
0
    switch (count) {
3836
0
    case 2:  sampleLoc = pos2;  break;
3837
0
    case 4:  sampleLoc = pos4;  break;
3838
0
    case 8:  sampleLoc = pos8;  break;
3839
0
    case 16: sampleLoc = pos16; break;
3840
0
    default:
3841
0
        sampleLoc = pos1;
3842
0
        numSamples = 1;
3843
0
    }
3844
3845
0
    TConstUnionArray* values = new TConstUnionArray(numSamples*2);
3846
3847
0
    for (int pos=0; pos<count; ++pos) {
3848
0
        TConstUnion x, y;
3849
0
        x.setDConst(sampleLoc[pos].x);
3850
0
        y.setDConst(sampleLoc[pos].y);
3851
3852
0
        (*values)[pos*2+0] = x;
3853
0
        (*values)[pos*2+1] = y;
3854
0
    }
3855
3856
0
    TType retType(EbtFloat, EvqConst, 2);
3857
3858
0
    if (numSamples != 1) {
3859
0
        TArraySizes* arraySizes = new TArraySizes;
3860
0
        arraySizes->addInnerSize(numSamples);
3861
0
        retType.transferArraySizes(arraySizes);
3862
0
    }
3863
3864
0
    return new TIntermConstantUnion(*values, retType);
3865
0
}
3866
3867
//
3868
// Decompose DX9 and DX10 sample intrinsics & object methods into AST
3869
//
3870
void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3871
12.4k
{
3872
12.4k
    if (node == nullptr || !node->getAsOperator())
3873
408
        return;
3874
3875
    // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it.
3876
11.9k
    const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
3877
370
        result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
3878
3879
370
        TIntermTyped* convertedResult = nullptr;
3880
3881
370
        TType retType;
3882
370
        getTextureReturnType(sampler, retType);
3883
3884
370
        if (retType.isStruct()) {
3885
            // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here)
3886
0
            TIntermAggregate* conversionAggregate = new TIntermAggregate;
3887
0
            convertedResult = conversionAggregate;
3888
3889
            // Convert vector output to return structure.  We will need a temp symbol to copy the results to.
3890
0
            TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType);
3891
3892
            // We also need a temp symbol to hold the result of the texture.  We don't want to re-fetch the
3893
            // sample each time we'll index into the result, so we'll copy to this, and index into the copy.
3894
0
            TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType());
3895
3896
            // Initial copy from texture to our sample result shadow.
3897
0
            TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc),
3898
0
                                                              result, loc);
3899
3900
0
            conversionAggregate->getSequence().push_back(shadowCopy);
3901
3902
0
            unsigned vec4Pos = 0;
3903
3904
0
            for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) {
3905
0
                const TType memberType(retType, m); // dereferenced type of the member we're about to assign.
3906
3907
                // Check for bad struct members.  This should have been caught upstream.  Complain, because
3908
                // wwe don't know what to do with it.  This algorithm could be generalized to handle
3909
                // other things, e.g, sub-structures, but HLSL doesn't allow them.
3910
0
                if (!memberType.isVector() && !memberType.isScalar()) {
3911
0
                    error(loc, "expected: scalar or vector type in texture structure", "", "");
3912
0
                    return nullptr;
3913
0
                }
3914
3915
                // Index into the struct variable to find the member to assign.
3916
0
                TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct,
3917
0
                                                                   intermediate.addSymbol(*structVar, loc),
3918
0
                                                                   intermediate.addConstantUnion(m, loc), loc);
3919
3920
0
                structMember->setType(memberType);
3921
3922
                // Assign each component of (possible) vector in struct member.
3923
0
                for (int component = 0; component < memberType.getVectorSize(); ++component) {
3924
0
                    TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect,
3925
0
                                                                     intermediate.addSymbol(*sampleShadow, loc),
3926
0
                                                                     intermediate.addConstantUnion(vec4Pos++, loc), loc);
3927
0
                    vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1));
3928
3929
0
                    TIntermTyped* memberAssign = nullptr;
3930
3931
0
                    if (memberType.isVector()) {
3932
                        // Vector member: we need to create an access chain to the vector component.
3933
3934
0
                        TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember,
3935
0
                                                                                 intermediate.addConstantUnion(component, loc), loc);
3936
3937
0
                        memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc);
3938
0
                    } else {
3939
                        // Scalar member: we can assign to it directly.
3940
0
                        memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc);
3941
0
                    }
3942
3943
3944
0
                    conversionAggregate->getSequence().push_back(memberAssign);
3945
0
                }
3946
0
            }
3947
3948
            // Add completed variable so the expression results in the whole struct value we just built.
3949
0
            conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc));
3950
3951
            // Make it a sequence.
3952
0
            intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc);
3953
370
        } else {
3954
            // vector clamp the output if template vector type is smaller than sample result.
3955
370
            if (retType.getVectorSize() < node->getVectorSize()) {
3956
                // Too many components.  Construct shorter vector from it.
3957
164
                const TOperator op = intermediate.mapTypeToConstructorOp(retType);
3958
3959
164
                convertedResult = constructBuiltIn(retType, op, result, loc, false);
3960
206
            } else {
3961
                // Enough components.  Use directly.
3962
206
                convertedResult = result;
3963
206
            }
3964
370
        }
3965
3966
370
        convertedResult->setLoc(loc);
3967
370
        return convertedResult;
3968
370
    };
3969
3970
11.9k
    const TOperator op  = node->getAsOperator()->getOp();
3971
11.9k
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
3972
3973
    // Bail out if not a sampler method.
3974
    // Note though this is odd to do before checking the op, because the op
3975
    // could be something that takes the arguments, and the function in question
3976
    // takes the result of the op.  So, this is not the final word.
3977
11.9k
    if (arguments != nullptr) {
3978
11.2k
        if (argAggregate == nullptr) {
3979
7.32k
            if (arguments->getAsTyped()->getBasicType() != EbtSampler)
3980
7.18k
                return;
3981
7.32k
        } else {
3982
3.91k
            if (argAggregate->getSequence().size() == 0 ||
3983
3.91k
                argAggregate->getSequence()[0] == nullptr ||
3984
3.91k
                argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler)
3985
3.09k
                return;
3986
3.91k
        }
3987
11.2k
    }
3988
3989
1.70k
    switch (op) {
3990
    // **** DX9 intrinsics: ****
3991
0
    case EOpTexture:
3992
0
        {
3993
            // Texture with ddx & ddy is really gradient form in HLSL
3994
0
            if (argAggregate->getSequence().size() == 4)
3995
0
                node->getAsAggregate()->setOperator(EOpTextureGrad);
3996
3997
0
            break;
3998
0
        }
3999
0
    case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different)
4000
0
        {
4001
0
            TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped();   // sampler
4002
0
            TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped();  // coord
4003
4004
0
            assert(argCoord->getVectorSize() == 4);
4005
0
            TIntermTyped *w = intermediate.addConstantUnion(3, loc, true);
4006
0
            TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc);
4007
4008
0
            TOperator constructOp = EOpNull;
4009
0
            const TSampler &sampler = argSamp->getType().getSampler();
4010
0
            int coordSize = 0;
4011
4012
0
            switch (sampler.dim)
4013
0
            {
4014
0
            case Esd1D:   constructOp = EOpConstructFloat; coordSize = 1; break; // 1D
4015
0
            case Esd2D:   constructOp = EOpConstructVec2;  coordSize = 2; break; // 2D
4016
0
            case Esd3D:   constructOp = EOpConstructVec3;  coordSize = 3; break; // 3D
4017
0
            case EsdCube: constructOp = EOpConstructVec3;  coordSize = 3; break; // also 3D
4018
0
            default:
4019
0
                error(loc, "unhandled DX9 texture LoD dimension", "", "");
4020
0
                break;
4021
0
            }
4022
4023
0
            TIntermAggregate *constructCoord = new TIntermAggregate(constructOp);
4024
0
            constructCoord->getSequence().push_back(argCoord);
4025
0
            constructCoord->setLoc(loc);
4026
0
            constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize));
4027
4028
0
            TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod);
4029
0
            tex->getSequence().push_back(argSamp);        // sampler
4030
0
            tex->getSequence().push_back(constructCoord); // coordinate
4031
0
            tex->getSequence().push_back(argLod);         // lod
4032
4033
0
            node = convertReturn(tex, sampler);
4034
4035
0
            break;
4036
0
        }
4037
4038
0
    case EOpTextureBias:
4039
0
        {
4040
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // sampler
4041
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // coord
4042
4043
            // HLSL puts bias in W component of coordinate.  We extract it and add it to
4044
            // the argument list, instead
4045
0
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
4046
0
            TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
4047
4048
0
            TOperator constructOp = EOpNull;
4049
0
            const TSampler& sampler = arg0->getType().getSampler();
4050
4051
0
            switch (sampler.dim) {
4052
0
            case Esd1D:   constructOp = EOpConstructFloat; break; // 1D
4053
0
            case Esd2D:   constructOp = EOpConstructVec2;  break; // 2D
4054
0
            case Esd3D:   constructOp = EOpConstructVec3;  break; // 3D
4055
0
            case EsdCube: constructOp = EOpConstructVec3;  break; // also 3D
4056
0
            default:
4057
0
                error(loc, "unhandled DX9 texture bias dimension", "", "");
4058
0
                break;
4059
0
            }
4060
4061
0
            TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
4062
0
            constructCoord->getSequence().push_back(arg1);
4063
0
            constructCoord->setLoc(loc);
4064
4065
            // The input vector should never be less than 2, since there's always a bias.
4066
            // The max is for safety, and should be a no-op.
4067
0
            constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
4068
4069
0
            TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
4070
0
            tex->getSequence().push_back(arg0);           // sampler
4071
0
            tex->getSequence().push_back(constructCoord); // coordinate
4072
0
            tex->getSequence().push_back(bias);           // bias
4073
4074
0
            node = convertReturn(tex, sampler);
4075
4076
0
            break;
4077
0
        }
4078
4079
    // **** DX10 methods: ****
4080
84
    case EOpMethodSample:     // fall through
4081
86
    case EOpMethodSampleBias: // ...
4082
86
        {
4083
86
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4084
86
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4085
86
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4086
86
            TIntermTyped* argBias   = nullptr;
4087
86
            TIntermTyped* argOffset = nullptr;
4088
86
            const TSampler& sampler = argTex->getType().getSampler();
4089
4090
86
            int nextArg = 3;
4091
4092
86
            if (op == EOpMethodSampleBias)  // SampleBias has a bias arg
4093
2
                argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
4094
4095
86
            TOperator textureOp = EOpTexture;
4096
4097
86
            if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
4098
0
                textureOp = EOpTextureOffset;
4099
0
                argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
4100
0
            }
4101
4102
86
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4103
4104
86
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4105
86
            txsample->getSequence().push_back(txcombine);
4106
86
            txsample->getSequence().push_back(argCoord);
4107
4108
86
            if (argOffset != nullptr)
4109
0
                txsample->getSequence().push_back(argOffset);
4110
4111
86
            if (argBias != nullptr)
4112
2
              txsample->getSequence().push_back(argBias);
4113
4114
86
            node = convertReturn(txsample, sampler);
4115
4116
86
            break;
4117
84
        }
4118
4119
0
    case EOpMethodSampleGrad: // ...
4120
0
        {
4121
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4122
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4123
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4124
0
            TIntermTyped* argDDX    = argAggregate->getSequence()[3]->getAsTyped();
4125
0
            TIntermTyped* argDDY    = argAggregate->getSequence()[4]->getAsTyped();
4126
0
            TIntermTyped* argOffset = nullptr;
4127
0
            const TSampler& sampler = argTex->getType().getSampler();
4128
4129
0
            TOperator textureOp = EOpTextureGrad;
4130
4131
0
            if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
4132
0
                textureOp = EOpTextureGradOffset;
4133
0
                argOffset = argAggregate->getSequence()[5]->getAsTyped();
4134
0
            }
4135
4136
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4137
4138
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4139
0
            txsample->getSequence().push_back(txcombine);
4140
0
            txsample->getSequence().push_back(argCoord);
4141
0
            txsample->getSequence().push_back(argDDX);
4142
0
            txsample->getSequence().push_back(argDDY);
4143
4144
0
            if (argOffset != nullptr)
4145
0
                txsample->getSequence().push_back(argOffset);
4146
4147
0
            node = convertReturn(txsample, sampler);
4148
4149
0
            break;
4150
84
        }
4151
4152
276
    case EOpMethodGetDimensions:
4153
276
        {
4154
            // AST returns a vector of results, which we break apart component-wise into
4155
            // separate values to assign to the HLSL method's outputs, ala:
4156
            //  tx . GetDimensions(width, height);
4157
            //      float2 sizeQueryTemp = EOpTextureQuerySize
4158
            //      width = sizeQueryTemp.X;
4159
            //      height = sizeQueryTemp.Y;
4160
4161
276
            TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4162
276
            const TType& texType = argTex->getType();
4163
4164
276
            assert(texType.getBasicType() == EbtSampler);
4165
4166
276
            const TSampler& sampler = texType.getSampler();
4167
276
            const TSamplerDim dim = sampler.dim;
4168
276
            const bool isImage = sampler.isImage();
4169
276
            const bool isMs = sampler.isMultiSample();
4170
276
            const int numArgs = (int)argAggregate->getSequence().size();
4171
4172
276
            int numDims = 0;
4173
4174
276
            switch (dim) {
4175
246
            case Esd1D:     numDims = 1; break; // W
4176
22
            case Esd2D:     numDims = 2; break; // W, H
4177
0
            case Esd3D:     numDims = 3; break; // W, H, D
4178
8
            case EsdCube:   numDims = 2; break; // W, H (cube)
4179
0
            case EsdBuffer: numDims = 1; break; // W (buffers)
4180
0
            case EsdRect:   numDims = 2; break; // W, H (rect)
4181
0
            default:
4182
0
                error(loc, "unhandled DX10 MethodGet dimension", "", "");
4183
0
                break;
4184
276
            }
4185
4186
            // Arrayed adds another dimension for the number of array elements
4187
276
            if (sampler.isArrayed())
4188
68
                ++numDims;
4189
4190
            // Establish whether the method itself is querying mip levels.  This can be false even
4191
            // if the underlying query requires a MIP level, due to the available HLSL method overloads.
4192
276
            const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
4193
4194
            // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
4195
            // True if:
4196
            //   1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
4197
            // or,
4198
            //   2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
4199
276
            const bool mipRequired =
4200
276
                ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
4201
2
                mipQuery; // 2...
4202
4203
            // AST assumes integer return.  Will be converted to float if required.
4204
276
            TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
4205
276
            sizeQuery->getSequence().push_back(argTex);
4206
4207
            // If we're building an LOD query, add the LOD.
4208
276
            if (mipRequired) {
4209
                // If the base HLSL query had no MIP level given, use level 0.
4210
274
                TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
4211
274
                    intermediate.addConstantUnion(0, loc, true);
4212
274
                sizeQuery->getSequence().push_back(queryLod);
4213
274
            }
4214
4215
276
            sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
4216
276
            sizeQuery->setLoc(loc);
4217
4218
            // Return value from size query
4219
276
            TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
4220
276
            tempArg->getWritableType().getQualifier().makeTemporary();
4221
276
            TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
4222
276
                                                                   intermediate.addSymbol(*tempArg, loc),
4223
276
                                                                   sizeQuery, loc);
4224
4225
            // Compound statement for assigning outputs
4226
276
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
4227
            // Index of first output parameter
4228
276
            const int outParamBase = mipQuery ? 2 : 1;
4229
4230
650
            for (int compNum = 0; compNum < numDims; ++compNum) {
4231
374
                TIntermTyped* indexedOut = nullptr;
4232
374
                TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
4233
4234
374
                if (numDims > 1) {
4235
178
                    TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
4236
178
                    indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
4237
178
                    indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
4238
178
                    indexedOut->setLoc(loc);
4239
196
                } else {
4240
196
                    indexedOut = sizeQueryReturn;
4241
196
                }
4242
4243
374
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
4244
374
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
4245
4246
374
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4247
374
            }
4248
4249
            // handle mip level parameter
4250
276
            if (mipQuery) {
4251
146
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4252
4253
146
                TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
4254
146
                levelsQuery->getSequence().push_back(argTex);
4255
146
                levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
4256
146
                levelsQuery->setLoc(loc);
4257
4258
146
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
4259
146
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4260
146
            }
4261
4262
            // 2DMS formats query # samples, which needs a different query op
4263
276
            if (sampler.isMultiSample()) {
4264
2
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4265
4266
2
                TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4267
2
                samplesQuery->getSequence().push_back(argTex);
4268
2
                samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4269
2
                samplesQuery->setLoc(loc);
4270
4271
2
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
4272
2
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4273
2
            }
4274
4275
276
            compoundStatement->setOperator(EOpSequence);
4276
276
            compoundStatement->setLoc(loc);
4277
276
            compoundStatement->setType(TType(EbtVoid));
4278
4279
276
            node = compoundStatement;
4280
4281
276
            break;
4282
276
        }
4283
4284
28
    case EOpMethodSampleCmp:  // fall through...
4285
246
    case EOpMethodSampleCmpLevelZero:
4286
246
        {
4287
246
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4288
246
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4289
246
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4290
246
            TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
4291
246
            TIntermTyped* argOffset = nullptr;
4292
4293
            // Sampler argument should be a sampler.
4294
246
            if (argSamp->getType().getBasicType() != EbtSampler) {
4295
44
                error(loc, "expected: sampler type", "", "");
4296
44
                return;
4297
44
            }
4298
4299
            // Sampler should be a SamplerComparisonState
4300
202
            if (! argSamp->getType().getSampler().isShadow()) {
4301
6
                error(loc, "expected: SamplerComparisonState", "", "");
4302
6
                return;
4303
6
            }
4304
4305
            // optional offset value
4306
196
            if (argAggregate->getSequence().size() > 4)
4307
10
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4308
4309
196
            const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
4310
4311
            // AST wants comparison value as one of the texture coordinates
4312
196
            TOperator constructOp = EOpNull;
4313
196
            switch (coordDimWithCmpVal) {
4314
            // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
4315
78
            case 2: constructOp = EOpConstructVec2;  break;
4316
74
            case 3: constructOp = EOpConstructVec3;  break;
4317
44
            case 4: constructOp = EOpConstructVec4;  break;
4318
0
            case 5: constructOp = EOpConstructVec4;  break; // cubeArrayShadow, cmp value is separate arg.
4319
0
            default:
4320
0
                error(loc, "unhandled DX10 MethodSample dimension", "", "");
4321
0
                break;
4322
196
            }
4323
4324
196
            TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
4325
196
            coordWithCmp->getSequence().push_back(argCoord);
4326
196
            if (coordDimWithCmpVal != 5) // cube array shadow is special.
4327
196
                coordWithCmp->getSequence().push_back(argCmpVal);
4328
196
            coordWithCmp->setLoc(loc);
4329
196
            coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
4330
4331
196
            TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
4332
196
            if (argOffset != nullptr)
4333
10
                textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
4334
4335
            // Create combined sampler & texture op
4336
196
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4337
196
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4338
196
            txsample->getSequence().push_back(txcombine);
4339
196
            txsample->getSequence().push_back(coordWithCmp);
4340
4341
196
            if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
4342
0
                txsample->getSequence().push_back(argCmpVal);
4343
4344
            // the LevelZero form uses 0 as an explicit LOD
4345
196
            if (op == EOpMethodSampleCmpLevelZero)
4346
176
                txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
4347
4348
            // Add offset if present
4349
196
            if (argOffset != nullptr)
4350
10
                txsample->getSequence().push_back(argOffset);
4351
4352
196
            txsample->setType(node->getType());
4353
196
            txsample->setLoc(loc);
4354
196
            node = txsample;
4355
4356
196
            break;
4357
196
        }
4358
4359
44
    case EOpMethodLoad:
4360
44
        {
4361
44
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4362
44
            TIntermTyped* argCoord  = argAggregate->getSequence()[1]->getAsTyped();
4363
44
            TIntermTyped* argOffset = nullptr;
4364
44
            TIntermTyped* lodComponent = nullptr;
4365
44
            TIntermTyped* coordSwizzle = nullptr;
4366
4367
44
            const TSampler& sampler = argTex->getType().getSampler();
4368
44
            const bool isMS = sampler.isMultiSample();
4369
44
            const bool isBuffer = sampler.dim == EsdBuffer;
4370
44
            const bool isImage = sampler.isImage();
4371
44
            const TBasicType coordBaseType = argCoord->getType().getBasicType();
4372
4373
            // Last component of coordinate is the mip level, for non-MS.  we separate them here:
4374
44
            if (isMS || isBuffer || isImage) {
4375
                // MS, Buffer, and Image have no LOD
4376
24
                coordSwizzle = argCoord;
4377
24
            } else {
4378
                // Extract coordinate
4379
20
                int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
4380
20
                TSwizzleSelectors<TVectorSelector> coordFields;
4381
42
                for (int i = 0; i < swizzleSize; ++i)
4382
22
                    coordFields.push_back(i);
4383
20
                TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
4384
20
                coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
4385
20
                coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
4386
4387
                // Extract LOD
4388
20
                TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
4389
20
                lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
4390
20
                lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
4391
20
            }
4392
4393
44
            const int numArgs    = (int)argAggregate->getSequence().size();
4394
44
            const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
4395
4396
            // Create texel fetch
4397
44
            const TOperator fetchOp = (isImage   ? EOpImageLoad :
4398
44
                                       hasOffset ? EOpTextureFetchOffset :
4399
44
                                       EOpTextureFetch);
4400
44
            TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
4401
4402
            // Build up the fetch
4403
44
            txfetch->getSequence().push_back(argTex);
4404
44
            txfetch->getSequence().push_back(coordSwizzle);
4405
4406
44
            if (isMS) {
4407
                // add 2DMS sample index
4408
0
                TIntermTyped* argSampleIdx  = argAggregate->getSequence()[2]->getAsTyped();
4409
0
                txfetch->getSequence().push_back(argSampleIdx);
4410
44
            } else if (isBuffer) {
4411
                // Nothing else to do for buffers.
4412
24
            } else if (isImage) {
4413
                // Nothing else to do for images.
4414
20
            } else {
4415
                // 2DMS and buffer have no LOD, but everything else does.
4416
20
                txfetch->getSequence().push_back(lodComponent);
4417
20
            }
4418
4419
            // Obtain offset arg, if there is one.
4420
44
            if (hasOffset) {
4421
0
                const int offsetPos  = (isMS ? 3 : 2);
4422
0
                argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
4423
0
                txfetch->getSequence().push_back(argOffset);
4424
0
            }
4425
4426
44
            node = convertReturn(txfetch, sampler);
4427
4428
44
            break;
4429
196
        }
4430
4431
8
    case EOpMethodSampleLevel:
4432
8
        {
4433
8
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4434
8
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4435
8
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4436
8
            TIntermTyped* argLod    = argAggregate->getSequence()[3]->getAsTyped();
4437
8
            TIntermTyped* argOffset = nullptr;
4438
8
            const TSampler& sampler = argTex->getType().getSampler();
4439
4440
8
            const int  numArgs = (int)argAggregate->getSequence().size();
4441
4442
8
            if (numArgs == 5) // offset, if present
4443
0
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4444
4445
8
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
4446
8
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4447
4448
8
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4449
4450
8
            txsample->getSequence().push_back(txcombine);
4451
8
            txsample->getSequence().push_back(argCoord);
4452
8
            txsample->getSequence().push_back(argLod);
4453
4454
8
            if (argOffset != nullptr)
4455
0
                txsample->getSequence().push_back(argOffset);
4456
4457
8
            node = convertReturn(txsample, sampler);
4458
4459
8
            break;
4460
196
        }
4461
4462
0
    case EOpMethodGather:
4463
0
        {
4464
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4465
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4466
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4467
0
            TIntermTyped* argOffset = nullptr;
4468
4469
            // Offset is optional
4470
0
            if (argAggregate->getSequence().size() > 3)
4471
0
                argOffset = argAggregate->getSequence()[3]->getAsTyped();
4472
4473
0
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
4474
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4475
4476
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4477
4478
0
            txgather->getSequence().push_back(txcombine);
4479
0
            txgather->getSequence().push_back(argCoord);
4480
            // Offset if not given is implicitly channel 0 (red)
4481
4482
0
            if (argOffset != nullptr)
4483
0
                txgather->getSequence().push_back(argOffset);
4484
4485
0
            txgather->setType(node->getType());
4486
0
            txgather->setLoc(loc);
4487
0
            node = txgather;
4488
4489
0
            break;
4490
196
        }
4491
4492
0
    case EOpMethodGatherRed:      // fall through...
4493
0
    case EOpMethodGatherGreen:    // ...
4494
0
    case EOpMethodGatherBlue:     // ...
4495
0
    case EOpMethodGatherAlpha:    // ...
4496
10
    case EOpMethodGatherCmpRed:   // ...
4497
10
    case EOpMethodGatherCmpGreen: // ...
4498
10
    case EOpMethodGatherCmpBlue:  // ...
4499
10
    case EOpMethodGatherCmpAlpha: // ...
4500
10
        {
4501
10
            int channel = 0;    // the channel we are gathering
4502
10
            int cmpValues = 0;  // 1 if there is a compare value (handier than a bool below)
4503
4504
10
            switch (op) {
4505
10
            case EOpMethodGatherCmpRed:   cmpValues = 1;  [[fallthrough]];
4506
10
            case EOpMethodGatherRed:      channel = 0; break;
4507
0
            case EOpMethodGatherCmpGreen: cmpValues = 1;  [[fallthrough]];
4508
0
            case EOpMethodGatherGreen:    channel = 1; break;
4509
0
            case EOpMethodGatherCmpBlue:  cmpValues = 1;  [[fallthrough]];
4510
0
            case EOpMethodGatherBlue:     channel = 2; break;
4511
0
            case EOpMethodGatherCmpAlpha: cmpValues = 1;  [[fallthrough]];
4512
0
            case EOpMethodGatherAlpha:    channel = 3; break;
4513
0
            default:                      assert(0);   break;
4514
10
            }
4515
4516
            // For now, we have nothing to map the component-wise comparison forms
4517
            // to, because neither GLSL nor SPIR-V has such an opcode.  Issue an
4518
            // unimplemented error instead.  Most of the machinery is here if that
4519
            // should ever become available.  However, red can be passed through
4520
            // to OpImageDrefGather.  G/B/A cannot, because that opcode does not
4521
            // accept a component.
4522
10
            if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
4523
0
                error(loc, "unimplemented: component-level gather compare", "", "");
4524
0
                return;
4525
0
            }
4526
4527
10
            int arg = 0;
4528
4529
10
            TIntermTyped* argTex        = argAggregate->getSequence()[arg++]->getAsTyped();
4530
10
            TIntermTyped* argSamp       = argAggregate->getSequence()[arg++]->getAsTyped();
4531
10
            TIntermTyped* argCoord      = argAggregate->getSequence()[arg++]->getAsTyped();
4532
10
            TIntermTyped* argOffset     = nullptr;
4533
10
            TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
4534
            // TIntermTyped* argStatus     = nullptr; // TODO: residency
4535
10
            TIntermTyped* argCmp        = nullptr;
4536
4537
10
            const TSamplerDim dim = argTex->getType().getSampler().dim;
4538
4539
10
            const int  argSize = (int)argAggregate->getSequence().size();
4540
10
            bool hasStatus     = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
4541
10
            bool hasOffset1    = false;
4542
10
            bool hasOffset4    = false;
4543
4544
            // Sampler argument should be a sampler.
4545
10
            if (argSamp->getType().getBasicType() != EbtSampler) {
4546
4
                error(loc, "expected: sampler type", "", "");
4547
4
                return;
4548
4
            }
4549
4550
            // Cmp forms require SamplerComparisonState
4551
6
            if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) {
4552
6
                error(loc, "expected: SamplerComparisonState", "", "");
4553
6
                return;
4554
6
            }
4555
4556
            // Only 2D forms can have offsets.  Discover if we have 0, 1 or 4 offsets.
4557
0
            if (dim == Esd2D) {
4558
0
                hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
4559
0
                hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
4560
0
            }
4561
4562
0
            assert(!(hasOffset1 && hasOffset4));
4563
4564
0
            TOperator textureOp = EOpTextureGather;
4565
4566
            // Compare forms have compare value
4567
0
            if (cmpValues != 0)
4568
0
                argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4569
4570
            // Some forms have single offset
4571
0
            if (hasOffset1) {
4572
0
                textureOp = EOpTextureGatherOffset;   // single offset form
4573
0
                argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4574
0
            }
4575
4576
            // Some forms have 4 gather offsets
4577
0
            if (hasOffset4) {
4578
0
                textureOp = EOpTextureGatherOffsets;  // note plural, for 4 offset form
4579
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4580
0
                    argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
4581
0
            }
4582
4583
            // Residency status
4584
0
            if (hasStatus) {
4585
                // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
4586
0
                error(loc, "unimplemented: residency status", "", "");
4587
0
                return;
4588
0
            }
4589
4590
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4591
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4592
4593
0
            TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
4594
4595
0
            txgather->getSequence().push_back(txcombine);
4596
0
            txgather->getSequence().push_back(argCoord);
4597
4598
            // AST wants an array of 4 offsets, where HLSL has separate args.  Here
4599
            // we construct an array from the separate args.
4600
0
            if (hasOffset4) {
4601
0
                TType arrayType(EbtInt, EvqTemporary, 2);
4602
0
                TArraySizes* arraySizes = new TArraySizes;
4603
0
                arraySizes->addInnerSize(4);
4604
0
                arrayType.transferArraySizes(arraySizes);
4605
4606
0
                TIntermAggregate* initList = new TIntermAggregate(EOpNull);
4607
4608
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4609
0
                    initList->getSequence().push_back(argOffsets[offsetNum]);
4610
4611
0
                argOffset = addConstructor(loc, initList, arrayType);
4612
0
            }
4613
4614
            // Add comparison value if we have one
4615
0
            if (argCmp != nullptr)
4616
0
                txgather->getSequence().push_back(argCmp);
4617
4618
            // Add offset (either 1, or an array of 4) if we have one
4619
0
            if (argOffset != nullptr)
4620
0
                txgather->getSequence().push_back(argOffset);
4621
4622
            // Add channel value if the sampler is not shadow
4623
0
            if (! argSamp->getType().getSampler().isShadow())
4624
0
                txgather->getSequence().push_back(argChannel);
4625
4626
0
            txgather->setType(node->getType());
4627
0
            txgather->setLoc(loc);
4628
0
            node = txgather;
4629
4630
0
            break;
4631
0
        }
4632
4633
0
    case EOpMethodCalculateLevelOfDetail:
4634
16
    case EOpMethodCalculateLevelOfDetailUnclamped:
4635
16
        {
4636
16
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4637
16
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4638
16
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4639
4640
16
            TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
4641
4642
16
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4643
16
            txquerylod->getSequence().push_back(txcombine);
4644
16
            txquerylod->getSequence().push_back(argCoord);
4645
4646
16
            TIntermTyped* lodComponent = intermediate.addConstantUnion(
4647
16
                op == EOpMethodCalculateLevelOfDetail ? 0 : 1,
4648
16
                loc, true);
4649
16
            TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
4650
16
            lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
4651
16
            node = lodComponentIdx;
4652
4653
16
            break;
4654
0
        }
4655
4656
0
    case EOpMethodGetSamplePosition:
4657
0
        {
4658
            // TODO: this entire decomposition exists because there is not yet a way to query
4659
            // the sample position directly through SPIR-V.  Instead, we return fixed sample
4660
            // positions for common cases.  *** If the sample positions are set differently,
4661
            // this will be wrong. ***
4662
4663
0
            TIntermTyped* argTex     = argAggregate->getSequence()[0]->getAsTyped();
4664
0
            TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped();
4665
4666
0
            TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4667
0
            samplesQuery->getSequence().push_back(argTex);
4668
0
            samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4669
0
            samplesQuery->setLoc(loc);
4670
4671
0
            TIntermAggregate* compoundStatement = nullptr;
4672
4673
0
            TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint));
4674
0
            outSampleCount->getWritableType().getQualifier().makeTemporary();
4675
0
            TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc),
4676
0
                                                              samplesQuery, loc);
4677
0
            compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4678
4679
0
            TIntermTyped* idxtest[4];
4680
4681
            // Create tests against 2, 4, 8, and 16 sample values
4682
0
            int count = 0;
4683
0
            for (int val = 2; val <= 16; val *= 2)
4684
0
                idxtest[count++] =
4685
0
                    intermediate.addBinaryNode(EOpEqual,
4686
0
                                               intermediate.addSymbol(*outSampleCount, loc),
4687
0
                                               intermediate.addConstantUnion(val, loc),
4688
0
                                               loc, TType(EbtBool));
4689
4690
0
            const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
4691
4692
            // Create index ops into position arrays given sample index.
4693
            // TODO: should it be clamped?
4694
0
            TIntermTyped* index[4];
4695
0
            count = 0;
4696
0
            for (int val = 2; val <= 16; val *= 2) {
4697
0
                index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc);
4698
0
                index[count++]->setType(TType(EbtFloat, EvqTemporary, 2));
4699
0
            }
4700
4701
            // Create expression as:
4702
            // (sampleCount == 2)  ? pos2[idx] :
4703
            // (sampleCount == 4)  ? pos4[idx] :
4704
            // (sampleCount == 8)  ? pos8[idx] :
4705
            // (sampleCount == 16) ? pos16[idx] : float2(0,0);
4706
0
            TIntermTyped* test =
4707
0
                intermediate.addSelection(idxtest[0], index[0],
4708
0
                    intermediate.addSelection(idxtest[1], index[1],
4709
0
                        intermediate.addSelection(idxtest[2], index[2],
4710
0
                            intermediate.addSelection(idxtest[3], index[3],
4711
0
                                                      getSamplePosArray(1), loc), loc), loc), loc);
4712
4713
0
            compoundStatement = intermediate.growAggregate(compoundStatement, test);
4714
0
            compoundStatement->setOperator(EOpSequence);
4715
0
            compoundStatement->setLoc(loc);
4716
0
            compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2));
4717
4718
0
            node = compoundStatement;
4719
4720
0
            break;
4721
0
        }
4722
4723
232
    case EOpSubpassLoad:
4724
232
        {
4725
232
            const TIntermTyped* argSubpass =
4726
232
                argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
4727
232
                arguments->getAsTyped();
4728
4729
232
            const TSampler& sampler = argSubpass->getType().getSampler();
4730
4731
            // subpass load: the multisample form is overloaded.  Here, we convert that to
4732
            // the EOpSubpassLoadMS opcode.
4733
232
            if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
4734
108
                node->getAsOperator()->setOp(EOpSubpassLoadMS);
4735
4736
232
            node = convertReturn(node, sampler);
4737
4738
232
            break;
4739
0
        }
4740
4741
4742
790
    default:
4743
790
        break; // most pass through unchanged
4744
1.70k
    }
4745
1.70k
}
4746
4747
//
4748
// Decompose geometry shader methods
4749
//
4750
void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4751
12.4k
{
4752
12.4k
    if (node == nullptr || !node->getAsOperator())
4753
408
        return;
4754
4755
11.9k
    const TOperator op  = node->getAsOperator()->getOp();
4756
11.9k
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4757
4758
11.9k
    switch (op) {
4759
8
    case EOpMethodAppend:
4760
8
        if (argAggregate) {
4761
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4762
8
            if (language != EShLangGeometry) {
4763
8
                node = nullptr;
4764
8
                return;
4765
8
            }
4766
4767
0
            TIntermAggregate* sequence = nullptr;
4768
0
            TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
4769
4770
0
            emit->setLoc(loc);
4771
0
            emit->setType(TType(EbtVoid));
4772
4773
0
            TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped();
4774
4775
            // This will be patched in finalization during finalizeAppendMethods()
4776
0
            sequence = intermediate.growAggregate(sequence, data, loc);
4777
0
            sequence = intermediate.growAggregate(sequence, emit);
4778
4779
0
            sequence->setOperator(EOpSequence);
4780
0
            sequence->setLoc(loc);
4781
0
            sequence->setType(TType(EbtVoid));
4782
4783
0
            gsAppends.push_back({sequence, loc});
4784
4785
0
            node = sequence;
4786
0
        }
4787
0
        break;
4788
4789
4
    case EOpMethodRestartStrip:
4790
4
        {
4791
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4792
4
            if (language != EShLangGeometry) {
4793
4
                node = nullptr;
4794
4
                return;
4795
4
            }
4796
4797
0
            TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
4798
0
            cut->setLoc(loc);
4799
0
            cut->setType(TType(EbtVoid));
4800
0
            node = cut;
4801
0
        }
4802
0
        break;
4803
4804
11.9k
    default:
4805
11.9k
        break; // most pass through unchanged
4806
11.9k
    }
4807
11.9k
}
4808
4809
//
4810
// Optionally decompose intrinsics to AST opcodes.
4811
//
4812
void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4813
12.4k
{
4814
    // Helper to find image data for image atomics:
4815
    // OpImageLoad(image[idx])
4816
    // We take the image load apart and add its params to the atomic op aggregate node
4817
12.4k
    const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
4818
0
        TIntermAggregate* loadOp = load->getAsAggregate();
4819
0
        if (loadOp == nullptr) {
4820
0
            error(loc, "unknown image type in atomic operation", "", "");
4821
0
            node = nullptr;
4822
0
            return;
4823
0
        }
4824
4825
0
        atomic->getSequence().push_back(loadOp->getSequence()[0]);
4826
0
        atomic->getSequence().push_back(loadOp->getSequence()[1]);
4827
0
    };
4828
4829
    // Return true if this is an imageLoad, which we will change to an image atomic.
4830
12.4k
    const auto isImageParam = [](TIntermTyped* image) -> bool {
4831
202
        TIntermAggregate* imageAggregate = image->getAsAggregate();
4832
202
        return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
4833
202
    };
4834
4835
12.4k
    const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* {
4836
0
        TSymbol* symbol = symbolTable.find(name);
4837
0
        if (nullptr == symbol) {
4838
0
            type.getQualifier().builtIn = builtin;
4839
4840
0
            TVariable* variable = new TVariable(NewPoolTString(name), type);
4841
4842
0
            symbolTable.insert(*variable);
4843
4844
0
            symbol = symbolTable.find(name);
4845
0
            assert(symbol && "Inserted symbol could not be found!");
4846
0
        }
4847
4848
0
        return intermediate.addSymbol(*(symbol->getAsVariable()), loc);
4849
0
    };
4850
4851
    // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
4852
    // opcodes for compatibility with existing software stacks.
4853
12.4k
    static const bool decomposeHlslIntrinsics = true;
4854
4855
12.4k
    if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
4856
160
        return;
4857
4858
12.2k
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4859
12.2k
    TIntermUnary* fnUnary = node->getAsUnaryNode();
4860
12.2k
    const TOperator op  = node->getAsOperator()->getOp();
4861
4862
12.2k
    switch (op) {
4863
18
    case EOpGenMul:
4864
18
        {
4865
            // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
4866
            // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
4867
            // we must reverse the operand order here.  Hence, arg0 gets sequence[1], etc.
4868
18
            TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
4869
18
            TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
4870
4871
18
            if (arg0->isVector() && arg1->isVector()) {  // vec * vec
4872
0
                node->getAsAggregate()->setOperator(EOpDot);
4873
18
            } else {
4874
18
                node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
4875
18
            }
4876
4877
18
            break;
4878
0
        }
4879
4880
96
    case EOpRcp:
4881
96
        {
4882
            // rcp(a) -> 1 / a
4883
96
            TIntermTyped* arg0 = fnUnary->getOperand();
4884
96
            TBasicType   type0 = arg0->getBasicType();
4885
96
            TIntermTyped* one  = intermediate.addConstantUnion(1, type0, loc, true);
4886
96
            node  = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
4887
4888
96
            break;
4889
0
        }
4890
4891
148
    case EOpAny: // fall through
4892
298
    case EOpAll:
4893
298
        {
4894
298
            TIntermTyped* typedArg = arguments->getAsTyped();
4895
4896
            // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool.
4897
            // We'll convert here.  Note that for efficiency, we could add a smarter
4898
            // decomposition for some type cases, e.g, maybe by decomposing a dot product.
4899
298
            if (typedArg->getType().getBasicType() != EbtBool) {
4900
278
                const TType boolType(EbtBool, EvqTemporary,
4901
278
                                     typedArg->getVectorSize(),
4902
278
                                     typedArg->getMatrixCols(),
4903
278
                                     typedArg->getMatrixRows(),
4904
278
                                     typedArg->isVector());
4905
4906
278
                typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg);
4907
278
                node->getAsUnaryNode()->setOperand(typedArg);
4908
278
            }
4909
4910
298
            break;
4911
148
        }
4912
4913
106
    case EOpSaturate:
4914
106
        {
4915
            // saturate(a) -> clamp(a,0,1)
4916
106
            TIntermTyped* arg0 = fnUnary->getOperand();
4917
106
            TBasicType   type0 = arg0->getBasicType();
4918
106
            TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
4919
4920
106
            clamp->getSequence().push_back(arg0);
4921
106
            clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
4922
106
            clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
4923
106
            clamp->setLoc(loc);
4924
106
            clamp->setType(node->getType());
4925
106
            clamp->getWritableType().getQualifier().makeTemporary();
4926
106
            node = clamp;
4927
4928
106
            break;
4929
148
        }
4930
4931
92
    case EOpSinCos:
4932
92
        {
4933
            // sincos(a,b,c) -> b = sin(a), c = cos(a)
4934
92
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
4935
92
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
4936
92
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
4937
4938
92
            TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
4939
92
            TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
4940
92
            TIntermTyped* sinAssign    = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
4941
92
            TIntermTyped* cosAssign    = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
4942
4943
92
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
4944
92
            compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
4945
92
            compoundStatement->setOperator(EOpSequence);
4946
92
            compoundStatement->setLoc(loc);
4947
92
            compoundStatement->setType(TType(EbtVoid));
4948
4949
92
            node = compoundStatement;
4950
4951
92
            break;
4952
148
        }
4953
4954
234
    case EOpClip:
4955
234
        {
4956
            // clip(a) -> if (any(a<0)) discard;
4957
234
            TIntermTyped*  arg0 = fnUnary->getOperand();
4958
234
            TBasicType     type0 = arg0->getBasicType();
4959
234
            TIntermTyped*  compareNode = nullptr;
4960
4961
            // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
4962
234
            if (!arg0->isScalar()) {
4963
                // component-wise compare: a < 0
4964
130
                TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
4965
130
                less->getSequence().push_back(arg0);
4966
130
                less->setLoc(loc);
4967
4968
                // make vec or mat of bool matching dimensions of input
4969
130
                less->setType(TType(EbtBool, EvqTemporary,
4970
130
                                    arg0->getType().getVectorSize(),
4971
130
                                    arg0->getType().getMatrixCols(),
4972
130
                                    arg0->getType().getMatrixRows(),
4973
130
                                    arg0->getType().isVector()));
4974
4975
                // calculate # of components for comparison const
4976
130
                const int constComponentCount =
4977
130
                    std::max(arg0->getType().getVectorSize(), 1) *
4978
130
                    std::max(arg0->getType().getMatrixCols(), 1) *
4979
130
                    std::max(arg0->getType().getMatrixRows(), 1);
4980
4981
130
                TConstUnion zero;
4982
130
                if (arg0->getType().isIntegerDomain())
4983
64
                    zero.setDConst(0);
4984
66
                else
4985
66
                    zero.setDConst(0.0);
4986
130
                TConstUnionArray zeros(constComponentCount, zero);
4987
4988
130
                less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
4989
4990
130
                compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
4991
130
            } else {
4992
104
                TIntermTyped* zero;
4993
104
                if (arg0->getType().isIntegerDomain())
4994
48
                    zero = intermediate.addConstantUnion(0, loc, true);
4995
56
                else
4996
56
                    zero = intermediate.addConstantUnion(0.0, type0, loc, true);
4997
104
                compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
4998
104
            }
4999
5000
234
            TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
5001
5002
234
            node = new TIntermSelection(compareNode, killNode, nullptr);
5003
234
            node->setLoc(loc);
5004
5005
234
            break;
5006
148
        }
5007
5008
104
    case EOpLog10:
5009
104
        {
5010
            // log10(a) -> log2(a) * 0.301029995663981  (== 1/log2(10))
5011
104
            TIntermTyped* arg0 = fnUnary->getOperand();
5012
104
            TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
5013
104
            TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
5014
5015
104
            node  = handleBinaryMath(loc, "mul", EOpMul, log2, base);
5016
5017
104
            break;
5018
148
        }
5019
5020
12
    case EOpDst:
5021
12
        {
5022
            // dest.x = 1;
5023
            // dest.y = src0.y * src1.y;
5024
            // dest.z = src0.z;
5025
            // dest.w = src1.w;
5026
5027
12
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5028
12
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5029
5030
12
            TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
5031
12
            TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
5032
12
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
5033
5034
12
            TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
5035
12
            TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
5036
12
            TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
5037
12
            TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
5038
5039
12
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5040
5041
12
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5042
12
            dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
5043
12
            dst->getSequence().push_back(src0z);
5044
12
            dst->getSequence().push_back(src1w);
5045
12
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5046
12
            dst->setLoc(loc);
5047
12
            node = dst;
5048
5049
12
            break;
5050
148
        }
5051
5052
60
    case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
5053
74
    case EOpInterlockedMin: // ...
5054
80
    case EOpInterlockedMax: // ...
5055
136
    case EOpInterlockedAnd: // ...
5056
154
    case EOpInterlockedOr:  // ...
5057
172
    case EOpInterlockedXor: // ...
5058
180
    case EOpInterlockedExchange: // always has output arg
5059
180
        {
5060
180
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5061
180
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // value
5062
180
            TIntermTyped* arg2 = nullptr;
5063
5064
180
            if (argAggregate->getSequence().size() > 2)
5065
102
                arg2 = argAggregate->getSequence()[2]->getAsTyped();
5066
5067
180
            const bool isImage = isImageParam(arg0);
5068
180
            const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
5069
180
            TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
5070
180
            atomic->setType(arg0->getType());
5071
180
            atomic->getWritableType().getQualifier().makeTemporary();
5072
180
            atomic->setLoc(loc);
5073
5074
180
            if (isImage) {
5075
                // orig_value = imageAtomicOp(image, loc, data)
5076
0
                imageAtomicParams(atomic, arg0);
5077
0
                atomic->getSequence().push_back(arg1);
5078
5079
0
                if (argAggregate->getSequence().size() > 2) {
5080
0
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5081
0
                } else {
5082
0
                    node = atomic; // no assignment needed, as there was no out var.
5083
0
                }
5084
180
            } else {
5085
                // Normal memory variable:
5086
                // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
5087
180
                if (argAggregate->getSequence().size() > 2) {
5088
                    // optional output param is present.  return value goes to arg2.
5089
102
                    atomic->getSequence().push_back(arg0);
5090
102
                    atomic->getSequence().push_back(arg1);
5091
5092
102
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5093
102
                } else {
5094
                    // Set the matching operator.  Since output is absent, this is all we need to do.
5095
78
                    node->getAsAggregate()->setOperator(atomicOp);
5096
78
                    node->setType(atomic->getType());
5097
78
                }
5098
180
            }
5099
5100
180
            break;
5101
172
        }
5102
5103
22
    case EOpInterlockedCompareExchange:
5104
22
        {
5105
22
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5106
22
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // cmp
5107
22
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();  // value
5108
22
            TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped();  // orig
5109
5110
22
            const bool isImage = isImageParam(arg0);
5111
22
            TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
5112
22
            atomic->setLoc(loc);
5113
22
            atomic->setType(arg2->getType());
5114
22
            atomic->getWritableType().getQualifier().makeTemporary();
5115
5116
22
            if (isImage) {
5117
0
                imageAtomicParams(atomic, arg0);
5118
22
            } else {
5119
22
                atomic->getSequence().push_back(arg0);
5120
22
            }
5121
5122
22
            atomic->getSequence().push_back(arg1);
5123
22
            atomic->getSequence().push_back(arg2);
5124
22
            node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
5125
5126
22
            break;
5127
172
        }
5128
5129
0
    case EOpEvaluateAttributeSnapped:
5130
0
        {
5131
            // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
5132
            // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
5133
            //   iU = (iU<<28)>>28
5134
            //   fU = ((float)iU)/16
5135
            // Targets might handle this natively, in which case they can disable
5136
            // decompositions.
5137
5138
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // value
5139
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // offset
5140
5141
0
            TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
5142
0
            TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
5143
0
                                                handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
5144
0
                                                i28);
5145
5146
0
            TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
5147
0
            TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
5148
0
                                                         intermediate.addConversion(EOpConstructFloat,
5149
0
                                                                                    TType(EbtFloat, EvqTemporary, 2), iU),
5150
0
                                                         recip16);
5151
5152
0
            TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
5153
0
            interp->getSequence().push_back(arg0);
5154
0
            interp->getSequence().push_back(floatOffset);
5155
0
            interp->setLoc(loc);
5156
0
            interp->setType(arg0->getType());
5157
0
            interp->getWritableType().getQualifier().makeTemporary();
5158
5159
0
            node = interp;
5160
5161
0
            break;
5162
172
        }
5163
5164
0
    case EOpLit:
5165
0
        {
5166
0
            TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
5167
0
            TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
5168
0
            TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
5169
5170
0
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5171
5172
            // Ambient
5173
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5174
5175
            // Diffuse:
5176
0
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5177
0
            TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
5178
0
            diffuse->getSequence().push_back(n_dot_l);
5179
0
            diffuse->getSequence().push_back(zero);
5180
0
            diffuse->setLoc(loc);
5181
0
            diffuse->setType(TType(EbtFloat));
5182
0
            dst->getSequence().push_back(diffuse);
5183
5184
            // Specular:
5185
0
            TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
5186
0
            min_ndot->getSequence().push_back(n_dot_l);
5187
0
            min_ndot->getSequence().push_back(n_dot_h);
5188
0
            min_ndot->setLoc(loc);
5189
0
            min_ndot->setType(TType(EbtFloat));
5190
5191
0
            TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
5192
0
            TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m);  // n_dot_h * m
5193
5194
0
            dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
5195
5196
            // One:
5197
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5198
5199
0
            dst->setLoc(loc);
5200
0
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5201
0
            node = dst;
5202
0
            break;
5203
172
        }
5204
5205
64
    case EOpAsDouble:
5206
64
        {
5207
            // asdouble accepts two 32 bit ints.  we can use EOpUint64BitsToDouble, but must
5208
            // first construct a uint64.
5209
64
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5210
64
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5211
5212
64
            if (arg0->getType().isVector()) { // TODO: ...
5213
34
                error(loc, "double2 conversion not implemented", "asdouble", "");
5214
34
                break;
5215
34
            }
5216
5217
30
            TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
5218
5219
30
            uint64->getSequence().push_back(arg0);
5220
30
            uint64->getSequence().push_back(arg1);
5221
30
            uint64->setType(TType(EbtUint, EvqTemporary, 2));  // convert 2 uints to a uint2
5222
30
            uint64->setLoc(loc);
5223
5224
            // bitcast uint2 to a double
5225
30
            TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
5226
30
            convert->getAsUnaryNode()->setOperand(uint64);
5227
30
            convert->setLoc(loc);
5228
30
            convert->setType(TType(EbtDouble, EvqTemporary));
5229
30
            node = convert;
5230
5231
30
            break;
5232
64
        }
5233
5234
170
    case EOpF16tof32:
5235
170
        {
5236
            // input uvecN with low 16 bits of each component holding a float16.  convert to float32.
5237
170
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5238
170
            TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
5239
170
            const int vecSize = argValue->getType().getVectorSize();
5240
5241
170
            TOperator constructOp = EOpNull;
5242
170
            switch (vecSize) {
5243
60
            case 1: constructOp = EOpNull;          break; // direct use, no construct needed
5244
46
            case 2: constructOp = EOpConstructVec2; break;
5245
32
            case 3: constructOp = EOpConstructVec3; break;
5246
32
            case 4: constructOp = EOpConstructVec4; break;
5247
0
            default: assert(0); break;
5248
170
            }
5249
5250
            // For scalar case, we don't need to construct another type.
5251
170
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5252
5253
170
            if (result) {
5254
110
                result->setType(TType(EbtFloat, EvqTemporary, vecSize));
5255
110
                result->setLoc(loc);
5256
110
            }
5257
5258
546
            for (int idx = 0; idx < vecSize; ++idx) {
5259
376
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5260
376
                TIntermTyped* component = argValue->getType().isVector() ?
5261
316
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5262
5263
376
                if (component != argValue)
5264
316
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5265
5266
376
                TIntermTyped* unpackOp  = new TIntermUnary(EOpUnpackHalf2x16);
5267
376
                unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
5268
376
                unpackOp->getAsUnaryNode()->setOperand(component);
5269
376
                unpackOp->setLoc(loc);
5270
5271
376
                TIntermTyped* lowOrder  = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
5272
5273
376
                if (result != nullptr) {
5274
316
                    result->getSequence().push_back(lowOrder);
5275
316
                    node = result;
5276
316
                } else {
5277
60
                    node = lowOrder;
5278
60
                }
5279
376
            }
5280
5281
170
            break;
5282
170
        }
5283
5284
30
    case EOpF32tof16:
5285
30
        {
5286
            // input floatN converted to 16 bit float in low order bits of each component of uintN
5287
30
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5288
5289
30
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5290
30
            const int vecSize = argValue->getType().getVectorSize();
5291
5292
30
            TOperator constructOp = EOpNull;
5293
30
            switch (vecSize) {
5294
14
            case 1: constructOp = EOpNull;           break; // direct use, no construct needed
5295
8
            case 2: constructOp = EOpConstructUVec2; break;
5296
4
            case 3: constructOp = EOpConstructUVec3; break;
5297
4
            case 4: constructOp = EOpConstructUVec4; break;
5298
0
            default: assert(0); break;
5299
30
            }
5300
5301
            // For scalar case, we don't need to construct another type.
5302
30
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5303
5304
30
            if (result) {
5305
16
                result->setType(TType(EbtUint, EvqTemporary, vecSize));
5306
16
                result->setLoc(loc);
5307
16
            }
5308
5309
88
            for (int idx = 0; idx < vecSize; ++idx) {
5310
58
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5311
58
                TIntermTyped* component = argValue->getType().isVector() ?
5312
44
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5313
5314
58
                if (component != argValue)
5315
44
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5316
5317
58
                TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
5318
58
                vec2ComponentAndZero->getSequence().push_back(component);
5319
58
                vec2ComponentAndZero->getSequence().push_back(zero);
5320
58
                vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
5321
58
                vec2ComponentAndZero->setLoc(loc);
5322
5323
58
                TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
5324
58
                packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
5325
58
                packOp->setLoc(loc);
5326
58
                packOp->setType(TType(EbtUint, EvqTemporary));
5327
5328
58
                if (result != nullptr) {
5329
44
                    result->getSequence().push_back(packOp);
5330
44
                    node = result;
5331
44
                } else {
5332
14
                    node = packOp;
5333
14
                }
5334
58
            }
5335
5336
30
            break;
5337
30
        }
5338
5339
60
    case EOpD3DCOLORtoUBYTE4:
5340
60
        {
5341
            // ivec4 ( x.zyxw * 255.001953 );
5342
60
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5343
60
            TSwizzleSelectors<TVectorSelector> selectors;
5344
60
            selectors.push_back(2);
5345
60
            selectors.push_back(1);
5346
60
            selectors.push_back(0);
5347
60
            selectors.push_back(3);
5348
60
            TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
5349
60
            TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
5350
60
            swizzled->setType(arg0->getType());
5351
60
            swizzled->getWritableType().getQualifier().makeTemporary();
5352
5353
60
            TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
5354
60
            TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
5355
60
            rangeConverted->setType(arg0->getType());
5356
60
            rangeConverted->getWritableType().getQualifier().makeTemporary();
5357
5358
60
            node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
5359
60
            node->setLoc(loc);
5360
60
            node->setType(TType(EbtInt, EvqTemporary, 4));
5361
60
            break;
5362
30
        }
5363
5364
8
    case EOpIsFinite:
5365
8
        {
5366
            // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
5367
            // it to !isnan && !isinf
5368
5369
8
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5370
5371
            // We'll make a temporary in case the RHS is cmoplex
5372
8
            TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
5373
8
            tempArg->getWritableType().getQualifier().makeTemporary();
5374
5375
8
            TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
5376
8
                                                                intermediate.addSymbol(*tempArg, loc),
5377
8
                                                                arg0, loc);
5378
5379
8
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
5380
5381
8
            const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(),
5382
8
                                 arg0->getMatrixRows());
5383
5384
8
            TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
5385
8
            isnan->setType(boolType);
5386
5387
8
            TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
5388
8
            notnan->setType(boolType);
5389
5390
8
            TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
5391
8
            isinf->setType(boolType);
5392
5393
8
            TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
5394
8
            notinf->setType(boolType);
5395
5396
8
            TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
5397
8
            andNode->setType(boolType);
5398
5399
8
            compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
5400
8
            compoundStatement->setOperator(EOpSequence);
5401
8
            compoundStatement->setLoc(loc);
5402
8
            compoundStatement->setType(boolType);
5403
5404
8
            node = compoundStatement;
5405
5406
8
            break;
5407
30
        }
5408
0
    case EOpWaveGetLaneCount:
5409
0
        {
5410
            // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol
5411
            // so that it inhabits the symbol table, but has a user-invalid name
5412
            // in-case some source HLSL defined the symbol also).
5413
0
            TType type(EbtUint, EvqVaryingIn);
5414
0
            node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type);
5415
0
            break;
5416
30
        }
5417
0
    case EOpWaveGetLaneIndex:
5418
0
        {
5419
            // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the
5420
            // symbol so that it inhabits the symbol table, but has a
5421
            // user-invalid name in-case some source HLSL defined the symbol
5422
            // also).
5423
0
            TType type(EbtUint, EvqVaryingIn);
5424
0
            node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type);
5425
0
            break;
5426
30
        }
5427
0
    case EOpWaveActiveCountBits:
5428
0
        {
5429
            // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin
5430
5431
            // uvec4 type.
5432
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5433
5434
            // Get the uvec4 return from subgroupBallot().
5435
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5436
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5437
5438
            // uint type.
5439
0
            TType uintType(EbtUint, EvqTemporary);
5440
5441
0
            node = intermediate.addBuiltInFunctionCall(loc,
5442
0
                EOpSubgroupBallotBitCount, true, res, uintType);
5443
5444
0
            break;
5445
30
        }
5446
0
    case EOpWavePrefixCountBits:
5447
0
        {
5448
            // Mapped to subgroupBallotExclusiveBitCount(subgroupBallot())
5449
            // builtin
5450
5451
            // uvec4 type.
5452
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5453
5454
            // Get the uvec4 return from subgroupBallot().
5455
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5456
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5457
5458
            // uint type.
5459
0
            TType uintType(EbtUint, EvqTemporary);
5460
5461
0
            node = intermediate.addBuiltInFunctionCall(loc,
5462
0
                EOpSubgroupBallotExclusiveBitCount, true, res, uintType);
5463
5464
0
            break;
5465
30
        }
5466
5467
10.7k
    default:
5468
10.7k
        break; // most pass through unchanged
5469
12.2k
    }
5470
12.2k
}
5471
5472
//
5473
// Handle seeing function call syntax in the grammar, which could be any of
5474
//  - .length() method
5475
//  - constructor
5476
//  - a call to a built-in function mapped to an operator
5477
//  - a call to a built-in function that will remain a function call (e.g., texturing)
5478
//  - user function
5479
//  - subroutine call (not implemented yet)
5480
//
5481
TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
5482
20.8k
{
5483
20.8k
    TIntermTyped* result = nullptr;
5484
5485
20.8k
    TOperator op = function->getBuiltInOp();
5486
20.8k
    if (op != EOpNull) {
5487
        //
5488
        // Then this should be a constructor.
5489
        // Don't go through the symbol table for constructors.
5490
        // Their parameters will be verified algorithmically.
5491
        //
5492
7.24k
        TType type(EbtVoid);  // use this to get the type back
5493
7.24k
        if (! constructorError(loc, arguments, *function, op, type)) {
5494
            //
5495
            // It's a constructor, of type 'type'.
5496
            //
5497
7.03k
            result = handleConstructor(loc, arguments, type);
5498
7.03k
            if (result == nullptr) {
5499
16
                error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
5500
16
                return nullptr;
5501
16
            }
5502
7.03k
        }
5503
13.5k
    } else {
5504
        //
5505
        // Find it in the symbol table.
5506
        //
5507
13.5k
        const TFunction* fnCandidate = nullptr;
5508
13.5k
        bool builtIn = false;
5509
13.5k
        int thisDepth = 0;
5510
5511
        // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
5512
        // and clamp the opposite arg.  Since that's complex, we farm it off to a separate method.
5513
        // It doesn't naturally fall out of processing an argument at a time in isolation.
5514
13.5k
        if (function->getName() == "mul")
5515
20
            addGenMulArgumentConversion(loc, *function, arguments);
5516
5517
13.5k
        TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5518
5519
        // TODO: this needs improvement: there's no way at present to look up a signature in
5520
        // the symbol table for an arbitrary type.  This is a temporary hack until that ability exists.
5521
        // It will have false positives, since it doesn't check arg counts or types.
5522
13.5k
        if (arguments) {
5523
            // Check if first argument is struct buffer type.  It may be an aggregate or a symbol, so we
5524
            // look for either case.
5525
5526
12.7k
            TIntermTyped* arg0 = nullptr;
5527
5528
12.7k
            if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0])
5529
4.21k
                arg0 = aggregate->getSequence()[0]->getAsTyped();
5530
8.50k
            else if (arguments->getAsSymbolNode())
5531
7.79k
                arg0 = arguments->getAsSymbolNode();
5532
5533
12.7k
            if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
5534
328
                static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
5535
5536
328
                if (function->getName().length() > methodPrefixSize &&
5537
322
                    isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
5538
288
                    const TString mangle = function->getName() + "(";
5539
288
                    TSymbol* symbol = symbolTable.find(mangle, &builtIn);
5540
5541
288
                    if (symbol)
5542
288
                        fnCandidate = symbol->getAsFunction();
5543
288
                }
5544
328
            }
5545
12.7k
        }
5546
5547
13.5k
        if (fnCandidate == nullptr)
5548
13.2k
            fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments);
5549
5550
13.5k
        if (fnCandidate) {
5551
            // This is a declared function that might map to
5552
            //  - a built-in operator,
5553
            //  - a built-in function not mapped to an operator, or
5554
            //  - a user function.
5555
5556
            // turn an implicit member-function resolution into an explicit call
5557
12.4k
            TString callerName;
5558
12.4k
            if (thisDepth == 0)
5559
12.3k
                callerName = fnCandidate->getMangledName();
5560
10
            else {
5561
                // get the explicit (full) name of the function
5562
10
                callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth];
5563
10
                callerName += fnCandidate->getMangledName();
5564
                // insert the implicit calling argument
5565
10
                pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments);
5566
10
            }
5567
5568
            // Convert 'in' arguments, so that types match.
5569
            // However, skip those that need expansion, that is covered next.
5570
12.4k
            if (arguments)
5571
11.6k
                addInputArgumentConversions(*fnCandidate, arguments);
5572
5573
            // Expand arguments.  Some arguments must physically expand to a different set
5574
            // than what the shader declared and passes.
5575
12.4k
            if (arguments && !builtIn)
5576
368
                expandArguments(loc, *fnCandidate, arguments);
5577
5578
            // Expansion may have changed the form of arguments
5579
12.4k
            aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5580
5581
12.4k
            op = fnCandidate->getBuiltInOp();
5582
12.4k
            if (builtIn && op != EOpNull) {
5583
                // SM 4.0 and above guarantees roundEven semantics for round()
5584
11.2k
                if (!hlslDX9Compatible() && op == EOpRound)
5585
96
                    op = EOpRoundEven;
5586
5587
                // A function call mapped to a built-in operation.
5588
11.2k
                result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments,
5589
11.2k
                                                             fnCandidate->getType());
5590
11.2k
                if (result == nullptr)  {
5591
0
                    error(arguments->getLoc(), " wrong operand type", "Internal Error",
5592
0
                        "built in unary operator function.  Type: %s",
5593
0
                        static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
5594
11.2k
                } else if (result->getAsOperator()) {
5595
11.0k
                    builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
5596
11.0k
                }
5597
11.2k
            } else {
5598
                // This is a function call not mapped to built-in operator.
5599
                // It could still be a built-in function, but only if PureOperatorBuiltins == false.
5600
1.18k
                result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
5601
1.18k
                TIntermAggregate* call = result->getAsAggregate();
5602
1.18k
                call->setName(callerName);
5603
5604
                // this is how we know whether the given function is a built-in function or a user-defined function
5605
                // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
5606
                // if builtIn == true, it's definitely a built-in function with EOpNull
5607
1.18k
                if (! builtIn) {
5608
1.11k
                    call->setUserDefined();
5609
1.11k
                    intermediate.addToCallGraph(infoSink, currentCaller, callerName);
5610
1.11k
                }
5611
1.18k
            }
5612
5613
            // for decompositions, since we want to operate on the function node, not the aggregate holding
5614
            // output conversions.
5615
12.4k
            const TIntermTyped* fnNode = result;
5616
5617
12.4k
            decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
5618
12.4k
            decomposeIntrinsic(loc, result, arguments);           // HLSL->AST intrinsic decompositions
5619
12.4k
            decomposeSampleMethods(loc, result, arguments);       // HLSL->AST sample method decompositions
5620
12.4k
            decomposeGeometryMethods(loc, result, arguments);     // HLSL->AST geometry method decompositions
5621
5622
            // Create the qualifier list, carried in the AST for the call.
5623
            // Because some arguments expand to multiple arguments, the qualifier list will
5624
            // be longer than the formal parameter list.
5625
12.4k
            if (result == fnNode && result->getAsAggregate()) {
5626
3.72k
                TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
5627
10.0k
                for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
5628
6.34k
                    TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
5629
6.34k
                    if (hasStructBuffCounter(*(*fnCandidate)[i].type)) {
5630
                        // add buffer and counter buffer argument qualifier
5631
2
                        qualifierList.push_back(qual);
5632
2
                        qualifierList.push_back(qual);
5633
6.34k
                    } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage,
5634
6.34k
                                             true)) {
5635
                        // add structure member expansion
5636
0
                        for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb)
5637
0
                            qualifierList.push_back(qual);
5638
6.34k
                    } else {
5639
                        // Normal 1:1 case
5640
6.34k
                        qualifierList.push_back(qual);
5641
6.34k
                    }
5642
6.34k
                }
5643
3.72k
            }
5644
5645
            // Convert 'out' arguments.  If it was a constant folded built-in, it won't be an aggregate anymore.
5646
            // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
5647
            // Also, build the qualifier list for user function calls, which are always called with an aggregate.
5648
            // We don't do this is if there has been a decomposition, which will have added its own conversions
5649
            // for output parameters.
5650
12.4k
            if (result == fnNode && result->getAsAggregate())
5651
3.72k
                result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
5652
12.4k
        }
5653
13.5k
    }
5654
5655
    // generic error recovery
5656
    // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to
5657
    //       reduce cascades
5658
20.7k
    if (result == nullptr)
5659
1.39k
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
5660
5661
20.7k
    return result;
5662
20.8k
}
5663
5664
// An initial argument list is difficult: it can be null, or a single node,
5665
// or an aggregate if more than one argument.  Add one to the front, maintaining
5666
// this lack of uniformity.
5667
void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments)
5668
10
{
5669
10
    if (arguments == nullptr)
5670
6
        arguments = front;
5671
4
    else if (arguments->getAsAggregate() != nullptr)
5672
2
        arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front);
5673
2
    else
5674
2
        arguments = intermediate.growAggregate(front, arguments);
5675
10
}
5676
5677
//
5678
// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat.  This is a
5679
// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
5680
// can look at both arguments insert explicit shape changes if required.
5681
//
5682
void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
5683
20
{
5684
20
    TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
5685
5686
20
    if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
5687
        // It really ought to have two arguments.
5688
0
        error(loc, "expected: mul arguments", "", "");
5689
0
        return;
5690
0
    }
5691
5692
20
    TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5693
20
    TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5694
5695
20
    if (arg0->isVector() && arg1->isVector()) {
5696
        // For:
5697
        //    vec * vec: it's handled during intrinsic selection, so while we could do it here,
5698
        //               we can also ignore it, which is easier.
5699
20
    } else if (arg0->isVector() && arg1->isMatrix()) {
5700
        // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
5701
0
        if (arg0->getVectorSize() < arg1->getMatrixCols()) {
5702
            // vec is smaller, so truncate larger mat dimension
5703
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5704
0
                                  0, arg0->getVectorSize(), arg1->getMatrixRows());
5705
0
            arg1 = addConstructor(loc, arg1, truncType);
5706
0
        } else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
5707
            // vec is larger, so truncate vec to mat size
5708
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5709
0
                                  arg1->getMatrixCols());
5710
0
            arg0 = addConstructor(loc, arg0, truncType);
5711
0
        }
5712
20
    } else if (arg0->isMatrix() && arg1->isVector()) {
5713
        // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
5714
16
        if (arg1->getVectorSize() < arg0->getMatrixRows()) {
5715
            // vec is smaller, so truncate larger mat dimension
5716
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5717
0
                                  0, arg0->getMatrixCols(), arg1->getVectorSize());
5718
0
            arg0 = addConstructor(loc, arg0, truncType);
5719
16
        } else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
5720
            // vec is larger, so truncate vec to mat size
5721
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5722
0
                                  arg0->getMatrixRows());
5723
0
            arg1 = addConstructor(loc, arg1, truncType);
5724
0
        }
5725
16
    } else if (arg0->isMatrix() && arg1->isMatrix()) {
5726
        // mat * mat: we clamp the smaller inner dimension to match the other matrix size.
5727
        // Remember, HLSL Mrc = GLSL/SPIRV Mcr.
5728
0
        if (arg0->getMatrixRows() > arg1->getMatrixCols()) {
5729
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5730
0
                                  0, arg0->getMatrixCols(), arg1->getMatrixCols());
5731
0
            arg0 = addConstructor(loc, arg0, truncType);
5732
0
        } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) {
5733
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5734
0
                                  0, arg0->getMatrixRows(), arg1->getMatrixRows());
5735
0
            arg1 = addConstructor(loc, arg1, truncType);
5736
0
        }
5737
4
    } else {
5738
        // It's something with scalars: we'll just leave it alone.  Function selection will handle it
5739
        // downstream.
5740
4
    }
5741
5742
    // Warn if we altered one of the arguments
5743
20
    if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1])
5744
0
        warn(loc, "mul() matrix size mismatch", "", "");
5745
5746
    // Put arguments back.  (They might be unchanged, in which case this is harmless).
5747
20
    argAggregate->getSequence()[0] = arg0;
5748
20
    argAggregate->getSequence()[1] = arg1;
5749
5750
20
    call[0].type = &arg0->getWritableType();
5751
20
    call[1].type = &arg1->getWritableType();
5752
20
}
5753
5754
//
5755
// Add any needed implicit conversions for function-call arguments to input parameters.
5756
//
5757
void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
5758
11.6k
{
5759
11.6k
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5760
5761
    // Replace a single argument with a single argument.
5762
11.6k
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5763
2.47k
        if (function.getParamCount() == 1)
5764
880
            arguments = arg;
5765
1.59k
        else {
5766
1.59k
            if (aggregate == nullptr)
5767
0
                arguments = arg;
5768
1.59k
            else
5769
1.59k
                aggregate->getSequence()[paramNum] = arg;
5770
1.59k
        }
5771
2.47k
    };
5772
5773
    // Process each argument's conversion
5774
28.3k
    for (int param = 0; param < function.getParamCount(); ++param) {
5775
16.6k
        if (! function[param].type->getQualifier().isParamInput())
5776
750
            continue;
5777
5778
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5779
        // is the single argument itself or its children are the arguments.  Only one argument
5780
        // means take 'arguments' itself as the one argument.
5781
15.9k
        TIntermTyped* arg = function.getParamCount() == 1
5782
15.9k
                                   ? arguments->getAsTyped()
5783
15.9k
                                   : (aggregate ?
5784
8.05k
                                        aggregate->getSequence()[param]->getAsTyped() :
5785
8.05k
                                        arguments->getAsTyped());
5786
15.9k
        if (*function[param].type != arg->getType()) {
5787
            // In-qualified arguments just need an extra node added above the argument to
5788
            // convert to the correct type.
5789
3.00k
            TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg);
5790
3.00k
            if (convArg != nullptr)
5791
2.47k
                convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg);
5792
3.00k
            if (convArg != nullptr)
5793
2.47k
                setArg(param, convArg);
5794
524
            else
5795
524
                error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param);
5796
12.9k
        } else {
5797
12.9k
            if (wasFlattened(arg)) {
5798
                // If both formal and calling arg are to be flattened, leave that to argument
5799
                // expansion, not conversion.
5800
0
                if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5801
                    // Will make a two-level subtree.
5802
                    // The deepest will copy member-by-member to build the structure to pass.
5803
                    // The level above that will be a two-operand EOpComma sequence that follows the copy by the
5804
                    // object itself.
5805
0
                    TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type);
5806
0
                    internalAggregate->getWritableType().getQualifier().makeTemporary();
5807
0
                    TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
5808
0
                                                                          internalAggregate->getName(),
5809
0
                                                                          getLanguage(),
5810
0
                                                                          internalAggregate->getType());
5811
0
                    internalSymbolNode->setLoc(arg->getLoc());
5812
                    // This makes the deepest level, the member-wise copy
5813
0
                    TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign,
5814
0
                                                               internalSymbolNode, arg)->getAsAggregate();
5815
5816
                    // Now, pair that with the resulting aggregate.
5817
0
                    assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
5818
0
                    assignAgg->setOperator(EOpComma);
5819
0
                    assignAgg->setType(internalAggregate->getType());
5820
0
                    setArg(param, assignAgg);
5821
0
                }
5822
0
            }
5823
12.9k
        }
5824
15.9k
    }
5825
11.6k
}
5826
5827
//
5828
// Add any needed implicit expansion of calling arguments from what the shader listed to what's
5829
// internally needed for the AST (given the constraints downstream).
5830
//
5831
void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments)
5832
368
{
5833
368
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5834
368
    int functionParamNumberOffset = 0;
5835
5836
    // Replace a single argument with a single argument.
5837
368
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5838
0
        if (function.getParamCount() + functionParamNumberOffset == 1)
5839
0
            arguments = arg;
5840
0
        else {
5841
0
            if (aggregate == nullptr)
5842
0
                arguments = arg;
5843
0
            else
5844
0
                aggregate->getSequence()[paramNum] = arg;
5845
0
        }
5846
0
    };
5847
5848
    // Replace a single argument with a list of arguments
5849
368
    const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) {
5850
0
        if (args.size() == 1)
5851
0
            setArg(paramNum, args.front());
5852
0
        else if (args.size() > 1) {
5853
0
            if (function.getParamCount() + functionParamNumberOffset == 1) {
5854
0
                arguments = intermediate.makeAggregate(args.front());
5855
0
                std::for_each(args.begin() + 1, args.end(),
5856
0
                    [&](TIntermTyped* arg) {
5857
0
                        arguments = intermediate.growAggregate(arguments, arg);
5858
0
                    });
5859
0
            } else {
5860
0
                auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum);
5861
0
                aggregate->getSequence().insert(it, args.begin(), args.end());
5862
0
            }
5863
0
            functionParamNumberOffset += (int)(args.size() - 1);
5864
0
        }
5865
0
    };
5866
5867
    // Process each argument's conversion
5868
960
    for (int param = 0; param < function.getParamCount(); ++param) {
5869
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5870
        // is the single argument itself or its children are the arguments.  Only one argument
5871
        // means take 'arguments' itself as the one argument.
5872
592
        TIntermTyped* arg = function.getParamCount() == 1
5873
592
                                   ? arguments->getAsTyped()
5874
592
                                   : (aggregate ?
5875
344
                                        aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() :
5876
344
                                        arguments->getAsTyped());
5877
5878
592
        if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5879
            // Need to pass the structure members instead of the structure.
5880
0
            TVector<TIntermTyped*> memberArgs;
5881
0
            for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb)
5882
0
                memberArgs.push_back(flattenAccess(arg, memb));
5883
0
            setArgList(param + functionParamNumberOffset, memberArgs);
5884
0
        }
5885
592
    }
5886
5887
    // TODO: if we need both hidden counter args (below) and struct expansion (above)
5888
    // the two algorithms need to be merged: Each assumes the list starts out 1:1 between
5889
    // parameters and arguments.
5890
5891
    // If any argument is a pass-by-reference struct buffer with an associated counter
5892
    // buffer, we have to add another hidden parameter for that counter.
5893
368
    if (aggregate)
5894
144
        addStructBuffArguments(loc, aggregate);
5895
368
}
5896
5897
//
5898
// Add any needed implicit output conversions for function-call arguments.  This
5899
// can require a new tree topology, complicated further by whether the function
5900
// has a return value.
5901
//
5902
// Returns a node of a subtree that evaluates to the return value of the function.
5903
//
5904
TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
5905
3.72k
{
5906
3.72k
    assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
5907
5908
3.72k
    const TSourceLoc& loc = intermNode.getLoc();
5909
5910
3.72k
    TIntermSequence argSequence; // temp sequence for unary node args
5911
5912
3.72k
    if (intermNode.getAsUnaryNode())
5913
0
        argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
5914
5915
3.72k
    TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
5916
5917
6.36k
    const auto needsConversion = [&](int argNum) {
5918
6.36k
        return function[argNum].type->getQualifier().isParamOutput() &&
5919
40
               (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
5920
40
                shouldConvertLValue(arguments[argNum]) ||
5921
16
                wasFlattened(arguments[argNum]->getAsTyped()));
5922
6.36k
    };
5923
5924
    // Will there be any output conversions?
5925
3.72k
    bool outputConversions = false;
5926
10.0k
    for (int i = 0; i < function.getParamCount(); ++i) {
5927
6.34k
        if (needsConversion(i)) {
5928
12
            outputConversions = true;
5929
12
            break;
5930
12
        }
5931
6.34k
    }
5932
5933
3.72k
    if (! outputConversions)
5934
3.71k
        return &intermNode;
5935
5936
    // Setup for the new tree, if needed:
5937
    //
5938
    // Output conversions need a different tree topology.
5939
    // Out-qualified arguments need a temporary of the correct type, with the call
5940
    // followed by an assignment of the temporary to the original argument:
5941
    //     void: function(arg, ...)  ->        (          function(tempArg, ...), arg = tempArg, ...)
5942
    //     ret = function(arg, ...)  ->  ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
5943
    // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
5944
12
    TIntermTyped* conversionTree = nullptr;
5945
12
    TVariable* tempRet = nullptr;
5946
12
    if (intermNode.getBasicType() != EbtVoid) {
5947
        // do the "tempRet = function(...), " bit from above
5948
0
        tempRet = makeInternalVariable("tempReturn", intermNode.getType());
5949
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
5950
0
        conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
5951
0
    } else
5952
12
        conversionTree = &intermNode;
5953
5954
12
    conversionTree = intermediate.makeAggregate(conversionTree);
5955
5956
    // Process each argument's conversion
5957
24
    for (int i = 0; i < function.getParamCount(); ++i) {
5958
12
        if (needsConversion(i)) {
5959
            // Out-qualified arguments needing conversion need to use the topology setup above.
5960
            // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
5961
5962
            // Make a temporary for what the function expects the argument to look like.
5963
12
            TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
5964
12
            tempArg->getWritableType().getQualifier().makeTemporary();
5965
12
            TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
5966
5967
            // This makes the deepest level, the member-wise copy
5968
12
            TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(),
5969
12
                                                    tempArgNode);
5970
12
            tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
5971
12
            conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
5972
5973
            // replace the argument with another node for the same tempArg variable
5974
12
            arguments[i] = intermediate.addSymbol(*tempArg, loc);
5975
12
        }
5976
12
    }
5977
5978
    // Finalize the tree topology (see bigger comment above).
5979
12
    if (tempRet) {
5980
        // do the "..., tempRet" bit from above
5981
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
5982
0
        conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
5983
0
    }
5984
5985
12
    conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
5986
5987
12
    return conversionTree;
5988
3.72k
}
5989
5990
//
5991
// Add any needed "hidden" counter buffer arguments for function calls.
5992
//
5993
// Modifies the 'aggregate' argument if needed.  Otherwise, is no-op.
5994
//
5995
void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate)
5996
144
{
5997
    // See if there are any SB types with counters.
5998
144
    const bool hasStructBuffArg =
5999
144
        std::any_of(aggregate->getSequence().begin(),
6000
144
                    aggregate->getSequence().end(),
6001
388
                    [this](const TIntermNode* node) {
6002
388
                        return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType());
6003
388
                    });
6004
6005
    // Nothing to do, if we didn't find one.
6006
144
    if (! hasStructBuffArg)
6007
142
        return;
6008
6009
2
    TIntermSequence argsWithCounterBuffers;
6010
6011
8
    for (int param = 0; param < int(aggregate->getSequence().size()); ++param) {
6012
6
        argsWithCounterBuffers.push_back(aggregate->getSequence()[param]);
6013
6014
6
        if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) {
6015
2
            const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode();
6016
2
            if (blockSym != nullptr) {
6017
2
                TType counterType;
6018
2
                counterBufferType(loc, counterType);
6019
6020
2
                const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName()));
6021
6022
2
                TVariable* variable = makeInternalVariable(counterBlockName, counterType);
6023
6024
                // Mark this buffer's counter block as being in use
6025
2
                structBufferCounter[counterBlockName] = true;
6026
6027
2
                TIntermSymbol* sym = intermediate.addSymbol(*variable, loc);
6028
2
                argsWithCounterBuffers.push_back(sym);
6029
2
            }
6030
2
        }
6031
6
    }
6032
6033
    // Swap with the temp list we've built up.
6034
2
    aggregate->getSequence().swap(argsWithCounterBuffers);
6035
2
}
6036
6037
6038
//
6039
// Do additional checking of built-in function calls that is not caught
6040
// by normal semantic checks on argument type, extension tagging, etc.
6041
//
6042
// Assumes there has been a semantically correct match to a built-in function prototype.
6043
//
6044
void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
6045
11.0k
{
6046
    // Set up convenience accessors to the argument(s).  There is almost always
6047
    // multiple arguments for the cases below, but when there might be one,
6048
    // check the unaryArg first.
6049
11.0k
    const TIntermSequence* argp = nullptr;   // confusing to use [] syntax on a pointer, so this is to help get a reference
6050
11.0k
    const TIntermTyped* unaryArg = nullptr;
6051
11.0k
    const TIntermTyped* arg0 = nullptr;
6052
11.0k
    if (callNode.getAsAggregate()) {
6053
3.61k
        argp = &callNode.getAsAggregate()->getSequence();
6054
3.61k
        if (argp->size() > 0)
6055
3.61k
            arg0 = (*argp)[0]->getAsTyped();
6056
7.44k
    } else {
6057
7.44k
        assert(callNode.getAsUnaryNode());
6058
7.44k
        unaryArg = callNode.getAsUnaryNode()->getOperand();
6059
7.44k
        arg0 = unaryArg;
6060
7.44k
    }
6061
11.0k
    const TIntermSequence& aggArgs = argp ? *argp : TIntermSequence();  // only valid when unaryArg is nullptr
6062
6063
11.0k
    switch (callNode.getOp()) {
6064
0
    case EOpTextureGather:
6065
0
    case EOpTextureGatherOffset:
6066
0
    case EOpTextureGatherOffsets:
6067
0
    {
6068
        // Figure out which variants are allowed by what extensions,
6069
        // and what arguments must be constant for which situations.
6070
6071
0
        TString featureString = fnCandidate.getName() + "(...)";
6072
0
        const char* feature = featureString.c_str();
6073
0
        int compArg = -1;  // track which argument, if any, is the constant component argument
6074
0
        switch (callNode.getOp()) {
6075
0
        case EOpTextureGather:
6076
            // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
6077
            // otherwise, need GL_ARB_texture_gather.
6078
0
            if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect ||
6079
0
                fnCandidate[0].type->getSampler().shadow) {
6080
0
                if (! fnCandidate[0].type->getSampler().shadow)
6081
0
                    compArg = 2;
6082
0
            }
6083
0
            break;
6084
0
        case EOpTextureGatherOffset:
6085
            // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
6086
0
            if (! fnCandidate[0].type->getSampler().shadow)
6087
0
                compArg = 3;
6088
0
            break;
6089
0
        case EOpTextureGatherOffsets:
6090
0
            if (! fnCandidate[0].type->getSampler().shadow)
6091
0
                compArg = 3;
6092
0
            break;
6093
0
        default:
6094
0
            break;
6095
0
        }
6096
6097
0
        if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
6098
0
            if (aggArgs[compArg]->getAsConstantUnion()) {
6099
0
                int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
6100
0
                if (value < 0 || value > 3)
6101
0
                    error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
6102
0
            } else
6103
0
                error(loc, "must be a compile-time constant:", feature, "component argument");
6104
0
        }
6105
6106
0
        break;
6107
0
    }
6108
6109
0
    case EOpTextureOffset:
6110
0
    case EOpTextureFetchOffset:
6111
0
    case EOpTextureProjOffset:
6112
0
    case EOpTextureLodOffset:
6113
0
    case EOpTextureProjLodOffset:
6114
0
    case EOpTextureGradOffset:
6115
0
    case EOpTextureProjGradOffset:
6116
0
    {
6117
        // Handle texture-offset limits checking
6118
        // Pick which argument has to hold constant offsets
6119
0
        int arg = -1;
6120
0
        switch (callNode.getOp()) {
6121
0
        case EOpTextureOffset:          arg = 2;  break;
6122
0
        case EOpTextureFetchOffset:     arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
6123
0
        case EOpTextureProjOffset:      arg = 2;  break;
6124
0
        case EOpTextureLodOffset:       arg = 3;  break;
6125
0
        case EOpTextureProjLodOffset:   arg = 3;  break;
6126
0
        case EOpTextureGradOffset:      arg = 4;  break;
6127
0
        case EOpTextureProjGradOffset:  arg = 4;  break;
6128
0
        default:
6129
0
            assert(0);
6130
0
            break;
6131
0
        }
6132
6133
0
        if (arg > 0) {
6134
0
            if (aggArgs[arg]->getAsConstantUnion() == nullptr)
6135
0
                error(loc, "argument must be compile-time constant", "texel offset", "");
6136
0
            else {
6137
0
                const TType& type = aggArgs[arg]->getAsTyped()->getType();
6138
0
                for (int c = 0; c < type.getVectorSize(); ++c) {
6139
0
                    int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
6140
0
                    if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
6141
0
                        error(loc, "value is out of range:", "texel offset",
6142
0
                              "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
6143
0
                }
6144
0
            }
6145
0
        }
6146
6147
0
        break;
6148
0
    }
6149
6150
0
    case EOpTextureQuerySamples:
6151
0
    case EOpImageQuerySamples:
6152
0
        break;
6153
6154
0
    case EOpImageAtomicAdd:
6155
0
    case EOpImageAtomicMin:
6156
0
    case EOpImageAtomicMax:
6157
0
    case EOpImageAtomicAnd:
6158
0
    case EOpImageAtomicOr:
6159
0
    case EOpImageAtomicXor:
6160
0
    case EOpImageAtomicExchange:
6161
0
    case EOpImageAtomicCompSwap:
6162
0
        break;
6163
6164
0
    case EOpInterpolateAtCentroid:
6165
0
    case EOpInterpolateAtSample:
6166
0
    case EOpInterpolateAtOffset:
6167
        // TODO(greg-lunarg): Re-enable this check. It currently gives false errors for builtins
6168
        // defined and passed as members of a struct. In this case the storage class is showing to be
6169
        // Function. See glslang #2584
6170
6171
        // Make sure the first argument is an interpolant, or an array element of an interpolant
6172
        // if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
6173
            // It might still be an array element.
6174
            //
6175
            // We could check more, but the semantics of the first argument are already met; the
6176
            // only way to turn an array into a float/vec* is array dereference and swizzle.
6177
            //
6178
            // ES and desktop 4.3 and earlier:  swizzles may not be used
6179
            // desktop 4.4 and later: swizzles may be used
6180
            // const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
6181
            // if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
6182
            //     error(loc, "first argument must be an interpolant, or interpolant-array element",
6183
            //           fnCandidate.getName().c_str(), "");
6184
        // }
6185
0
        break;
6186
6187
11.0k
    default:
6188
11.0k
        break;
6189
11.0k
    }
6190
11.0k
}
6191
6192
//
6193
// Handle seeing something in a grammar production that can be done by calling
6194
// a constructor.
6195
//
6196
// The constructor still must be "handled" by handleFunctionCall(), which will
6197
// then call handleConstructor().
6198
//
6199
TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
6200
7.51k
{
6201
7.51k
    TOperator op = intermediate.mapTypeToConstructorOp(type);
6202
6203
7.51k
    if (op == EOpNull) {
6204
36
        error(loc, "cannot construct this type", type.getBasicString(), "");
6205
36
        return nullptr;
6206
36
    }
6207
6208
7.48k
    TString empty("");
6209
6210
7.48k
    return new TFunction(&empty, type, op);
6211
7.51k
}
6212
6213
//
6214
// Handle seeing a "COLON semantic" at the end of a type declaration,
6215
// by updating the type according to the semantic.
6216
//
6217
void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn,
6218
                                      const TString& upperCase)
6219
1.25k
{
6220
    // Parse and return semantic number.  If limit is 0, it will be ignored.  Otherwise, if the parsed
6221
    // semantic number is >= limit, errorMsg is issued and 0 is returned.
6222
    // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet
6223
    // accept those in lambda functions.
6224
1.25k
    const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int {
6225
56
        size_t pos = semantic.find_last_not_of("0123456789");
6226
56
        if (pos == std::string::npos)
6227
0
            return 0u;
6228
6229
56
        unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1);
6230
6231
56
        if (limit != 0 && semanticNum >= limit) {
6232
4
            error(loc, errorMsg, semantic.c_str(), "");
6233
4
            return 0u;
6234
4
        }
6235
6236
52
        return semanticNum;
6237
56
    };
6238
6239
1.25k
    if (builtIn == EbvNone && hlslDX9Compatible()) {
6240
0
        if (language == EShLangVertex) {
6241
0
            if (qualifier.isParamOutput()) {
6242
0
                if (upperCase == "POSITION") {
6243
0
                    builtIn = EbvPosition;
6244
0
                }
6245
0
                if (upperCase == "PSIZE") {
6246
0
                    builtIn = EbvPointSize;
6247
0
                }
6248
0
            }
6249
0
        } else if (language == EShLangFragment) {
6250
0
            if (qualifier.isParamInput() && upperCase == "VPOS") {
6251
0
                builtIn = EbvFragCoord;
6252
0
            }
6253
0
            if (qualifier.isParamOutput()) {
6254
0
                if (upperCase.compare(0, 5, "COLOR") == 0) {
6255
0
                    qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6256
0
                    nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6257
0
                }
6258
0
                if (upperCase == "DEPTH") {
6259
0
                    builtIn = EbvFragDepth;
6260
0
                }
6261
0
            }
6262
0
        }
6263
0
    }
6264
6265
1.25k
    switch(builtIn) {
6266
1.12k
    case EbvNone:
6267
        // Get location numbers from fragment outputs, instead of
6268
        // auto-assigning them.
6269
1.12k
        if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) {
6270
20
            qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6271
20
            nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6272
1.10k
        } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) {
6273
26
            builtIn = EbvClipDistance;
6274
26
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic");
6275
1.07k
        } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) {
6276
10
            builtIn = EbvCullDistance;
6277
10
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic");
6278
10
        }
6279
1.12k
        break;
6280
48
    case EbvPosition:
6281
        // adjust for stage in/out
6282
48
        if (language == EShLangFragment)
6283
2
            builtIn = EbvFragCoord;
6284
48
        break;
6285
0
    case EbvFragStencilRef:
6286
0
        error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
6287
0
        break;
6288
0
    case EbvTessLevelInner:
6289
0
    case EbvTessLevelOuter:
6290
0
        qualifier.patch = true;
6291
0
        break;
6292
82
    default:
6293
82
        break;
6294
1.25k
    }
6295
6296
1.25k
    if (qualifier.builtIn == EbvNone)
6297
1.23k
        qualifier.builtIn = builtIn;
6298
1.25k
    qualifier.semanticName = intermediate.addSemanticName(upperCase);
6299
1.25k
}
6300
6301
//
6302
// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
6303
//
6304
// 'location' has the "c[Subcomponent]" part.
6305
// 'component' points to the "component" part, or nullptr if not present.
6306
//
6307
void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
6308
                                        const glslang::TString* component)
6309
106
{
6310
106
    if (location.size() == 0 || location[0] != 'c') {
6311
2
        error(loc, "expected 'c'", "packoffset", "");
6312
2
        return;
6313
2
    }
6314
104
    if (location.size() == 1)
6315
12
        return;
6316
92
    if (! isdigit(location[1])) {
6317
0
        error(loc, "expected number after 'c'", "packoffset", "");
6318
0
        return;
6319
0
    }
6320
6321
92
    qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
6322
92
    if (component != nullptr) {
6323
50
        int componentOffset = 0;
6324
50
        switch ((*component)[0]) {
6325
10
        case 'x': componentOffset =  0; break;
6326
24
        case 'y': componentOffset =  4; break;
6327
6
        case 'z': componentOffset =  8; break;
6328
10
        case 'w': componentOffset = 12; break;
6329
0
        default:
6330
0
            componentOffset = -1;
6331
0
            break;
6332
50
        }
6333
50
        if (componentOffset < 0 || component->size() > 1) {
6334
0
            error(loc, "expected {x, y, z, w} for component", "packoffset", "");
6335
0
            return;
6336
0
        }
6337
50
        qualifier.layoutOffset += componentOffset;
6338
50
    }
6339
92
}
6340
6341
//
6342
// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
6343
//
6344
// 'profile' points to the shader_profile part, or nullptr if not present.
6345
// 'desc' is the type# part.
6346
//
6347
void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
6348
                                      const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
6349
1.04k
{
6350
1.04k
    if (profile != nullptr)
6351
2
        warn(loc, "ignoring shader_profile", "register", "");
6352
6353
1.04k
    if (desc.size() < 1) {
6354
0
        error(loc, "expected register type", "register", "");
6355
0
        return;
6356
0
    }
6357
6358
1.04k
    int regNumber = 0;
6359
1.04k
    if (desc.size() > 1) {
6360
1.03k
        if (isdigit(desc[1]))
6361
1.03k
            regNumber = atoi(desc.substr(1, desc.size()).c_str());
6362
2
        else {
6363
2
            error(loc, "expected register number after register type", "register", "");
6364
2
            return;
6365
2
        }
6366
1.03k
    }
6367
6368
    // more information about register types see
6369
    // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register
6370
1.03k
    const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding();
6371
1.03k
    switch (std::tolower(desc[0])) {
6372
2
    case 'c':
6373
        // c register is the register slot in the global const buffer
6374
        // each slot is a vector of 4 32 bit components
6375
2
        qualifier.layoutOffset = regNumber * 4 * 4;
6376
2
        break;
6377
        // const buffer register slot
6378
38
    case 'b':
6379
        // textrues and structured buffers
6380
552
    case 't':
6381
        // samplers
6382
910
    case 's':
6383
        // uav resources
6384
1.03k
    case 'u':
6385
        // if nothing else has set the binding, do so now
6386
        // (other mechanisms override this one)
6387
1.03k
        if (!qualifier.hasBinding())
6388
1.02k
            qualifier.layoutBinding = regNumber + subComponent;
6389
6390
        // This handles per-register layout sets numbers.  For the global mode which sets
6391
        // every symbol to the same value, see setLinkageLayoutSets().
6392
1.03k
        if ((resourceInfo.size() % 3) == 0) {
6393
            // Apply per-symbol resource set and binding.
6394
1.03k
            for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) {
6395
0
                if (strcmp(desc.c_str(), it[0].c_str()) == 0) {
6396
0
                    qualifier.layoutSet = atoi(it[1].c_str());
6397
0
                    qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent;
6398
0
                    break;
6399
0
                }
6400
0
            }
6401
1.03k
        }
6402
1.03k
        break;
6403
6
    default:
6404
6
        warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
6405
6
        break;
6406
1.03k
    }
6407
6408
    // space
6409
1.03k
    unsigned int setNumber;
6410
1.03k
    const auto crackSpace = [&]() -> bool {
6411
224
        const int spaceLen = 5;
6412
224
        if (spaceDesc->size() < spaceLen + 1)
6413
2
            return false;
6414
222
        if (spaceDesc->compare(0, spaceLen, "space") != 0)
6415
6
            return false;
6416
216
        if (! isdigit((*spaceDesc)[spaceLen]))
6417
4
            return false;
6418
212
        setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
6419
212
        return true;
6420
216
    };
6421
6422
    // if nothing else has set the set, do so now
6423
    // (other mechanisms override this one)
6424
1.03k
    if (spaceDesc && !qualifier.hasSet()) {
6425
224
        if (! crackSpace()) {
6426
12
            error(loc, "expected spaceN", "register", "");
6427
12
            return;
6428
12
        }
6429
212
        qualifier.layoutSet = setNumber;
6430
212
    }
6431
1.03k
}
6432
6433
// Convert to a scalar boolean, or if not allowed by HLSL semantics,
6434
// report an error and return nullptr.
6435
TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition,
6436
                                                             bool mustBeScalar)
6437
280
{
6438
280
    if (mustBeScalar && !condition->getType().isScalarOrVec1()) {
6439
0
        error(loc, "requires a scalar", "conditional expression", "");
6440
0
        return nullptr;
6441
0
    }
6442
6443
280
    return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()),
6444
280
                                      condition);
6445
280
}
6446
6447
//
6448
// Same error message for all places assignments don't work.
6449
//
6450
void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
6451
1.57k
{
6452
1.57k
    error(loc, "", op, "cannot convert from '%s' to '%s'",
6453
1.57k
        right.c_str(), left.c_str());
6454
1.57k
}
6455
6456
//
6457
// Same error message for all places unary operations don't work.
6458
//
6459
void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
6460
0
{
6461
0
    error(loc, " wrong operand type", op,
6462
0
        "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
6463
0
        op, operand.c_str());
6464
0
}
6465
6466
//
6467
// Same error message for all binary operations don't work.
6468
//
6469
void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
6470
20
{
6471
20
    error(loc, " wrong operand types:", op,
6472
20
        "no operation '%s' exists that takes a left-hand operand of type '%s' and "
6473
20
        "a right operand of type '%s' (or there is no acceptable conversion)",
6474
20
        op, left.c_str(), right.c_str());
6475
20
}
6476
6477
//
6478
// A basic type of EbtVoid is a key that the name string was seen in the source, but
6479
// it was not found as a variable in the symbol table.  If so, give the error
6480
// message and insert a dummy variable in the symbol table to prevent future errors.
6481
//
6482
void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
6483
2.65k
{
6484
2.65k
    TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
6485
2.65k
    if (! symbol)
6486
352
        return;
6487
6488
2.29k
    if (symbol->getType().getBasicType() == EbtVoid) {
6489
158
        error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
6490
6491
        // Add to symbol table to prevent future error messages on the same name
6492
158
        if (symbol->getName().size() > 0) {
6493
158
            TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
6494
158
            symbolTable.insert(*fakeVariable);
6495
6496
            // substitute a symbol node for this new variable
6497
158
            nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
6498
158
        }
6499
158
    }
6500
2.29k
}
6501
6502
//
6503
// Both test, and if necessary spit out an error, to see if the node is really
6504
// a constant.
6505
//
6506
void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
6507
0
{
6508
0
    if (node->getQualifier().storage != EvqConst)
6509
0
        error(node->getLoc(), "constant expression required", token, "");
6510
0
}
6511
6512
//
6513
// Both test, and if necessary spit out an error, to see if the node is really
6514
// an integer.
6515
//
6516
void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
6517
590
{
6518
590
    if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
6519
584
        return;
6520
6521
6
    error(node->getLoc(), "scalar integer expression required", token, "");
6522
6
}
6523
6524
//
6525
// Both test, and if necessary spit out an error, to see if we are currently
6526
// globally scoped.
6527
//
6528
void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
6529
0
{
6530
0
    if (! symbolTable.atGlobalLevel())
6531
0
        error(loc, "not allowed in nested scope", token, "");
6532
0
}
6533
6534
bool HlslParseContext::builtInName(const TString& /*identifier*/)
6535
26
{
6536
26
    return false;
6537
26
}
6538
6539
//
6540
// Make sure there is enough data and not too many arguments provided to the
6541
// constructor to build something of the type of the constructor.  Also returns
6542
// the type of the constructor.
6543
//
6544
// Returns true if there was an error in construction.
6545
//
6546
bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
6547
                                        TOperator op, TType& type)
6548
7.24k
{
6549
7.24k
    type.shallowCopy(function.getType());
6550
6551
7.24k
    bool constructingMatrix = false;
6552
7.24k
    switch (op) {
6553
0
    case EOpConstructTextureSampler:
6554
0
        error(loc, "unhandled texture constructor", "constructor", "");
6555
0
        return true;
6556
318
    case EOpConstructMat2x2:
6557
624
    case EOpConstructMat2x3:
6558
962
    case EOpConstructMat2x4:
6559
1.29k
    case EOpConstructMat3x2:
6560
1.68k
    case EOpConstructMat3x3:
6561
2.13k
    case EOpConstructMat3x4:
6562
2.49k
    case EOpConstructMat4x2:
6563
2.90k
    case EOpConstructMat4x3:
6564
3.44k
    case EOpConstructMat4x4:
6565
3.44k
    case EOpConstructDMat2x2:
6566
3.44k
    case EOpConstructDMat2x3:
6567
3.44k
    case EOpConstructDMat2x4:
6568
3.44k
    case EOpConstructDMat3x2:
6569
3.44k
    case EOpConstructDMat3x3:
6570
3.44k
    case EOpConstructDMat3x4:
6571
3.44k
    case EOpConstructDMat4x2:
6572
3.44k
    case EOpConstructDMat4x3:
6573
3.44k
    case EOpConstructDMat4x4:
6574
3.44k
    case EOpConstructIMat2x2:
6575
3.44k
    case EOpConstructIMat2x3:
6576
3.44k
    case EOpConstructIMat2x4:
6577
3.48k
    case EOpConstructIMat3x2:
6578
3.48k
    case EOpConstructIMat3x3:
6579
3.48k
    case EOpConstructIMat3x4:
6580
3.54k
    case EOpConstructIMat4x2:
6581
3.54k
    case EOpConstructIMat4x3:
6582
3.60k
    case EOpConstructIMat4x4:
6583
3.60k
    case EOpConstructUMat2x2:
6584
3.60k
    case EOpConstructUMat2x3:
6585
3.60k
    case EOpConstructUMat2x4:
6586
3.63k
    case EOpConstructUMat3x2:
6587
3.63k
    case EOpConstructUMat3x3:
6588
3.63k
    case EOpConstructUMat3x4:
6589
3.65k
    case EOpConstructUMat4x2:
6590
3.65k
    case EOpConstructUMat4x3:
6591
3.70k
    case EOpConstructUMat4x4:
6592
3.70k
    case EOpConstructBMat2x2:
6593
3.70k
    case EOpConstructBMat2x3:
6594
3.70k
    case EOpConstructBMat2x4:
6595
3.71k
    case EOpConstructBMat3x2:
6596
3.71k
    case EOpConstructBMat3x3:
6597
3.71k
    case EOpConstructBMat3x4:
6598
3.72k
    case EOpConstructBMat4x2:
6599
3.72k
    case EOpConstructBMat4x3:
6600
3.74k
    case EOpConstructBMat4x4:
6601
3.74k
        constructingMatrix = true;
6602
3.74k
        break;
6603
3.50k
    default:
6604
3.50k
        break;
6605
7.24k
    }
6606
6607
    //
6608
    // Walk the arguments for first-pass checks and collection of information.
6609
    //
6610
6611
7.24k
    int size = 0;
6612
7.24k
    bool constType = true;
6613
7.24k
    bool full = false;
6614
7.24k
    bool overFull = false;
6615
7.24k
    bool matrixInMatrix = false;
6616
7.24k
    bool arrayArg = false;
6617
49.7k
    for (int arg = 0; arg < function.getParamCount(); ++arg) {
6618
42.4k
        if (function[arg].type->isArray()) {
6619
8
            if (function[arg].type->isUnsizedArray()) {
6620
                // Can't construct from an unsized array.
6621
0
                error(loc, "array argument must be sized", "constructor", "");
6622
0
                return true;
6623
0
            }
6624
8
            arrayArg = true;
6625
8
        }
6626
42.4k
        if (constructingMatrix && function[arg].type->isMatrix())
6627
0
            matrixInMatrix = true;
6628
6629
        // 'full' will go to true when enough args have been seen.  If we loop
6630
        // again, there is an extra argument.
6631
42.4k
        if (full) {
6632
            // For vectors and matrices, it's okay to have too many components
6633
            // available, but not okay to have unused arguments.
6634
116
            overFull = true;
6635
116
        }
6636
6637
42.4k
        size += function[arg].type->computeNumComponents();
6638
42.4k
        if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
6639
6.76k
            full = true;
6640
6641
42.4k
        if (function[arg].type->getQualifier().storage != EvqConst)
6642
310
            constType = false;
6643
42.4k
    }
6644
6645
7.24k
    if (constType)
6646
6.97k
        type.getQualifier().storage = EvqConst;
6647
6648
7.24k
    if (type.isArray()) {
6649
30
        if (function.getParamCount() == 0) {
6650
0
            error(loc, "array constructor must have at least one argument", "constructor", "");
6651
0
            return true;
6652
0
        }
6653
6654
30
        if (type.isUnsizedArray()) {
6655
            // auto adapt the constructor type to the number of arguments
6656
0
            type.changeOuterArraySize(function.getParamCount());
6657
30
        } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) {
6658
6
            error(loc, "array constructor needs one argument per array element", "constructor", "");
6659
6
            return true;
6660
6
        }
6661
6662
24
        if (type.isArrayOfArrays()) {
6663
            // Types have to match, but we're still making the type.
6664
            // Finish making the type, and the comparison is done later
6665
            // when checking for conversion.
6666
0
            TArraySizes& arraySizes = *type.getArraySizes();
6667
6668
            // At least the dimensionalities have to match.
6669
0
            if (! function[0].type->isArray() ||
6670
0
                arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) {
6671
0
                error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
6672
0
                return true;
6673
0
            }
6674
6675
0
            if (arraySizes.isInnerUnsized()) {
6676
                // "Arrays of arrays ..., and the size for any dimension is optional"
6677
                // That means we need to adopt (from the first argument) the other array sizes into the type.
6678
0
                for (int d = 1; d < arraySizes.getNumDims(); ++d) {
6679
0
                    if (arraySizes.getDimSize(d) == UnsizedArraySize) {
6680
0
                        arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1));
6681
0
                    }
6682
0
                }
6683
0
            }
6684
0
        }
6685
24
    }
6686
6687
    // Some array -> array type casts are okay
6688
7.23k
    if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
6689
0
        !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
6690
0
        type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
6691
0
        return false;
6692
6693
7.23k
    if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
6694
0
        error(loc, "constructing non-array constituent from array argument", "constructor", "");
6695
0
        return true;
6696
0
    }
6697
6698
7.23k
    if (matrixInMatrix && ! type.isArray()) {
6699
0
        return false;
6700
0
    }
6701
6702
7.23k
    if (overFull) {
6703
44
        error(loc, "too many arguments", "constructor", "");
6704
44
        return true;
6705
44
    }
6706
6707
7.19k
    if (op == EOpConstructStruct && ! type.isArray()) {
6708
22
        if (isScalarConstructor(node))
6709
0
            return false;
6710
6711
        // Self-type construction: e.g, we can construct a struct from a single identically typed object.
6712
22
        if (function.getParamCount() == 1 && type == *function[0].type)
6713
20
            return false;
6714
6715
2
        if ((int)type.getStruct()->size() != function.getParamCount()) {
6716
2
            error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
6717
2
            return true;
6718
2
        }
6719
2
    }
6720
6721
7.17k
    if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
6722
7.01k
        (op == EOpConstructStruct && size < type.computeNumComponents())) {
6723
152
        error(loc, "not enough data provided for construction", "constructor", "");
6724
152
        return true;
6725
152
    }
6726
6727
7.01k
    return false;
6728
7.17k
}
6729
6730
// See if 'node', in the context of constructing aggregates, is a scalar argument
6731
// to a constructor.
6732
//
6733
bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
6734
22
{
6735
    // Obviously, it must be a scalar, but an aggregate node might not be fully
6736
    // completed yet: holding a sequence of initializers under an aggregate
6737
    // would not yet be typed, so don't check it's type.  This corresponds to
6738
    // the aggregate operator also not being set yet. (An aggregate operation
6739
    // that legitimately yields a scalar will have a getOp() of that operator,
6740
    // not EOpNull.)
6741
6742
22
    return node->getAsTyped() != nullptr &&
6743
22
           node->getAsTyped()->isScalar() &&
6744
0
           (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
6745
22
}
6746
6747
// Checks to see if a void variable has been declared and raise an error message for such a case
6748
//
6749
// returns true in case of an error
6750
//
6751
bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
6752
26.3k
{
6753
26.3k
    if (basicType == EbtVoid) {
6754
24
        error(loc, "illegal use of type 'void'", identifier.c_str(), "");
6755
24
        return true;
6756
24
    }
6757
6758
26.2k
    return false;
6759
26.3k
}
6760
6761
//
6762
// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
6763
//
6764
void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
6765
1.26k
{
6766
    // move from parameter/unknown qualifiers to pipeline in/out qualifiers
6767
1.26k
    switch (qualifier.storage) {
6768
384
    case EvqIn:
6769
384
        qualifier.storage = EvqVaryingIn;
6770
384
        break;
6771
0
    case EvqOut:
6772
0
        qualifier.storage = EvqVaryingOut;
6773
0
        break;
6774
882
    default:
6775
882
        break;
6776
1.26k
    }
6777
1.26k
}
6778
6779
//
6780
// Merge characteristics of the 'src' qualifier into the 'dst'.
6781
//
6782
void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
6783
1.91k
{
6784
    // Storage qualification
6785
1.91k
    if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
6786
1.35k
        dst.storage = src.storage;
6787
564
    else if ((dst.storage == EvqIn  && src.storage == EvqOut) ||
6788
564
             (dst.storage == EvqOut && src.storage == EvqIn))
6789
0
        dst.storage = EvqInOut;
6790
564
    else if ((dst.storage == EvqIn    && src.storage == EvqConst) ||
6791
564
             (dst.storage == EvqConst && src.storage == EvqIn))
6792
0
        dst.storage = EvqConstReadOnly;
6793
6794
    // Layout qualifiers
6795
1.91k
    mergeObjectLayoutQualifiers(dst, src, false);
6796
6797
    // individual qualifiers
6798
28.7k
#define MERGE_SINGLETON(field) dst.field |= src.field;
6799
1.91k
    MERGE_SINGLETON(invariant);
6800
1.91k
    MERGE_SINGLETON(noContraction);
6801
1.91k
    MERGE_SINGLETON(centroid);
6802
1.91k
    MERGE_SINGLETON(smooth);
6803
1.91k
    MERGE_SINGLETON(flat);
6804
1.91k
    MERGE_SINGLETON(nopersp);
6805
1.91k
    MERGE_SINGLETON(patch);
6806
1.91k
    MERGE_SINGLETON(sample);
6807
1.91k
    MERGE_SINGLETON(coherent);
6808
1.91k
    MERGE_SINGLETON(volatil);
6809
1.91k
    MERGE_SINGLETON(restrict);
6810
1.91k
    MERGE_SINGLETON(readonly);
6811
1.91k
    MERGE_SINGLETON(writeonly);
6812
1.91k
    MERGE_SINGLETON(specConstant);
6813
1.91k
    MERGE_SINGLETON(nonUniform);
6814
1.91k
}
6815
6816
// used to flatten the sampler type space into a single dimension
6817
// correlates with the declaration of defaultSamplerPrecision[]
6818
int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
6819
0
{
6820
0
    int arrayIndex = sampler.arrayed ? 1 : 0;
6821
0
    int shadowIndex = sampler.shadow ? 1 : 0;
6822
0
    int externalIndex = sampler.external ? 1 : 0;
6823
6824
0
    return EsdNumDims *
6825
0
           (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
6826
0
}
6827
6828
//
6829
// Do size checking for an array type's size.
6830
//
6831
void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
6832
484
{
6833
484
    bool isConst = false;
6834
484
    sizePair.size = 1;
6835
484
    sizePair.node = nullptr;
6836
6837
484
    TIntermConstantUnion* constant = expr->getAsConstantUnion();
6838
484
    if (constant) {
6839
        // handle true (non-specialization) constant
6840
472
        sizePair.size = constant->getConstArray()[0].getIConst();
6841
472
        isConst = true;
6842
472
    } else {
6843
        // see if it's a specialization constant instead
6844
12
        if (expr->getQualifier().isSpecConstant()) {
6845
0
            isConst = true;
6846
0
            sizePair.node = expr;
6847
0
            TIntermSymbol* symbol = expr->getAsSymbolNode();
6848
0
            if (symbol && symbol->getConstArray().size() > 0)
6849
0
                sizePair.size = symbol->getConstArray()[0].getIConst();
6850
0
        }
6851
12
    }
6852
6853
484
    if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
6854
12
        error(loc, "array size must be a constant integer expression", "", "");
6855
12
        return;
6856
12
    }
6857
6858
472
    if (sizePair.size <= 0) {
6859
2
        error(loc, "array size must be a positive integer", "", "");
6860
2
        return;
6861
2
    }
6862
472
}
6863
6864
//
6865
// Require array to be completely sized
6866
//
6867
void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
6868
0
{
6869
0
    if (arraySizes.hasUnsized())
6870
0
        error(loc, "array size required", "", "");
6871
0
}
6872
6873
void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
6874
0
{
6875
0
    const TTypeList& structure = *type.getStruct();
6876
0
    for (int m = 0; m < (int)structure.size(); ++m) {
6877
0
        const TType& member = *structure[m].type;
6878
0
        if (member.isArray())
6879
0
            arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
6880
0
    }
6881
0
}
6882
6883
//
6884
// Do all the semantic checking for declaring or redeclaring an array, with and
6885
// without a size, and make the right changes to the symbol table.
6886
//
6887
void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
6888
                                    TSymbol*& symbol, bool track)
6889
192
{
6890
192
    if (symbol == nullptr) {
6891
192
        bool currentScope;
6892
192
        symbol = symbolTable.find(identifier, nullptr, &currentScope);
6893
6894
192
        if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
6895
            // bad shader (errors already reported) trying to redeclare a built-in name as an array
6896
0
            return;
6897
0
        }
6898
192
        if (symbol == nullptr || ! currentScope) {
6899
            //
6900
            // Successfully process a new definition.
6901
            // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
6902
            //
6903
190
            symbol = new TVariable(&identifier, type);
6904
190
            symbolTable.insert(*symbol);
6905
190
            if (track && symbolTable.atGlobalLevel())
6906
100
                trackLinkage(*symbol);
6907
6908
190
            return;
6909
190
        }
6910
2
        if (symbol->getAsAnonMember()) {
6911
0
            error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
6912
0
            symbol = nullptr;
6913
0
            return;
6914
0
        }
6915
2
    }
6916
6917
    //
6918
    // Process a redeclaration.
6919
    //
6920
6921
2
    if (symbol == nullptr) {
6922
0
        error(loc, "array variable name expected", identifier.c_str(), "");
6923
0
        return;
6924
0
    }
6925
6926
    // redeclareBuiltinVariable() should have already done the copyUp()
6927
2
    TType& existingType = symbol->getWritableType();
6928
6929
2
    if (existingType.isSizedArray()) {
6930
        // be more lenient for input arrays to geometry shaders and tessellation control outputs,
6931
        // where the redeclaration is the same size
6932
2
        return;
6933
2
    }
6934
6935
0
    existingType.updateArraySizes(type);
6936
0
}
6937
6938
//
6939
// Enforce non-initializer type/qualifier rules.
6940
//
6941
void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type,
6942
                                    TIntermTyped*& initializer)
6943
26.2k
{
6944
    //
6945
    // Make the qualifier make sense, given that there is an initializer.
6946
    //
6947
26.2k
    if (initializer == nullptr) {
6948
9.80k
        if (type.getQualifier().storage == EvqConst ||
6949
9.67k
            type.getQualifier().storage == EvqConstReadOnly) {
6950
132
            initializer = intermediate.makeAggregate(loc);
6951
132
            warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
6952
132
        }
6953
9.80k
    }
6954
26.2k
}
6955
6956
//
6957
// See if the identifier is a built-in symbol that can be redeclared, and if so,
6958
// copy the symbol table's read-only built-in variable to the current
6959
// global level, where it can be modified based on the passed in type.
6960
//
6961
// Returns nullptr if no redeclaration took place; meaning a normal declaration still
6962
// needs to occur for it, not necessarily an error.
6963
//
6964
// Returns a redeclared and type-modified variable if a redeclared occurred.
6965
//
6966
TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
6967
                                                    const TQualifier& /*qualifier*/,
6968
                                                    const TShaderQualifiers& /*publicType*/)
6969
0
{
6970
0
    if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
6971
0
        return nullptr;
6972
6973
0
    return nullptr;
6974
0
}
6975
6976
//
6977
// Generate index to the array element in a structure buffer (SSBO)
6978
//
6979
TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
6980
9.78k
{
6981
    // Bail out if not a struct buffer
6982
9.78k
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
6983
9.42k
        return nullptr;
6984
6985
    // Runtime sized array is always the last element.
6986
358
    const TTypeList* bufferStruct = buffer->getType().getStruct();
6987
358
    TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
6988
6989
358
    TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
6990
358
    argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
6991
6992
358
    return argArray;
6993
9.78k
}
6994
6995
//
6996
// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
6997
// (template) type.   E.g, StructuredBuffer<MyType> -> MyType.  Else return nullptr.
6998
//
6999
TType* HlslParseContext::getStructBufferContentType(const TType& type) const
7000
25.2k
{
7001
25.2k
    if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer)
7002
23.7k
        return nullptr;
7003
7004
1.46k
    const int memberCount = (int)type.getStruct()->size();
7005
1.46k
    assert(memberCount > 0);
7006
7007
1.46k
    TType* contentType = (*type.getStruct())[memberCount-1].type;
7008
7009
1.46k
    return contentType->isUnsizedArray() ? contentType : nullptr;
7010
25.2k
}
7011
7012
//
7013
// If an existing struct buffer has a sharable type, then share it.
7014
//
7015
void HlslParseContext::shareStructBufferType(TType& type)
7016
708
{
7017
    // PackOffset must be equivalent to share types on a per-member basis.
7018
    // Note: cannot use auto type due to recursion.  Thus, this is a std::function.
7019
708
    const std::function<bool(TType& lhs, TType& rhs)>
7020
1.43k
    compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
7021
1.43k
        if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
7022
0
            return false;
7023
7024
1.43k
        if (lhs.isStruct() != rhs.isStruct())
7025
0
            return false;
7026
7027
1.43k
        if (lhs.getQualifier().builtIn != rhs.getQualifier().builtIn)
7028
536
            return false;
7029
7030
898
        if (lhs.isStruct() && rhs.isStruct()) {
7031
472
            if (lhs.getStruct()->size() != rhs.getStruct()->size())
7032
0
                return false;
7033
7034
944
            for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
7035
472
                if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
7036
0
                    return false;
7037
472
        }
7038
7039
898
        return true;
7040
898
    };
7041
7042
    // We need to compare certain qualifiers in addition to the type.
7043
1.22k
    const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
7044
1.22k
        if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
7045
262
            return false;
7046
7047
        // If both are structures, recursively look for packOffset equality
7048
        // as well as type equality.
7049
962
        return compareQualifiers(lhs, rhs) && lhs == rhs;
7050
1.22k
    };
7051
7052
    // This is an exhaustive O(N) search, but real world shaders have
7053
    // only a small number of these.
7054
1.51k
    for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
7055
        // If the deep structure matches, modulo qualifiers, use it
7056
1.22k
        if (typeEqual(*structBufferTypes[idx], type)) {
7057
416
            type.shallowCopy(*structBufferTypes[idx]);
7058
416
            return;
7059
416
        }
7060
1.22k
    }
7061
7062
    // Otherwise, remember it:
7063
292
    TType* typeCopy = new TType;
7064
292
    typeCopy->shallowCopy(type);
7065
292
    structBufferTypes.push_back(typeCopy);
7066
292
}
7067
7068
void HlslParseContext::paramFix(TType& type)
7069
13.7M
{
7070
13.7M
    switch (type.getQualifier().storage) {
7071
6
    case EvqConst:
7072
6
        type.getQualifier().storage = EvqConstReadOnly;
7073
6
        break;
7074
2
    case EvqGlobal:
7075
12.4M
    case EvqTemporary:
7076
12.4M
        type.getQualifier().storage = EvqIn;
7077
12.4M
        break;
7078
26
    case EvqBuffer:
7079
26
        {
7080
            // SSBO parameter.  These do not go through the declareBlock path since they are fn parameters.
7081
26
            correctUniform(type.getQualifier());
7082
26
            TQualifier bufferQualifier = globalBufferDefaults;
7083
26
            mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
7084
26
            bufferQualifier.storage = type.getQualifier().storage;
7085
26
            bufferQualifier.readonly = type.getQualifier().readonly;
7086
26
            bufferQualifier.coherent = type.getQualifier().coherent;
7087
26
            bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn;
7088
26
            type.getQualifier() = bufferQualifier;
7089
26
            break;
7090
2
        }
7091
1.33M
    default:
7092
1.33M
        break;
7093
13.7M
    }
7094
13.7M
}
7095
7096
void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
7097
14.8k
{
7098
14.8k
    if (type.containsSpecializationSize())
7099
0
        error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
7100
14.8k
}
7101
7102
//
7103
// Layout qualifier stuff.
7104
//
7105
7106
// Put the id's layout qualification into the public type, for qualifiers not having a number set.
7107
// This is before we know any type information for error checking.
7108
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
7109
38
{
7110
38
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7111
7112
38
    if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
7113
0
        qualifier.layoutMatrix = ElmRowMajor;
7114
0
        return;
7115
0
    }
7116
38
    if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
7117
0
        qualifier.layoutMatrix = ElmColumnMajor;
7118
0
        return;
7119
0
    }
7120
38
    if (id == "push_constant") {
7121
22
        requireVulkan(loc, "push_constant");
7122
22
        qualifier.layoutPushConstant = true;
7123
22
        return;
7124
22
    }
7125
16
    if (language == EShLangGeometry || language == EShLangTessEvaluation) {
7126
6
        if (id == TQualifier::getGeometryString(ElgTriangles)) {
7127
            // publicType.shaderQualifiers.geometry = ElgTriangles;
7128
0
            warn(loc, "ignored", id.c_str(), "");
7129
0
            return;
7130
0
        }
7131
6
        if (language == EShLangGeometry) {
7132
4
            if (id == TQualifier::getGeometryString(ElgPoints)) {
7133
                // publicType.shaderQualifiers.geometry = ElgPoints;
7134
0
                warn(loc, "ignored", id.c_str(), "");
7135
0
                return;
7136
0
            }
7137
4
            if (id == TQualifier::getGeometryString(ElgLineStrip)) {
7138
                // publicType.shaderQualifiers.geometry = ElgLineStrip;
7139
0
                warn(loc, "ignored", id.c_str(), "");
7140
0
                return;
7141
0
            }
7142
4
            if (id == TQualifier::getGeometryString(ElgLines)) {
7143
                // publicType.shaderQualifiers.geometry = ElgLines;
7144
0
                warn(loc, "ignored", id.c_str(), "");
7145
0
                return;
7146
0
            }
7147
4
            if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
7148
                // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
7149
0
                warn(loc, "ignored", id.c_str(), "");
7150
0
                return;
7151
0
            }
7152
4
            if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
7153
                // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
7154
0
                warn(loc, "ignored", id.c_str(), "");
7155
0
                return;
7156
0
            }
7157
4
            if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
7158
                // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
7159
0
                warn(loc, "ignored", id.c_str(), "");
7160
0
                return;
7161
0
            }
7162
4
        } else {
7163
2
            assert(language == EShLangTessEvaluation);
7164
7165
            // input primitive
7166
2
            if (id == TQualifier::getGeometryString(ElgTriangles)) {
7167
                // publicType.shaderQualifiers.geometry = ElgTriangles;
7168
0
                warn(loc, "ignored", id.c_str(), "");
7169
0
                return;
7170
0
            }
7171
2
            if (id == TQualifier::getGeometryString(ElgQuads)) {
7172
                // publicType.shaderQualifiers.geometry = ElgQuads;
7173
0
                warn(loc, "ignored", id.c_str(), "");
7174
0
                return;
7175
0
            }
7176
2
            if (id == TQualifier::getGeometryString(ElgIsolines)) {
7177
                // publicType.shaderQualifiers.geometry = ElgIsolines;
7178
0
                warn(loc, "ignored", id.c_str(), "");
7179
0
                return;
7180
0
            }
7181
7182
            // vertex spacing
7183
2
            if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
7184
                // publicType.shaderQualifiers.spacing = EvsEqual;
7185
0
                warn(loc, "ignored", id.c_str(), "");
7186
0
                return;
7187
0
            }
7188
2
            if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
7189
                // publicType.shaderQualifiers.spacing = EvsFractionalEven;
7190
0
                warn(loc, "ignored", id.c_str(), "");
7191
0
                return;
7192
0
            }
7193
2
            if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
7194
                // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
7195
0
                warn(loc, "ignored", id.c_str(), "");
7196
0
                return;
7197
0
            }
7198
7199
            // triangle order
7200
2
            if (id == TQualifier::getVertexOrderString(EvoCw)) {
7201
                // publicType.shaderQualifiers.order = EvoCw;
7202
0
                warn(loc, "ignored", id.c_str(), "");
7203
0
                return;
7204
0
            }
7205
2
            if (id == TQualifier::getVertexOrderString(EvoCcw)) {
7206
                // publicType.shaderQualifiers.order = EvoCcw;
7207
0
                warn(loc, "ignored", id.c_str(), "");
7208
0
                return;
7209
0
            }
7210
7211
            // point mode
7212
2
            if (id == "point_mode") {
7213
                // publicType.shaderQualifiers.pointMode = true;
7214
0
                warn(loc, "ignored", id.c_str(), "");
7215
0
                return;
7216
0
            }
7217
2
        }
7218
6
    }
7219
16
    if (language == EShLangFragment) {
7220
0
        if (id == "origin_upper_left") {
7221
            // publicType.shaderQualifiers.originUpperLeft = true;
7222
0
            warn(loc, "ignored", id.c_str(), "");
7223
0
            return;
7224
0
        }
7225
0
        if (id == "pixel_center_integer") {
7226
            // publicType.shaderQualifiers.pixelCenterInteger = true;
7227
0
            warn(loc, "ignored", id.c_str(), "");
7228
0
            return;
7229
0
        }
7230
0
        if (id == "early_fragment_tests") {
7231
            // publicType.shaderQualifiers.earlyFragmentTests = true;
7232
0
            warn(loc, "ignored", id.c_str(), "");
7233
0
            return;
7234
0
        }
7235
0
        for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
7236
0
            if (id == TQualifier::getLayoutDepthString(depth)) {
7237
                // publicType.shaderQualifiers.layoutDepth = depth;
7238
0
                warn(loc, "ignored", id.c_str(), "");
7239
0
                return;
7240
0
            }
7241
0
        }
7242
0
        if (id.compare(0, 13, "blend_support") == 0) {
7243
0
            bool found = false;
7244
0
            for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
7245
0
                if (id == TQualifier::getBlendEquationString(be)) {
7246
0
                    requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
7247
0
                    intermediate.addBlendEquation(be);
7248
                    // publicType.shaderQualifiers.blendEquation = true;
7249
0
                    warn(loc, "ignored", id.c_str(), "");
7250
0
                    found = true;
7251
0
                    break;
7252
0
                }
7253
0
            }
7254
0
            if (! found)
7255
0
                error(loc, "unknown blend equation", "blend_support", "");
7256
0
            return;
7257
0
        }
7258
0
    }
7259
16
    error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
7260
16
}
7261
7262
// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
7263
// This is before we know any type information for error checking.
7264
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id,
7265
                                          const TIntermTyped* node)
7266
590
{
7267
590
    const char* feature = "layout-id value";
7268
    // const char* nonLiteralFeature = "non-literal layout-id value";
7269
7270
590
    integerCheck(node, feature);
7271
590
    const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
7272
590
    int value = 0;
7273
590
    if (constUnion) {
7274
586
        value = constUnion->getConstArray()[0].getIConst();
7275
586
    }
7276
7277
590
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7278
7279
590
    if (id == "offset") {
7280
0
        qualifier.layoutOffset = value;
7281
0
        return;
7282
590
    } else if (id == "align") {
7283
        // "The specified alignment must be a power of 2, or a compile-time error results."
7284
0
        if (! IsPow2(value))
7285
0
            error(loc, "must be a power of 2", "align", "");
7286
0
        else
7287
0
            qualifier.layoutAlign = value;
7288
0
        return;
7289
590
    } else if (id == "location") {
7290
12
        if ((unsigned int)value >= TQualifier::layoutLocationEnd)
7291
0
            error(loc, "location is too large", id.c_str(), "");
7292
12
        else
7293
12
            qualifier.layoutLocation = value;
7294
12
        return;
7295
578
    } else if (id == "set") {
7296
0
        if ((unsigned int)value >= TQualifier::layoutSetEnd)
7297
0
            error(loc, "set is too large", id.c_str(), "");
7298
0
        else
7299
0
            qualifier.layoutSet = value;
7300
0
        return;
7301
578
    } else if (id == "binding") {
7302
36
        if ((unsigned int)value >= TQualifier::layoutBindingEnd)
7303
0
            error(loc, "binding is too large", id.c_str(), "");
7304
36
        else
7305
36
            qualifier.layoutBinding = value;
7306
36
        return;
7307
542
    } else if (id == "component") {
7308
4
        if ((unsigned)value >= TQualifier::layoutComponentEnd)
7309
0
            error(loc, "component is too large", id.c_str(), "");
7310
4
        else
7311
4
            qualifier.layoutComponent = value;
7312
4
        return;
7313
538
    } else if (id.compare(0, 4, "xfb_") == 0) {
7314
        // "Any shader making any static use (after preprocessing) of any of these
7315
        // *xfb_* qualifiers will cause the shader to be in a transform feedback
7316
        // capturing mode and hence responsible for describing the transform feedback
7317
        // setup."
7318
0
        intermediate.setXfbMode();
7319
0
        if (id == "xfb_buffer") {
7320
            // "It is a compile-time error to specify an *xfb_buffer* that is greater than
7321
            // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
7322
0
            if (value >= resources.maxTransformFeedbackBuffers)
7323
0
                error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d",
7324
0
                      resources.maxTransformFeedbackBuffers);
7325
0
            if (value >= (int)TQualifier::layoutXfbBufferEnd)
7326
0
                error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
7327
0
            else
7328
0
                qualifier.layoutXfbBuffer = value;
7329
0
            return;
7330
0
        } else if (id == "xfb_offset") {
7331
0
            if (value >= (int)TQualifier::layoutXfbOffsetEnd)
7332
0
                error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
7333
0
            else
7334
0
                qualifier.layoutXfbOffset = value;
7335
0
            return;
7336
0
        } else if (id == "xfb_stride") {
7337
            // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
7338
            // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
7339
0
            if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
7340
0
                error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
7341
0
                      resources.maxTransformFeedbackInterleavedComponents);
7342
0
            else if (value >= (int)TQualifier::layoutXfbStrideEnd)
7343
0
                error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
7344
0
            if (value < (int)TQualifier::layoutXfbStrideEnd)
7345
0
                qualifier.layoutXfbStride = value;
7346
0
            return;
7347
0
        }
7348
0
    }
7349
7350
538
    if (id == "input_attachment_index") {
7351
520
        requireVulkan(loc, "input_attachment_index");
7352
520
        if (value >= (int)TQualifier::layoutAttachmentEnd)
7353
0
            error(loc, "attachment index is too large", id.c_str(), "");
7354
520
        else
7355
520
            qualifier.layoutAttachment = value;
7356
520
        return;
7357
520
    }
7358
18
    if (id == "constant_id") {
7359
0
        setSpecConstantId(loc, qualifier, value);
7360
0
        return;
7361
0
    }
7362
7363
18
    switch (language) {
7364
0
    case EShLangVertex:
7365
0
        break;
7366
7367
8
    case EShLangTessControl:
7368
8
        if (id == "vertices") {
7369
0
            if (value == 0)
7370
0
                error(loc, "must be greater than 0", "vertices", "");
7371
0
            else
7372
                // publicType.shaderQualifiers.vertices = value;
7373
0
                warn(loc, "ignored", id.c_str(), "");
7374
0
            return;
7375
0
        }
7376
8
        break;
7377
7378
8
    case EShLangTessEvaluation:
7379
0
        break;
7380
7381
4
    case EShLangGeometry:
7382
4
        if (id == "invocations") {
7383
0
            if (value == 0)
7384
0
                error(loc, "must be at least 1", "invocations", "");
7385
0
            else
7386
                // publicType.shaderQualifiers.invocations = value;
7387
0
                warn(loc, "ignored", id.c_str(), "");
7388
0
            return;
7389
0
        }
7390
4
        if (id == "max_vertices") {
7391
            // publicType.shaderQualifiers.vertices = value;
7392
0
            warn(loc, "ignored", id.c_str(), "");
7393
0
            if (value > resources.maxGeometryOutputVertices)
7394
0
                error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
7395
0
            return;
7396
0
        }
7397
4
        if (id == "stream") {
7398
0
            qualifier.layoutStream = value;
7399
0
            return;
7400
0
        }
7401
4
        break;
7402
7403
4
    case EShLangFragment:
7404
0
        if (id == "index") {
7405
0
            qualifier.layoutIndex = value;
7406
0
            return;
7407
0
        }
7408
0
        break;
7409
7410
0
    case EShLangCompute:
7411
0
        if (id.compare(0, 11, "local_size_") == 0) {
7412
0
            if (id == "local_size_x") {
7413
                // publicType.shaderQualifiers.localSize[0] = value;
7414
0
                warn(loc, "ignored", id.c_str(), "");
7415
0
                return;
7416
0
            }
7417
0
            if (id == "local_size_y") {
7418
                // publicType.shaderQualifiers.localSize[1] = value;
7419
0
                warn(loc, "ignored", id.c_str(), "");
7420
0
                return;
7421
0
            }
7422
0
            if (id == "local_size_z") {
7423
                // publicType.shaderQualifiers.localSize[2] = value;
7424
0
                warn(loc, "ignored", id.c_str(), "");
7425
0
                return;
7426
0
            }
7427
0
            if (spvVersion.spv != 0) {
7428
0
                if (id == "local_size_x_id") {
7429
                    // publicType.shaderQualifiers.localSizeSpecId[0] = value;
7430
0
                    warn(loc, "ignored", id.c_str(), "");
7431
0
                    return;
7432
0
                }
7433
0
                if (id == "local_size_y_id") {
7434
                    // publicType.shaderQualifiers.localSizeSpecId[1] = value;
7435
0
                    warn(loc, "ignored", id.c_str(), "");
7436
0
                    return;
7437
0
                }
7438
0
                if (id == "local_size_z_id") {
7439
                    // publicType.shaderQualifiers.localSizeSpecId[2] = value;
7440
0
                    warn(loc, "ignored", id.c_str(), "");
7441
0
                    return;
7442
0
                }
7443
0
            }
7444
0
        }
7445
0
        break;
7446
7447
6
    default:
7448
6
        break;
7449
18
    }
7450
7451
18
    error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
7452
18
}
7453
7454
void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value)
7455
58
{
7456
58
    if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
7457
0
        error(loc, "specialization-constant id is too large", "constant_id", "");
7458
58
    } else {
7459
58
        qualifier.layoutSpecConstantId = value;
7460
58
        qualifier.specConstant = true;
7461
58
        if (! intermediate.addUsedConstantId(value))
7462
14
            error(loc, "specialization-constant id already used", "constant_id", "");
7463
58
    }
7464
58
    return;
7465
58
}
7466
7467
// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
7468
//
7469
// "More than one layout qualifier may appear in a single declaration.
7470
// Additionally, the same layout-qualifier-name can occur multiple times
7471
// within a layout qualifier or across multiple layout qualifiers in the
7472
// same declaration. When the same layout-qualifier-name occurs
7473
// multiple times, in a single declaration, the last occurrence overrides
7474
// the former occurrence(s).  Further, if such a layout-qualifier-name
7475
// will effect subsequent declarations or other observable behavior, it
7476
// is only the last occurrence that will have any effect, behaving as if
7477
// the earlier occurrence(s) within the declaration are not present.
7478
// This is also true for overriding layout-qualifier-names, where one
7479
// overrides the other (e.g., row_major vs. column_major); only the last
7480
// occurrence has any effect."
7481
//
7482
void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
7483
3.41k
{
7484
3.41k
    if (src.hasMatrix())
7485
1.04k
        dst.layoutMatrix = src.layoutMatrix;
7486
3.41k
    if (src.hasPacking())
7487
1.09k
        dst.layoutPacking = src.layoutPacking;
7488
7489
3.41k
    if (src.hasStream())
7490
0
        dst.layoutStream = src.layoutStream;
7491
7492
3.41k
    if (src.hasFormat())
7493
0
        dst.layoutFormat = src.layoutFormat;
7494
7495
3.41k
    if (src.hasXfbBuffer())
7496
0
        dst.layoutXfbBuffer = src.layoutXfbBuffer;
7497
7498
3.41k
    if (src.hasAlign())
7499
0
        dst.layoutAlign = src.layoutAlign;
7500
7501
3.41k
    if (! inheritOnly) {
7502
1.91k
        if (src.hasLocation())
7503
0
            dst.layoutLocation = src.layoutLocation;
7504
1.91k
        if (src.hasComponent())
7505
0
            dst.layoutComponent = src.layoutComponent;
7506
1.91k
        if (src.hasIndex())
7507
0
            dst.layoutIndex = src.layoutIndex;
7508
7509
1.91k
        if (src.hasOffset())
7510
98
            dst.layoutOffset = src.layoutOffset;
7511
7512
1.91k
        if (src.hasSet())
7513
0
            dst.layoutSet = src.layoutSet;
7514
1.91k
        if (src.layoutBinding != TQualifier::layoutBindingEnd)
7515
0
            dst.layoutBinding = src.layoutBinding;
7516
7517
1.91k
        if (src.hasXfbStride())
7518
0
            dst.layoutXfbStride = src.layoutXfbStride;
7519
1.91k
        if (src.hasXfbOffset())
7520
0
            dst.layoutXfbOffset = src.layoutXfbOffset;
7521
1.91k
        if (src.hasAttachment())
7522
0
            dst.layoutAttachment = src.layoutAttachment;
7523
1.91k
        if (src.hasSpecConstantId())
7524
2
            dst.layoutSpecConstantId = src.layoutSpecConstantId;
7525
7526
1.91k
        if (src.layoutPushConstant)
7527
18
            dst.layoutPushConstant = true;
7528
1.91k
    }
7529
3.41k
}
7530
7531
7532
//
7533
// Look up a function name in the symbol table, and make sure it is a function.
7534
//
7535
// First, look for an exact match.  If there is none, use the generic selector
7536
// TParseContextBase::selectFunction() to find one, parameterized by the
7537
// convertible() and better() predicates defined below.
7538
//
7539
// Return the function symbol if found, otherwise nullptr.
7540
//
7541
const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth,
7542
                                                TIntermTyped*& args)
7543
13.2k
{
7544
13.2k
    if (symbolTable.isFunctionNameVariable(call.getName())) {
7545
2
        error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
7546
2
        return nullptr;
7547
2
    }
7548
7549
    // first, look for an exact match
7550
13.2k
    bool dummyScope;
7551
13.2k
    TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth);
7552
13.2k
    if (symbol)
7553
9.05k
        return symbol->getAsFunction();
7554
7555
    // no exact match, use the generic selector, parameterized by the GLSL rules
7556
7557
    // create list of candidates to send
7558
4.21k
    TVector<const TFunction*> candidateList;
7559
4.21k
    symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
7560
7561
    // These built-in ops can accept any type, so we bypass the argument selection
7562
4.21k
    if (candidateList.size() == 1 && builtIn &&
7563
380
        (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
7564
372
         candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
7565
368
         candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
7566
368
         candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter ||
7567
368
         candidateList[0]->getBuiltInOp() == EOpMethodConsume)) {
7568
12
        return candidateList[0];
7569
12
    }
7570
7571
4.20k
    bool allowOnlyUpConversions = true;
7572
7573
    // can 'from' convert to 'to'?
7574
318k
    const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
7575
318k
        if (from == to)
7576
6.13k
            return true;
7577
7578
        // no aggregate conversions
7579
311k
        if (from.isArray()  || to.isArray() ||
7580
311k
            from.isStruct() || to.isStruct())
7581
742
            return false;
7582
7583
311k
        switch (op) {
7584
0
        case EOpInterlockedAdd:
7585
0
        case EOpInterlockedAnd:
7586
0
        case EOpInterlockedCompareExchange:
7587
0
        case EOpInterlockedCompareStore:
7588
0
        case EOpInterlockedExchange:
7589
0
        case EOpInterlockedMax:
7590
0
        case EOpInterlockedMin:
7591
0
        case EOpInterlockedOr:
7592
0
        case EOpInterlockedXor:
7593
            // We do not promote the texture or image type for these ocodes.  Normally that would not
7594
            // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
7595
            // stage it's merely e.g, a basic integer type.
7596
            //
7597
            // Instead, we want to promote other arguments, but stay within the same family.  In other
7598
            // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
7599
            // but it is allowed to promote its other arguments.
7600
0
            if (arg == 0)
7601
0
                return false;
7602
0
            break;
7603
4.03k
        case EOpMethodSample:
7604
4.11k
        case EOpMethodSampleBias:
7605
5.08k
        case EOpMethodSampleCmp:
7606
12.3k
        case EOpMethodSampleCmpLevelZero:
7607
12.3k
        case EOpMethodSampleGrad:
7608
12.7k
        case EOpMethodSampleLevel:
7609
15.5k
        case EOpMethodLoad:
7610
35.6k
        case EOpMethodGetDimensions:
7611
35.6k
        case EOpMethodGetSamplePosition:
7612
35.6k
        case EOpMethodGather:
7613
35.6k
        case EOpMethodCalculateLevelOfDetail:
7614
36.6k
        case EOpMethodCalculateLevelOfDetailUnclamped:
7615
36.6k
        case EOpMethodGatherRed:
7616
36.6k
        case EOpMethodGatherGreen:
7617
36.6k
        case EOpMethodGatherBlue:
7618
36.6k
        case EOpMethodGatherAlpha:
7619
36.6k
        case EOpMethodGatherCmp:
7620
37.0k
        case EOpMethodGatherCmpRed:
7621
37.0k
        case EOpMethodGatherCmpGreen:
7622
37.0k
        case EOpMethodGatherCmpBlue:
7623
37.0k
        case EOpMethodGatherCmpAlpha:
7624
37.0k
        case EOpMethodAppend:
7625
37.0k
        case EOpMethodRestartStrip:
7626
            // those are method calls, the object type can not be changed
7627
            // they are equal if the dim and type match (is dim sufficient?)
7628
37.0k
            if (arg == 0)
7629
33.8k
                return from.getSampler().type == to.getSampler().type &&
7630
11.2k
                       from.getSampler().arrayed == to.getSampler().arrayed &&
7631
6.38k
                       from.getSampler().shadow == to.getSampler().shadow &&
7632
6.38k
                       from.getSampler().ms == to.getSampler().ms &&
7633
6.06k
                       from.getSampler().dim == to.getSampler().dim;
7634
3.16k
            break;
7635
274k
        default:
7636
274k
            break;
7637
311k
        }
7638
7639
        // basic types have to be convertible
7640
277k
        if (allowOnlyUpConversions)
7641
198k
            if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
7642
34.2k
                return false;
7643
7644
        // shapes have to be convertible
7645
243k
        if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
7646
234k
            (from.isScalarOrVec1() && to.isVector())    ||
7647
212k
            (from.isScalarOrVec1() && to.isMatrix())    ||
7648
69.7k
            (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
7649
180k
            return true;
7650
7651
        // TODO: what are the matrix rules? they go here
7652
7653
62.6k
        return false;
7654
243k
    };
7655
7656
    // Is 'to2' a better conversion than 'to1'?
7657
    // Ties should not be considered as better.
7658
    // Assumes 'convertible' already said true.
7659
557k
    const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
7660
        // exact match is always better than mismatch
7661
557k
        if (from == to2)
7662
26.7k
            return from != to1;
7663
530k
        if (from == to1)
7664
130k
            return false;
7665
7666
        // shape changes are always worse
7667
400k
        if (from.isScalar() || from.isVector()) {
7668
400k
            if (from.getVectorSize() == to2.getVectorSize() &&
7669
58.2k
                from.getVectorSize() != to1.getVectorSize())
7670
37.4k
                return true;
7671
362k
            if (from.getVectorSize() == to1.getVectorSize() &&
7672
163k
                from.getVectorSize() != to2.getVectorSize())
7673
143k
                return false;
7674
362k
        }
7675
7676
        // Handle sampler betterness: An exact sampler match beats a non-exact match.
7677
        // (If we just looked at basic type, all EbtSamplers would look the same).
7678
        // If any type is not a sampler, just use the linearize function below.
7679
219k
        if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
7680
            // We can ignore the vector size in the comparison.
7681
5.52k
            TSampler to1Sampler = to1.getSampler();
7682
5.52k
            TSampler to2Sampler = to2.getSampler();
7683
7684
5.52k
            to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
7685
7686
5.52k
            if (from.getSampler() == to2Sampler)
7687
3.16k
                return from.getSampler() != to1Sampler;
7688
2.36k
            if (from.getSampler() == to1Sampler)
7689
1.51k
                return false;
7690
2.36k
        }
7691
7692
        // Might or might not be changing shape, which means basic type might
7693
        // or might not match, so within that, the question is how big a
7694
        // basic-type conversion is being done.
7695
        //
7696
        // Use a hierarchy of domains, translated to order of magnitude
7697
        // in a linearized view:
7698
        //   - floating-point vs. integer
7699
        //     - 32 vs. 64 bit (or width in general)
7700
        //       - bool vs. non bool
7701
        //         - signed vs. not signed
7702
859k
        const auto linearize = [](const TBasicType& basicType) -> int {
7703
859k
            switch (basicType) {
7704
221k
            case EbtBool:     return 1;
7705
85.2k
            case EbtInt:      return 10;
7706
64.2k
            case EbtUint:     return 11;
7707
0
            case EbtInt64:    return 20;
7708
0
            case EbtUint64:   return 21;
7709
142k
            case EbtFloat:    return 100;
7710
80
            case EbtDouble:   return 110;
7711
345k
            default:          return 0;
7712
859k
            }
7713
859k
        };
7714
7715
214k
        return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
7716
214k
               abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
7717
219k
    };
7718
7719
    // for ambiguity reporting
7720
4.20k
    bool tie = false;
7721
7722
    // send to the generic selector
7723
4.20k
    const TFunction* bestMatch = nullptr;
7724
7725
    // printf has var args and is in the symbol table as "printf()",
7726
    // mangled to "printf("
7727
4.20k
    if (call.getName() == "printf") {
7728
0
        TSymbol* symbol = symbolTable.find("printf(", &builtIn);
7729
0
        if (symbol)
7730
0
            return symbol->getAsFunction();
7731
0
    }
7732
7733
4.20k
    bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7734
7735
4.20k
    if (bestMatch == nullptr) {
7736
        // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
7737
        // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
7738
        // upconversions possible.
7739
1.75k
        allowOnlyUpConversions = false;
7740
1.75k
        bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7741
1.75k
    }
7742
7743
4.20k
    if (bestMatch == nullptr) {
7744
958
        error(loc, "no matching overloaded function found", call.getName().c_str(), "");
7745
958
        return nullptr;
7746
958
    }
7747
7748
    // For built-ins, we can convert across the arguments.  This will happen in several steps:
7749
    // Step 1:  If there's an exact match, use it.
7750
    // Step 2a: Otherwise, get the operator from the best match and promote arguments:
7751
    // Step 2b: reconstruct the TFunction based on the new arg types
7752
    // Step 3:  Re-select after type promotion is applied, to find proper candidate.
7753
3.24k
    if (builtIn) {
7754
        // Step 1: If there's an exact match, use it.
7755
3.14k
        if (call.getMangledName() == bestMatch->getMangledName())
7756
0
            return bestMatch;
7757
7758
        // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
7759
        // are that kind of operator.
7760
3.14k
        if (args != nullptr) {
7761
            // The arg list can be a unary node, or an aggregate.  We have to handle both.
7762
            // We will use the normal promote() facilities, which require an interm node.
7763
3.14k
            TIntermOperator* promote = nullptr;
7764
7765
3.14k
            if (call.getParamCount() == 1) {
7766
1.30k
                promote = new TIntermUnary(bestMatch->getBuiltInOp());
7767
1.30k
                promote->getAsUnaryNode()->setOperand(args->getAsTyped());
7768
1.83k
            } else {
7769
1.83k
                promote = new TIntermAggregate(bestMatch->getBuiltInOp());
7770
1.83k
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7771
1.83k
            }
7772
7773
3.14k
            if (! intermediate.promote(promote))
7774
200
                return nullptr;
7775
7776
            // Obtain the promoted arg list.
7777
2.94k
            if (call.getParamCount() == 1) {
7778
1.30k
                args = promote->getAsUnaryNode()->getOperand();
7779
1.63k
            } else {
7780
1.63k
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7781
1.63k
            }
7782
2.94k
        }
7783
7784
        // Step 2b: reconstruct the TFunction based on the new arg types
7785
2.94k
        TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
7786
7787
2.94k
        if (args->getAsAggregate()) {
7788
            // Handle aggregates: put all args into the new function call
7789
6.07k
            for (int arg = 0; arg < int(args->getAsAggregate()->getSequence().size()); ++arg) {
7790
                // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
7791
4.44k
                TParameter param = { nullptr, new TType, nullptr };
7792
4.44k
                param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
7793
4.44k
                convertedCall.addParameter(param);
7794
4.44k
            }
7795
1.63k
        } else if (args->getAsUnaryNode()) {
7796
            // Handle unaries: put all args into the new function call
7797
0
            TParameter param = { nullptr, new TType, nullptr };
7798
0
            param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
7799
0
            convertedCall.addParameter(param);
7800
1.30k
        } else if (args->getAsTyped()) {
7801
            // Handle bare e.g, floats, not in an aggregate.
7802
1.30k
            TParameter param = { nullptr, new TType, nullptr };
7803
1.30k
            param.type->shallowCopy(args->getAsTyped()->getType());
7804
1.30k
            convertedCall.addParameter(param);
7805
1.30k
        } else {
7806
0
            assert(0); // unknown argument list.
7807
0
            return nullptr;
7808
0
        }
7809
7810
        // Step 3: Re-select after type promotion, to find proper candidate
7811
        // send to the generic selector
7812
2.94k
        bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
7813
7814
        // At this point, there should be no tie.
7815
2.94k
    }
7816
7817
3.04k
    if (tie)
7818
540
        error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
7819
7820
    // Append default parameter values if needed
7821
3.04k
    if (!tie && bestMatch != nullptr) {
7822
2.55k
        for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
7823
52
            handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
7824
52
        }
7825
2.50k
    }
7826
7827
3.04k
    return bestMatch;
7828
3.24k
}
7829
7830
//
7831
// Do everything necessary to handle a typedef declaration, for a single symbol.
7832
//
7833
// 'parseType' is the type part of the declaration (to the left)
7834
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7835
//
7836
void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
7837
0
{
7838
0
    TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
7839
0
    if (! symbolTable.insert(*typeSymbol))
7840
0
        error(loc, "name already defined", "typedef", identifier.c_str());
7841
0
}
7842
7843
// Do everything necessary to handle a struct declaration, including
7844
// making IO aliases because HLSL allows mixed IO in a struct that specializes
7845
// based on the usage (input, output, uniform, none).
7846
void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
7847
916
{
7848
    // If it was named, which means the type can be reused later, add
7849
    // it to the symbol table.  (Unless it's a block, in which
7850
    // case the name is not a type.)
7851
916
    if (type.getBasicType() == EbtBlock || structName.size() == 0)
7852
196
        return;
7853
7854
720
    TVariable* userTypeDef = new TVariable(&structName, type, true);
7855
720
    if (! symbolTable.insert(*userTypeDef)) {
7856
12
        error(loc, "redefinition", structName.c_str(), "struct");
7857
12
        return;
7858
12
    }
7859
7860
    // See if we need IO aliases for the structure typeList
7861
7862
3.69k
    const auto condAlloc = [](bool pred, TTypeList*& list) {
7863
3.69k
        if (pred && list == nullptr)
7864
62
            list = new TTypeList;
7865
3.69k
    };
7866
7867
708
    tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
7868
1.93k
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7869
1.23k
        condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
7870
1.23k
        condAlloc(  hasInput(member->type->getQualifier()), newLists.input);
7871
1.23k
        condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
7872
7873
1.23k
        if (member->type->isStruct()) {
7874
32
            auto it = ioTypeMap.find(member->type->getStruct());
7875
32
            if (it != ioTypeMap.end()) {
7876
0
                condAlloc(it->second.uniform != nullptr, newLists.uniform);
7877
0
                condAlloc(it->second.input   != nullptr, newLists.input);
7878
0
                condAlloc(it->second.output  != nullptr, newLists.output);
7879
0
            }
7880
32
        }
7881
1.23k
    }
7882
708
    if (newLists.uniform == nullptr &&
7883
688
        newLists.input   == nullptr &&
7884
666
        newLists.output  == nullptr) {
7885
        // Won't do any IO caching, clear up the type and get out now.
7886
1.81k
        for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
7887
1.14k
            clearUniformInputOutput(member->type->getQualifier());
7888
666
        return;
7889
666
    }
7890
7891
    // We have IO involved.
7892
7893
    // Make a pure typeList for the symbol table, and cache side copies of IO versions.
7894
124
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7895
82
        const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
7896
0
            if (s != nullptr) {
7897
0
                ioMember.type = new TType;
7898
0
                ioMember.type->shallowCopy(*member->type);
7899
0
                ioMember.type->setStruct(s);
7900
0
            }
7901
0
        };
7902
102
        const auto newMember = [&](TTypeLoc& m) {
7903
102
            if (m.type == nullptr) {
7904
102
                m.type = new TType;
7905
102
                m.type->shallowCopy(*member->type);
7906
102
            }
7907
102
        };
7908
7909
82
        TTypeLoc newUniformMember = { nullptr, member->loc };
7910
82
        TTypeLoc newInputMember   = { nullptr, member->loc };
7911
82
        TTypeLoc newOutputMember  = { nullptr, member->loc };
7912
82
        if (member->type->isStruct()) {
7913
            // swap in an IO child if there is one
7914
0
            auto it = ioTypeMap.find(member->type->getStruct());
7915
0
            if (it != ioTypeMap.end()) {
7916
0
                inheritStruct(it->second.uniform, newUniformMember);
7917
0
                inheritStruct(it->second.input,   newInputMember);
7918
0
                inheritStruct(it->second.output,  newOutputMember);
7919
0
            }
7920
0
        }
7921
82
        if (newLists.uniform) {
7922
60
            newMember(newUniformMember);
7923
7924
            // inherit default matrix layout (changeable via #pragma pack_matrix), if none given.
7925
60
            if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone)
7926
14
                newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
7927
7928
60
            correctUniform(newUniformMember.type->getQualifier());
7929
60
            newLists.uniform->push_back(newUniformMember);
7930
60
        }
7931
82
        if (newLists.input) {
7932
22
            newMember(newInputMember);
7933
22
            correctInput(newInputMember.type->getQualifier());
7934
22
            newLists.input->push_back(newInputMember);
7935
22
        }
7936
82
        if (newLists.output) {
7937
20
            newMember(newOutputMember);
7938
20
            correctOutput(newOutputMember.type->getQualifier());
7939
20
            newLists.output->push_back(newOutputMember);
7940
20
        }
7941
7942
        // make original pure
7943
82
        clearUniformInputOutput(member->type->getQualifier());
7944
82
    }
7945
42
    ioTypeMap[type.getStruct()] = newLists;
7946
42
}
7947
7948
// Lookup a user-type by name.
7949
// If found, fill in the type and return the defining symbol.
7950
// If not found, return nullptr.
7951
TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
7952
39.5k
{
7953
39.5k
    TSymbol* symbol = symbolTable.find(typeName);
7954
39.5k
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
7955
1.01k
        type.shallowCopy(symbol->getType());
7956
1.01k
        return symbol;
7957
1.01k
    } else
7958
38.5k
        return nullptr;
7959
39.5k
}
7960
7961
//
7962
// Do everything necessary to handle a variable (non-block) declaration.
7963
// Either redeclaring a variable, or making a new one, updating the symbol
7964
// table, and all error checking.
7965
//
7966
// Returns a subtree node that computes an initializer, if needed.
7967
// Returns nullptr if there is no code to execute for initialization.
7968
//
7969
// 'parseType' is the type part of the declaration (to the left)
7970
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7971
//
7972
TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type,
7973
                                               TIntermTyped* initializer)
7974
26.3k
{
7975
26.3k
    if (voidErrorCheck(loc, identifier, type.getBasicType()))
7976
24
        return nullptr;
7977
7978
    // Global consts with initializers that are non-const act like EvqGlobal in HLSL.
7979
    // This test is implicitly recursive, because initializers propagate constness
7980
    // up the aggregate node tree during creation.  E.g, for:
7981
    //    { { 1, 2 }, { 3, 4 } }
7982
    // the initializer list is marked EvqConst at the top node, and remains so here.  However:
7983
    //    { 1, { myvar, 2 }, 3 }
7984
    // is not a const intializer, and still becomes EvqGlobal here.
7985
7986
26.2k
    const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
7987
7988
26.2k
    if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
7989
        // Force to global
7990
30
        type.getQualifier().storage = EvqGlobal;
7991
30
    }
7992
7993
    // make const and initialization consistent
7994
26.2k
    fixConstInit(loc, identifier, type, initializer);
7995
7996
    // Check for redeclaration of built-ins and/or attempting to declare a reserved name
7997
26.2k
    TSymbol* symbol = nullptr;
7998
7999
26.2k
    inheritGlobalDefaults(type.getQualifier());
8000
8001
26.2k
    const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true);
8002
8003
    // correct IO in the type
8004
26.2k
    switch (type.getQualifier().storage) {
8005
650
    case EvqGlobal:
8006
19.8k
    case EvqTemporary:
8007
19.8k
        clearUniformInputOutput(type.getQualifier());
8008
19.8k
        break;
8009
4.61k
    case EvqUniform:
8010
4.61k
    case EvqBuffer:
8011
4.61k
        correctUniform(type.getQualifier());
8012
4.61k
        if (type.isStruct()) {
8013
38
            auto it = ioTypeMap.find(type.getStruct());
8014
38
            if (it != ioTypeMap.end())
8015
0
                type.setStruct(it->second.uniform);
8016
38
        }
8017
8018
4.61k
        break;
8019
1.82k
    default:
8020
1.82k
        break;
8021
26.2k
    }
8022
8023
    // Declare the variable
8024
26.2k
    if (type.isArray()) {
8025
        // array case
8026
192
        declareArray(loc, identifier, type, symbol, !flattenVar);
8027
26.0k
    } else {
8028
        // non-array case
8029
26.0k
        if (symbol == nullptr)
8030
26.0k
            symbol = declareNonArray(loc, identifier, type, !flattenVar);
8031
0
        else if (type != symbol->getType())
8032
0
            error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
8033
26.0k
    }
8034
8035
26.2k
    if (symbol == nullptr)
8036
1.55k
        return nullptr;
8037
8038
24.7k
    if (flattenVar)
8039
38
        flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel());
8040
8041
24.7k
    TVariable* variable = symbol->getAsVariable();
8042
8043
24.7k
    if (initializer == nullptr) {
8044
8.85k
        if (intermediate.getDebugInfo())
8045
0
            return executeDeclaration(loc, variable);
8046
8.85k
        else
8047
8.85k
            return nullptr;
8048
8.85k
    }
8049
8050
    // Deal with initializer
8051
15.8k
    if (variable == nullptr) {
8052
0
        error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
8053
0
        return nullptr;
8054
0
    }
8055
15.8k
    return executeInitializer(loc, initializer, variable);
8056
15.8k
}
8057
8058
// Pick up global defaults from the provide global defaults into dst.
8059
void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
8060
26.2k
{
8061
26.2k
    if (dst.storage == EvqVaryingOut) {
8062
0
        if (! dst.hasStream() && language == EShLangGeometry)
8063
0
            dst.layoutStream = globalOutputDefaults.layoutStream;
8064
0
        if (! dst.hasXfbBuffer())
8065
0
            dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
8066
0
    }
8067
26.2k
}
8068
8069
//
8070
// Make an internal-only variable whose name is for debug purposes only
8071
// and won't be searched for.  Callers will only use the return value to use
8072
// the variable, not the name to look it up.  It is okay if the name
8073
// is the same as other names; there won't be any conflict.
8074
//
8075
TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
8076
3.09k
{
8077
3.09k
    TString* nameString = NewPoolTString(name);
8078
3.09k
    TVariable* variable = new TVariable(nameString, type);
8079
3.09k
    symbolTable.makeInternalVariable(*variable);
8080
8081
3.09k
    return variable;
8082
3.09k
}
8083
8084
// Make a symbol node holding a new internal temporary variable.
8085
TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name,
8086
                                                          const TType& type) const
8087
154
{
8088
154
    TVariable* tmpVar = makeInternalVariable(name, type);
8089
154
    tmpVar->getWritableType().getQualifier().makeTemporary();
8090
8091
154
    return intermediate.addSymbol(*tmpVar, loc);
8092
154
}
8093
8094
//
8095
// Declare a non-array variable, the main point being there is no redeclaration
8096
// for resizing allowed.
8097
//
8098
// Return the successfully declared variable.
8099
//
8100
TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
8101
                                             bool track)
8102
26.0k
{
8103
    // make a new variable
8104
26.0k
    TVariable* variable = new TVariable(&identifier, type);
8105
8106
    // add variable to symbol table
8107
26.0k
    if (symbolTable.insert(*variable)) {
8108
24.5k
        if (track && symbolTable.atGlobalLevel())
8109
5.67k
            trackLinkage(*variable);
8110
24.5k
        return variable;
8111
24.5k
    }
8112
8113
1.55k
    error(loc, "redefinition", variable->getName().c_str(), "");
8114
1.55k
    return nullptr;
8115
26.0k
}
8116
8117
// Return a declaration of a temporary variable
8118
//
8119
// This is used to force a variable to be declared in the correct scope
8120
// when debug information is being generated.
8121
8122
TIntermNode* HlslParseContext::executeDeclaration(const TSourceLoc& loc, TVariable* variable)
8123
0
{
8124
  //
8125
  // Identifier must be of type temporary.
8126
  //
8127
0
  TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8128
0
  if (qualifier != EvqTemporary)
8129
0
      return nullptr;
8130
8131
0
  TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8132
0
  return handleDeclare(loc, intermSymbol);
8133
0
}
8134
8135
//
8136
// Handle all types of initializers from the grammar.
8137
//
8138
// Returning nullptr just means there is no code to execute to handle the
8139
// initializer, which will, for example, be the case for constant initializers.
8140
//
8141
// Returns a subtree that accomplished the initialization.
8142
//
8143
TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
8144
15.8k
{
8145
    //
8146
    // Identifier must be of type constant, a global, or a temporary, and
8147
    // starting at version 120, desktop allows uniforms to have initializers.
8148
    //
8149
15.8k
    TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8150
8151
    //
8152
    // If the initializer was from braces { ... }, we convert the whole subtree to a
8153
    // constructor-style subtree, allowing the rest of the code to operate
8154
    // identically for both kinds of initializers.
8155
    //
8156
    //
8157
    // Type can't be deduced from the initializer list, so a skeletal type to
8158
    // follow has to be passed in.  Constness and specialization-constness
8159
    // should be deduced bottom up, not dictated by the skeletal type.
8160
    //
8161
15.8k
    TType skeletalType;
8162
15.8k
    skeletalType.shallowCopy(variable->getType());
8163
15.8k
    skeletalType.getQualifier().makeTemporary();
8164
15.8k
    if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
8165
722
        initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
8166
15.8k
    if (initializer == nullptr) {
8167
        // error recovery; don't leave const without constant values
8168
30
        if (qualifier == EvqConst)
8169
8
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8170
30
        return nullptr;
8171
30
    }
8172
8173
    // Fix outer arrayness if variable is unsized, getting size from the initializer
8174
15.8k
    if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray())
8175
6
        variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
8176
8177
    // Inner arrayness can also get set by an initializer
8178
15.8k
    if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
8179
2
        initializer->getType().getArraySizes()->getNumDims() ==
8180
2
        variable->getType().getArraySizes()->getNumDims()) {
8181
        // adopt unsized sizes from the initializer's sizes
8182
4
        for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
8183
2
            if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) {
8184
0
                variable->getWritableType().getArraySizes()->setDimSize(d,
8185
0
                    initializer->getType().getArraySizes()->getDimSize(d));
8186
0
            }
8187
2
        }
8188
2
    }
8189
8190
    // Uniform and global consts require a constant initializer
8191
15.8k
    if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
8192
10
        error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
8193
10
        variable->getWritableType().getQualifier().storage = EvqTemporary;
8194
10
        return nullptr;
8195
10
    }
8196
8197
    // Const variables require a constant initializer
8198
15.8k
    if (qualifier == EvqConst) {
8199
1.01k
        if (initializer->getType().getQualifier().storage != EvqConst) {
8200
26
            variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
8201
26
            qualifier = EvqConstReadOnly;
8202
26
        }
8203
1.01k
    }
8204
8205
15.8k
    if (qualifier == EvqConst || qualifier == EvqUniform) {
8206
        // Compile-time tagging of the variable with its constant value...
8207
8208
992
        initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
8209
992
        if (initializer != nullptr && variable->getType() != initializer->getType())
8210
30
            initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer);
8211
992
        if (initializer == nullptr || !initializer->getAsConstantUnion() ||
8212
988
                                      variable->getType() != initializer->getType()) {
8213
4
            error(loc, "non-matching or non-convertible constant type for const initializer",
8214
4
                variable->getType().getStorageQualifierString(), "");
8215
4
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8216
4
            return nullptr;
8217
4
        }
8218
8219
988
        variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
8220
14.8k
    } else {
8221
        // normal assigning of a value to a variable...
8222
14.8k
        specializationCheck(loc, initializer->getType(), "initializer");
8223
14.8k
        TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8224
14.8k
        TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
8225
14.8k
        if (initNode == nullptr)
8226
1.57k
            assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
8227
14.8k
        return initNode;
8228
14.8k
    }
8229
8230
988
    return nullptr;
8231
15.8k
}
8232
8233
//
8234
// Reprocess any initializer-list { ... } parts of the initializer.
8235
// Need to hierarchically assign correct types and implicit
8236
// conversions. Will do this mimicking the same process used for
8237
// creating a constructor-style initializer, ensuring we get the
8238
// same form.
8239
//
8240
// Returns a node representing an expression for the initializer list expressed
8241
// as the correct type.
8242
//
8243
// Returns nullptr if there is an error.
8244
//
8245
TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
8246
                                                       TIntermTyped* initializer, TIntermTyped* scalarInit)
8247
1.38k
{
8248
    // Will operate recursively.  Once a subtree is found that is constructor style,
8249
    // everything below it is already good: Only the "top part" of the initializer
8250
    // can be an initializer list, where "top part" can extend for several (or all) levels.
8251
8252
    // see if we have bottomed out in the tree within the initializer-list part
8253
1.38k
    TIntermAggregate* initList = initializer->getAsAggregate();
8254
1.38k
    if (initList == nullptr || initList->getOp() != EOpNull) {
8255
        // We don't have a list, but if it's a scalar and the 'type' is a
8256
        // composite, we need to lengthen below to make it useful.
8257
        // Otherwise, this is an already formed object to initialize with.
8258
534
        if (type.isScalar() || !initializer->getType().isScalar())
8259
332
            return initializer;
8260
202
        else
8261
202
            initList = intermediate.makeAggregate(initializer);
8262
534
    }
8263
8264
    // Of the initializer-list set of nodes, need to process bottom up,
8265
    // so recurse deep, then process on the way up.
8266
8267
    // Go down the tree here...
8268
1.04k
    if (type.isArray()) {
8269
        // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
8270
        // Later on, initializer execution code will deal with array size logic.
8271
98
        TType arrayType;
8272
98
        arrayType.shallowCopy(type);                     // sharing struct stuff is fine
8273
98
        arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
8274
8275
        // edit array sizes to fill in unsized dimensions
8276
98
        if (type.isUnsizedArray())
8277
6
            arrayType.changeOuterArraySize((int)initList->getSequence().size());
8278
8279
        // set unsized array dimensions that can be derived from the initializer's first element
8280
98
        if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
8281
0
            TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
8282
0
            if (firstInit->getType().isArray() &&
8283
0
                arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
8284
0
                for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) {
8285
0
                    if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize)
8286
0
                        arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
8287
0
                }
8288
0
            }
8289
0
        }
8290
8291
        // lengthen list to be long enough
8292
98
        lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
8293
8294
        // recursively process each element
8295
98
        TType elementType(arrayType, 0); // dereferenced type
8296
374
        for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
8297
282
            initList->getSequence()[i] = convertInitializerList(loc, elementType,
8298
282
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8299
282
            if (initList->getSequence()[i] == nullptr)
8300
6
                return nullptr;
8301
282
        }
8302
8303
92
        return addConstructor(loc, initList, arrayType);
8304
950
    } else if (type.isStruct()) {
8305
        // do we have implicit assignments to opaques?
8306
152
        for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) {
8307
50
            if ((*type.getStruct())[i].type->containsOpaque()) {
8308
0
                error(loc, "cannot implicitly initialize opaque members", "initializer list", "");
8309
0
                return nullptr;
8310
0
            }
8311
50
        }
8312
8313
        // lengthen list to be long enough
8314
102
        lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
8315
8316
102
        if (type.getStruct()->size() != initList->getSequence().size()) {
8317
4
            error(loc, "wrong number of structure members", "initializer list", "");
8318
4
            return nullptr;
8319
4
        }
8320
302
        for (size_t i = 0; i < type.getStruct()->size(); ++i) {
8321
208
            initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type,
8322
208
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8323
208
            if (initList->getSequence()[i] == nullptr)
8324
4
                return nullptr;
8325
208
        }
8326
848
    } else if (type.isMatrix()) {
8327
340
        if (type.computeNumComponents() == (int)initList->getSequence().size()) {
8328
            // This means the matrix is initialized component-wise, rather than as
8329
            // a series of rows and columns.  We can just use the list directly as
8330
            // a constructor; no further processing needed.
8331
292
        } else {
8332
            // lengthen list to be long enough
8333
48
            lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
8334
8335
48
            if (type.getMatrixCols() != (int)initList->getSequence().size()) {
8336
2
                error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
8337
2
                return nullptr;
8338
2
            }
8339
46
            TType vectorType(type, 0); // dereferenced type
8340
214
            for (int i = 0; i < type.getMatrixCols(); ++i) {
8341
168
                initList->getSequence()[i] = convertInitializerList(loc, vectorType,
8342
168
                                                                    initList->getSequence()[i]->getAsTyped(), scalarInit);
8343
168
                if (initList->getSequence()[i] == nullptr)
8344
0
                    return nullptr;
8345
168
            }
8346
46
        }
8347
508
    } else if (type.isVector()) {
8348
        // lengthen list to be long enough
8349
330
        lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
8350
8351
        // error check; we're at bottom, so work is finished below
8352
330
        if (type.getVectorSize() != (int)initList->getSequence().size()) {
8353
0
            error(loc, "wrong vector size (or rows in a matrix column):", "initializer list",
8354
0
                  type.getCompleteString().c_str());
8355
0
            return nullptr;
8356
0
        }
8357
330
    } else if (type.isScalar()) {
8358
        // lengthen list to be long enough
8359
178
        lengthenList(loc, initList->getSequence(), 1, scalarInit);
8360
8361
178
        if ((int)initList->getSequence().size() != 1) {
8362
4
            error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
8363
4
            return nullptr;
8364
4
        }
8365
178
    } else {
8366
0
        error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
8367
0
        return nullptr;
8368
0
    }
8369
8370
    // Now that the subtree is processed, process this node as if the
8371
    // initializer list is a set of arguments to a constructor.
8372
936
    TIntermTyped* emulatedConstructorArguments;
8373
936
    if (initList->getSequence().size() == 1)
8374
216
        emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
8375
720
    else
8376
720
        emulatedConstructorArguments = initList;
8377
8378
936
    return addConstructor(loc, emulatedConstructorArguments, type);
8379
1.04k
}
8380
8381
// Lengthen list to be long enough to cover any gap from the current list size
8382
// to 'size'. If the list is longer, do nothing.
8383
// The value to lengthen with is the default for short lists.
8384
//
8385
// By default, lists that are too short due to lack of initializers initialize to zero.
8386
// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
8387
// based on whether something is passed in as 'scalarInit'.
8388
//
8389
// 'scalarInit' must be safe to use each time this is called (no side effects replication).
8390
//
8391
void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
8392
756
{
8393
1.55k
    for (int c = (int)list.size(); c < size; ++c) {
8394
798
        if (scalarInit == nullptr)
8395
798
            list.push_back(intermediate.addConstantUnion(0, loc));
8396
0
        else
8397
0
            list.push_back(scalarInit);
8398
798
    }
8399
756
}
8400
8401
//
8402
// Test for the correctness of the parameters passed to various constructor functions
8403
// and also convert them to the right data type, if allowed and required.
8404
//
8405
// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
8406
//
8407
TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8408
7.03k
{
8409
7.03k
    if (node == nullptr)
8410
0
        return nullptr;
8411
8412
    // Construct identical type
8413
7.03k
    if (type == node->getType())
8414
102
        return node;
8415
8416
    // Handle the idiom "(struct type)<scalar value>"
8417
6.93k
    if (type.isStruct() && isScalarConstructor(node)) {
8418
        // 'node' will almost always get used multiple times, so should not be used directly,
8419
        // it would create a DAG instead of a tree, which might be okay (would
8420
        // like to formalize that for constants and symbols), but if it has
8421
        // side effects, they would get executed multiple times, which is not okay.
8422
0
        if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
8423
0
            TIntermAggregate* seq = intermediate.makeAggregate(loc);
8424
0
            TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
8425
0
            seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
8426
0
            seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
8427
0
            seq->setOp(EOpComma);
8428
0
            seq->setType(type);
8429
0
            return seq;
8430
0
        } else
8431
0
            return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
8432
0
    }
8433
8434
6.93k
    return addConstructor(loc, node, type);
8435
6.93k
}
8436
8437
// Add a constructor, either from the grammar, or other programmatic reasons.
8438
//
8439
// 'node' is what to construct from.
8440
// 'type' is what type to construct.
8441
//
8442
// Returns the constructed object.
8443
// Return nullptr if it can't be done.
8444
//
8445
TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8446
7.97k
{
8447
7.97k
    TIntermAggregate* aggrNode = node->getAsAggregate();
8448
7.97k
    TOperator op = intermediate.mapTypeToConstructorOp(type);
8449
8450
7.97k
    if (op == EOpConstructTextureSampler)
8451
0
        return intermediate.setAggregateOperator(aggrNode, op, type, loc);
8452
8453
7.97k
    TTypeList::const_iterator memberTypes;
8454
7.97k
    if (op == EOpConstructStruct)
8455
100
        memberTypes = type.getStruct()->begin();
8456
8457
7.97k
    TType elementType;
8458
7.97k
    if (type.isArray()) {
8459
108
        TType dereferenced(type, 0);
8460
108
        elementType.shallowCopy(dereferenced);
8461
108
    } else
8462
7.86k
        elementType.shallowCopy(type);
8463
8464
7.97k
    bool singleArg;
8465
7.97k
    if (aggrNode != nullptr) {
8466
6.29k
        if (aggrNode->getOp() != EOpNull)
8467
6
            singleArg = true;
8468
6.29k
        else
8469
6.29k
            singleArg = false;
8470
6.29k
    } else
8471
1.68k
        singleArg = true;
8472
8473
7.97k
    TIntermTyped *newNode;
8474
7.97k
    if (singleArg) {
8475
        // Handle array -> array conversion
8476
        // Constructing an array of one type from an array of another type is allowed,
8477
        // assuming there are enough components available (semantic-checked earlier).
8478
1.68k
        if (type.isArray() && node->isArray())
8479
0
            newNode = convertArray(node, type);
8480
8481
        // If structure constructor or array constructor is being called
8482
        // for only one parameter inside the aggregate, we need to call constructAggregate function once.
8483
1.68k
        else if (type.isArray())
8484
0
            newNode = constructAggregate(node, elementType, 1, node->getLoc());
8485
1.68k
        else if (op == EOpConstructStruct)
8486
42
            newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
8487
1.64k
        else {
8488
            // shape conversion for matrix constructor from scalar.  HLSL semantics are: scalar
8489
            // is replicated into every element of the matrix (not just the diagnonal), so
8490
            // that is handled specially here.
8491
1.64k
            if (type.isMatrix() && node->getType().isScalarOrVec1())
8492
146
                node = intermediate.addShapeConversion(type, node);
8493
8494
1.64k
            newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
8495
1.64k
        }
8496
8497
1.68k
        if (newNode && (type.isArray() || op == EOpConstructStruct))
8498
42
            newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
8499
8500
1.68k
        return newNode;
8501
1.68k
    }
8502
8503
    //
8504
    // Handle list of arguments.
8505
    //
8506
6.29k
    TIntermSequence& sequenceVector = aggrNode->getSequence();    // Stores the information about the parameter to the constructor
8507
    // if the structure constructor contains more than one parameter, then construct
8508
    // each parameter
8509
8510
6.29k
    int paramCount = 0;  // keeps a track of the constructor parameter number being checked
8511
8512
    // for each parameter to the constructor call, check to see if the right type is passed or convert them
8513
    // to the right type if possible (and allowed).
8514
    // for structure constructors, just check if the right type is passed, no conversion is allowed.
8515
8516
6.29k
    for (TIntermSequence::iterator p = sequenceVector.begin();
8517
50.2k
        p != sequenceVector.end(); p++, paramCount++) {
8518
43.9k
        if (type.isArray())
8519
310
            newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
8520
43.6k
        else if (op == EOpConstructStruct)
8521
154
            newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
8522
43.4k
        else
8523
43.4k
            newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
8524
8525
43.9k
        if (newNode)
8526
43.9k
            *p = newNode;
8527
18
        else
8528
18
            return nullptr;
8529
43.9k
    }
8530
8531
6.27k
    TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
8532
8533
6.27k
    return constructor;
8534
6.29k
}
8535
8536
// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
8537
// for the parameter to the constructor (passed to this function). Essentially, it converts
8538
// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
8539
// float, then float is converted to int.
8540
//
8541
// Returns nullptr for an error or the constructed node.
8542
//
8543
TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node,
8544
                                                 const TSourceLoc& loc, bool subset)
8545
45.2k
{
8546
45.2k
    TIntermTyped* newNode;
8547
45.2k
    TOperator basicOp;
8548
8549
    //
8550
    // First, convert types as needed.
8551
    //
8552
45.2k
    switch (op) {
8553
0
    case EOpConstructF16Vec2:
8554
0
    case EOpConstructF16Vec3:
8555
0
    case EOpConstructF16Vec4:
8556
0
    case EOpConstructF16Mat2x2:
8557
0
    case EOpConstructF16Mat2x3:
8558
0
    case EOpConstructF16Mat2x4:
8559
0
    case EOpConstructF16Mat3x2:
8560
0
    case EOpConstructF16Mat3x3:
8561
0
    case EOpConstructF16Mat3x4:
8562
0
    case EOpConstructF16Mat4x2:
8563
0
    case EOpConstructF16Mat4x3:
8564
0
    case EOpConstructF16Mat4x4:
8565
0
    case EOpConstructFloat16:
8566
0
        basicOp = EOpConstructFloat16;
8567
0
        break;
8568
8569
1.28k
    case EOpConstructVec2:
8570
2.81k
    case EOpConstructVec3:
8571
6.30k
    case EOpConstructVec4:
8572
7.57k
    case EOpConstructMat2x2:
8573
9.28k
    case EOpConstructMat2x3:
8574
11.8k
    case EOpConstructMat2x4:
8575
13.6k
    case EOpConstructMat3x2:
8576
16.9k
    case EOpConstructMat3x3:
8577
21.6k
    case EOpConstructMat3x4:
8578
24.3k
    case EOpConstructMat4x2:
8579
28.7k
    case EOpConstructMat4x3:
8580
36.5k
    case EOpConstructMat4x4:
8581
37.6k
    case EOpConstructFloat:
8582
37.6k
        basicOp = EOpConstructFloat;
8583
37.6k
        break;
8584
8585
4
    case EOpConstructDVec2:
8586
10
    case EOpConstructDVec3:
8587
10
    case EOpConstructDVec4:
8588
10
    case EOpConstructDMat2x2:
8589
10
    case EOpConstructDMat2x3:
8590
10
    case EOpConstructDMat2x4:
8591
10
    case EOpConstructDMat3x2:
8592
10
    case EOpConstructDMat3x3:
8593
10
    case EOpConstructDMat3x4:
8594
10
    case EOpConstructDMat4x2:
8595
10
    case EOpConstructDMat4x3:
8596
10
    case EOpConstructDMat4x4:
8597
14
    case EOpConstructDouble:
8598
14
        basicOp = EOpConstructDouble;
8599
14
        break;
8600
8601
0
    case EOpConstructI16Vec2:
8602
0
    case EOpConstructI16Vec3:
8603
0
    case EOpConstructI16Vec4:
8604
0
    case EOpConstructInt16:
8605
0
        basicOp = EOpConstructInt16;
8606
0
        break;
8607
8608
250
    case EOpConstructIVec2:
8609
272
    case EOpConstructIVec3:
8610
512
    case EOpConstructIVec4:
8611
512
    case EOpConstructIMat2x2:
8612
512
    case EOpConstructIMat2x3:
8613
512
    case EOpConstructIMat2x4:
8614
1.01k
    case EOpConstructIMat3x2:
8615
1.01k
    case EOpConstructIMat3x3:
8616
1.01k
    case EOpConstructIMat3x4:
8617
1.89k
    case EOpConstructIMat4x2:
8618
1.89k
    case EOpConstructIMat4x3:
8619
3.90k
    case EOpConstructIMat4x4:
8620
4.02k
    case EOpConstructInt:
8621
4.02k
        basicOp = EOpConstructInt;
8622
4.02k
        break;
8623
8624
0
    case EOpConstructU16Vec2:
8625
0
    case EOpConstructU16Vec3:
8626
0
    case EOpConstructU16Vec4:
8627
0
    case EOpConstructUint16:
8628
0
        basicOp = EOpConstructUint16;
8629
0
        break;
8630
8631
218
    case EOpConstructUVec2:
8632
480
    case EOpConstructUVec3:
8633
760
    case EOpConstructUVec4:
8634
760
    case EOpConstructUMat2x2:
8635
760
    case EOpConstructUMat2x3:
8636
760
    case EOpConstructUMat2x4:
8637
1.02k
    case EOpConstructUMat3x2:
8638
1.02k
    case EOpConstructUMat3x3:
8639
1.02k
    case EOpConstructUMat3x4:
8640
1.43k
    case EOpConstructUMat4x2:
8641
1.43k
    case EOpConstructUMat4x3:
8642
2.61k
    case EOpConstructUMat4x4:
8643
2.66k
    case EOpConstructUint:
8644
2.66k
        basicOp = EOpConstructUint;
8645
2.66k
        break;
8646
8647
16
    case EOpConstructBVec2:
8648
22
    case EOpConstructBVec3:
8649
30
    case EOpConstructBVec4:
8650
30
    case EOpConstructBMat2x2:
8651
30
    case EOpConstructBMat2x3:
8652
30
    case EOpConstructBMat2x4:
8653
144
    case EOpConstructBMat3x2:
8654
144
    case EOpConstructBMat3x3:
8655
144
    case EOpConstructBMat3x4:
8656
320
    case EOpConstructBMat4x2:
8657
320
    case EOpConstructBMat4x3:
8658
896
    case EOpConstructBMat4x4:
8659
900
    case EOpConstructBool:
8660
900
        basicOp = EOpConstructBool;
8661
900
        break;
8662
8663
6
    default:
8664
6
        error(loc, "unsupported construction", "", "");
8665
8666
6
        return nullptr;
8667
45.2k
    }
8668
45.2k
    newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
8669
45.2k
    if (newNode == nullptr) {
8670
26
        error(loc, "can't convert", "constructor", "");
8671
26
        return nullptr;
8672
26
    }
8673
8674
    //
8675
    // Now, if there still isn't an operation to do the construction, and we need one, add one.
8676
    //
8677
8678
    // Otherwise, skip out early.
8679
45.2k
    if (subset || (newNode != node && newNode->getType() == type))
8680
44.6k
        return newNode;
8681
8682
    // setAggregateOperator will insert a new node for the constructor, as needed.
8683
624
    return intermediate.setAggregateOperator(newNode, op, type, loc);
8684
45.2k
}
8685
8686
// Convert the array in node to the requested type, which is also an array.
8687
// Returns nullptr on failure, otherwise returns aggregate holding the list of
8688
// elements needed to construct the array.
8689
TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
8690
0
{
8691
0
    assert(node->isArray() && type.isArray());
8692
0
    if (node->getType().computeNumComponents() < type.computeNumComponents())
8693
0
        return nullptr;
8694
8695
    // TODO: write an argument replicator, for the case the argument should not be
8696
    // executed multiple times, yet multiple copies are needed.
8697
8698
0
    TIntermTyped* constructee = node->getAsTyped();
8699
    // track where we are in consuming the argument
8700
0
    int constructeeElement = 0;
8701
0
    int constructeeComponent = 0;
8702
8703
    // bump up to the next component to consume
8704
0
    const auto getNextComponent = [&]() {
8705
0
        TIntermTyped* component;
8706
0
        component = handleBracketDereference(node->getLoc(), constructee,
8707
0
                                             intermediate.addConstantUnion(constructeeElement, node->getLoc()));
8708
0
        if (component->isVector())
8709
0
            component = handleBracketDereference(node->getLoc(), component,
8710
0
                                                 intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
8711
        // bump component pointer up
8712
0
        ++constructeeComponent;
8713
0
        if (constructeeComponent == constructee->getVectorSize()) {
8714
0
            constructeeComponent = 0;
8715
0
            ++constructeeElement;
8716
0
        }
8717
0
        return component;
8718
0
    };
8719
8720
    // make one subnode per constructed array element
8721
0
    TIntermAggregate* constructor = nullptr;
8722
0
    TType derefType(type, 0);
8723
0
    TType speculativeComponentType(derefType, 0);
8724
0
    TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
8725
0
    TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
8726
0
    TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
8727
0
    for (int e = 0; e < type.getOuterArraySize(); ++e) {
8728
        // construct an element
8729
0
        TIntermTyped* elementArg;
8730
0
        if (type.getVectorSize() == constructee->getVectorSize()) {
8731
            // same element shape
8732
0
            elementArg = handleBracketDereference(node->getLoc(), constructee,
8733
0
                                                  intermediate.addConstantUnion(e, node->getLoc()));
8734
0
        } else {
8735
            // mismatched element shapes
8736
0
            if (type.getVectorSize() == 1)
8737
0
                elementArg = getNextComponent();
8738
0
            else {
8739
                // make a vector
8740
0
                TIntermAggregate* elementConstructee = nullptr;
8741
0
                for (int c = 0; c < type.getVectorSize(); ++c)
8742
0
                    elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
8743
0
                elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
8744
0
            }
8745
0
        }
8746
        // convert basic types
8747
0
        elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
8748
0
        if (elementArg == nullptr)
8749
0
            return nullptr;
8750
        // combine with top-level constructor
8751
0
        constructor = intermediate.growAggregate(constructor, elementArg);
8752
0
    }
8753
8754
0
    return constructor;
8755
0
}
8756
8757
// This function tests for the type of the parameters to the structure or array constructor. Raises
8758
// an error message if the expected type does not match the parameter passed to the constructor.
8759
//
8760
// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
8761
//
8762
TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
8763
                                                   const TSourceLoc& loc)
8764
506
{
8765
    // Handle cases that map more 1:1 between constructor arguments and constructed.
8766
506
    TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
8767
506
    if (converted == nullptr || converted->getType() != type) {
8768
4
        error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
8769
4
            node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
8770
8771
4
        return nullptr;
8772
4
    }
8773
8774
502
    return converted;
8775
506
}
8776
8777
//
8778
// Do everything needed to add an interface block.
8779
//
8780
void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName)
8781
736
{
8782
736
    assert(type.getWritableStruct() != nullptr);
8783
8784
    // Clean up top-level decorations that don't belong.
8785
736
    switch (type.getQualifier().storage) {
8786
168
    case EvqUniform:
8787
736
    case EvqBuffer:
8788
736
        correctUniform(type.getQualifier());
8789
736
        break;
8790
0
    case EvqVaryingIn:
8791
0
        correctInput(type.getQualifier());
8792
0
        break;
8793
0
    case EvqVaryingOut:
8794
0
        correctOutput(type.getQualifier());
8795
0
        break;
8796
0
    default:
8797
0
        break;
8798
736
    }
8799
8800
736
    TTypeList& typeList = *type.getWritableStruct();
8801
    // fix and check for member storage qualifiers and types that don't belong within a block
8802
1.61k
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8803
878
        TType& memberType = *typeList[member].type;
8804
878
        TQualifier& memberQualifier = memberType.getQualifier();
8805
878
        const TSourceLoc& memberLoc = typeList[member].loc;
8806
878
        globalQualifierFix(memberLoc, memberQualifier);
8807
878
        memberQualifier.storage = type.getQualifier().storage;
8808
8809
878
        if (memberType.isStruct()) {
8810
            // clean up and pick up the right set of decorations
8811
136
            auto it = ioTypeMap.find(memberType.getStruct());
8812
136
            switch (type.getQualifier().storage) {
8813
34
            case EvqUniform:
8814
136
            case EvqBuffer:
8815
136
                correctUniform(type.getQualifier());
8816
136
                if (it != ioTypeMap.end() && it->second.uniform)
8817
8
                    memberType.setStruct(it->second.uniform);
8818
136
                break;
8819
0
            case EvqVaryingIn:
8820
0
                correctInput(type.getQualifier());
8821
0
                if (it != ioTypeMap.end() && it->second.input)
8822
0
                    memberType.setStruct(it->second.input);
8823
0
                break;
8824
0
            case EvqVaryingOut:
8825
0
                correctOutput(type.getQualifier());
8826
0
                if (it != ioTypeMap.end() && it->second.output)
8827
0
                    memberType.setStruct(it->second.output);
8828
0
                break;
8829
0
            default:
8830
0
                break;
8831
136
            }
8832
136
        }
8833
878
    }
8834
8835
    // Make default block qualification, and adjust the member qualifications
8836
8837
736
    TQualifier defaultQualification;
8838
736
    switch (type.getQualifier().storage) {
8839
168
    case EvqUniform:    defaultQualification = globalUniformDefaults;    break;
8840
568
    case EvqBuffer:     defaultQualification = globalBufferDefaults;     break;
8841
0
    case EvqVaryingIn:  defaultQualification = globalInputDefaults;      break;
8842
0
    case EvqVaryingOut: defaultQualification = globalOutputDefaults;     break;
8843
0
    default:            defaultQualification.clear();                    break;
8844
736
    }
8845
8846
    // Special case for "push_constant uniform", which has a default of std430,
8847
    // contrary to normal uniform defaults, and can't have a default tracked for it.
8848
736
    if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
8849
60
        type.getQualifier().layoutPacking = ElpStd430;
8850
8851
    // fix and check for member layout qualifiers
8852
8853
736
    mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
8854
8855
736
    bool memberWithLocation = false;
8856
736
    bool memberWithoutLocation = false;
8857
1.61k
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8858
878
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
8859
878
        const TSourceLoc& memberLoc = typeList[member].loc;
8860
878
        if (memberQualifier.hasStream()) {
8861
0
            if (defaultQualification.layoutStream != memberQualifier.layoutStream)
8862
0
                error(memberLoc, "member cannot contradict block", "stream", "");
8863
0
        }
8864
8865
        // "This includes a block's inheritance of the
8866
        // current global default buffer, a block member's inheritance of the block's
8867
        // buffer, and the requirement that any *xfb_buffer* declared on a block
8868
        // member must match the buffer inherited from the block."
8869
878
        if (memberQualifier.hasXfbBuffer()) {
8870
0
            if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
8871
0
                error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
8872
0
        }
8873
8874
878
        if (memberQualifier.hasLocation()) {
8875
0
            switch (type.getQualifier().storage) {
8876
0
            case EvqVaryingIn:
8877
0
            case EvqVaryingOut:
8878
0
                memberWithLocation = true;
8879
0
                break;
8880
0
            default:
8881
0
                break;
8882
0
            }
8883
0
        } else
8884
878
            memberWithoutLocation = true;
8885
8886
878
        TQualifier newMemberQualification = defaultQualification;
8887
878
        mergeQualifiers(newMemberQualification, memberQualifier);
8888
878
        memberQualifier = newMemberQualification;
8889
878
    }
8890
8891
    // Process the members
8892
736
    fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
8893
736
    fixXfbOffsets(type.getQualifier(), typeList);
8894
736
    fixBlockUniformOffsets(type.getQualifier(), typeList);
8895
8896
    // reverse merge, so that currentBlockQualifier now has all layout information
8897
    // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
8898
736
    mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
8899
8900
    //
8901
    // Build and add the interface block as a new type named 'blockName'
8902
    //
8903
8904
    // Use the instance name as the interface name if one exists, else the block name.
8905
736
    const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
8906
8907
736
    TType blockType(&typeList, interfaceName, type.getQualifier());
8908
736
    if (type.isArray())
8909
14
        blockType.transferArraySizes(type.getArraySizes());
8910
8911
    // Add the variable, as anonymous or named instanceName.
8912
    // Make an anonymous variable if no name was provided.
8913
736
    if (instanceName == nullptr)
8914
166
        instanceName = NewPoolTString("");
8915
8916
736
    TVariable& variable = *new TVariable(instanceName, blockType);
8917
736
    if (! symbolTable.insert(variable)) {
8918
10
        if (*instanceName == "")
8919
4
            error(loc, "nameless block contains a member that already has a name at global scope",
8920
4
                  "" /* blockName->c_str() */, "");
8921
6
        else
8922
6
            error(loc, "block instance name redefinition", variable.getName().c_str(), "");
8923
8924
10
        return;
8925
10
    }
8926
8927
    // Save it in the AST for linker use.
8928
726
    if (symbolTable.atGlobalLevel())
8929
712
        trackLinkage(variable);
8930
726
}
8931
8932
//
8933
// "For a block, this process applies to the entire block, or until the first member
8934
// is reached that has a location layout qualifier. When a block member is declared with a location
8935
// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
8936
// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
8937
// until the next member declared with a location qualifier. The values used for locations do not have to be
8938
// declared in increasing order."
8939
void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
8940
736
{
8941
    // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
8942
    // have a location layout qualifier, or a compile-time error results."
8943
736
    if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
8944
0
        error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
8945
736
    else {
8946
736
        if (memberWithLocation) {
8947
            // remove any block-level location and make it per *every* member
8948
0
            int nextLocation = 0;  // by the rule above, initial value is not relevant
8949
0
            if (qualifier.hasAnyLocation()) {
8950
0
                nextLocation = qualifier.layoutLocation;
8951
0
                qualifier.layoutLocation = TQualifier::layoutLocationEnd;
8952
0
                if (qualifier.hasComponent()) {
8953
                    // "It is a compile-time error to apply the *component* qualifier to a ... block"
8954
0
                    error(loc, "cannot apply to a block", "component", "");
8955
0
                }
8956
0
                if (qualifier.hasIndex()) {
8957
0
                    error(loc, "cannot apply to a block", "index", "");
8958
0
                }
8959
0
            }
8960
0
            for (unsigned int member = 0; member < typeList.size(); ++member) {
8961
0
                TQualifier& memberQualifier = typeList[member].type->getQualifier();
8962
0
                const TSourceLoc& memberLoc = typeList[member].loc;
8963
0
                if (! memberQualifier.hasLocation()) {
8964
0
                    if (nextLocation >= (int)TQualifier::layoutLocationEnd)
8965
0
                        error(memberLoc, "location is too large", "location", "");
8966
0
                    memberQualifier.layoutLocation = nextLocation;
8967
0
                    memberQualifier.layoutComponent = 0;
8968
0
                }
8969
0
                nextLocation = memberQualifier.layoutLocation +
8970
0
                               intermediate.computeTypeLocationSize(*typeList[member].type, language);
8971
0
            }
8972
0
        }
8973
736
    }
8974
736
}
8975
8976
void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
8977
736
{
8978
    // "If a block is qualified with xfb_offset, all its
8979
    // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
8980
    // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
8981
    // offsets."
8982
8983
736
    if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
8984
736
        return;
8985
8986
0
    int nextOffset = qualifier.layoutXfbOffset;
8987
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8988
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
8989
0
        bool contains64BitType = false;
8990
0
        bool contains32BitType = false;
8991
0
        bool contains16BitType = false;
8992
0
        int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType);
8993
        // see if we need to auto-assign an offset to this member
8994
0
        if (! memberQualifier.hasXfbOffset()) {
8995
            // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8"
8996
0
            if (contains64BitType)
8997
0
                RoundToPow2(nextOffset, 8);
8998
0
            else if (contains32BitType)
8999
0
                RoundToPow2(nextOffset, 4);
9000
            // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2"
9001
0
            else if (contains16BitType)
9002
0
                RoundToPow2(nextOffset, 2);
9003
0
            memberQualifier.layoutXfbOffset = nextOffset;
9004
0
        } else
9005
0
            nextOffset = memberQualifier.layoutXfbOffset;
9006
0
        nextOffset += memberSize;
9007
0
    }
9008
9009
    // The above gave all block members an offset, so we can take it off the block now,
9010
    // which will avoid double counting the offset usage.
9011
0
    qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
9012
0
}
9013
9014
// Calculate and save the offset of each block member, using the recursively
9015
// defined block offset rules and the user-provided offset and align.
9016
//
9017
// Also, compute and save the total size of the block. For the block's size, arrayness
9018
// is not taken into account, as each element is backed by a separate buffer.
9019
//
9020
void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
9021
736
{
9022
736
    if (! qualifier.isUniformOrBuffer())
9023
0
        return;
9024
736
    if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
9025
676
        return;
9026
9027
60
    int offset = 0;
9028
60
    int memberSize;
9029
178
    for (unsigned int member = 0; member < typeList.size(); ++member) {
9030
118
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
9031
118
        const TSourceLoc& memberLoc = typeList[member].loc;
9032
9033
        // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
9034
9035
        // modify just the children's view of matrix layout, if there is one for this member
9036
118
        TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
9037
118
        int dummyStride;
9038
118
        int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride,
9039
118
                                                              qualifier.layoutPacking,
9040
118
                                                              subMatrixLayout != ElmNone
9041
118
                                                                  ? subMatrixLayout == ElmRowMajor
9042
118
                                                                  : qualifier.layoutMatrix == ElmRowMajor);
9043
118
        if (memberQualifier.hasOffset()) {
9044
            // "The specified offset must be a multiple
9045
            // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
9046
30
            if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
9047
0
                error(memberLoc, "must be a multiple of the member's alignment", "offset",
9048
0
                    "(layout offset = %d | member alignment = %d)", memberQualifier.layoutOffset, memberAlignment);
9049
9050
            // "The offset qualifier forces the qualified member to start at or after the specified
9051
            // integral-constant expression, which will be its byte offset from the beginning of the buffer.
9052
            // "The actual offset of a member is computed as
9053
            // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
9054
30
            offset = std::max(offset, memberQualifier.layoutOffset);
9055
30
        }
9056
9057
        // "The actual alignment of a member will be the greater of the specified align alignment and the standard
9058
        // (e.g., std140) base alignment for the member's type."
9059
118
        if (memberQualifier.hasAlign())
9060
0
            memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
9061
9062
        // "If the resulting offset is not a multiple of the actual alignment,
9063
        // increase it to the first offset that is a multiple of
9064
        // the actual alignment."
9065
118
        RoundToPow2(offset, memberAlignment);
9066
118
        typeList[member].type->getQualifier().layoutOffset = offset;
9067
118
        offset += memberSize;
9068
118
    }
9069
60
}
9070
9071
// For an identifier that is already declared, add more qualification to it.
9072
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
9073
0
{
9074
0
    TSymbol* symbol = symbolTable.find(identifier);
9075
0
    if (symbol == nullptr) {
9076
0
        error(loc, "identifier not previously declared", identifier.c_str(), "");
9077
0
        return;
9078
0
    }
9079
0
    if (symbol->getAsFunction()) {
9080
0
        error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
9081
0
        return;
9082
0
    }
9083
9084
0
    if (qualifier.isAuxiliary() ||
9085
0
        qualifier.isMemory() ||
9086
0
        qualifier.isInterpolation() ||
9087
0
        qualifier.hasLayout() ||
9088
0
        qualifier.storage != EvqTemporary ||
9089
0
        qualifier.precision != EpqNone) {
9090
0
        error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
9091
0
        return;
9092
0
    }
9093
9094
    // For read-only built-ins, add a new symbol for holding the modified qualifier.
9095
    // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
9096
0
    if (symbol->isReadOnly())
9097
0
        symbol = symbolTable.copyUp(symbol);
9098
9099
0
    if (qualifier.invariant) {
9100
0
        if (intermediate.inIoAccessed(identifier))
9101
0
            error(loc, "cannot change qualification after use", "invariant", "");
9102
0
        symbol->getWritableType().getQualifier().invariant = true;
9103
0
    } else if (qualifier.noContraction) {
9104
0
        if (intermediate.inIoAccessed(identifier))
9105
0
            error(loc, "cannot change qualification after use", "precise", "");
9106
0
        symbol->getWritableType().getQualifier().noContraction = true;
9107
0
    } else if (qualifier.specConstant) {
9108
0
        symbol->getWritableType().getQualifier().makeSpecConstant();
9109
0
        if (qualifier.hasSpecConstantId())
9110
0
            symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
9111
0
    } else
9112
0
        warn(loc, "unknown requalification", "", "");
9113
0
}
9114
9115
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
9116
0
{
9117
0
    for (unsigned int i = 0; i < identifiers.size(); ++i)
9118
0
        addQualifierToExisting(loc, qualifier, *identifiers[i]);
9119
0
}
9120
9121
//
9122
// Update the intermediate for the given input geometry
9123
//
9124
bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9125
14
{
9126
    // these can be declared on non-entry-points, in which case they lose their meaning
9127
14
    if (! parsingEntrypointParameters)
9128
14
        return true;
9129
9130
0
    switch (geometry) {
9131
0
    case ElgPoints:             // fall through
9132
0
    case ElgLines:              // ...
9133
0
    case ElgTriangles:          // ...
9134
0
    case ElgLinesAdjacency:     // ...
9135
0
    case ElgTrianglesAdjacency: // ...
9136
0
        if (! intermediate.setInputPrimitive(geometry)) {
9137
0
            error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9138
0
            return false;
9139
0
        }
9140
0
        break;
9141
9142
0
    default:
9143
0
        error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
9144
0
        return false;
9145
0
    }
9146
9147
0
    return true;
9148
0
}
9149
9150
//
9151
// Update the intermediate for the given output geometry
9152
//
9153
bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9154
12
{
9155
    // If this is not a geometry shader, ignore.  It might be a mixed shader including several stages.
9156
    // Since that's an OK situation, return true for success.
9157
12
    if (language != EShLangGeometry)
9158
12
        return true;
9159
9160
    // these can be declared on non-entry-points, in which case they lose their meaning
9161
0
    if (! parsingEntrypointParameters)
9162
0
        return true;
9163
9164
0
    switch (geometry) {
9165
0
    case ElgPoints:
9166
0
    case ElgLineStrip:
9167
0
    case ElgTriangleStrip:
9168
0
        if (! intermediate.setOutputPrimitive(geometry)) {
9169
0
            error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9170
0
            return false;
9171
0
        }
9172
0
        break;
9173
0
    default:
9174
0
        error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
9175
0
        return false;
9176
0
    }
9177
9178
0
    return true;
9179
0
}
9180
9181
//
9182
// Selection attributes
9183
//
9184
void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
9185
    const TAttributes& attributes)
9186
78
{
9187
78
    if (selection == nullptr)
9188
0
        return;
9189
9190
78
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9191
0
        switch (it->name) {
9192
0
        case EatFlatten:
9193
0
            selection->setFlatten();
9194
0
            break;
9195
0
        case EatBranch:
9196
0
            selection->setDontFlatten();
9197
0
            break;
9198
0
        default:
9199
0
            warn(loc, "attribute does not apply to a selection", "", "");
9200
0
            break;
9201
0
        }
9202
0
    }
9203
78
}
9204
9205
//
9206
// Switch attributes
9207
//
9208
void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
9209
    const TAttributes& attributes)
9210
30
{
9211
30
    if (selection == nullptr)
9212
0
        return;
9213
9214
30
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9215
0
        switch (it->name) {
9216
0
        case EatFlatten:
9217
0
            selection->setFlatten();
9218
0
            break;
9219
0
        case EatBranch:
9220
0
            selection->setDontFlatten();
9221
0
            break;
9222
0
        default:
9223
0
            warn(loc, "attribute does not apply to a switch", "", "");
9224
0
            break;
9225
0
        }
9226
0
    }
9227
30
}
9228
9229
//
9230
// Loop attributes
9231
//
9232
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
9233
    const TAttributes& attributes)
9234
130
{
9235
130
    if (loop == nullptr)
9236
0
        return;
9237
9238
150
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9239
20
        switch (it->name) {
9240
12
        case EatUnroll:
9241
12
            loop->setUnroll();
9242
12
            break;
9243
8
        case EatLoop:
9244
8
            loop->setDontUnroll();
9245
8
            break;
9246
0
        default:
9247
0
            warn(loc, "attribute does not apply to a loop", "", "");
9248
0
            break;
9249
20
        }
9250
20
    }
9251
130
}
9252
9253
//
9254
// Updating default qualifier for the case of a declaration with just a qualifier,
9255
// no type, block, or identifier.
9256
//
9257
void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
9258
0
{
9259
0
    if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
9260
0
        assert(language == EShLangTessControl || language == EShLangGeometry);
9261
        // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
9262
0
    }
9263
0
    if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
9264
0
        if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
9265
0
            error(loc, "cannot change previously set layout value", "invocations", "");
9266
0
    }
9267
0
    if (publicType.shaderQualifiers.geometry != ElgNone) {
9268
0
        if (publicType.qualifier.storage == EvqVaryingIn) {
9269
0
            switch (publicType.shaderQualifiers.geometry) {
9270
0
            case ElgPoints:
9271
0
            case ElgLines:
9272
0
            case ElgLinesAdjacency:
9273
0
            case ElgTriangles:
9274
0
            case ElgTrianglesAdjacency:
9275
0
            case ElgQuads:
9276
0
            case ElgIsolines:
9277
0
                break;
9278
0
            default:
9279
0
                error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9280
0
                      "");
9281
0
            }
9282
0
        } else if (publicType.qualifier.storage == EvqVaryingOut) {
9283
0
            handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
9284
0
        } else
9285
0
            error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9286
0
                  GetStorageQualifierString(publicType.qualifier.storage));
9287
0
    }
9288
0
    if (publicType.shaderQualifiers.spacing != EvsNone)
9289
0
        intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
9290
0
    if (publicType.shaderQualifiers.order != EvoNone)
9291
0
        intermediate.setVertexOrder(publicType.shaderQualifiers.order);
9292
0
    if (publicType.shaderQualifiers.pointMode)
9293
0
        intermediate.setPointMode();
9294
0
    for (int i = 0; i < 3; ++i) {
9295
0
        if (publicType.shaderQualifiers.localSize[i] > 1) {
9296
0
            int max = 0;
9297
0
            switch (i) {
9298
0
            case 0: max = resources.maxComputeWorkGroupSizeX; break;
9299
0
            case 1: max = resources.maxComputeWorkGroupSizeY; break;
9300
0
            case 2: max = resources.maxComputeWorkGroupSizeZ; break;
9301
0
            default: break;
9302
0
            }
9303
0
            if (intermediate.getLocalSize(i) > (unsigned int)max)
9304
0
                error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
9305
9306
            // Fix the existing constant gl_WorkGroupSize with this new information.
9307
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9308
0
            workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
9309
0
        }
9310
0
        if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
9311
0
            intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
9312
            // Set the workgroup built-in variable as a specialization constant
9313
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9314
0
            workGroupSize->getWritableType().getQualifier().specConstant = true;
9315
0
        }
9316
0
    }
9317
0
    if (publicType.shaderQualifiers.earlyFragmentTests)
9318
0
        intermediate.setEarlyFragmentTests();
9319
9320
0
    const TQualifier& qualifier = publicType.qualifier;
9321
9322
0
    switch (qualifier.storage) {
9323
0
    case EvqUniform:
9324
0
        if (qualifier.hasMatrix())
9325
0
            globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
9326
0
        if (qualifier.hasPacking())
9327
0
            globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
9328
0
        break;
9329
0
    case EvqBuffer:
9330
0
        if (qualifier.hasMatrix())
9331
0
            globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
9332
0
        if (qualifier.hasPacking())
9333
0
            globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
9334
0
        break;
9335
0
    case EvqVaryingIn:
9336
0
        break;
9337
0
    case EvqVaryingOut:
9338
0
        if (qualifier.hasStream())
9339
0
            globalOutputDefaults.layoutStream = qualifier.layoutStream;
9340
0
        if (qualifier.hasXfbBuffer())
9341
0
            globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
9342
0
        if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
9343
0
            if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
9344
0
                error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d",
9345
0
                      qualifier.layoutXfbBuffer);
9346
0
        }
9347
0
        break;
9348
0
    default:
9349
0
        error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
9350
0
        return;
9351
0
    }
9352
0
}
9353
9354
//
9355
// Take the sequence of statements that has been built up since the last case/default,
9356
// put it on the list of top-level nodes for the current (inner-most) switch statement,
9357
// and follow that by the case/default we are on now.  (See switch topology comment on
9358
// TIntermSwitch.)
9359
//
9360
void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
9361
158
{
9362
158
    TIntermSequence* switchSequence = switchSequenceStack.back();
9363
9364
158
    if (statements) {
9365
62
        statements->setOperator(EOpSequence);
9366
62
        switchSequence->push_back(statements);
9367
62
    }
9368
158
    if (branchNode) {
9369
        // check all previous cases for the same label (or both are 'default')
9370
306
        for (unsigned int s = 0; s < switchSequence->size(); ++s) {
9371
178
            TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
9372
178
            if (prevBranch) {
9373
104
                TIntermTyped* prevExpression = prevBranch->getExpression();
9374
104
                TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
9375
104
                if (prevExpression == nullptr && newExpression == nullptr)
9376
0
                    error(branchNode->getLoc(), "duplicate label", "default", "");
9377
104
                else if (prevExpression != nullptr &&
9378
104
                    newExpression != nullptr &&
9379
104
                    prevExpression->getAsConstantUnion() &&
9380
104
                    newExpression->getAsConstantUnion() &&
9381
104
                    prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
9382
104
                    newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
9383
6
                    error(branchNode->getLoc(), "duplicated value", "case", "");
9384
104
            }
9385
178
        }
9386
128
        switchSequence->push_back(branchNode);
9387
128
    }
9388
158
}
9389
9390
//
9391
// Turn the top-level node sequence built up of wrapupSwitchSubsequence
9392
// into a switch node.
9393
//
9394
TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
9395
                                         TIntermAggregate* lastStatements, const TAttributes& attributes)
9396
30
{
9397
30
    wrapupSwitchSubsequence(lastStatements, nullptr);
9398
9399
30
    if (expression == nullptr ||
9400
30
        (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
9401
28
        expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
9402
2
        error(loc, "condition must be a scalar integer expression", "switch", "");
9403
9404
    // If there is nothing to do, drop the switch but still execute the expression
9405
30
    TIntermSequence* switchSequence = switchSequenceStack.back();
9406
30
    if (switchSequence->size() == 0)
9407
0
        return expression;
9408
9409
30
    if (lastStatements == nullptr) {
9410
        // emulate a break for error recovery
9411
0
        lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
9412
0
        lastStatements->setOperator(EOpSequence);
9413
0
        switchSequence->push_back(lastStatements);
9414
0
    }
9415
9416
30
    TIntermAggregate* body = new TIntermAggregate(EOpSequence);
9417
30
    body->getSequence() = *switchSequenceStack.back();
9418
30
    body->setLoc(loc);
9419
9420
30
    TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
9421
30
    switchNode->setLoc(loc);
9422
30
    handleSwitchAttributes(loc, switchNode, attributes);
9423
9424
30
    return switchNode;
9425
30
}
9426
9427
// Make a new symbol-table level that is made out of the members of a structure.
9428
// This should be done as an anonymous struct (name is "") so that the symbol table
9429
// finds the members with no explicit reference to a 'this' variable.
9430
void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators)
9431
916
{
9432
    // member variables
9433
916
    TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
9434
916
    symbolTable.pushThis(thisVariable);
9435
9436
    // member functions
9437
956
    for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) {
9438
        // member should have a prefix matching currentTypePrefix.back()
9439
        // but, symbol lookup within the class scope will just use the
9440
        // unprefixed name. Hence, there are two: one fully prefixed and
9441
        // one with no prefix.
9442
40
        TFunction& member = *it->function->clone();
9443
40
        member.removePrefix(currentTypePrefix.back());
9444
40
        symbolTable.insert(member);
9445
40
    }
9446
916
}
9447
9448
// Track levels of class/struct/namespace nesting with a prefix string using
9449
// the type names separated by the scoping operator. E.g., two levels
9450
// would look like:
9451
//
9452
//   outer::inner
9453
//
9454
// The string is empty when at normal global level.
9455
//
9456
void HlslParseContext::pushNamespace(const TString& typeName)
9457
1.92k
{
9458
    // make new type prefix
9459
1.92k
    TString newPrefix;
9460
1.92k
    if (currentTypePrefix.size() > 0)
9461
10
        newPrefix = currentTypePrefix.back();
9462
1.92k
    newPrefix.append(typeName);
9463
1.92k
    newPrefix.append(scopeMangler);
9464
1.92k
    currentTypePrefix.push_back(newPrefix);
9465
1.92k
}
9466
9467
// Opposite of pushNamespace(), see above
9468
void HlslParseContext::popNamespace()
9469
1.92k
{
9470
1.92k
    currentTypePrefix.pop_back();
9471
1.92k
}
9472
9473
// Use the class/struct nesting string to create a global name for
9474
// a member of a class/struct.
9475
void HlslParseContext::getFullNamespaceName(TString*& name) const
9476
6.13M
{
9477
6.13M
    if (currentTypePrefix.size() == 0)
9478
6.13M
        return;
9479
9480
128
    TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
9481
128
    fullName->append(*name);
9482
128
    name = fullName;
9483
128
}
9484
9485
// Helper function to add the namespace scope mangling syntax to a string.
9486
void HlslParseContext::addScopeMangler(TString& name)
9487
40
{
9488
40
    name.append(scopeMangler);
9489
40
}
9490
9491
// Return true if this has uniform-interface like decorations.
9492
bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
9493
1.23k
{
9494
1.23k
    return qualifier.hasUniformLayout() ||
9495
1.19k
           qualifier.layoutPushConstant;
9496
1.23k
}
9497
9498
// Potentially not the opposite of hasUniform(), as if some characteristic is
9499
// ever used for more than one thing (e.g., uniform or input), hasUniform() should
9500
// say it exists, but clearUniform() should leave it in place.
9501
void HlslParseContext::clearUniform(TQualifier& qualifier)
9502
27.9k
{
9503
27.9k
    qualifier.clearUniformLayout();
9504
27.9k
    qualifier.layoutPushConstant = false;
9505
27.9k
}
9506
9507
// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
9508
bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
9509
1.42k
{
9510
1.42k
    switch (qualifier.builtIn) {
9511
48
    case EbvPosition:
9512
48
    case EbvPointSize:
9513
48
        return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
9514
18
    case EbvClipDistance:
9515
28
    case EbvCullDistance:
9516
28
        return language != EShLangVertex && language != EShLangCompute;
9517
4
    case EbvFragCoord:
9518
4
    case EbvFace:
9519
4
    case EbvHelperInvocation:
9520
4
    case EbvLayer:
9521
4
    case EbvPointCoord:
9522
4
    case EbvSampleId:
9523
6
    case EbvSampleMask:
9524
6
    case EbvSamplePosition:
9525
6
    case EbvViewportIndex:
9526
6
        return language == EShLangFragment;
9527
0
    case EbvGlobalInvocationId:
9528
0
    case EbvLocalInvocationIndex:
9529
0
    case EbvLocalInvocationId:
9530
0
    case EbvNumWorkGroups:
9531
0
    case EbvWorkGroupId:
9532
0
    case EbvWorkGroupSize:
9533
0
        return language == EShLangCompute;
9534
0
    case EbvInvocationId:
9535
0
        return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
9536
0
    case EbvPatchVertices:
9537
0
        return language == EShLangTessControl || language == EShLangTessEvaluation;
9538
0
    case EbvInstanceId:
9539
0
    case EbvInstanceIndex:
9540
0
    case EbvVertexId:
9541
0
    case EbvVertexIndex:
9542
0
        return language == EShLangVertex;
9543
0
    case EbvPrimitiveId:
9544
0
        return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl;
9545
0
    case EbvTessLevelInner:
9546
0
    case EbvTessLevelOuter:
9547
0
        return language == EShLangTessEvaluation;
9548
0
    case EbvTessCoord:
9549
0
        return language == EShLangTessEvaluation;
9550
0
    case EbvViewIndex:
9551
0
        return language != EShLangCompute;
9552
1.34k
    default:
9553
1.34k
        return false;
9554
1.42k
    }
9555
1.42k
}
9556
9557
// Return true if there are decorations to preserve for input-like storage.
9558
bool HlslParseContext::hasInput(const TQualifier& qualifier) const
9559
1.23k
{
9560
1.23k
    if (qualifier.hasAnyLocation())
9561
6
        return true;
9562
9563
1.22k
    if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
9564
0
        return true;
9565
9566
1.22k
    if (language == EShLangTessEvaluation && qualifier.patch)
9567
0
        return true;
9568
9569
1.22k
    if (isInputBuiltIn(qualifier))
9570
16
        return true;
9571
9572
1.20k
    return false;
9573
1.22k
}
9574
9575
// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
9576
bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
9577
2.50k
{
9578
2.50k
    switch (qualifier.builtIn) {
9579
28
    case EbvPosition:
9580
28
    case EbvPointSize:
9581
28
    case EbvClipVertex:
9582
28
    case EbvClipDistance:
9583
28
    case EbvCullDistance:
9584
28
        return language != EShLangFragment && language != EShLangCompute;
9585
80
    case EbvFragDepth:
9586
80
    case EbvFragDepthGreater:
9587
80
    case EbvFragDepthLesser:
9588
82
    case EbvSampleMask:
9589
82
        return language == EShLangFragment;
9590
0
    case EbvLayer:
9591
0
    case EbvViewportIndex:
9592
0
        return language == EShLangGeometry || language == EShLangVertex;
9593
0
    case EbvPrimitiveId:
9594
0
        return language == EShLangGeometry;
9595
0
    case EbvTessLevelInner:
9596
0
    case EbvTessLevelOuter:
9597
0
        return language == EShLangTessControl;
9598
2.39k
    default:
9599
2.39k
        return false;
9600
2.50k
    }
9601
2.50k
}
9602
9603
// Return true if there are decorations to preserve for output-like storage.
9604
bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
9605
1.23k
{
9606
1.23k
    if (qualifier.hasAnyLocation())
9607
6
        return true;
9608
9609
1.22k
    if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
9610
0
        return true;
9611
9612
1.22k
    if (language == EShLangTessControl && qualifier.patch)
9613
0
        return true;
9614
9615
1.22k
    if (language == EShLangGeometry && qualifier.hasStream())
9616
0
        return true;
9617
9618
1.22k
    if (isOutputBuiltIn(qualifier))
9619
14
        return true;
9620
9621
1.21k
    return false;
9622
1.22k
}
9623
9624
// Make the IO decorations etc. be appropriate only for an input interface.
9625
void HlslParseContext::correctInput(TQualifier& qualifier)
9626
202
{
9627
202
    clearUniform(qualifier);
9628
202
    if (language == EShLangVertex)
9629
2
        qualifier.clearInterstage();
9630
202
    if (language != EShLangTessEvaluation)
9631
200
        qualifier.patch = false;
9632
202
    if (language != EShLangFragment) {
9633
192
        qualifier.clearInterpolation();
9634
192
        qualifier.sample = false;
9635
192
    }
9636
9637
202
    qualifier.clearStreamLayout();
9638
202
    qualifier.clearXfbLayout();
9639
9640
202
    if (! isInputBuiltIn(qualifier))
9641
144
        qualifier.builtIn = EbvNone;
9642
202
}
9643
9644
// Make the IO decorations etc. be appropriate only for an output interface.
9645
void HlslParseContext::correctOutput(TQualifier& qualifier)
9646
1.27k
{
9647
1.27k
    clearUniform(qualifier);
9648
1.27k
    if (language == EShLangFragment)
9649
24
        qualifier.clearInterstage();
9650
1.27k
    if (language != EShLangGeometry)
9651
1.27k
        qualifier.clearStreamLayout();
9652
1.27k
    if (language == EShLangFragment)
9653
24
        qualifier.clearXfbLayout();
9654
1.27k
    if (language != EShLangTessControl)
9655
346
        qualifier.patch = false;
9656
9657
    // Fixes Test/hlsl.entry-inout.vert (SV_Position will not become a varying).
9658
1.27k
    if (qualifier.builtIn == EbvNone)
9659
1.26k
        qualifier.builtIn = qualifier.declaredBuiltIn;
9660
9661
1.27k
    switch (qualifier.builtIn) {
9662
0
    case EbvFragDepth:
9663
0
        intermediate.setDepthReplacing();
9664
0
        intermediate.setDepth(EldAny);
9665
0
        break;
9666
0
    case EbvFragDepthGreater:
9667
0
        intermediate.setDepthReplacing();
9668
0
        intermediate.setDepth(EldGreater);
9669
0
        qualifier.builtIn = EbvFragDepth;
9670
0
        break;
9671
0
    case EbvFragDepthLesser:
9672
0
        intermediate.setDepthReplacing();
9673
0
        intermediate.setDepth(EldLess);
9674
0
        qualifier.builtIn = EbvFragDepth;
9675
0
        break;
9676
1.27k
    default:
9677
1.27k
        break;
9678
1.27k
    }
9679
9680
1.27k
    if (! isOutputBuiltIn(qualifier))
9681
1.26k
        qualifier.builtIn = EbvNone;
9682
1.27k
}
9683
9684
// Make the IO decorations etc. be appropriate only for uniform type interfaces.
9685
void HlslParseContext::correctUniform(TQualifier& qualifier)
9686
34.4k
{
9687
34.4k
    if (qualifier.declaredBuiltIn == EbvNone)
9688
34.3k
        qualifier.declaredBuiltIn = qualifier.builtIn;
9689
9690
34.4k
    qualifier.builtIn = EbvNone;
9691
34.4k
    qualifier.clearInterstage();
9692
34.4k
    qualifier.clearInterstageLayout();
9693
34.4k
}
9694
9695
// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
9696
void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
9697
26.4k
{
9698
26.4k
    clearUniform(qualifier);
9699
26.4k
    correctUniform(qualifier);
9700
26.4k
}
9701
9702
9703
// Set texture return type.  Returns success (not all types are valid).
9704
bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc)
9705
1.24M
{
9706
    // Seed the output with an invalid index.  We will set it to a valid one if we can.
9707
1.24M
    sampler.structReturnIndex = TSampler::noReturnStruct;
9708
9709
    // Arrays aren't supported.
9710
1.24M
    if (retType.isArray()) {
9711
0
        error(loc, "Arrays not supported in texture template types", "", "");
9712
0
        return false;
9713
0
    }
9714
9715
    // If return type is a vector, remember the vector size in the sampler, and return.
9716
1.24M
    if (retType.isVector() || retType.isScalar()) {
9717
1.24M
        sampler.vectorSize = retType.getVectorSize();
9718
1.24M
        return true;
9719
1.24M
    }
9720
9721
    // If it wasn't a vector, it must be a struct meeting certain requirements.  The requirements
9722
    // are checked below: just check for struct-ness here.
9723
2
    if (!retType.isStruct()) {
9724
0
        error(loc, "Invalid texture template type", "", "");
9725
0
        return false;
9726
0
    }
9727
9728
    // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
9729
2
    if (sampler.isSubpass()) {
9730
0
        error(loc, "Unimplemented: structure template type in subpass input", "", "");
9731
0
        return false;
9732
0
    }
9733
9734
2
    TTypeList* members = retType.getWritableStruct();
9735
9736
    // Check for too many or not enough structure members.
9737
2
    if (members->size() > 4 || members->size() == 0) {
9738
0
        error(loc, "Invalid member count in texture template structure", "", "");
9739
0
        return false;
9740
0
    }
9741
9742
    // Error checking: We must have <= 4 total components, all of the same basic type.
9743
2
    unsigned totalComponents = 0;
9744
6
    for (unsigned m = 0; m < members->size(); ++m) {
9745
        // Check for bad member types
9746
4
        if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) {
9747
0
            error(loc, "Invalid texture template struct member type", "", "");
9748
0
            return false;
9749
0
        }
9750
9751
4
        const unsigned memberVectorSize = (*members)[m].type->getVectorSize();
9752
4
        totalComponents += memberVectorSize;
9753
9754
        // too many total member components
9755
4
        if (totalComponents > 4) {
9756
0
            error(loc, "Too many components in texture template structure type", "", "");
9757
0
            return false;
9758
0
        }
9759
9760
        // All members must be of a common basic type
9761
4
        if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) {
9762
0
            error(loc, "Texture template structure members must same basic type", "", "");
9763
0
            return false;
9764
0
        }
9765
4
    }
9766
9767
    // If the structure in the return type already exists in the table, we'll use it.  Otherwise, we'll make
9768
    // a new entry.  This is a linear search, but it hardly ever happens, and the list cannot be very large.
9769
2
    for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) {
9770
0
        if (textureReturnStruct[idx] == members) {
9771
0
            sampler.structReturnIndex = idx;
9772
0
            return true;
9773
0
        }
9774
0
    }
9775
9776
    // It wasn't found as an existing entry.  See if we have room for a new one.
9777
2
    if (textureReturnStruct.size() >= TSampler::structReturnSlots) {
9778
0
        error(loc, "Texture template struct return slots exceeded", "", "");
9779
0
        return false;
9780
0
    }
9781
9782
    // Insert it in the vector that tracks struct return types.
9783
2
    sampler.structReturnIndex = unsigned(textureReturnStruct.size());
9784
2
    textureReturnStruct.push_back(members);
9785
9786
    // Success!
9787
2
    return true;
9788
2
}
9789
9790
// Return the sampler return type in retType.
9791
void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const
9792
778
{
9793
778
    if (sampler.hasReturnStruct()) {
9794
0
        assert(textureReturnStruct.size() >= sampler.structReturnIndex);
9795
9796
        // We land here if the texture return is a structure.
9797
0
        TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex];
9798
9799
0
        const TType resultType(blockStruct, "");
9800
0
        retType.shallowCopy(resultType);
9801
778
    } else {
9802
        // We land here if the texture return is a vector or scalar.
9803
778
        const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize());
9804
778
        retType.shallowCopy(resultType);
9805
778
    }
9806
778
}
9807
9808
9809
// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type
9810
TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const
9811
460
{
9812
460
    const auto it = builtInTessLinkageSymbols.find(biType);
9813
460
    if (it == builtInTessLinkageSymbols.end())  // if it wasn't declared by the user, return nullptr
9814
384
        return nullptr;
9815
9816
76
    return intermediate.addSymbol(*it->second->getAsVariable());
9817
460
}
9818
9819
// Find the patch constant function (issues error, returns nullptr if not found)
9820
const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc)
9821
0
{
9822
0
    if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
9823
0
        error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
9824
0
        return nullptr;
9825
0
    }
9826
9827
0
    const TString mangledName = patchConstantFunctionName + "(";
9828
9829
    // create list of PCF candidates
9830
0
    TVector<const TFunction*> candidateList;
9831
0
    bool builtIn;
9832
0
    symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
9833
9834
    // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
9835
    // allow any disambiguation of overloads.
9836
0
    if (candidateList.empty()) {
9837
0
        error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
9838
0
        return nullptr;
9839
0
    }
9840
9841
    // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
9842
    // HLSL picks the last one in shader source order.  Since that isn't yet implemented here, error
9843
    // out if there is more than one candidate.
9844
0
    if (candidateList.size() > 1) {
9845
0
        error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
9846
0
        return nullptr;
9847
0
    }
9848
9849
0
    return candidateList[0];
9850
0
}
9851
9852
// Finalization step: Add patch constant function invocation
9853
void HlslParseContext::addPatchConstantInvocation()
9854
1.75k
{
9855
1.75k
    TSourceLoc loc;
9856
1.75k
    loc.init();
9857
9858
    // If there's no patch constant function, or we're not a HS, do nothing.
9859
1.75k
    if (patchConstantFunctionName.empty() || language != EShLangTessControl)
9860
1.75k
        return;
9861
9862
    // Look for built-in variables in a function's parameter list.
9863
0
    const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
9864
0
        for (int p=0; p<function.getParamCount(); ++p) {
9865
0
            TStorageQualifier storage = function[p].type->getQualifier().storage;
9866
9867
0
            if (storage == EvqConstReadOnly) // treated identically to input
9868
0
                storage = EvqIn;
9869
9870
0
            if (function[p].getDeclaredBuiltIn() != EbvNone)
9871
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage));
9872
0
            else
9873
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
9874
0
        }
9875
0
    };
9876
9877
    // If we synthesize a built-in interface variable, we must add it to the linkage.
9878
0
    const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
9879
0
        if (name == nullptr) {
9880
0
            error(loc, "unable to locate patch function parameter name", "", "");
9881
0
            return;
9882
0
        } else {
9883
0
            TVariable& variable = *new TVariable(name, type);
9884
0
            if (! symbolTable.insert(variable)) {
9885
0
                error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
9886
0
                return;
9887
0
            }
9888
9889
0
            globalQualifierFix(loc, variable.getWritableType().getQualifier());
9890
9891
0
            if (symbolNode != nullptr)
9892
0
                *symbolNode = intermediate.addSymbol(variable);
9893
9894
0
            trackLinkage(variable);
9895
0
        }
9896
0
    };
9897
9898
0
    const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) {
9899
0
        const TType& type = *patchConstantFunction[param].type;
9900
0
        const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn();
9901
9902
0
        return type.isSizedArray() && biType == EbvOutputPatch;
9903
0
    };
9904
9905
    // We will perform these steps.  Each is in a scoped block for separation: they could
9906
    // become separate functions to make addPatchConstantInvocation shorter.
9907
    //
9908
    // 1. Union the interfaces, and create built-ins for anything present in the PCF and
9909
    //    declared as a built-in variable that isn't present in the entry point's signature.
9910
    //
9911
    // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main,
9912
    //    or the ones we created.  Matching is based on built-in type.  We may use synthesized
9913
    //    variables from (1) above.
9914
    //
9915
    // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
9916
    //
9917
    // 3. Create a return sequence: copy the return value (if any) from the PCF to a
9918
    //    (non-sanitized) output variable.  In case this may involve multiple copies, such as for
9919
    //    an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
9920
    //    indirections into a complex R-value coming from the call to the PCF.
9921
    //
9922
    // 4. Create a barrier.
9923
    //
9924
    // 5/5B. Call the PCF inside an if test for (invocation id == 0).
9925
9926
0
    TFunction* patchConstantFunctionPtr = const_cast<TFunction*>(findPatchConstantFunction(loc));
9927
9928
0
    if (patchConstantFunctionPtr == nullptr)
9929
0
        return;
9930
9931
0
    TFunction& patchConstantFunction = *patchConstantFunctionPtr;
9932
9933
0
    const int pcfParamCount = patchConstantFunction.getParamCount();
9934
0
    TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
9935
0
    TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
9936
9937
0
    int outPatchParam = -1; // -1 means there isn't one.
9938
9939
    // ================ Step 1A: Union Interfaces ================
9940
    // Our patch constant function.
9941
0
    {
9942
0
        std::set<tInterstageIoData> pcfBuiltIns;  // patch constant function built-ins
9943
0
        std::set<tInterstageIoData> epfBuiltIns;  // entry point function built-ins
9944
9945
0
        assert(entryPointFunction);
9946
0
        assert(entryPointFunctionBody);
9947
9948
0
        findBuiltIns(patchConstantFunction, pcfBuiltIns);
9949
0
        findBuiltIns(*entryPointFunction,   epfBuiltIns);
9950
9951
        // Find the set of built-ins in the PCF that are not present in the entry point.
9952
0
        std::set<tInterstageIoData> notInEntryPoint;
9953
9954
0
        notInEntryPoint = pcfBuiltIns;
9955
9956
        // std::set_difference not usable on unordered containers
9957
0
        for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
9958
0
            notInEntryPoint.erase(*bi);
9959
9960
        // Now we'll add those to the entry and to the linkage.
9961
0
        for (int p=0; p<pcfParamCount; ++p) {
9962
0
            const TBuiltInVariable biType   = patchConstantFunction[p].getDeclaredBuiltIn();
9963
0
            TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
9964
9965
            // Track whether there is an output patch param
9966
0
            if (isOutputPatch(patchConstantFunction, p)) {
9967
0
                if (outPatchParam >= 0) {
9968
                    // Presently we only support one per ctrl pt input.
9969
0
                    error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
9970
0
                    return;
9971
0
                }
9972
0
                outPatchParam = p;
9973
0
            }
9974
9975
0
            if (biType != EbvNone) {
9976
0
                TType* paramType = patchConstantFunction[p].type->clone();
9977
9978
0
                if (storage == EvqConstReadOnly) // treated identically to input
9979
0
                    storage = EvqIn;
9980
9981
                // Presently, the only non-built-in we support is InputPatch, which is treated as
9982
                // a pseudo-built-in.
9983
0
                if (biType == EbvInputPatch) {
9984
0
                    builtInTessLinkageSymbols[biType] = inputPatch;
9985
0
                } else if (biType == EbvOutputPatch) {
9986
                    // Nothing...
9987
0
                } else {
9988
                    // Use the original declaration type for the linkage
9989
0
                    paramType->getQualifier().builtIn = biType;
9990
0
                    if (biType == EbvTessLevelInner || biType == EbvTessLevelOuter)
9991
0
                        paramType->getQualifier().patch = true;
9992
9993
0
                    if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
9994
0
                        addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
9995
0
                }
9996
0
            }
9997
0
        }
9998
9999
        // If we didn't find it because the shader made one, add our own.
10000
0
        if (invocationIdSym == nullptr) {
10001
0
            TType invocationIdType(EbtUint, EvqIn, 1);
10002
0
            TString* invocationIdName = NewPoolTString("InvocationId");
10003
0
            invocationIdType.getQualifier().builtIn = EbvInvocationId;
10004
0
            addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
10005
0
        }
10006
10007
0
        assert(invocationIdSym);
10008
0
    }
10009
10010
0
    TIntermTyped* pcfArguments = nullptr;
10011
0
    TVariable* perCtrlPtVar = nullptr;
10012
10013
    // ================ Step 1B: Argument synthesis ================
10014
    // Create pcfArguments for synthesis of patchconstantfunction invocation
10015
0
    {
10016
0
        for (int p=0; p<pcfParamCount; ++p) {
10017
0
            TIntermTyped* inputArg = nullptr;
10018
10019
0
            if (p == outPatchParam) {
10020
0
                if (perCtrlPtVar == nullptr) {
10021
0
                    perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
10022
0
                                                        *patchConstantFunction[outPatchParam].type);
10023
10024
0
                    perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
10025
0
                }
10026
0
                inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
10027
0
            } else {
10028
                // find which built-in it is
10029
0
                const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
10030
10031
0
                if (biType == EbvInputPatch && inputPatch == nullptr) {
10032
0
                    error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", "");
10033
0
                    return;
10034
0
                }
10035
10036
0
                inputArg = findTessLinkageSymbol(biType);
10037
10038
0
                if (inputArg == nullptr) {
10039
0
                    error(loc, "unable to find patch constant function built-in variable", "", "");
10040
0
                    return;
10041
0
                }
10042
0
            }
10043
10044
0
            if (pcfParamCount == 1)
10045
0
                pcfArguments = inputArg;
10046
0
            else
10047
0
                pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
10048
0
        }
10049
0
    }
10050
10051
    // ================ Step 2: Synthesize call to PCF ================
10052
0
    TIntermAggregate* pcfCallSequence = nullptr;
10053
0
    TIntermTyped* pcfCall = nullptr;
10054
10055
0
    {
10056
        // Create a function call to the patchconstantfunction
10057
0
        if (pcfArguments)
10058
0
            addInputArgumentConversions(patchConstantFunction, pcfArguments);
10059
10060
        // Synthetic call.
10061
0
        pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
10062
0
        pcfCall->getAsAggregate()->setUserDefined();
10063
0
        pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
10064
0
        intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
10065
0
                                    patchConstantFunction.getMangledName());
10066
10067
0
        if (pcfCall->getAsAggregate()) {
10068
0
            TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
10069
0
            for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
10070
0
                TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
10071
0
                qualifierList.push_back(qual);
10072
0
            }
10073
0
            pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
10074
0
        }
10075
0
    }
10076
10077
    // ================ Step 2B: Per Control Point synthesis ================
10078
    // If there is per control point data, we must either emulate that with multiple
10079
    // invocations of the entry point to build up an array, or (TODO:) use a yet
10080
    // unavailable extension to look across the SIMD lanes.  This is the former
10081
    // as a placeholder for the latter.
10082
0
    if (outPatchParam >= 0) {
10083
        // We must introduce a local temp variable of the type wanted by the PCF input.
10084
0
        const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
10085
10086
0
        if (entryPointFunction->getType().getBasicType() == EbtVoid) {
10087
0
            error(loc, "entry point must return a value for use with patch constant function", "", "");
10088
0
            return;
10089
0
        }
10090
10091
        // Create calls to wrapped main to fill in the array.  We will substitute fixed values
10092
        // of invocation ID when calling the wrapped main.
10093
10094
        // This is the type of the each member of the per ctrl point array.
10095
0
        const TType derefType(perCtrlPtVar->getType(), 0);
10096
10097
0
        for (int cpt = 0; cpt < arraySize; ++cpt) {
10098
            // TODO: improve.  substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
10099
            // for this function.
10100
0
            const TString origName = entryPointFunction->getName().substr(1);
10101
0
            TFunction callee(&origName, TType(EbtVoid));
10102
0
            TIntermTyped* callingArgs = nullptr;
10103
10104
0
            for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
10105
0
                TParameter& param = (*entryPointFunction)[i];
10106
0
                TType& paramType = *param.type;
10107
10108
0
                if (paramType.getQualifier().isParamOutput()) {
10109
0
                    error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
10110
0
                    return;
10111
0
                }
10112
10113
0
                if (paramType.getQualifier().isParamInput())  {
10114
0
                    TIntermTyped* arg = nullptr;
10115
0
                    if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) {
10116
                        // substitute invocation ID with the array element ID
10117
0
                        arg = intermediate.addConstantUnion(cpt, loc);
10118
0
                    } else {
10119
0
                        TVariable* argVar = makeInternalVariable(*param.name, *param.type);
10120
0
                        argVar->getWritableType().getQualifier().makeTemporary();
10121
0
                        arg = intermediate.addSymbol(*argVar);
10122
0
                    }
10123
10124
0
                    handleFunctionArgument(&callee, callingArgs, arg);
10125
0
                }
10126
0
            }
10127
10128
            // Call and assign to per ctrl point variable
10129
0
            currentCaller = intermediate.getEntryPointMangledName().c_str();
10130
0
            TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
10131
0
            TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
10132
0
            TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
10133
0
            TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
10134
0
            element->setType(derefType);
10135
0
            element->setLoc(loc);
10136
10137
0
            pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
10138
0
                                                         handleAssign(loc, EOpAssign, element, callReturn));
10139
0
        }
10140
0
    }
10141
10142
    // ================ Step 3: Create return Sequence ================
10143
    // Return sequence: copy PCF result to a temporary, then to shader output variable.
10144
0
    if (pcfCall->getBasicType() != EbtVoid) {
10145
0
        const TType* retType = &patchConstantFunction.getType();  // return type from the PCF
10146
0
        TType outType; // output type that goes with the return type.
10147
0
        outType.shallowCopy(*retType);
10148
10149
        // substitute the output type
10150
0
        const auto newLists = ioTypeMap.find(retType->getStruct());
10151
0
        if (newLists != ioTypeMap.end())
10152
0
            outType.setStruct(newLists->second.output);
10153
10154
        // Substitute the top level type's built-in type
10155
0
        if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
10156
0
            outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
10157
10158
0
        outType.getQualifier().patch = true; // make it a per-patch variable
10159
10160
0
        TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
10161
0
        pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
10162
10163
0
        if (pcfOutput->getType().isStruct())
10164
0
            flatten(*pcfOutput, false);
10165
10166
0
        assignToInterface(*pcfOutput);
10167
10168
0
        TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
10169
10170
        // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
10171
        // repeated calls to the PCF:
10172
0
        TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
10173
0
        pcfCallResult->getWritableType().getQualifier().makeTemporary();
10174
10175
0
        TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
10176
0
        TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
10177
0
        TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym,
10178
0
                                                   intermediate.addSymbol(*pcfCallResult, loc));
10179
10180
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
10181
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
10182
0
    } else {
10183
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
10184
0
    }
10185
10186
    // ================ Step 4: Barrier ================
10187
0
    TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
10188
0
    barrier->setLoc(loc);
10189
0
    barrier->setType(TType(EbtVoid));
10190
0
    epBodySeq.insert(epBodySeq.end(), barrier);
10191
10192
    // ================ Step 5: Test on invocation ID ================
10193
0
    TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
10194
0
    TIntermTyped* cmp =  intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
10195
10196
10197
    // ================ Step 5B: Create if statement on Invocation ID == 0 ================
10198
0
    intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
10199
0
    TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
10200
0
    invocationIdTest->setLoc(loc);
10201
10202
    // add our test sequence before the return.
10203
0
    epBodySeq.insert(epBodySeq.end(), invocationIdTest);
10204
0
}
10205
10206
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
10207
// shader is entirely compiled).
10208
// Preserve order of remaining symbols.
10209
void HlslParseContext::removeUnusedStructBufferCounters()
10210
1.75k
{
10211
1.75k
    const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
10212
1.75k
                                      [this](const TSymbol* sym) {
10213
1.50k
                                          const auto sbcIt = structBufferCounter.find(sym->getName());
10214
1.50k
                                          return sbcIt != structBufferCounter.end() && !sbcIt->second;
10215
1.50k
                                      });
10216
10217
1.75k
    linkageSymbols.erase(endIt, linkageSymbols.end());
10218
1.75k
}
10219
10220
// Finalization step: patch texture shadow modes to match samplers they were combined with
10221
void HlslParseContext::fixTextureShadowModes()
10222
1.75k
{
10223
3.22k
    for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) {
10224
1.46k
        TSampler& sampler = (*symbol)->getWritableType().getSampler();
10225
10226
1.46k
        if (sampler.isTexture()) {
10227
1.19k
            const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId());
10228
1.19k
            if (shadowMode != textureShadowVariant.end()) {
10229
10230
6
                if (shadowMode->second->overloaded())
10231
                    // Texture needs legalization if it's been seen with both shadow and non-shadow modes.
10232
0
                    intermediate.setNeedsLegalization();
10233
10234
6
                sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId());
10235
6
            }
10236
1.19k
        }
10237
1.46k
    }
10238
1.75k
}
10239
10240
// Finalization step: patch append methods to use proper stream output, which isn't known until
10241
// main is parsed, which could happen after the append method is parsed.
10242
void HlslParseContext::finalizeAppendMethods()
10243
1.75k
{
10244
1.75k
    TSourceLoc loc;
10245
1.75k
    loc.init();
10246
10247
    // Nothing to do: bypass test for valid stream output.
10248
1.75k
    if (gsAppends.empty())
10249
1.75k
        return;
10250
10251
0
    if (gsStreamOutput == nullptr) {
10252
0
        error(loc, "unable to find output symbol for Append()", "", "");
10253
0
        return;
10254
0
    }
10255
10256
    // Patch append sequences, now that we know the stream output symbol.
10257
0
    for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) {
10258
0
        append->node->getSequence()[0] =
10259
0
            handleAssign(append->loc, EOpAssign,
10260
0
                         intermediate.addSymbol(*gsStreamOutput, append->loc),
10261
0
                         append->node->getSequence()[0]->getAsTyped());
10262
0
    }
10263
0
}
10264
10265
// post-processing
10266
void HlslParseContext::finish()
10267
1.75k
{
10268
    // Error check: There was a dangling .mips operator.  These are not nested constructs in the grammar, so
10269
    // cannot be detected there.  This is not strictly needed in a non-validating parser; it's just helpful.
10270
1.75k
    if (! mipsOperatorMipArg.empty()) {
10271
0
        error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", "");
10272
0
    }
10273
10274
1.75k
    removeUnusedStructBufferCounters();
10275
1.75k
    addPatchConstantInvocation();
10276
1.75k
    fixTextureShadowModes();
10277
1.75k
    finalizeAppendMethods();
10278
10279
    // Communicate out (esp. for command line) that we formed AST that will make
10280
    // illegal AST SPIR-V and it needs transforms to legalize it.
10281
1.75k
    if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization))
10282
0
        infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize";
10283
10284
1.75k
    TParseContextBase::finish();
10285
1.75k
}
10286
10287
} // end namespace glslang