Coverage Report

Created: 2026-04-12 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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
1.90k
    TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink,
60
1.90k
                      forwardCompatible, messages, &sourceEntryPointName),
61
1.90k
    annotationNestingLevel(0),
62
1.90k
    inputPatch(nullptr),
63
1.90k
    nextInLocation(0), nextOutLocation(0),
64
1.90k
    entryPointFunction(nullptr),
65
1.90k
    entryPointFunctionBody(nullptr),
66
1.90k
    gsStreamOutput(nullptr),
67
1.90k
    clipDistanceOutput(nullptr),
68
1.90k
    cullDistanceOutput(nullptr),
69
1.90k
    clipDistanceInput(nullptr),
70
1.90k
    cullDistanceInput(nullptr),
71
1.90k
    parsingEntrypointParameters(false)
72
1.90k
{
73
1.90k
    globalUniformDefaults.clear();
74
1.90k
    globalUniformDefaults.layoutMatrix = ElmRowMajor;
75
1.90k
    globalUniformDefaults.layoutPacking = ElpStd140;
76
77
1.90k
    globalBufferDefaults.clear();
78
1.90k
    globalBufferDefaults.layoutMatrix = ElmRowMajor;
79
1.90k
    globalBufferDefaults.layoutPacking = ElpStd430;
80
81
1.90k
    globalInputDefaults.clear();
82
1.90k
    globalOutputDefaults.clear();
83
84
1.90k
    clipSemanticNSizeIn.fill(0);
85
1.90k
    cullSemanticNSizeIn.fill(0);
86
1.90k
    clipSemanticNSizeOut.fill(0);
87
1.90k
    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
1.90k
    if (language == EShLangVertex ||
93
1.45k
        language == EShLangTessControl ||
94
1.34k
        language == EShLangTessEvaluation ||
95
1.23k
        language == EShLangGeometry)
96
784
        globalOutputDefaults.layoutXfbBuffer = 0;
97
98
1.90k
    if (language == EShLangGeometry)
99
112
        globalOutputDefaults.layoutStream = 0;
100
1.90k
}
101
102
HlslParseContext::~HlslParseContext()
103
1.90k
{
104
1.90k
}
105
106
void HlslParseContext::initializeExtensionBehavior()
107
112
{
108
112
    TParseContextBase::initializeExtensionBehavior();
109
110
    // HLSL allows #line by default.
111
112
    extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
112
112
}
113
114
void HlslParseContext::setLimits(const TBuiltInResource& r)
115
112
{
116
112
    resources = r;
117
112
    intermediate.setLimits(resources);
118
112
}
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
224
{
127
224
    currentScanner = &input;
128
224
    ppContext.setInput(input, versionWillBeError);
129
130
224
    HlslScanContext scanContext(*this, ppContext);
131
224
    HlslGrammar grammar(scanContext, *this);
132
224
    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
17
        const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
136
17
        infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column
137
17
                      << ", HLSL parsing failed.\n";
138
17
        ++numErrors;
139
17
        return false;
140
17
    }
141
142
207
    finish();
143
144
207
    return numErrors == 0;
145
224
}
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
0
{
153
0
    if (node == nullptr || node->getAsTyped() == nullptr)
154
0
        return false;
155
156
0
    const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
157
0
    const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
158
159
    // If it's a swizzled/indexed aggregate, look at the left node instead.
160
0
    if (lhsAsBinary != nullptr &&
161
0
        (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
162
0
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
163
0
    if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
164
0
        return true;
165
166
0
    return false;
167
0
}
168
169
void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName,
170
                                              TTypeList* newTypeList)
171
0
{
172
0
    newTypeList = nullptr;
173
0
    correctUniform(memberType.getQualifier());
174
0
    if (memberType.isStruct()) {
175
0
        auto it = ioTypeMap.find(memberType.getStruct());
176
0
        if (it != ioTypeMap.end() && it->second.uniform)
177
0
            newTypeList = it->second.uniform;
178
0
    }
179
0
    TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
180
0
}
181
182
//
183
// Return a TLayoutFormat corresponding to the given texture type.
184
//
185
TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
186
6.38k
{
187
6.38k
    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
6.38k
    const int components = txType.getVectorSize();
194
6.38k
    const TBasicType txBasicType = txType.getBasicType();
195
196
6.38k
    const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
197
6.38k
        if (intermediate.getNoStorageFormat())
198
0
            return ElfNone;
199
200
6.38k
        return components == 1 ? v1 :
201
6.38k
               components == 2 ? v2 : v4;
202
6.38k
    };
203
204
6.38k
    switch (txBasicType) {
205
2.12k
    case EbtFloat: return selectFormat(ElfR32f,  ElfRg32f,  ElfRgba32f);
206
2.12k
    case EbtInt:   return selectFormat(ElfR32i,  ElfRg32i,  ElfRgba32i);
207
2.12k
    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
6.38k
    }
212
6.38k
}
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
0
{
222
0
    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
0
    if (node->getType().getBasicType() == EbtSampler) {
237
0
        intermediate.setNeedsLegalization();
238
0
        return false;
239
0
    }
240
241
    // Let the base class check errors
242
0
    return TParseContextBase::lValueErrorCheck(loc, op, node);
243
0
}
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
4
{
255
4
    if (node == nullptr)
256
4
        return nullptr;
257
258
0
    TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
259
0
    TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
260
0
    TIntermAggregate* sequence = nullptr;
261
262
0
    TIntermTyped* lhs = nodeAsUnary  ? nodeAsUnary->getOperand() :
263
0
                        nodeAsBinary ? nodeAsBinary->getLeft() :
264
0
                        nullptr;
265
266
    // Early bail out if there is no conversion to apply
267
0
    if (!shouldConvertLValue(lhs)) {
268
0
        if (lhs != nullptr)
269
0
            if (lValueErrorCheck(loc, op, lhs))
270
0
                return nullptr;
271
0
        return node;
272
0
    }
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
0
    const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
278
0
        TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
279
0
        loadOp->setLoc(loc);
280
0
        loadOp->getSequence().push_back(object);
281
0
        loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
282
0
        loadOp->setType(derefType);
283
284
0
        sequence = intermediate.growAggregate(sequence,
285
0
                                              intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
286
0
                                              loc);
287
0
    };
288
289
    // Helper to create a store.
290
0
    const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
291
0
        TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
292
0
        storeOp->getSequence().push_back(object);
293
0
        storeOp->getSequence().push_back(coord);
294
0
        storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
295
0
        storeOp->setLoc(loc);
296
0
        storeOp->setType(TType(EbtVoid));
297
298
0
        sequence = intermediate.growAggregate(sequence, storeOp);
299
0
    };
300
301
    // Helper to create an assign.
302
0
    const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
303
0
        sequence = intermediate.growAggregate(sequence,
304
0
                                              intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
305
0
                                              loc);
306
0
    };
307
308
    // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
309
0
    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
0
        sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
312
0
        sequence->setOperator(EOpSequence);
313
0
        sequence->setLoc(loc);
314
0
        sequence->setType(derefType);
315
316
0
        return sequence;
317
0
    };
318
319
    // Helper to add unary op
320
0
    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
0
    const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
329
0
        if (swizzle == nullptr)  // not a swizzle or index
330
0
            return true;
331
332
        // Track which components are being set.
333
0
        std::array<bool, 4> compIsSet;
334
0
        compIsSet.fill(false);
335
336
0
        const TIntermConstantUnion* asConst     = swizzle->getRight()->getAsConstantUnion();
337
0
        const TIntermAggregate*     asAggregate = swizzle->getRight()->getAsAggregate();
338
339
        // This could be either a direct index, or a swizzle.
340
0
        if (asConst) {
341
0
            compIsSet[asConst->getConstArray()[0].getIConst()] = true;
342
0
        } else if (asAggregate) {
343
0
            const TIntermSequence& seq = asAggregate->getSequence();
344
0
            for (int comp=0; comp<int(seq.size()); ++comp)
345
0
                compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
346
0
        } else {
347
0
            assert(0);
348
0
        }
349
350
        // Return true if all components are being set by the index or swizzle
351
0
        return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
352
0
                           [](bool isSet) { return isSet; } );
353
0
    };
354
355
    // Create swizzle matching input swizzle
356
0
    const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
357
0
        if (swizzle)
358
0
            return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
359
0
        else
360
0
            return var;
361
0
    };
362
363
0
    TIntermBinary*    lhsAsBinary    = lhs->getAsBinaryNode();
364
0
    TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
365
0
    bool lhsIsSwizzle = false;
366
367
    // If it's a swizzled L-value, remember the swizzle, and use the LHS.
368
0
    if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
369
0
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
370
0
        lhsIsSwizzle = true;
371
0
    }
372
373
0
    TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
374
0
    TIntermTyped* coord  = lhsAsAggregate->getSequence()[1]->getAsTyped();
375
376
0
    const TSampler& texSampler = object->getType().getSampler();
377
378
0
    TType objDerefType;
379
0
    getTextureReturnType(texSampler, objDerefType);
380
381
0
    if (nodeAsBinary) {
382
0
        TIntermTyped* rhs = nodeAsBinary->getRight();
383
0
        const TOperator assignOp = nodeAsBinary->getOp();
384
385
0
        bool isModifyOp = false;
386
387
0
        switch (assignOp) {
388
0
        case EOpAddAssign:
389
0
        case EOpSubAssign:
390
0
        case EOpMulAssign:
391
0
        case EOpVectorTimesMatrixAssign:
392
0
        case EOpVectorTimesScalarAssign:
393
0
        case EOpMatrixTimesScalarAssign:
394
0
        case EOpMatrixTimesMatrixAssign:
395
0
        case EOpDivAssign:
396
0
        case EOpModAssign:
397
0
        case EOpAndAssign:
398
0
        case EOpInclusiveOrAssign:
399
0
        case EOpExclusiveOrAssign:
400
0
        case EOpLeftShiftAssign:
401
0
        case EOpRightShiftAssign:
402
0
            isModifyOp = true;
403
0
            [[fallthrough]];
404
0
        case EOpAssign:
405
0
            {
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
0
                TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
431
0
                TIntermTyped* coordTmp = coord;
432
433
0
                if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
434
0
                    rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
435
436
                    // Partial updates not yet supported
437
0
                    if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
438
0
                        error(loc, "unimplemented: partial image updates", "", "");
439
0
                    }
440
441
                    // Assign storeTemp = rhs
442
0
                    if (isModifyOp) {
443
                        // We have to make a temp var for the coordinate, to avoid evaluating it twice.
444
0
                        coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
445
0
                        makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
446
0
                        makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
447
0
                    }
448
449
                    // rhsTmp op= rhs.
450
0
                    makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
451
0
                }
452
453
0
                makeStore(object, coordTmp, rhsTmp);         // add a store
454
0
                return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
455
0
            }
456
457
0
        default:
458
0
            break;
459
0
        }
460
0
    }
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
0
{
524
0
    if (pragmaCallback)
525
0
        pragmaCallback(loc.line, tokens);
526
527
0
    if (tokens.size() == 0)
528
0
        return;
529
530
    // These pragmas are case insensitive in HLSL, so we'll compare in lower case.
531
0
    TVector<TString> lowerTokens = tokens;
532
533
0
    for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it)
534
0
        std::transform(it->begin(), it->end(), it->begin(), ::tolower);
535
536
    // Handle pack_matrix
537
0
    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
0
        if (lowerTokens[2] == "row_major") {
542
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor;
543
0
        } else if (lowerTokens[2] == "column_major") {
544
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
545
0
        } else {
546
            // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major)
547
0
            warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), "");
548
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
549
0
        }
550
0
        return;
551
0
    }
552
553
    // Handle once
554
0
    if (lowerTokens[0] == "once") {
555
0
        warn(loc, "not implemented", "#pragma once", "");
556
0
        return;
557
0
    }
558
0
}
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
0
{
572
0
    int startPos[MaxSwizzleSelectors];
573
0
    int numComps = 0;
574
0
    TString compString = fields;
575
576
    // Find where each component starts,
577
    // recording the first character position after the '_'.
578
0
    for (size_t c = 0; c < compString.size(); ++c) {
579
0
        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
0
    }
592
593
    // Process each component
594
0
    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
0
    return true;
616
0
}
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
0
{
625
0
    int col = -1;
626
627
    // right number of comps?
628
0
    if (selector.size() != rows)
629
0
        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
195
{
649
195
    int thisDepth;
650
195
    TSymbol* symbol = symbolTable.find(*string, thisDepth);
651
195
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
652
0
        error(loc, "expected symbol, not user-defined type", string->c_str(), "");
653
0
        return nullptr;
654
0
    }
655
656
195
    const TVariable* variable = nullptr;
657
195
    const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
658
195
    TIntermTyped* node = nullptr;
659
195
    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
0
        if (thisDepth > 0) {
664
0
            variable = getImplicitThis(thisDepth);
665
0
            if (variable == nullptr)
666
0
                error(loc, "cannot access member variables (static member function?)", "this", "");
667
0
        }
668
0
        if (variable == nullptr)
669
0
            variable = anon->getAnonContainer().getAsVariable();
670
671
0
        TIntermTyped* container = intermediate.addSymbol(*variable, loc);
672
0
        TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
673
0
        node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
674
675
0
        node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
676
0
        if (node->getType().hiddenMember())
677
0
            error(loc, "member of nameless block was not redeclared", string->c_str(), "");
678
195
    } 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
195
        variable = symbol ? symbol->getAsVariable() : nullptr;
684
195
        if (variable) {
685
1
            if ((variable->getType().getBasicType() == EbtBlock ||
686
1
                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
194
        } else {
691
194
            if (symbol)
692
0
                error(loc, "variable name expected", string->c_str(), "");
693
194
        }
694
695
        // Recovery, if it wasn't found or was not a variable.
696
195
        if (variable == nullptr) {
697
194
            error(loc, "unknown variable", string->c_str(), "");
698
194
            variable = new TVariable(string, TType(EbtVoid));
699
194
        }
700
701
195
        if (variable->getType().getQualifier().isFrontEndConstant())
702
0
            node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
703
195
        else
704
195
            node = intermediate.addSymbol(*variable, loc);
705
195
    }
706
707
195
    if (variable->getType().getQualifier().isIo())
708
1
        intermediate.addIoAccessed(*string);
709
710
195
    return node;
711
195
}
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
2
{
720
    // handle r-value operator[] on textures and images.  l-values will be processed later.
721
2
    if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
722
0
        const TSampler& sampler = base->getType().getSampler();
723
0
        if (sampler.isImage() || sampler.isTexture()) {
724
0
            if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) {
725
                // The first operator[] to a .mips[] sequence is the mip level.  We'll remember it.
726
0
                mipsOperatorMipArg.back().mipLevel = index;
727
0
                return base;  // next [] index is to the same base.
728
0
            } else {
729
0
                TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
730
731
0
                TType sampReturnType;
732
0
                getTextureReturnType(sampler, sampReturnType);
733
734
0
                load->setType(sampReturnType);
735
0
                load->setLoc(loc);
736
0
                load->getSequence().push_back(base);
737
0
                load->getSequence().push_back(index);
738
739
                // Textures need a MIP.  If we saw one go by, use it.  Otherwise, use zero.
740
0
                if (sampler.isTexture()) {
741
0
                    if (! mipsOperatorMipArg.empty()) {
742
0
                        load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel);
743
0
                        mipsOperatorMipArg.pop_back();
744
0
                    } else {
745
0
                        load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
746
0
                    }
747
0
                }
748
749
0
                return load;
750
0
            }
751
0
        }
752
0
    }
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
2
    TIntermTyped* sbArray = indexStructBufferContent(loc, base);
757
2
    if (sbArray != nullptr) {
758
        // Now we'll apply the [] index to that array
759
0
        const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
760
761
0
        TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
762
0
        const TType derefType(sbArray->getType(), 0);
763
0
        element->setType(derefType);
764
0
        return element;
765
0
    }
766
767
2
    return nullptr;
768
2
}
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
2
{
774
2
    const TBasicType indexBasicType = index->getType().getBasicType();
775
2
    const int vecSize = index->getType().getVectorSize();
776
777
    // We can use int types directly as the index
778
2
    if (indexBasicType == EbtInt || indexBasicType == EbtUint ||
779
1
        indexBasicType == EbtInt64 || indexBasicType == EbtUint64)
780
1
        return index;
781
782
    // Cast index to unsigned integer if it isn't one.
783
1
    return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index);
784
2
}
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
2
{
791
2
    index = makeIntegerIndex(index);
792
793
2
    if (index == nullptr) {
794
0
        error(loc, " unknown index type ", "", "");
795
0
        return nullptr;
796
0
    }
797
798
2
    TIntermTyped* result = handleBracketOperator(loc, base, index);
799
800
2
    if (result != nullptr)
801
0
        return result;  // it was handled as an operator[]
802
803
2
    bool flattened = false;
804
2
    int64_t indexValue = 0;
805
2
    if (index->getQualifier().isFrontEndConstant()) {
806
2
        if (index->getType().contains64BitInt()) {
807
0
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getI64Const();
808
2
        } else if (index->getType().getBasicType() == EbtUint) {
809
1
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getUConst();
810
1
        } else {
811
1
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
812
1
        }
813
2
    }
814
815
2
    variableCheck(base);
816
2
    if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
817
2
        if (base->getAsSymbolNode())
818
1
            error(loc, " left of '[' is not of type array, matrix, or vector ",
819
1
                  base->getAsSymbolNode()->getName().c_str(), "");
820
1
        else
821
1
            error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
822
2
    } else if (base->getType().getQualifier().isFrontEndConstant() &&
823
0
               index->getQualifier().isFrontEndConstant()) {
824
        // both base and index are front-end constants
825
0
        checkIndex(loc, base->getType(), indexValue);
826
0
        return intermediate.foldDereference(base, indexValue, loc);
827
0
    } else {
828
        // at least one of base and index is variable...
829
830
0
        if (index->getQualifier().isFrontEndConstant())
831
0
            checkIndex(loc, base->getType(), indexValue);
832
833
0
        if (base->getType().isScalarOrVec1())
834
0
            result = base;
835
0
        else if (base->getAsSymbolNode() && wasFlattened(base)) {
836
0
            if (index->getQualifier().storage != EvqConst)
837
0
                error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
838
839
0
            result = flattenAccess(base, indexValue);
840
0
            flattened = (result != base);
841
0
        } else {
842
0
            if (index->getQualifier().isFrontEndConstant()) {
843
0
                if (base->getType().isUnsizedArray())
844
0
                    base->getWritableType().updateImplicitArraySize(indexValue + 1);
845
0
                else
846
0
                    checkIndex(loc, base->getType(), indexValue);
847
0
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
848
0
            } else
849
0
                result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
850
0
        }
851
0
    }
852
853
2
    if (result == nullptr) {
854
        // Insert dummy error-recovery result
855
2
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
856
2
    } else {
857
        // If the array reference was flattened, it has the correct type.  E.g, if it was
858
        // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
859
        // In that case, we preserve the qualifiers.
860
0
        if (!flattened) {
861
            // Insert valid dereferenced result
862
0
            TType newType(base->getType(), 0);  // dereferenced type
863
0
            if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
864
0
                newType.getQualifier().storage = EvqConst;
865
0
            else
866
0
                newType.getQualifier().storage = EvqTemporary;
867
0
            result->setType(newType);
868
0
        }
869
0
    }
870
871
2
    return result;
872
2
}
873
874
// Handle seeing a binary node with a math operation.
875
TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op,
876
                                                 TIntermTyped* left, TIntermTyped* right)
877
0
{
878
0
    TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
879
0
    if (result == nullptr)
880
0
        binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
881
882
0
    return result;
883
0
}
884
885
// Handle seeing a unary node with a math operation.
886
TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op,
887
                                                TIntermTyped* childNode)
888
0
{
889
0
    TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
890
891
0
    if (result)
892
0
        return result;
893
0
    else
894
0
        unaryOpError(loc, str, childNode->getCompleteString());
895
896
0
    return childNode;
897
0
}
898
//
899
// Return true if the name is a struct buffer method
900
//
901
bool HlslParseContext::isStructBufferMethod(const TString& name) const
902
0
{
903
0
    return
904
0
        name == "GetDimensions"              ||
905
0
        name == "Load"                       ||
906
0
        name == "Load2"                      ||
907
0
        name == "Load3"                      ||
908
0
        name == "Load4"                      ||
909
0
        name == "Store"                      ||
910
0
        name == "Store2"                     ||
911
0
        name == "Store3"                     ||
912
0
        name == "Store4"                     ||
913
0
        name == "InterlockedAdd"             ||
914
0
        name == "InterlockedAnd"             ||
915
0
        name == "InterlockedCompareExchange" ||
916
0
        name == "InterlockedCompareStore"    ||
917
0
        name == "InterlockedExchange"        ||
918
0
        name == "InterlockedMax"             ||
919
0
        name == "InterlockedMin"             ||
920
0
        name == "InterlockedOr"              ||
921
0
        name == "InterlockedXor"             ||
922
0
        name == "IncrementCounter"           ||
923
0
        name == "DecrementCounter"           ||
924
0
        name == "Append"                     ||
925
0
        name == "Consume";
926
0
}
927
928
//
929
// Handle seeing a base.field dereference in the grammar, where 'field' is a
930
// swizzle or member variable.
931
//
932
TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
933
4
{
934
4
    variableCheck(base);
935
936
4
    if (base->isArray()) {
937
0
        error(loc, "cannot apply to an array:", ".", field.c_str());
938
0
        return base;
939
0
    }
940
941
4
    TIntermTyped* result = base;
942
943
4
    if (base->getType().getBasicType() == EbtSampler) {
944
        // Handle .mips[mipid][pos] operation on textures
945
0
        const TSampler& sampler = base->getType().getSampler();
946
0
        if (sampler.isTexture() && field == "mips") {
947
            // Push a null to signify that we expect a mip level under operator[] next.
948
0
            mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr));
949
            // Keep 'result' pointing to 'base', since we expect an operator[] to go by next.
950
0
        } else {
951
0
            if (field == "mips")
952
0
                error(loc, "unexpected texture type for .mips[][] operator:",
953
0
                      base->getType().getCompleteString().c_str(), "");
954
0
            else
955
0
                error(loc, "unexpected operator on texture type:", field.c_str(),
956
0
                      base->getType().getCompleteString().c_str());
957
0
        }
958
4
    } else if (base->isVector() || base->isScalar()) {
959
4
        TSwizzleSelectors<TVectorSelector> selectors;
960
4
        parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
961
962
4
        if (base->isScalar()) {
963
4
            if (selectors.size() == 1)
964
0
                return result;
965
4
            else {
966
4
                TType type(base->getBasicType(), EvqTemporary, selectors.size());
967
4
                return addConstructor(loc, base, type);
968
4
            }
969
4
        }
970
        // Use EOpIndexDirect (below) with vec1.x so that it remains l-value (Test/hlsl.swizzle.vec1.comp)
971
0
        if (base->getVectorSize() == 1 && selectors.size() > 1) {
972
0
            TType scalarType(base->getBasicType(), EvqTemporary, 1);
973
0
            TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
974
0
            return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
975
0
        }
976
977
0
        if (base->getType().getQualifier().isFrontEndConstant())
978
0
            result = intermediate.foldSwizzle(base, selectors, loc);
979
0
        else {
980
0
            if (selectors.size() == 1) {
981
0
                TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
982
0
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
983
0
                result->setType(TType(base->getBasicType(), EvqTemporary));
984
0
            } else {
985
0
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
986
0
                result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
987
0
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
988
0
                                selectors.size()));
989
0
            }
990
0
        }
991
0
    } else if (base->isMatrix()) {
992
0
        TSwizzleSelectors<TMatrixSelector> selectors;
993
0
        if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
994
0
            return result;
995
996
0
        if (selectors.size() == 1) {
997
            // Representable by m[c][r]
998
0
            if (base->getType().getQualifier().isFrontEndConstant()) {
999
0
                result = intermediate.foldDereference(base, selectors[0].coord1, loc);
1000
0
                result = intermediate.foldDereference(result, selectors[0].coord2, loc);
1001
0
            } else {
1002
0
                result = intermediate.addIndex(EOpIndexDirect, base,
1003
0
                                               intermediate.addConstantUnion(selectors[0].coord1, loc),
1004
0
                                               loc);
1005
0
                TType dereferencedCol(base->getType(), 0);
1006
0
                result->setType(dereferencedCol);
1007
0
                result = intermediate.addIndex(EOpIndexDirect, result,
1008
0
                                               intermediate.addConstantUnion(selectors[0].coord2, loc),
1009
0
                                               loc);
1010
0
                TType dereferenced(dereferencedCol, 0);
1011
0
                result->setType(dereferenced);
1012
0
            }
1013
0
        } else {
1014
0
            int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
1015
0
            if (column >= 0) {
1016
                // Representable by m[c]
1017
0
                if (base->getType().getQualifier().isFrontEndConstant())
1018
0
                    result = intermediate.foldDereference(base, column, loc);
1019
0
                else {
1020
0
                    result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc),
1021
0
                                                   loc);
1022
0
                    TType dereferenced(base->getType(), 0);
1023
0
                    result->setType(dereferenced);
1024
0
                }
1025
0
            } else {
1026
                // general case, not a column, not a single component
1027
0
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
1028
0
                result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
1029
0
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
1030
0
                                      selectors.size()));
1031
0
           }
1032
0
        }
1033
0
    } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
1034
0
        const TTypeList* fields = base->getType().getStruct();
1035
0
        bool fieldFound = false;
1036
0
        int member;
1037
0
        for (member = 0; member < (int)fields->size(); ++member) {
1038
0
            if ((*fields)[member].type->getFieldName() == field) {
1039
0
                fieldFound = true;
1040
0
                break;
1041
0
            }
1042
0
        }
1043
0
        if (fieldFound) {
1044
0
            if (base->getAsSymbolNode() && wasFlattened(base)) {
1045
0
                result = flattenAccess(base, member);
1046
0
            } else {
1047
0
                if (base->getType().getQualifier().storage == EvqConst)
1048
0
                    result = intermediate.foldDereference(base, member, loc);
1049
0
                else {
1050
0
                    TIntermTyped* index = intermediate.addConstantUnion(member, loc);
1051
0
                    result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
1052
0
                    result->setType(*(*fields)[member].type);
1053
0
                }
1054
0
            }
1055
0
        } else
1056
0
            error(loc, "no such field in structure", field.c_str(), "");
1057
0
    } else
1058
0
        error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
1059
1060
0
    return result;
1061
4
}
1062
1063
//
1064
// Return true if the field should be treated as a built-in method.
1065
// Return false otherwise.
1066
//
1067
bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
1068
0
{
1069
0
    if (base == nullptr)
1070
0
        return false;
1071
1072
0
    variableCheck(base);
1073
1074
0
    if (base->getType().getBasicType() == EbtSampler) {
1075
0
        return true;
1076
0
    } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
1077
0
        return true;
1078
0
    } else if (field == "Append" ||
1079
0
               field == "RestartStrip") {
1080
        // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
1081
        // the code is around in the shader source.
1082
0
        return true;
1083
0
    } else
1084
0
        return false;
1085
0
}
1086
1087
// Independently establish a built-in that is a member of a structure.
1088
// 'arraySizes' are what's desired for the independent built-in, whatever
1089
// the higher-level source/expression of them was.
1090
void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
1091
                                    const TQualifier& outerQualifier)
1092
0
{
1093
    // Because of arrays of structs, we might be asked more than once,
1094
    // but the arraySizes passed in should have captured the whole thing
1095
    // the first time.
1096
    // However, clip/cull rely on multiple updates.
1097
0
    if (!isClipOrCullDistance(memberType))
1098
0
        if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) !=
1099
0
            splitBuiltIns.end())
1100
0
            return;
1101
1102
0
    TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType);
1103
1104
0
    if (arraySizes != nullptr && !memberType.isArray())
1105
0
        ioVar->getWritableType().copyArraySizes(*arraySizes);
1106
1107
0
    splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
1108
0
    if (!isClipOrCullDistance(ioVar->getType()))
1109
0
        trackLinkage(*ioVar);
1110
1111
    // Merge qualifier from the user structure
1112
0
    mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
1113
1114
    // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the
1115
    // shader declared them).  This is done after mergeQualifiers(), in case fixBuiltInIoType looks
1116
    // at the qualifier to determine e.g, in or out qualifications.
1117
0
    fixBuiltInIoType(ioVar->getWritableType());
1118
1119
    // But, not location, we're losing that
1120
0
    ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1121
0
}
1122
1123
// Split a type into
1124
//   1. a struct of non-I/O members
1125
//   2. a collection of independent I/O variables
1126
void HlslParseContext::split(const TVariable& variable)
1127
0
{
1128
    // Create a new variable:
1129
0
    const TType& clonedType = *variable.getType().clone();
1130
0
    const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
1131
0
    splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
1132
0
}
1133
1134
// Recursive implementation of split().
1135
// Returns reference to the modified type.
1136
const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
1137
0
{
1138
0
    if (type.isStruct()) {
1139
0
        TTypeList* userStructure = type.getWritableStruct();
1140
0
        for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
1141
0
            if (ioType->type->isBuiltIn()) {
1142
                // move out the built-in
1143
0
                splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
1144
0
                ioType = userStructure->erase(ioType);
1145
0
            } else {
1146
0
                split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier);
1147
0
                ++ioType;
1148
0
            }
1149
0
        }
1150
0
    }
1151
1152
0
    return type;
1153
0
}
1154
1155
// Is this an aggregate that should be flattened?
1156
// Can be applied to intermediate levels of type in a hierarchy.
1157
// Some things like flattening uniform arrays are only about the top level
1158
// of the aggregate, triggered on 'topLevel'.
1159
bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const
1160
15
{
1161
15
    switch (qualifier) {
1162
0
    case EvqVaryingIn:
1163
0
    case EvqVaryingOut:
1164
0
        return type.isStruct() || type.isArray();
1165
8
    case EvqUniform:
1166
8
        return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) ||
1167
8
               (type.isStruct() && type.containsOpaque());
1168
7
    default:
1169
7
        return false;
1170
15
    };
1171
0
}
1172
1173
// Top level variable flattening: construct data
1174
void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed)
1175
0
{
1176
0
    const TType& type = variable.getType();
1177
1178
    // If it's a standalone built-in, there is nothing to flatten
1179
0
    if (type.isBuiltIn() && !type.isStruct())
1180
0
        return;
1181
1182
1183
0
    auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
1184
0
                                                  TFlattenData(type.getQualifier().layoutBinding,
1185
0
                                                               type.getQualifier().layoutLocation)));
1186
1187
0
    if (type.isStruct() && type.getStruct()->size()==0)
1188
0
        return;
1189
    // if flattening arrayed io struct, array each member of dereferenced type
1190
0
    if (arrayed) {
1191
0
        const TType dereferencedType(type, 0);
1192
0
        flatten(variable, dereferencedType, entry.first->second, variable.getName(), linkage,
1193
0
                type.getQualifier(), type.getArraySizes());
1194
0
    } else {
1195
0
        flatten(variable, type, entry.first->second, variable.getName(), linkage,
1196
0
                type.getQualifier(), nullptr);
1197
0
    }
1198
0
}
1199
1200
// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1201
//
1202
// This is mutually recursive with flattenStruct and flattenArray.
1203
// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1204
// members, and later on, we want to turn a path through the tree structure into a final
1205
// location in this linear sequence.
1206
//
1207
// If the tree was N-ary, that can be directly calculated.  However, we are dealing with
1208
// arbitrary numbers - perhaps a struct of 7 members containing an array of 3.  Thus, we must
1209
// build a data structure to allow the sequence of bracket and dot operators on arrays and
1210
// structs to arrive at the proper member.
1211
//
1212
// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1213
// The leaves are the indexes into the flattened member array.
1214
// Each level will have the next location for the Nth item stored sequentially, so for instance:
1215
//
1216
// struct { float2 a[2]; int b; float4 c[3] };
1217
//
1218
// This will produce the following flattened tree:
1219
// Pos: 0  1   2    3  4    5  6   7     8   9  10   11  12 13
1220
//     (3, 7,  8,   5, 6,   0, 1,  2,   11, 12, 13,   3,  4, 5}
1221
//
1222
// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1223
//   (0+2) = 8  -->  (8+1) = 12 -->   12 = 4
1224
//
1225
// so the 4th flattened member in traversal order is ours.
1226
//
1227
int HlslParseContext::flatten(const TVariable& variable, const TType& type,
1228
                              TFlattenData& flattenData, TString name, bool linkage,
1229
                              const TQualifier& outerQualifier,
1230
                              const TArraySizes* builtInArraySizes)
1231
0
{
1232
    // If something is an arrayed struct, the array flattener will recursively call flatten()
1233
    // to then flatten the struct, so this is an "if else": we don't do both.
1234
0
    if (type.isArray())
1235
0
        return flattenArray(variable, type, flattenData, name, linkage, outerQualifier);
1236
0
    else if (type.isStruct())
1237
0
        return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes);
1238
0
    else {
1239
0
        assert(0); // should never happen
1240
0
        return -1;
1241
0
    }
1242
0
}
1243
1244
// Add a single flattened member to the flattened data being tracked for the composite
1245
// Returns true for the final flattening level.
1246
int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData,
1247
                                         const TString& memberName, bool linkage,
1248
                                         const TQualifier& outerQualifier,
1249
                                         const TArraySizes* builtInArraySizes)
1250
0
{
1251
0
    if (!shouldFlatten(type, outerQualifier.storage, false)) {
1252
        // This is as far as we flatten.  Insert the variable.
1253
0
        TVariable* memberVariable = makeInternalVariable(memberName, type);
1254
0
        mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1255
1256
0
        if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
1257
0
            memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1258
1259
0
        if (memberVariable->getType().isBuiltIn()) {
1260
            // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number)
1261
0
            memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1262
0
        } else {
1263
            // inherited locations must be auto bumped, not replicated
1264
0
            if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
1265
0
                memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
1266
0
                flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language);
1267
0
                nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
1268
0
            }
1269
0
        }
1270
1271
        // Only propagate arraysizes here for arrayed io
1272
0
        if (variable.getType().getQualifier().isArrayedIo(language) && builtInArraySizes != nullptr)
1273
0
            memberVariable->getWritableType().copyArraySizes(*builtInArraySizes);
1274
1275
0
        flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
1276
0
        flattenData.members.push_back(memberVariable);
1277
1278
0
        if (linkage)
1279
0
            trackLinkage(*memberVariable);
1280
1281
0
        return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference
1282
0
    } else {
1283
        // Further recursion required
1284
0
        return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes);
1285
0
    }
1286
0
}
1287
1288
// Figure out the mapping between an aggregate's top members and an
1289
// equivalent set of individual variables.
1290
//
1291
// Assumes shouldFlatten() or equivalent was called first.
1292
int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type,
1293
                                    TFlattenData& flattenData, TString name, bool linkage,
1294
                                    const TQualifier& outerQualifier,
1295
                                    const TArraySizes* builtInArraySizes)
1296
0
{
1297
0
    assert(type.isStruct());
1298
1299
0
    auto members = *type.getStruct();
1300
1301
    // Reserve space for this tree level.
1302
0
    int start = static_cast<int>(flattenData.offsets.size());
1303
0
    int pos = start;
1304
0
    flattenData.offsets.resize(int(pos + members.size()), -1);
1305
1306
0
    for (int member = 0; member < (int)members.size(); ++member) {
1307
0
        TType& dereferencedType = *members[member].type;
1308
0
        if (dereferencedType.isBuiltIn())
1309
0
            splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier);
1310
0
        else {
1311
0
            const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1312
0
                                                name + "." + dereferencedType.getFieldName(),
1313
0
                                                linkage, outerQualifier,
1314
0
                                                builtInArraySizes == nullptr && dereferencedType.isArray()
1315
0
                                                                       ? dereferencedType.getArraySizes()
1316
0
                                                                       : builtInArraySizes);
1317
0
            flattenData.offsets[pos++] = mpos;
1318
0
        }
1319
0
    }
1320
1321
0
    return start;
1322
0
}
1323
1324
// Figure out mapping between an array's members and an
1325
// equivalent set of individual variables.
1326
//
1327
// Assumes shouldFlatten() or equivalent was called first.
1328
int HlslParseContext::flattenArray(const TVariable& variable, const TType& type,
1329
                                   TFlattenData& flattenData, TString name, bool linkage,
1330
                                   const TQualifier& outerQualifier)
1331
0
{
1332
0
    assert(type.isSizedArray());
1333
1334
0
    const int size = type.getOuterArraySize();
1335
0
    const TType dereferencedType(type, 0);
1336
1337
0
    if (name.empty())
1338
0
        name = variable.getName();
1339
1340
    // Reserve space for this tree level.
1341
0
    int start = static_cast<int>(flattenData.offsets.size());
1342
0
    int pos   = start;
1343
0
    flattenData.offsets.resize(int(pos + size), -1);
1344
1345
0
    for (int element=0; element < size; ++element) {
1346
0
        char elementNumBuf[20];  // sufficient for MAXINT
1347
0
        snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
1348
0
        const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1349
0
                                            name + elementNumBuf, linkage, outerQualifier,
1350
0
                                            type.getArraySizes());
1351
1352
0
        flattenData.offsets[pos++] = mpos;
1353
0
    }
1354
1355
0
    return start;
1356
0
}
1357
1358
// Return true if we have flattened this node.
1359
bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1360
6
{
1361
6
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1362
3
           wasFlattened(node->getAsSymbolNode()->getId());
1363
6
}
1364
1365
// Return true if we have split this structure
1366
bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1367
6
{
1368
6
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1369
3
           wasSplit(node->getAsSymbolNode()->getId());
1370
6
}
1371
1372
// Turn an access into an aggregate that was flattened to instead be
1373
// an access to the individual variable the member was flattened to.
1374
// Assumes wasFlattened() or equivalent was called first.
1375
TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
1376
0
{
1377
0
    const TType dereferencedType(base->getType(), member);  // dereferenced type
1378
0
    const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
1379
0
    TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage,
1380
0
                                            dereferencedType, symbolNode.getFlattenSubset());
1381
1382
0
    return flattened ? flattened : base;
1383
0
}
1384
TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage,
1385
    const TType& dereferencedType, int subset)
1386
0
{
1387
0
    const auto flattenData = flattenMap.find(uniqueId);
1388
1389
0
    if (flattenData == flattenMap.end())
1390
0
        return nullptr;
1391
1392
    // Calculate new cumulative offset from the packed tree
1393
0
    int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member];
1394
1395
0
    TIntermSymbol* subsetSymbol;
1396
0
    if (!shouldFlatten(dereferencedType, outerStorage, false)) {
1397
        // Finished flattening: create symbol for variable
1398
0
        member = flattenData->second.offsets[newSubset];
1399
0
        const TVariable* memberVariable = flattenData->second.members[member];
1400
0
        subsetSymbol = intermediate.addSymbol(*memberVariable);
1401
0
        subsetSymbol->setFlattenSubset(-1);
1402
0
    } else {
1403
1404
        // If this is not the final flattening, accumulate the position and return
1405
        // an object of the partially dereferenced type.
1406
0
        subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", getLanguage(), dereferencedType);
1407
0
        subsetSymbol->setFlattenSubset(newSubset);
1408
0
    }
1409
1410
0
    return subsetSymbol;
1411
0
}
1412
1413
// For finding where the first leaf is in a subtree of a multi-level aggregate
1414
// that is just getting a subset assigned. Follows the same logic as flattenAccess,
1415
// but logically going down the "left-most" tree branch each step of the way.
1416
//
1417
// Returns the offset into the first leaf of the subset.
1418
int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const
1419
0
{
1420
0
    const TIntermSymbol* sym = node.getAsSymbolNode();
1421
0
    if (sym == nullptr)
1422
0
        return 0;
1423
0
    if (!sym->isArray() && !sym->isStruct())
1424
0
        return 0;
1425
0
    int subset = sym->getFlattenSubset();
1426
0
    if (subset == -1)
1427
0
        return 0;
1428
1429
    // Getting this far means a partial aggregate is identified by the flatten subset.
1430
    // Find the first leaf of the subset.
1431
1432
0
    const auto flattenData = flattenMap.find(sym->getId());
1433
0
    if (flattenData == flattenMap.end())
1434
0
        return 0;
1435
1436
0
    return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets);
1437
1438
0
    do {
1439
0
        subset = flattenData->second.offsets[subset];
1440
0
    } while (true);
1441
0
}
1442
// Recursively do the desent
1443
int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const
1444
0
{
1445
0
    if (!type.isArray() && !type.isStruct())
1446
0
        return offsets[subset];
1447
0
    TType derefType(type, 0);
1448
0
    return findSubtreeOffset(derefType, offsets[subset], offsets);
1449
0
}
1450
1451
// Find and return the split IO TVariable for id, or nullptr if none.
1452
TVariable* HlslParseContext::getSplitNonIoVar(long long id) const
1453
0
{
1454
0
    const auto splitNonIoVar = splitNonIoVars.find(id);
1455
0
    if (splitNonIoVar == splitNonIoVars.end())
1456
0
        return nullptr;
1457
1458
0
    return splitNonIoVar->second;
1459
0
}
1460
1461
// Pass through to base class after remembering built-in mappings.
1462
void HlslParseContext::trackLinkage(TSymbol& symbol)
1463
9
{
1464
9
    TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
1465
1466
9
    if (biType != EbvNone)
1467
0
        builtInTessLinkageSymbols[biType] = symbol.clone();
1468
1469
9
    TParseContextBase::trackLinkage(symbol);
1470
9
}
1471
1472
1473
// Returns true if the built-in is a clip or cull distance variable.
1474
bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn)
1475
6
{
1476
6
    return builtIn == EbvClipDistance || builtIn == EbvCullDistance;
1477
6
}
1478
1479
// Some types require fixed array sizes in SPIR-V, but can be scalars or
1480
// arrays of sizes SPIR-V doesn't allow.  For example, tessellation factors.
1481
// This creates the right size.  A conversion is performed when the internal
1482
// type is copied to or from the external type.  This corrects the externally
1483
// facing input or output type to abide downstream semantics.
1484
void HlslParseContext::fixBuiltInIoType(TType& type)
1485
0
{
1486
0
    int requiredArraySize = 0;
1487
0
    int requiredVectorSize = 0;
1488
1489
0
    switch (type.getQualifier().builtIn) {
1490
0
    case EbvTessLevelOuter: requiredArraySize = 4; break;
1491
0
    case EbvTessLevelInner: requiredArraySize = 2; break;
1492
1493
0
    case EbvSampleMask:
1494
0
        {
1495
            // Promote scalar to array of size 1.  Leave existing arrays alone.
1496
0
            if (!type.isArray())
1497
0
                requiredArraySize = 1;
1498
0
            break;
1499
0
        }
1500
1501
0
    case EbvWorkGroupId:        requiredVectorSize = 3; break;
1502
0
    case EbvGlobalInvocationId: requiredVectorSize = 3; break;
1503
0
    case EbvLocalInvocationId:  requiredVectorSize = 3; break;
1504
0
    case EbvTessCoord:          requiredVectorSize = 3; break;
1505
1506
0
    default:
1507
0
        if (isClipOrCullDistance(type)) {
1508
0
            const int loc = type.getQualifier().layoutLocation;
1509
1510
0
            if (type.getQualifier().builtIn == EbvClipDistance) {
1511
0
                if (type.getQualifier().storage == EvqVaryingIn)
1512
0
                    clipSemanticNSizeIn[loc] = type.getVectorSize();
1513
0
                else
1514
0
                    clipSemanticNSizeOut[loc] = type.getVectorSize();
1515
0
            } else {
1516
0
                if (type.getQualifier().storage == EvqVaryingIn)
1517
0
                    cullSemanticNSizeIn[loc] = type.getVectorSize();
1518
0
                else
1519
0
                    cullSemanticNSizeOut[loc] = type.getVectorSize();
1520
0
            }
1521
0
        }
1522
1523
0
        return;
1524
0
    }
1525
1526
    // Alter or set vector size as needed.
1527
0
    if (requiredVectorSize > 0) {
1528
0
        TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize);
1529
0
        newType.getQualifier() = type.getQualifier();
1530
1531
0
        type.shallowCopy(newType);
1532
0
    }
1533
1534
    // Alter or set array size as needed.
1535
0
    if (requiredArraySize > 0) {
1536
0
        if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) {
1537
0
            TArraySizes* arraySizes = new TArraySizes;
1538
0
            arraySizes->addInnerSize(requiredArraySize);
1539
0
            type.transferArraySizes(arraySizes);
1540
0
        }
1541
0
    }
1542
0
}
1543
1544
// Variables that correspond to the user-interface in and out of a stage
1545
// (not the built-in interface) are
1546
//  - assigned locations
1547
//  - registered as a linkage node (part of the stage's external interface).
1548
// Assumes it is called in the order in which locations should be assigned.
1549
void HlslParseContext::assignToInterface(TVariable& variable)
1550
0
{
1551
0
    const auto assignLocation = [&](TVariable& variable) {
1552
0
        TType& type = variable.getWritableType();
1553
0
        if (!type.isStruct() || type.getStruct()->size() > 0) {
1554
0
            TQualifier& qualifier = type.getQualifier();
1555
0
            if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1556
0
                if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) {
1557
                    // Strip off the outer array dimension for those having an extra one.
1558
0
                    int size;
1559
0
                    if (type.isArray() && qualifier.isArrayedIo(language)) {
1560
0
                        TType elementType(type, 0);
1561
0
                        size = intermediate.computeTypeLocationSize(elementType, language);
1562
0
                    } else
1563
0
                        size = intermediate.computeTypeLocationSize(type, language);
1564
1565
0
                    if (qualifier.storage == EvqVaryingIn) {
1566
0
                        variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
1567
0
                        nextInLocation += size;
1568
0
                    } else {
1569
0
                        variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
1570
0
                        nextOutLocation += size;
1571
0
                    }
1572
0
                }
1573
0
                trackLinkage(variable);
1574
0
            }
1575
0
        }
1576
0
    };
1577
1578
0
    if (wasFlattened(variable.getUniqueId())) {
1579
0
        auto& memberList = flattenMap[variable.getUniqueId()].members;
1580
0
        for (auto member = memberList.begin(); member != memberList.end(); ++member)
1581
0
            assignLocation(**member);
1582
0
    } else if (wasSplit(variable.getUniqueId())) {
1583
0
        TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId());
1584
0
        assignLocation(*splitIoVar);
1585
0
    } else {
1586
0
        assignLocation(variable);
1587
0
    }
1588
0
}
1589
1590
//
1591
// Handle seeing a function declarator in the grammar.  This is the precursor
1592
// to recognizing a function prototype or function definition.
1593
//
1594
void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
1595
448k
{
1596
    //
1597
    // Multiple declarations of the same function name are allowed.
1598
    //
1599
    // If this is a definition, the definition production code will check for redefinitions
1600
    // (we don't know at this point if it's a definition or not).
1601
    //
1602
448k
    bool builtIn;
1603
448k
    TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1604
448k
    const TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1605
1606
448k
    if (prototype) {
1607
        // All built-in functions are defined, even though they don't have a body.
1608
        // Count their prototype as a definition instead.
1609
448k
        if (symbolTable.atBuiltInLevel())
1610
448k
            function.setDefined();
1611
0
        else {
1612
0
            if (prevDec && ! builtIn)
1613
0
                symbol->getAsFunction()->setPrototyped();  // need a writable one, but like having prevDec as a const
1614
0
            function.setPrototyped();
1615
0
        }
1616
448k
    }
1617
1618
    // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1619
    // other forms of name collisions.
1620
448k
    if (! symbolTable.insert(function))
1621
0
        error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
1622
448k
}
1623
1624
// For struct buffers with counters, we must pass the counter buffer as hidden parameter.
1625
// This adds the hidden parameter to the parameter list in 'paramNodes' if needed.
1626
// Otherwise, it's a no-op
1627
void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param,
1628
                                                         TIntermAggregate*& paramNodes)
1629
0
{
1630
0
    if (! hasStructBuffCounter(*param.type))
1631
0
        return;
1632
1633
0
    const TString counterBlockName(intermediate.addCounterBufferName(*param.name));
1634
1635
0
    TType counterType;
1636
0
    counterBufferType(loc, counterType);
1637
0
    TVariable *variable = makeInternalVariable(counterBlockName, counterType);
1638
1639
0
    if (! symbolTable.insert(*variable))
1640
0
        error(loc, "redefinition", variable->getName().c_str(), "");
1641
1642
0
    paramNodes = intermediate.growAggregate(paramNodes,
1643
0
                                            intermediate.addSymbol(*variable, loc),
1644
0
                                            loc);
1645
0
}
1646
1647
//
1648
// Handle seeing the function prototype in front of a function definition in the grammar.
1649
// The body is handled after this function returns.
1650
//
1651
// Returns an aggregate of parameter-symbol nodes.
1652
//
1653
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
1654
                                                             const TAttributes& attributes,
1655
                                                             TIntermNode*& entryPointTree)
1656
2
{
1657
2
    currentCaller = function.getMangledName();
1658
2
    TSymbol* symbol = symbolTable.find(function.getMangledName());
1659
2
    TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1660
1661
2
    if (prevDec == nullptr)
1662
0
        error(loc, "can't find function", function.getName().c_str(), "");
1663
    // Note:  'prevDec' could be 'function' if this is the first time we've seen function
1664
    // as it would have just been put in the symbol table.  Otherwise, we're looking up
1665
    // an earlier occurrence.
1666
1667
2
    if (prevDec && prevDec->isDefined()) {
1668
        // Then this function already has a body.
1669
0
        error(loc, "function already has a body", function.getName().c_str(), "");
1670
0
    }
1671
2
    if (prevDec && ! prevDec->isDefined()) {
1672
2
        prevDec->setDefined();
1673
1674
        // Remember the return type for later checking for RETURN statements.
1675
2
        currentFunctionType = &(prevDec->getType());
1676
2
    } else
1677
0
        currentFunctionType = new TType(EbtVoid);
1678
2
    functionReturnsValue = false;
1679
1680
    // Entry points need different I/O and other handling, transform it so the
1681
    // rest of this function doesn't care.
1682
2
    entryPointTree = transformEntryPoint(loc, function, attributes);
1683
1684
    //
1685
    // New symbol table scope for body of function plus its arguments
1686
    //
1687
2
    pushScope();
1688
1689
    //
1690
    // Insert parameters into the symbol table.
1691
    // If the parameter has no name, it's not an error, just don't insert it
1692
    // (could be used for unused args).
1693
    //
1694
    // Also, accumulate the list of parameters into the AST, so lower level code
1695
    // knows where to find parameters.
1696
    //
1697
2
    TIntermAggregate* paramNodes = new TIntermAggregate;
1698
2
    for (int i = 0; i < function.getParamCount(); i++) {
1699
0
        TParameter& param = function[i];
1700
0
        if (param.name != nullptr) {
1701
0
            TVariable *variable = new TVariable(param.name, *param.type);
1702
1703
0
            if (i == 0 && function.hasImplicitThis()) {
1704
                // Anonymous 'this' members are already in a symbol-table level,
1705
                // and we need to know what function parameter to map them to.
1706
0
                symbolTable.makeInternalVariable(*variable);
1707
0
                pushImplicitThis(variable);
1708
0
            }
1709
1710
            // Insert the parameters with name in the symbol table.
1711
0
            if (! symbolTable.insert(*variable))
1712
0
                error(loc, "redefinition", variable->getName().c_str(), "");
1713
1714
            // Add parameters to the AST list.
1715
0
            if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) {
1716
                // Expand the AST parameter nodes (but not the name mangling or symbol table view)
1717
                // for structures that need to be flattened.
1718
0
                flatten(*variable, false);
1719
0
                const TTypeList* structure = variable->getType().getStruct();
1720
0
                for (int mem = 0; mem < (int)structure->size(); ++mem) {
1721
0
                    paramNodes = intermediate.growAggregate(paramNodes,
1722
0
                                                            flattenAccess(variable->getUniqueId(), mem,
1723
0
                                                                          variable->getType().getQualifier().storage,
1724
0
                                                                          *(*structure)[mem].type),
1725
0
                                                            loc);
1726
0
                }
1727
0
            } else {
1728
                // Add the parameter to the AST
1729
0
                paramNodes = intermediate.growAggregate(paramNodes,
1730
0
                                                        intermediate.addSymbol(*variable, loc),
1731
0
                                                        loc);
1732
0
            }
1733
1734
            // Add hidden AST parameter for struct buffer counters, if needed.
1735
0
            addStructBufferHiddenCounterParam(loc, param, paramNodes);
1736
0
        } else
1737
0
            paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
1738
0
    }
1739
2
    if (function.hasIllegalImplicitThis())
1740
0
        pushImplicitThis(nullptr);
1741
1742
2
    intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1743
2
    loopNestingLevel = 0;
1744
2
    controlFlowNestingLevel = 0;
1745
2
    postEntryPointReturn = false;
1746
1747
2
    return paramNodes;
1748
2
}
1749
1750
// Handle all [attrib] attribute for the shader entry point
1751
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
1752
0
{
1753
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1754
0
        switch (it->name) {
1755
0
        case EatNumThreads:
1756
0
        {
1757
0
            const TIntermSequence& sequence = it->args->getSequence();
1758
0
            for (int lid = 0; lid < int(sequence.size()); ++lid)
1759
0
                intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1760
0
            break;
1761
0
        }
1762
0
        case EatInstance: 
1763
0
        {
1764
0
            int invocations;
1765
1766
0
            if (!it->getInt(invocations)) {
1767
0
                error(loc, "invalid instance", "", "");
1768
0
            } else {
1769
0
                if (!intermediate.setInvocations(invocations))
1770
0
                    error(loc, "cannot change previously set instance attribute", "", "");
1771
0
            }
1772
0
            break;
1773
0
        }
1774
0
        case EatMaxVertexCount:
1775
0
        {
1776
0
            int maxVertexCount;
1777
1778
0
            if (! it->getInt(maxVertexCount)) {
1779
0
                error(loc, "invalid maxvertexcount", "", "");
1780
0
            } else {
1781
0
                if (! intermediate.setVertices(maxVertexCount))
1782
0
                    error(loc, "cannot change previously set maxvertexcount attribute", "", "");
1783
0
            }
1784
0
            break;
1785
0
        }
1786
0
        case EatPatchConstantFunc:
1787
0
        {
1788
0
            TString pcfName;
1789
0
            if (! it->getString(pcfName, 0, false)) {
1790
0
                error(loc, "invalid patch constant function", "", "");
1791
0
            } else {
1792
0
                patchConstantFunctionName = pcfName;
1793
0
            }
1794
0
            break;
1795
0
        }
1796
0
        case EatDomain:
1797
0
        {
1798
            // Handle [domain("...")]
1799
0
            TString domainStr;
1800
0
            if (! it->getString(domainStr)) {
1801
0
                error(loc, "invalid domain", "", "");
1802
0
            } else {
1803
0
                TLayoutGeometry domain = ElgNone;
1804
1805
0
                if (domainStr == "tri") {
1806
0
                    domain = ElgTriangles;
1807
0
                } else if (domainStr == "quad") {
1808
0
                    domain = ElgQuads;
1809
0
                } else if (domainStr == "isoline") {
1810
0
                    domain = ElgIsolines;
1811
0
                } else {
1812
0
                    error(loc, "unsupported domain type", domainStr.c_str(), "");
1813
0
                }
1814
1815
0
                if (language == EShLangTessEvaluation) {
1816
0
                    if (! intermediate.setInputPrimitive(domain))
1817
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1818
0
                } else {
1819
0
                    if (! intermediate.setOutputPrimitive(domain))
1820
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1821
0
                }
1822
0
            }
1823
0
            break;
1824
0
        }
1825
0
        case EatOutputTopology:
1826
0
        {
1827
            // Handle [outputtopology("...")]
1828
0
            TString topologyStr;
1829
0
            if (! it->getString(topologyStr)) {
1830
0
                error(loc, "invalid outputtopology", "", "");
1831
0
            } else {
1832
0
                TVertexOrder vertexOrder = EvoNone;
1833
0
                TLayoutGeometry primitive = ElgNone;
1834
1835
0
                if (topologyStr == "point") {
1836
0
                    intermediate.setPointMode();
1837
0
                } else if (topologyStr == "line") {
1838
0
                    primitive = ElgIsolines;
1839
0
                } else if (topologyStr == "triangle_cw") {
1840
0
                    vertexOrder = EvoCw;
1841
0
                    primitive = ElgTriangles;
1842
0
                } else if (topologyStr == "triangle_ccw") {
1843
0
                    vertexOrder = EvoCcw;
1844
0
                    primitive = ElgTriangles;
1845
0
                } else {
1846
0
                    error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
1847
0
                }
1848
1849
0
                if (vertexOrder != EvoNone) {
1850
0
                    if (! intermediate.setVertexOrder(vertexOrder)) {
1851
0
                        error(loc, "cannot change previously set outputtopology",
1852
0
                              TQualifier::getVertexOrderString(vertexOrder), "");
1853
0
                    }
1854
0
                }
1855
0
                if (primitive != ElgNone)
1856
0
                    intermediate.setOutputPrimitive(primitive);
1857
0
            }
1858
0
            break;
1859
0
        }
1860
0
        case EatPartitioning:
1861
0
        {
1862
            // Handle [partitioning("...")]
1863
0
            TString partitionStr;
1864
0
            if (! it->getString(partitionStr)) {
1865
0
                error(loc, "invalid partitioning", "", "");
1866
0
            } else {
1867
0
                TVertexSpacing partitioning = EvsNone;
1868
1869
0
                if (partitionStr == "integer") {
1870
0
                    partitioning = EvsEqual;
1871
0
                } else if (partitionStr == "fractional_even") {
1872
0
                    partitioning = EvsFractionalEven;
1873
0
                } else if (partitionStr == "fractional_odd") {
1874
0
                    partitioning = EvsFractionalOdd;
1875
                    //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
1876
0
                } else {
1877
0
                    error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
1878
0
                }
1879
1880
0
                if (! intermediate.setVertexSpacing(partitioning))
1881
0
                    error(loc, "cannot change previously set partitioning",
1882
0
                          TQualifier::getVertexSpacingString(partitioning), "");
1883
0
            }
1884
0
            break;
1885
0
        }
1886
0
        case EatOutputControlPoints:
1887
0
        {
1888
            // Handle [outputcontrolpoints("...")]
1889
0
            int ctrlPoints;
1890
0
            if (! it->getInt(ctrlPoints)) {
1891
0
                error(loc, "invalid outputcontrolpoints", "", "");
1892
0
            } else {
1893
0
                if (! intermediate.setVertices(ctrlPoints)) {
1894
0
                    error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
1895
0
                }
1896
0
            }
1897
0
            break;
1898
0
        }
1899
0
        case EatEarlyDepthStencil:
1900
0
            intermediate.setEarlyFragmentTests();
1901
0
            break;
1902
0
        case EatBuiltIn:
1903
0
        case EatLocation:
1904
            // tolerate these because of dual use of entrypoint and type attributes
1905
0
            break;
1906
0
        default:
1907
0
            warn(loc, "attribute does not apply to entry point", "", "");
1908
0
            break;
1909
0
        }
1910
0
    }
1911
0
}
1912
1913
// Update the given type with any type-like attribute information in the
1914
// attributes.
1915
void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
1916
    bool allowEntry)
1917
1.45M
{
1918
1.45M
    if (attributes.size() == 0)
1919
1.45M
        return;
1920
1921
0
    int value;
1922
0
    TString builtInString;
1923
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1924
0
        switch (it->name) {
1925
0
        case EatLocation:
1926
            // location
1927
0
            if (it->getInt(value))
1928
0
                type.getQualifier().layoutLocation = value;
1929
0
            else
1930
0
                error(loc, "needs a literal integer", "location", "");
1931
0
            break;
1932
0
        case EatBinding:
1933
            // binding
1934
0
            if (it->getInt(value)) {
1935
0
                type.getQualifier().layoutBinding = value;
1936
0
                type.getQualifier().layoutSet = 0;
1937
0
            } else
1938
0
                error(loc, "needs a literal integer", "binding", "");
1939
            // set
1940
0
            if (it->getInt(value, 1))
1941
0
                type.getQualifier().layoutSet = value;
1942
0
            break;
1943
0
        case EatGlobalBinding:
1944
            // global cbuffer binding
1945
0
            if (it->getInt(value))
1946
0
                globalUniformBinding = value;
1947
0
            else
1948
0
                error(loc, "needs a literal integer", "global binding", "");
1949
            // global cbuffer set
1950
0
            if (it->getInt(value, 1))
1951
0
                globalUniformSet = value;
1952
0
            break;
1953
0
        case EatInputAttachment:
1954
            // input attachment
1955
0
            if (it->getInt(value))
1956
0
                type.getQualifier().layoutAttachment = value;
1957
0
            else
1958
0
                error(loc, "needs a literal integer", "input attachment", "");
1959
0
            break;
1960
0
        case EatBuiltIn:
1961
            // PointSize built-in
1962
0
            if (it->getString(builtInString, 0, false)) {
1963
0
                if (builtInString == "PointSize")
1964
0
                    type.getQualifier().builtIn = EbvPointSize;
1965
0
            }
1966
0
            break;
1967
0
        case EatPushConstant:
1968
            // push_constant
1969
0
            type.getQualifier().layoutPushConstant = true;
1970
0
            break;
1971
0
        case EatConstantId:
1972
            // specialization constant
1973
0
            if (type.getQualifier().storage != EvqConst) {
1974
0
                error(loc, "needs a const type", "constant_id", "");
1975
0
                break;
1976
0
            }
1977
0
            if (it->getInt(value)) {
1978
0
                TSourceLoc loc;
1979
0
                loc.init();
1980
0
                setSpecConstantId(loc, type.getQualifier(), (unsigned)value);
1981
0
            }
1982
0
            break;
1983
1984
        // image formats
1985
0
        case EatFormatRgba32f:      type.getQualifier().layoutFormat = ElfRgba32f;      break;
1986
0
        case EatFormatRgba16f:      type.getQualifier().layoutFormat = ElfRgba16f;      break;
1987
0
        case EatFormatR32f:         type.getQualifier().layoutFormat = ElfR32f;         break;
1988
0
        case EatFormatRgba8:        type.getQualifier().layoutFormat = ElfRgba8;        break;
1989
0
        case EatFormatRgba8Snorm:   type.getQualifier().layoutFormat = ElfRgba8Snorm;   break;
1990
0
        case EatFormatRg32f:        type.getQualifier().layoutFormat = ElfRg32f;        break;
1991
0
        case EatFormatRg16f:        type.getQualifier().layoutFormat = ElfRg16f;        break;
1992
0
        case EatFormatR11fG11fB10f: type.getQualifier().layoutFormat = ElfR11fG11fB10f; break;
1993
0
        case EatFormatR16f:         type.getQualifier().layoutFormat = ElfR16f;         break;
1994
0
        case EatFormatRgba16:       type.getQualifier().layoutFormat = ElfRgba16;       break;
1995
0
        case EatFormatRgb10A2:      type.getQualifier().layoutFormat = ElfRgb10A2;      break;
1996
0
        case EatFormatRg16:         type.getQualifier().layoutFormat = ElfRg16;         break;
1997
0
        case EatFormatRg8:          type.getQualifier().layoutFormat = ElfRg8;          break;
1998
0
        case EatFormatR16:          type.getQualifier().layoutFormat = ElfR16;          break;
1999
0
        case EatFormatR8:           type.getQualifier().layoutFormat = ElfR8;           break;
2000
0
        case EatFormatRgba16Snorm:  type.getQualifier().layoutFormat = ElfRgba16Snorm;  break;
2001
0
        case EatFormatRg16Snorm:    type.getQualifier().layoutFormat = ElfRg16Snorm;    break;
2002
0
        case EatFormatRg8Snorm:     type.getQualifier().layoutFormat = ElfRg8Snorm;     break;
2003
0
        case EatFormatR16Snorm:     type.getQualifier().layoutFormat = ElfR16Snorm;     break;
2004
0
        case EatFormatR8Snorm:      type.getQualifier().layoutFormat = ElfR8Snorm;      break;
2005
0
        case EatFormatRgba32i:      type.getQualifier().layoutFormat = ElfRgba32i;      break;
2006
0
        case EatFormatRgba16i:      type.getQualifier().layoutFormat = ElfRgba16i;      break;
2007
0
        case EatFormatRgba8i:       type.getQualifier().layoutFormat = ElfRgba8i;       break;
2008
0
        case EatFormatR32i:         type.getQualifier().layoutFormat = ElfR32i;         break;
2009
0
        case EatFormatRg32i:        type.getQualifier().layoutFormat = ElfRg32i;        break;
2010
0
        case EatFormatRg16i:        type.getQualifier().layoutFormat = ElfRg16i;        break;
2011
0
        case EatFormatRg8i:         type.getQualifier().layoutFormat = ElfRg8i;         break;
2012
0
        case EatFormatR16i:         type.getQualifier().layoutFormat = ElfR16i;         break;
2013
0
        case EatFormatR8i:          type.getQualifier().layoutFormat = ElfR8i;          break;
2014
0
        case EatFormatRgba32ui:     type.getQualifier().layoutFormat = ElfRgba32ui;     break;
2015
0
        case EatFormatRgba16ui:     type.getQualifier().layoutFormat = ElfRgba16ui;     break;
2016
0
        case EatFormatRgba8ui:      type.getQualifier().layoutFormat = ElfRgba8ui;      break;
2017
0
        case EatFormatR32ui:        type.getQualifier().layoutFormat = ElfR32ui;        break;
2018
0
        case EatFormatRgb10a2ui:    type.getQualifier().layoutFormat = ElfRgb10a2ui;    break;
2019
0
        case EatFormatRg32ui:       type.getQualifier().layoutFormat = ElfRg32ui;       break;
2020
0
        case EatFormatRg16ui:       type.getQualifier().layoutFormat = ElfRg16ui;       break;
2021
0
        case EatFormatRg8ui:        type.getQualifier().layoutFormat = ElfRg8ui;        break;
2022
0
        case EatFormatR16ui:        type.getQualifier().layoutFormat = ElfR16ui;        break;
2023
0
        case EatFormatR8ui:         type.getQualifier().layoutFormat = ElfR8ui;         break;
2024
0
        case EatFormatUnknown:      type.getQualifier().layoutFormat = ElfNone;         break;
2025
2026
0
        case EatNonWritable:  type.getQualifier().readonly = true;   break;
2027
0
        case EatNonReadable:  type.getQualifier().writeonly = true;  break;
2028
2029
0
        default:
2030
0
            if (! allowEntry)
2031
0
                warn(loc, "attribute does not apply to a type", "", "");
2032
0
            break;
2033
0
        }
2034
0
    }
2035
0
}
2036
2037
//
2038
// Do all special handling for the entry point, including wrapping
2039
// the shader's entry point with the official entry point that will call it.
2040
//
2041
// The following:
2042
//
2043
//    retType shaderEntryPoint(args...) // shader declared entry point
2044
//    { body }
2045
//
2046
// Becomes
2047
//
2048
//    out retType ret;
2049
//    in iargs<that are input>...;
2050
//    out oargs<that are output> ...;
2051
//
2052
//    void shaderEntryPoint()    // synthesized, but official, entry point
2053
//    {
2054
//        args<that are input> = iargs...;
2055
//        ret = @shaderEntryPoint(args...);
2056
//        oargs = args<that are output>...;
2057
//    }
2058
//    retType @shaderEntryPoint(args...)
2059
//    { body }
2060
//
2061
// The symbol table will still map the original entry point name to the
2062
// the modified function and its new name:
2063
//
2064
//    symbol table:  shaderEntryPoint  ->   @shaderEntryPoint
2065
//
2066
// Returns nullptr if no entry-point tree was built, otherwise, returns
2067
// a subtree that creates the entry point.
2068
//
2069
TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
2070
                                                   const TAttributes& attributes)
2071
2
{
2072
    // Return true if this is a tessellation patch constant function input to a domain shader.
2073
2
    const auto isDsPcfInput = [this](const TType& type) {
2074
0
        return language == EShLangTessEvaluation &&
2075
0
        type.contains([](const TType* t) {
2076
0
                return t->getQualifier().builtIn == EbvTessLevelOuter ||
2077
0
                       t->getQualifier().builtIn == EbvTessLevelInner;
2078
0
            });
2079
0
    };
2080
2081
    // if we aren't in the entry point, fix the IO as such and exit
2082
2
    if (! isEntrypointName(userFunction.getName())) {
2083
2
        remapNonEntryPointIO(userFunction);
2084
2
        return nullptr;
2085
2
    }
2086
2087
0
    entryPointFunction = &userFunction; // needed in finish()
2088
2089
    // Handle entry point attributes
2090
0
    handleEntryPointAttributes(loc, attributes);
2091
2092
    // entry point logic...
2093
2094
    // Move parameters and return value to shader in/out
2095
0
    TVariable* entryPointOutput; // gets created in remapEntryPointIO
2096
0
    TVector<TVariable*> inputs;
2097
0
    TVector<TVariable*> outputs;
2098
0
    remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
2099
2100
    // Further this return/in/out transform by flattening, splitting, and assigning locations
2101
0
    const auto makeVariableInOut = [&](TVariable& variable) {
2102
0
        if (variable.getType().isStruct()) {
2103
0
            bool arrayed = variable.getType().getQualifier().isArrayedIo(language);
2104
0
            flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */, arrayed);
2105
0
        }
2106
        // TODO: flatten arrays too
2107
        // TODO: flatten everything in I/O
2108
        // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed
2109
2110
        // For clip and cull distance, multiple output variables potentially get merged
2111
        // into one in assignClipCullDistance.  That code in assignClipCullDistance
2112
        // handles the interface logic, so we avoid it here in that case.
2113
0
        if (!isClipOrCullDistance(variable.getType()))
2114
0
            assignToInterface(variable);
2115
0
    };
2116
0
    if (entryPointOutput != nullptr)
2117
0
        makeVariableInOut(*entryPointOutput);
2118
0
    for (auto it = inputs.begin(); it != inputs.end(); ++it)
2119
0
        if (!isDsPcfInput((*it)->getType()))  // wait until the end for PCF input (see comment below)
2120
0
            makeVariableInOut(*(*it));
2121
0
    for (auto it = outputs.begin(); it != outputs.end(); ++it)
2122
0
        makeVariableInOut(*(*it));
2123
2124
    // In the domain shader, PCF input must be at the end of the linkage.  That's because in the
2125
    // hull shader there is no ordering: the output comes from the separate PCF, which does not
2126
    // participate in the argument list.  That is always put at the end of the HS linkage, so the
2127
    // input side of the DS must match.  The argument may be in any position in the DS argument list
2128
    // however, so this ensures the linkage is built in the correct order regardless of argument order.
2129
0
    if (language == EShLangTessEvaluation) {
2130
0
        for (auto it = inputs.begin(); it != inputs.end(); ++it)
2131
0
            if (isDsPcfInput((*it)->getType()))
2132
0
                makeVariableInOut(*(*it));
2133
0
    }
2134
2135
    // Add uniform parameters to the $Global uniform block.
2136
0
    TVector<TVariable*> opaque_uniforms;
2137
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2138
0
        TType& paramType = *userFunction[i].type;
2139
0
        TString& paramName = *userFunction[i].name;
2140
0
        if (paramType.getQualifier().storage == EvqUniform) {
2141
0
            if (!paramType.containsOpaque()) {
2142
                // Add it to the global uniform block.
2143
0
                growGlobalUniformBlock(loc, paramType, paramName);
2144
0
            } else {
2145
                // Declare it as a separate variable.
2146
0
                TVariable *var = makeInternalVariable(paramName.c_str(), paramType);
2147
0
                opaque_uniforms.push_back(var);
2148
0
            }
2149
0
        }
2150
0
    }
2151
2152
    // Synthesize the call
2153
2154
0
    pushScope(); // matches the one in handleFunctionBody()
2155
2156
    // new signature
2157
0
    TType voidType(EbtVoid);
2158
0
    TFunction synthEntryPoint(&userFunction.getName(), voidType);
2159
0
    TIntermAggregate* synthParams = new TIntermAggregate();
2160
0
    intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
2161
0
    intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
2162
0
    intermediate.incrementEntryPointCount();
2163
0
    TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
2164
2165
    // change original name
2166
0
    userFunction.addPrefix("@");                         // change the name in the function, but not in the symbol table
2167
2168
    // Copy inputs (shader-in -> calling arg), while building up the call node
2169
0
    TVector<TVariable*> argVars;
2170
0
    TIntermAggregate* synthBody = new TIntermAggregate();
2171
0
    auto inputIt = inputs.begin();
2172
0
    auto opaqueUniformIt = opaque_uniforms.begin();
2173
0
    TIntermTyped* callingArgs = nullptr;
2174
2175
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2176
0
        TParameter& param = userFunction[i];
2177
0
        argVars.push_back(makeInternalVariable(*param.name, *param.type));
2178
0
        argVars.back()->getWritableType().getQualifier().makeTemporary();
2179
2180
        // Track the input patch, which is the only non-builtin supported by hull shader PCF.
2181
0
        if (param.getDeclaredBuiltIn() == EbvInputPatch)
2182
0
            inputPatch = argVars.back();
2183
2184
0
        TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
2185
0
        handleFunctionArgument(&callee, callingArgs, arg);
2186
0
        if (param.type->getQualifier().isParamInput()) {
2187
0
            TIntermTyped* input = intermediate.addSymbol(**inputIt);
2188
0
            if (input->getType().getQualifier().builtIn == EbvFragCoord && intermediate.getDxPositionW()) {
2189
                // Replace FragCoord W with reciprocal
2190
0
                auto pos_xyz = handleDotDereference(loc, input, "xyz");
2191
0
                auto pos_w   = handleDotDereference(loc, input, "w");
2192
0
                auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2193
0
                auto recip_w = intermediate.addBinaryMath(EOpDiv, one, pos_w, loc);
2194
0
                TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
2195
0
                dst->getSequence().push_back(pos_xyz);
2196
0
                dst->getSequence().push_back(recip_w);
2197
0
                dst->setType(TType(EbtFloat, EvqTemporary, 4));
2198
0
                dst->setLoc(loc);
2199
0
                input = dst;
2200
0
            }
2201
0
            intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, input));
2202
0
            inputIt++;
2203
0
        }
2204
0
        if (param.type->getQualifier().storage == EvqUniform) {
2205
0
            if (!param.type->containsOpaque()) {
2206
                // Look it up in the $Global uniform block.
2207
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2208
0
                                                                   handleVariable(loc, param.name)));
2209
0
            } else {
2210
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2211
0
                                                                   intermediate.addSymbol(**opaqueUniformIt)));
2212
0
                ++opaqueUniformIt;
2213
0
            }
2214
0
        }
2215
0
    }
2216
2217
    // Call
2218
0
    currentCaller = synthEntryPoint.getMangledName();
2219
0
    TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
2220
0
    currentCaller = userFunction.getMangledName();
2221
2222
    // Return value
2223
0
    if (entryPointOutput) {
2224
0
        TIntermTyped* returnAssign;
2225
2226
        // For hull shaders, the wrapped entry point return value is written to
2227
        // an array element as indexed by invocation ID, which we might have to make up.
2228
        // This is required to match SPIR-V semantics.
2229
0
        if (language == EShLangTessControl) {
2230
0
            TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
2231
2232
            // If there is no user declared invocation ID, we must make one.
2233
0
            if (invocationIdSym == nullptr) {
2234
0
                TType invocationIdType(EbtUint, EvqIn, 1);
2235
0
                TString* invocationIdName = NewPoolTString("InvocationId");
2236
0
                invocationIdType.getQualifier().builtIn = EbvInvocationId;
2237
2238
0
                TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
2239
2240
0
                globalQualifierFix(loc, variable->getWritableType().getQualifier());
2241
0
                trackLinkage(*variable);
2242
2243
0
                invocationIdSym = intermediate.addSymbol(*variable);
2244
0
            }
2245
2246
0
            TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
2247
0
                                                          invocationIdSym, loc);
2248
2249
            // Set the type of the array element being dereferenced
2250
0
            const TType derefElementType(entryPointOutput->getType(), 0);
2251
0
            element->setType(derefElementType);
2252
2253
0
            returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
2254
0
        } else {
2255
0
            returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
2256
0
        }
2257
0
        intermediate.growAggregate(synthBody, returnAssign);
2258
0
    } else
2259
0
        intermediate.growAggregate(synthBody, callReturn);
2260
2261
    // Output copies
2262
0
    auto outputIt = outputs.begin();
2263
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2264
0
        TParameter& param = userFunction[i];
2265
2266
        // GS outputs are via emit, so we do not copy them here.
2267
0
        if (param.type->getQualifier().isParamOutput()) {
2268
0
            if (param.getDeclaredBuiltIn() == EbvGsOutputStream) {
2269
                // GS output stream does not assign outputs here: it's the Append() method
2270
                // which writes to the output, probably multiple times separated by Emit.
2271
                // We merely remember the output to use, here.
2272
0
                gsStreamOutput = *outputIt;
2273
0
            } else {
2274
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
2275
0
                                                                   intermediate.addSymbol(**outputIt),
2276
0
                                                                   intermediate.addSymbol(*argVars[i])));
2277
0
            }
2278
2279
0
            outputIt++;
2280
0
        }
2281
0
    }
2282
2283
    // Put the pieces together to form a full function subtree
2284
    // for the synthesized entry point.
2285
0
    synthBody->setOperator(EOpSequence);
2286
0
    TIntermNode* synthFunctionDef = synthParams;
2287
0
    handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
2288
2289
0
    entryPointFunctionBody = synthBody;
2290
2291
0
    return synthFunctionDef;
2292
2
}
2293
2294
void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody,
2295
                                          TIntermNode*& node)
2296
0
{
2297
0
    node = intermediate.growAggregate(node, functionBody);
2298
0
    intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
2299
0
    node->getAsAggregate()->setName(function.getMangledName().c_str());
2300
2301
0
    popScope();
2302
0
    if (function.hasImplicitThis())
2303
0
        popImplicitThis();
2304
2305
0
    if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
2306
0
        error(loc, "function does not return a value:", "", function.getName().c_str());
2307
0
}
2308
2309
// AST I/O is done through shader globals declared in the 'in' or 'out'
2310
// storage class.  An HLSL entry point has a return value, input parameters
2311
// and output parameters.  These need to get remapped to the AST I/O.
2312
void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
2313
    TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
2314
0
{
2315
    // We might have in input structure type with no decorations that caused it
2316
    // to look like an input type, yet it has (e.g.) interpolation types that
2317
    // must be modified that turn it into an input type.
2318
    // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
2319
0
    const auto synthesizeEditedInput = [this](TType& type) {
2320
        // True if a type needs to be 'flat'
2321
0
        const auto needsFlat = [](const TType& type) {
2322
0
            return type.containsBasicType(EbtInt) ||
2323
0
                    type.containsBasicType(EbtUint) ||
2324
0
                    type.containsBasicType(EbtInt64) ||
2325
0
                    type.containsBasicType(EbtUint64) ||
2326
0
                    type.containsBasicType(EbtBool) ||
2327
0
                    type.containsBasicType(EbtDouble);
2328
0
        };
2329
2330
0
        if (language == EShLangFragment && needsFlat(type)) {
2331
0
            if (type.isStruct()) {
2332
0
                TTypeList* finalList = nullptr;
2333
0
                auto it = ioTypeMap.find(type.getStruct());
2334
0
                if (it == ioTypeMap.end() || it->second.input == nullptr) {
2335
                    // Getting here means we have no input struct, but we need one.
2336
0
                    auto list = new TTypeList;
2337
0
                    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
2338
0
                        TType* newType = new TType;
2339
0
                        newType->shallowCopy(*member->type);
2340
0
                        TTypeLoc typeLoc = { newType, member->loc };
2341
0
                        list->push_back(typeLoc);
2342
0
                    }
2343
                    // install the new input type
2344
0
                    if (it == ioTypeMap.end()) {
2345
0
                        tIoKinds newLists = { list, nullptr, nullptr };
2346
0
                        ioTypeMap[type.getStruct()] = newLists;
2347
0
                    } else
2348
0
                        it->second.input = list;
2349
0
                    finalList = list;
2350
0
                } else
2351
0
                    finalList = it->second.input;
2352
                // edit for 'flat'
2353
0
                for (auto member = finalList->begin(); member != finalList->end(); ++member) {
2354
0
                    if (needsFlat(*member->type)) {
2355
0
                        member->type->getQualifier().clearInterpolation();
2356
0
                        member->type->getQualifier().flat = true;
2357
0
                    }
2358
0
                }
2359
0
            } else {
2360
0
                type.getQualifier().clearInterpolation();
2361
0
                type.getQualifier().flat = true;
2362
0
            }
2363
0
        }
2364
0
    };
2365
2366
    // Do the actual work to make a type be a shader input or output variable,
2367
    // and clear the original to be non-IO (for use as a normal function parameter/return).
2368
0
    const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
2369
0
        TVariable* ioVariable = makeInternalVariable(name, type);
2370
0
        clearUniformInputOutput(type.getQualifier());
2371
0
        if (type.isStruct()) {
2372
0
            auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
2373
0
            if (newLists != ioTypeMap.end()) {
2374
0
                if (storage == EvqVaryingIn && newLists->second.input)
2375
0
                    ioVariable->getWritableType().setStruct(newLists->second.input);
2376
0
                else if (storage == EvqVaryingOut && newLists->second.output)
2377
0
                    ioVariable->getWritableType().setStruct(newLists->second.output);
2378
0
            }
2379
0
        }
2380
0
        if (storage == EvqVaryingIn) {
2381
0
            correctInput(ioVariable->getWritableType().getQualifier());
2382
0
            if (language == EShLangTessEvaluation)
2383
0
                if (!ioVariable->getType().isArray())
2384
0
                    ioVariable->getWritableType().getQualifier().patch = true;
2385
0
        } else {
2386
0
            correctOutput(ioVariable->getWritableType().getQualifier());
2387
0
        }
2388
0
        ioVariable->getWritableType().getQualifier().storage = storage;
2389
2390
0
        fixBuiltInIoType(ioVariable->getWritableType());
2391
2392
0
        return ioVariable;
2393
0
    };
2394
2395
    // return value is actually a shader-scoped output (out)
2396
0
    if (function.getType().getBasicType() == EbtVoid) {
2397
0
        returnValue = nullptr;
2398
0
    } else {
2399
0
        if (language == EShLangTessControl) {
2400
            // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
2401
            // array in SPIR-V semantics.  We'll write to it indexed by invocation ID.
2402
2403
0
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2404
2405
0
            TType outputType;
2406
0
            outputType.shallowCopy(function.getType());
2407
2408
            // vertices has necessarily already been set when handling entry point attributes.
2409
0
            TArraySizes* arraySizes = new TArraySizes;
2410
0
            arraySizes->addInnerSize(intermediate.getVertices());
2411
0
            outputType.transferArraySizes(arraySizes);
2412
2413
0
            clearUniformInputOutput(function.getWritableType().getQualifier());
2414
0
            returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
2415
0
        } else {
2416
0
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2417
0
        }
2418
0
    }
2419
2420
    // parameters are actually shader-scoped inputs and outputs (in or out)
2421
0
    for (int i = 0; i < function.getParamCount(); i++) {
2422
0
        TType& paramType = *function[i].type;
2423
0
        if (paramType.getQualifier().isParamInput()) {
2424
0
            synthesizeEditedInput(paramType);
2425
0
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
2426
0
            inputs.push_back(argAsGlobal);
2427
0
        }
2428
0
        if (paramType.getQualifier().isParamOutput()) {
2429
0
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
2430
0
            outputs.push_back(argAsGlobal);
2431
0
        }
2432
0
    }
2433
0
}
2434
2435
// An HLSL function that looks like an entry point, but is not,
2436
// declares entry point IO built-ins, but these have to be undone.
2437
void HlslParseContext::remapNonEntryPointIO(TFunction& function)
2438
2
{
2439
    // return value
2440
2
    if (function.getType().getBasicType() != EbtVoid)
2441
0
        clearUniformInputOutput(function.getWritableType().getQualifier());
2442
2443
    // parameters.
2444
    // References to structuredbuffer types are left unmodified
2445
2
    for (int i = 0; i < function.getParamCount(); i++)
2446
0
        if (!isReference(*function[i].type))
2447
0
            clearUniformInputOutput(function[i].type->getQualifier());
2448
2
}
2449
2450
TIntermNode* HlslParseContext::handleDeclare(const TSourceLoc& loc, TIntermTyped* var)
2451
0
{
2452
0
    return intermediate.addUnaryNode(EOpDeclare, var, loc, TType(EbtVoid));
2453
0
}
2454
2455
// Handle function returns, including type conversions to the function return type
2456
// if necessary.
2457
TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
2458
0
{
2459
0
    functionReturnsValue = true;
2460
2461
0
    if (currentFunctionType->getBasicType() == EbtVoid) {
2462
0
        error(loc, "void function cannot return a value", "return", "");
2463
0
        return intermediate.addBranch(EOpReturn, loc);
2464
0
    } else if (*currentFunctionType != value->getType()) {
2465
0
        value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
2466
0
        if (value && *currentFunctionType != value->getType())
2467
0
            value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value);
2468
0
        if (value == nullptr || *currentFunctionType != value->getType()) {
2469
0
            error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
2470
0
            return value;
2471
0
        }
2472
0
    }
2473
2474
0
    return intermediate.addBranch(EOpReturn, value, loc);
2475
0
}
2476
2477
void HlslParseContext::handleFunctionArgument(TFunction* function,
2478
                                              TIntermTyped*& arguments, TIntermTyped* newArg)
2479
12
{
2480
12
    TParameter param = { nullptr, new TType, nullptr };
2481
12
    param.type->shallowCopy(newArg->getType());
2482
2483
12
    function->addParameter(param);
2484
12
    if (arguments)
2485
0
        arguments = intermediate.growAggregate(arguments, newArg);
2486
12
    else
2487
12
        arguments = newArg;
2488
12
}
2489
2490
// FragCoord may require special loading: we can optionally reciprocate W.
2491
TIntermTyped* HlslParseContext::assignFromFragCoord(const TSourceLoc& loc, TOperator op,
2492
                                                    TIntermTyped* left, TIntermTyped* right)
2493
0
{
2494
    // If we are not asked for reciprocal W, use a plain old assign.
2495
0
    if (!intermediate.getDxPositionW())
2496
0
        return intermediate.addAssign(op, left, right, loc);
2497
2498
    // If we get here, we should reciprocate W.
2499
0
    TIntermAggregate* assignList = nullptr;
2500
2501
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2502
0
    TVariable* rhsTempVar = nullptr;
2503
0
    rhsTempVar = makeInternalVariable("@fragcoord", right->getType());
2504
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2505
2506
0
    {
2507
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2508
0
        assignList = intermediate.growAggregate(assignList,
2509
0
            intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2510
0
    }
2511
2512
    // tmp.w = 1.0 / tmp.w
2513
0
    {
2514
0
        const int W = 3;
2515
2516
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2517
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2518
0
        TIntermTyped* index = intermediate.addConstantUnion(W, loc);
2519
2520
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2521
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2522
2523
0
        const TType derefType(right->getType(), 0);
2524
2525
0
        lhsElement->setType(derefType);
2526
0
        rhsElement->setType(derefType);
2527
2528
0
        auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2529
0
        auto recip_w = intermediate.addBinaryMath(EOpDiv, one, rhsElement, loc);
2530
2531
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, recip_w, loc));
2532
0
    }
2533
2534
    // Assign the rhs temp (now with W reciprocal) to the final output
2535
0
    {
2536
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2537
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2538
0
    }
2539
2540
0
    assert(assignList != nullptr);
2541
0
    assignList->setOperator(EOpSequence);
2542
2543
0
    return assignList;
2544
0
}
2545
2546
// Position may require special handling: we can optionally invert Y.
2547
// See: https://github.com/KhronosGroup/glslang/issues/1173
2548
//      https://github.com/KhronosGroup/glslang/issues/494
2549
TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
2550
                                               TIntermTyped* left, TIntermTyped* right)
2551
0
{
2552
    // If we are not asked for Y inversion, use a plain old assign.
2553
0
    if (!intermediate.getInvertY())
2554
0
        return intermediate.addAssign(op, left, right, loc);
2555
2556
    // If we get here, we should invert Y.
2557
0
    TIntermAggregate* assignList = nullptr;
2558
2559
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2560
0
    TVariable* rhsTempVar = nullptr;
2561
0
    rhsTempVar = makeInternalVariable("@position", right->getType());
2562
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2563
2564
0
    {
2565
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2566
0
        assignList = intermediate.growAggregate(assignList,
2567
0
                                                intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2568
0
    }
2569
2570
    // pos.y = -pos.y
2571
0
    {
2572
0
        const int Y = 1;
2573
2574
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2575
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2576
0
        TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
2577
2578
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2579
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2580
2581
0
        const TType derefType(right->getType(), 0);
2582
2583
0
        lhsElement->setType(derefType);
2584
0
        rhsElement->setType(derefType);
2585
2586
0
        TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
2587
2588
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
2589
0
    }
2590
2591
    // Assign the rhs temp (now with Y inversion) to the final output
2592
0
    {
2593
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2594
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2595
0
    }
2596
2597
0
    assert(assignList != nullptr);
2598
0
    assignList->setOperator(EOpSequence);
2599
2600
0
    return assignList;
2601
0
}
2602
2603
// Clip and cull distance require special handling due to a semantic mismatch.  In HLSL,
2604
// these can be float scalar, float vector, or arrays of float scalar or float vector.
2605
// In SPIR-V, they are arrays of scalar floats in all cases.  We must copy individual components
2606
// (e.g, both x and y components of a float2) out into the destination float array.
2607
//
2608
// The values are assigned to sequential members of the output array.  The inner dimension
2609
// is vector components.  The outer dimension is array elements.
2610
TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId,
2611
                                                           TIntermTyped* left, TIntermTyped* right)
2612
0
{
2613
0
    switch (language) {
2614
0
    case EShLangFragment:
2615
0
    case EShLangVertex:
2616
0
    case EShLangGeometry:
2617
0
        break;
2618
0
    default:
2619
0
        error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", "");
2620
0
        return nullptr;
2621
0
    }
2622
2623
0
    TVariable** clipCullVar = nullptr;
2624
2625
    // Figure out if we are assigning to, or from, clip or cull distance.
2626
0
    const bool isOutput = isClipOrCullDistance(left->getType());
2627
2628
    // This is the rvalue or lvalue holding the clip or cull distance.
2629
0
    TIntermTyped* clipCullNode = isOutput ? left : right;
2630
    // This is the value going into or out of the clip or cull distance.
2631
0
    TIntermTyped* internalNode = isOutput ? right : left;
2632
2633
0
    const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn;
2634
2635
0
    decltype(clipSemanticNSizeIn)* semanticNSize = nullptr;
2636
2637
    // Refer to either the clip or the cull distance, depending on semantic.
2638
0
    switch (builtInType) {
2639
0
    case EbvClipDistance:
2640
0
        clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput;
2641
0
        semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn;
2642
0
        break;
2643
0
    case EbvCullDistance:
2644
0
        clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput;
2645
0
        semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn;
2646
0
        break;
2647
2648
    // called invalidly: we expected a clip or a cull distance.
2649
    // static compile time problem: should not happen.
2650
0
    default: assert(0); return nullptr;
2651
0
    }
2652
2653
    // This is the offset in the destination array of a given semantic's data
2654
0
    std::array<int, maxClipCullRegs> semanticOffset;
2655
2656
    // Calculate offset of variable of semantic N in destination array
2657
0
    int arrayLoc = 0;
2658
0
    int vecItems = 0;
2659
2660
0
    for (int x = 0; x < maxClipCullRegs; ++x) {
2661
        // See if we overflowed the vec4 packing
2662
0
        if ((vecItems + (*semanticNSize)[x]) > 4) {
2663
0
            arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4
2664
0
            vecItems = 0;
2665
0
        }
2666
2667
0
        semanticOffset[x] = arrayLoc;
2668
0
        vecItems += (*semanticNSize)[x];
2669
0
        arrayLoc += (*semanticNSize)[x];
2670
0
    }
2671
2672
2673
    // It can have up to 2 array dimensions (in the case of geometry shader inputs)
2674
0
    const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes();
2675
0
    const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0;
2676
    // vector sizes:
2677
0
    const int internalVectorSize = internalNode->getType().getVectorSize();
2678
    // array sizes, or 1 if it's not an array:
2679
0
    const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1);
2680
0
    const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1);
2681
2682
    // The created type may be an array of arrays, e.g, for geometry shader inputs.
2683
0
    const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput);
2684
2685
    // If we haven't created the output already, create it now.
2686
0
    if (*clipCullVar == nullptr) {
2687
        // ClipDistance and CullDistance are handled specially in the entry point input/output copy
2688
        // algorithm, because they may need to be unpacked from components of vectors (or a scalar)
2689
        // into a float array, or vice versa.  Here, we make the array the right size and type,
2690
        // which depends on the incoming data, which has several potential dimensions:
2691
        //    * Semantic ID
2692
        //    * vector size
2693
        //    * array size
2694
        // Of those, semantic ID and array size cannot appear simultaneously.
2695
        //
2696
        // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two
2697
        // array dimensions.  The shader's declaration may have one or two array dimensions.  One is always
2698
        // the geometry's dimension.
2699
2700
0
        const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed;
2701
2702
0
        const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1);
2703
0
        const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1;
2704
2705
0
        TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1);
2706
0
        clipCullType.getQualifier() = clipCullNode->getType().getQualifier();
2707
2708
        // Create required array dimension
2709
0
        TArraySizes* arraySizes = new TArraySizes;
2710
0
        if (isImplicitlyArrayed)
2711
0
            arraySizes->addInnerSize(requiredOuterArraySize);
2712
0
        arraySizes->addInnerSize(requiredInnerArraySize);
2713
0
        clipCullType.transferArraySizes(arraySizes);
2714
2715
        // Obtain symbol name: we'll use that for the symbol we introduce.
2716
0
        TIntermSymbol* sym = clipCullNode->getAsSymbolNode();
2717
0
        assert(sym != nullptr);
2718
2719
        // We are moving the semantic ID from the layout location, so it is no longer needed or
2720
        // desired there.
2721
0
        clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
2722
2723
        // Create variable and track its linkage
2724
0
        *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType);
2725
2726
0
        trackLinkage(**clipCullVar);
2727
0
    }
2728
2729
    // Create symbol for the clip or cull variable.
2730
0
    TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar);
2731
2732
    // vector sizes:
2733
0
    const int clipCullVectorSize = clipCullSym->getType().getVectorSize();
2734
2735
    // array sizes, or 1 if it's not an array:
2736
0
    const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes();
2737
0
    const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1;
2738
0
    const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0);
2739
2740
    // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics.
2741
    // fixBuiltInIoType() should have handled that upstream.
2742
0
    assert(clipCullSym->getType().isArray());
2743
0
    assert(clipCullSym->getType().getVectorSize() == 1);
2744
0
    assert(clipCullSym->getType().getBasicType() == EbtFloat);
2745
2746
    // We may be creating multiple sub-assignments.  This is an aggregate to hold them.
2747
    // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
2748
0
    TIntermAggregate* assignList = nullptr;
2749
2750
    // Holds individual component assignments as we make them.
2751
0
    TIntermTyped* clipCullAssign = nullptr;
2752
2753
    // If the types are homomorphic, use a simple assign.  No need to mess about with
2754
    // individual components.
2755
0
    if (clipCullSym->getType().isArray() == internalNode->getType().isArray() &&
2756
0
        clipCullInnerArraySize == internalInnerArraySize &&
2757
0
        clipCullOuterArraySize == internalOuterArraySize &&
2758
0
        clipCullVectorSize == internalVectorSize) {
2759
2760
0
        if (isOutput)
2761
0
            clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc);
2762
0
        else
2763
0
            clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc);
2764
2765
0
        assignList = intermediate.growAggregate(assignList, clipCullAssign);
2766
0
        assignList->setOperator(EOpSequence);
2767
2768
0
        return assignList;
2769
0
    }
2770
2771
    // We are going to copy each component of the internal (per array element if indicated) to sequential
2772
    // array elements of the clipCullSym.  This tracks the lhs element we're writing to as we go along.
2773
    // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above.
2774
0
    int clipCullInnerArrayPos = semanticOffset[semanticId];
2775
0
    int clipCullOuterArrayPos = 0;
2776
2777
    // Lambda to add an index to a node, set the type of the result, and return the new node.
2778
0
    const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* {
2779
0
        const TType derefType(node->getType(), 0);
2780
0
        node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc);
2781
0
        node->setType(derefType);
2782
0
        return node;
2783
0
    };
2784
2785
    // Loop through every component of every element of the internal, and copy to or from the matching external.
2786
0
    for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) {
2787
0
        for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) {
2788
0
            for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) {
2789
                // clip/cull array member to read from / write to:
2790
0
                TIntermTyped* clipCullMember = clipCullSym;
2791
2792
                // If implicitly arrayed, there is an outer array dimension involved
2793
0
                if (isImplicitlyArrayed)
2794
0
                    clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos);
2795
2796
                // Index into proper array position for clip cull member
2797
0
                clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++);
2798
2799
                // if needed, start over with next outer array slice.
2800
0
                if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) {
2801
0
                    clipCullInnerArrayPos = semanticOffset[semanticId];
2802
0
                    ++clipCullOuterArrayPos;
2803
0
                }
2804
2805
                // internal member to read from / write to:
2806
0
                TIntermTyped* internalMember = internalNode;
2807
2808
                // If internal node has outer array dimension, index appropriately.
2809
0
                if (internalArrayDims > 1)
2810
0
                    internalMember = addIndex(internalMember, internalOuterArrayPos);
2811
2812
                // If internal node has inner array dimension, index appropriately.
2813
0
                if (internalArrayDims > 0)
2814
0
                    internalMember = addIndex(internalMember, internalInnerArrayPos);
2815
2816
                // If internal node is a vector, extract the component of interest.
2817
0
                if (internalNode->getType().isVector())
2818
0
                    internalMember = addIndex(internalMember, internalComponent);
2819
2820
                // Create an assignment: output from internal to clip cull, or input from clip cull to internal.
2821
0
                if (isOutput)
2822
0
                    clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc);
2823
0
                else
2824
0
                    clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc);
2825
2826
                // Track assignment in the sequence.
2827
0
                assignList = intermediate.growAggregate(assignList, clipCullAssign);
2828
0
            }
2829
0
        }
2830
0
    }
2831
2832
0
    assert(assignList != nullptr);
2833
0
    assignList->setOperator(EOpSequence);
2834
2835
0
    return assignList;
2836
0
}
2837
2838
// Some simple source assignments need to be flattened to a sequence
2839
// of AST assignments. Catch these and flatten, otherwise, pass through
2840
// to intermediate.addAssign().
2841
//
2842
// Also, assignment to matrix swizzles requires multiple component assignments,
2843
// intercept those as well.
2844
TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
2845
                                             TIntermTyped* right)
2846
3
{
2847
3
    if (left == nullptr || right == nullptr)
2848
0
        return nullptr;
2849
2850
    // writing to opaques will require fixing transforms
2851
3
    if (left->getType().containsOpaque())
2852
0
        intermediate.setNeedsLegalization();
2853
2854
3
    if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
2855
0
        return handleAssignToMatrixSwizzle(loc, op, left, right);
2856
2857
    // Return true if the given node is an index operation into a split variable.
2858
6
    const auto indexesSplit = [this](const TIntermTyped* node) -> bool {
2859
6
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2860
2861
6
        if (binaryNode == nullptr)
2862
6
            return false;
2863
2864
0
        return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) &&
2865
0
               wasSplit(binaryNode->getLeft());
2866
6
    };
2867
2868
    // Return symbol if node is symbol or index ref
2869
6
    const auto getSymbol = [](const TIntermTyped* node) -> const TIntermSymbol* {
2870
6
        const TIntermSymbol* symbolNode = node->getAsSymbolNode();
2871
6
        if (symbolNode != nullptr)
2872
3
            return symbolNode;
2873
2874
3
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2875
3
        if (binaryNode != nullptr && (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect))
2876
0
            return binaryNode->getLeft()->getAsSymbolNode();
2877
2878
3
        return nullptr;
2879
3
    };
2880
2881
    // Return true if this stage assigns clip position with potentially inverted Y
2882
3
    const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
2883
3
        return node->getType().getQualifier().builtIn == EbvPosition &&
2884
0
               (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
2885
3
    };
2886
2887
3
    const TIntermSymbol* leftSymbol = getSymbol(left);
2888
3
    const TIntermSymbol* rightSymbol = getSymbol(right);
2889
2890
3
    const bool isSplitLeft    = wasSplit(left) || indexesSplit(left);
2891
3
    const bool isSplitRight   = wasSplit(right) || indexesSplit(right);
2892
2893
3
    const bool isFlattenLeft  = wasFlattened(leftSymbol);
2894
3
    const bool isFlattenRight = wasFlattened(rightSymbol);
2895
2896
    // OK to do a single assign if neither side is split or flattened.  Otherwise,
2897
    // fall through to a member-wise copy.
2898
3
    if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) {
2899
        // Clip and cull distance requires more processing.  See comment above assignClipCullDistance.
2900
3
        if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) {
2901
0
            const bool isOutput = isClipOrCullDistance(left->getType());
2902
2903
0
            const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
2904
0
            return assignClipCullDistance(loc, op, semanticId, left, right);
2905
3
        } else if (assignsClipPos(left)) {
2906
            // Position can require special handling: see comment above assignPosition
2907
0
            return assignPosition(loc, op, left, right);
2908
3
        } else if (left->getQualifier().builtIn == EbvSampleMask) {
2909
            // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars
2910
            // in the shader.  Copy the scalar RHS into the LHS array element zero, if that happens.
2911
0
            if (left->isArray() && !right->isArray()) {
2912
0
                const TType derefType(left->getType(), 0);
2913
0
                left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc);
2914
0
                left->setType(derefType);
2915
                // Fall through to add assign.
2916
0
            }
2917
0
        }
2918
2919
3
        return intermediate.addAssign(op, left, right, loc);
2920
3
    }
2921
2922
0
    TIntermAggregate* assignList = nullptr;
2923
0
    const TVector<TVariable*>* leftVariables = nullptr;
2924
0
    const TVector<TVariable*>* rightVariables = nullptr;
2925
2926
    // A temporary to store the right node's value, so we don't keep indirecting into it
2927
    // if it's not a simple symbol.
2928
0
    TVariable* rhsTempVar = nullptr;
2929
2930
    // If the RHS is a simple symbol node, we'll copy it for each member.
2931
0
    TIntermSymbol* cloneSymNode = nullptr;
2932
2933
0
    int memberCount = 0;
2934
2935
    // Track how many items there are to copy.
2936
0
    if (left->getType().isStruct())
2937
0
        memberCount = (int)left->getType().getStruct()->size();
2938
0
    if (left->getType().isArray())
2939
0
        memberCount = left->getType().getCumulativeArraySize();
2940
2941
0
    if (isFlattenLeft)
2942
0
        leftVariables = &flattenMap.find(leftSymbol->getId())->second.members;
2943
2944
0
    if (isFlattenRight) {
2945
0
        rightVariables = &flattenMap.find(rightSymbol->getId())->second.members;
2946
0
    } else {
2947
        // The RHS is not flattened.  There are several cases:
2948
        // 1. 1 item to copy:  Use the RHS directly.
2949
        // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
2950
        // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member.
2951
2952
0
        if (memberCount <= 1) {
2953
            // case 1: we'll use the symbol directly below.  Nothing to do.
2954
0
        } else {
2955
0
            if (right->getAsSymbolNode() != nullptr) {
2956
                // case 2: we'll copy the symbol per iteration below.
2957
0
                cloneSymNode = right->getAsSymbolNode();
2958
0
            } else {
2959
                // case 3: assign to a temp, and indirect into that.
2960
0
                rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
2961
0
                rhsTempVar->getWritableType().getQualifier().makeTemporary();
2962
0
                TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
2963
2964
                // Add this to the aggregate being built.
2965
0
                assignList = intermediate.growAggregate(assignList,
2966
0
                                                        intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
2967
0
            }
2968
0
        }
2969
0
    }
2970
2971
    // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in
2972
    // variables, which is awkward when copying between split and unsplit structures.  This variable tracks
2973
    // array indirections so they can be percolated from outer structs to inner variables.
2974
0
    std::vector <int> arrayElement;
2975
2976
0
    TStorageQualifier leftStorage = left->getType().getQualifier().storage;
2977
0
    TStorageQualifier rightStorage = right->getType().getQualifier().storage;
2978
2979
0
    int leftOffsetStart = findSubtreeOffset(*left);
2980
0
    int rightOffsetStart = findSubtreeOffset(*right);
2981
0
    int leftOffset = leftOffsetStart;
2982
0
    int rightOffset = rightOffsetStart;
2983
2984
0
    const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember,
2985
0
                               bool flattened)
2986
0
                           -> TIntermTyped * {
2987
0
        const bool split     = isLeft ? isSplitLeft   : isSplitRight;
2988
2989
0
        TIntermTyped* subTree;
2990
0
        const TType derefType(type, member);
2991
0
        const TVariable* builtInVar = nullptr;
2992
0
        if ((flattened || split) && derefType.isBuiltIn()) {
2993
0
            auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData(
2994
0
                                                   derefType.getQualifier().builtIn,
2995
0
                                                   isLeft ? leftStorage : rightStorage));
2996
0
            if (splitPair != splitBuiltIns.end())
2997
0
                builtInVar = splitPair->second;
2998
0
        }
2999
0
        if (builtInVar != nullptr) {
3000
            // copy from interstage IO built-in if needed
3001
0
            subTree = intermediate.addSymbol(*builtInVar);
3002
3003
0
            if (subTree->getType().isArray()) {
3004
                // Arrayness of builtIn symbols isn't handled by the normal recursion:
3005
                // it's been extracted and moved to the built-in.
3006
0
                if (!arrayElement.empty()) {
3007
0
                    const TType splitDerefType(subTree->getType(), arrayElement.back());
3008
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3009
0
                                                    intermediate.addConstantUnion(arrayElement.back(), loc), loc);
3010
0
                    subTree->setType(splitDerefType);
3011
0
                } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
3012
                    // This might also be a stage with arrayed outputs, in which case there's an index
3013
                    // operation we should transfer to the output builtin.
3014
3015
0
                    const TType splitDerefType(subTree->getType(), 0);
3016
0
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3017
0
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3018
0
                    subTree->setType(splitDerefType);
3019
0
                }
3020
0
            }
3021
0
        } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
3022
0
            if (isLeft) {
3023
                // offset will cycle through variables for arrayed io
3024
0
                if (leftOffset >= static_cast<int>(leftVariables->size()))
3025
0
                    leftOffset = leftOffsetStart;
3026
0
                subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]);
3027
0
            } else {
3028
                // offset will cycle through variables for arrayed io
3029
0
                if (rightOffset >= static_cast<int>(rightVariables->size()))
3030
0
                    rightOffset = rightOffsetStart;
3031
0
                subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]);
3032
0
            }
3033
3034
            // arrayed io
3035
0
            if (subTree->getType().isArray()) {
3036
0
                if (!arrayElement.empty()) {
3037
0
                    const TType derefType(subTree->getType(), arrayElement.front());
3038
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3039
0
                                                    intermediate.addConstantUnion(arrayElement.front(), loc), loc);
3040
0
                    subTree->setType(derefType);
3041
0
                } else {
3042
                    // There's an index operation we should transfer to the output builtin.
3043
0
                    assert(splitNode->getAsOperator() != nullptr &&
3044
0
                           splitNode->getAsOperator()->getOp() == EOpIndexIndirect);
3045
0
                    const TType splitDerefType(subTree->getType(), 0);
3046
0
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3047
0
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3048
0
                    subTree->setType(splitDerefType);
3049
0
                }
3050
0
            }
3051
0
        } else {
3052
            // Index operator if it's an aggregate, else EOpNull
3053
0
            const TOperator accessOp = type.isArray()  ? EOpIndexDirect
3054
0
                                     : type.isStruct() ? EOpIndexDirectStruct
3055
0
                                     : EOpNull;
3056
0
            if (accessOp == EOpNull) {
3057
0
                subTree = splitNode;
3058
0
            } else {
3059
0
                subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc),
3060
0
                                                loc);
3061
0
                const TType splitDerefType(splitNode->getType(), splitMember);
3062
0
                subTree->setType(splitDerefType);
3063
0
            }
3064
0
        }
3065
3066
0
        return subTree;
3067
0
    };
3068
3069
    // Use the proper RHS node: a new symbol from a TVariable, copy
3070
    // of an TIntermSymbol node, or sometimes the right node directly.
3071
0
    right = rhsTempVar != nullptr   ? intermediate.addSymbol(*rhsTempVar, loc) :
3072
0
            cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) :
3073
0
            right;
3074
3075
    // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
3076
    // whole thing.  So, we'll resort to an explicit type via std::function.
3077
0
    const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3078
0
                             bool topLevel)>
3079
0
    traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3080
0
                   bool topLevel) -> void {
3081
        // If we get here, we are assigning to or from a whole array or struct that must be
3082
        // flattened, so have to do member-by-member assignment:
3083
3084
0
        bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel);
3085
0
        bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel);
3086
3087
0
        if ((left->getType().isArray() || right->getType().isArray()) &&
3088
0
              (shouldFlattenSubsetLeft  || isSplitLeft ||
3089
0
               shouldFlattenSubsetRight || isSplitRight)) {
3090
0
            const int elementsL = left->getType().isArray()  ? left->getType().getOuterArraySize()  : 1;
3091
0
            const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
3092
3093
            // The arrays might not be the same size,
3094
            // e.g., if the size has been forced for EbvTessLevelInner/Outer.
3095
0
            const int elementsToCopy = std::min(elementsL, elementsR);
3096
3097
            // array case
3098
0
            for (int element = 0; element < elementsToCopy; ++element) {
3099
0
                arrayElement.push_back(element);
3100
3101
                // Add a new AST symbol node if we have a temp variable holding a complex RHS.
3102
0
                TIntermTyped* subLeft  = getMember(true,  left->getType(),  element, left, element,
3103
0
                                                   shouldFlattenSubsetLeft);
3104
0
                TIntermTyped* subRight = getMember(false, right->getType(), element, right, element,
3105
0
                                                   shouldFlattenSubsetRight);
3106
3107
0
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  element, splitLeft,
3108
0
                                                                       element, shouldFlattenSubsetLeft)
3109
0
                                                           : subLeft;
3110
0
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight,
3111
0
                                                                       element, shouldFlattenSubsetRight)
3112
0
                                                           : subRight;
3113
3114
0
                traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3115
3116
0
                arrayElement.pop_back();
3117
0
            }
3118
0
        } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft  || isSplitLeft ||
3119
0
                                                  shouldFlattenSubsetRight || isSplitRight)) {
3120
            // struct case
3121
0
            const auto& membersL = *left->getType().getStruct();
3122
0
            const auto& membersR = *right->getType().getStruct();
3123
3124
            // These track the members in the split structures corresponding to the same in the unsplit structures,
3125
            // which we traverse in parallel.
3126
0
            int memberL = 0;
3127
0
            int memberR = 0;
3128
3129
            // Handle empty structure assignment
3130
0
            if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
3131
0
                assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3132
3133
0
            for (int member = 0; member < int(membersL.size()); ++member) {
3134
0
                const TType& typeL = *membersL[member].type;
3135
0
                const TType& typeR = *membersR[member].type;
3136
3137
0
                TIntermTyped* subLeft  = getMember(true,  left->getType(), member, left, member,
3138
0
                                                   shouldFlattenSubsetLeft);
3139
0
                TIntermTyped* subRight = getMember(false, right->getType(), member, right, member,
3140
0
                                                   shouldFlattenSubsetRight);
3141
3142
                // If there is no splitting, use the same values to avoid inefficiency.
3143
0
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  member, splitLeft,
3144
0
                                                                       memberL, shouldFlattenSubsetLeft)
3145
0
                                                           : subLeft;
3146
0
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight,
3147
0
                                                                       memberR, shouldFlattenSubsetRight)
3148
0
                                                           : subRight;
3149
3150
0
                if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) {
3151
                    // Clip and cull distance built-in assignment is complex in its own right, and is handled in
3152
                    // a separate function dedicated to that task.  See comment above assignClipCullDistance;
3153
3154
0
                    const bool isOutput = isClipOrCullDistance(subSplitLeft->getType());
3155
3156
                    // Since all clip/cull semantics boil down to the same built-in type, we need to get the
3157
                    // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping.
3158
0
                    const TType derefType((isOutput ? left : right)->getType(), member);
3159
0
                    const int semanticId = derefType.getQualifier().layoutLocation;
3160
3161
0
                    TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId,
3162
0
                                                                              subSplitLeft, subSplitRight);
3163
3164
0
                    assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
3165
0
                } else if (subSplitRight->getType().getQualifier().builtIn == EbvFragCoord) {
3166
                    // FragCoord can require special handling: see comment above assignFromFragCoord
3167
0
                    TIntermTyped* fragCoordAssign = assignFromFragCoord(loc, op, subSplitLeft, subSplitRight);
3168
0
                    assignList = intermediate.growAggregate(assignList, fragCoordAssign, loc);
3169
0
                } else if (assignsClipPos(subSplitLeft)) {
3170
                    // Position can require special handling: see comment above assignPosition
3171
0
                    TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
3172
0
                    assignList = intermediate.growAggregate(assignList, positionAssign, loc);
3173
0
                } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
3174
0
                           !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
3175
                    // If this is the final flattening (no nested types below to flatten)
3176
                    // we'll copy the member, else recurse into the type hierarchy.
3177
                    // However, if splitting the struct, that means we can copy a whole
3178
                    // subtree here IFF it does not itself contain any interstage built-in
3179
                    // IO variables, so we only have to recurse into it if there's something
3180
                    // for splitting to do.  That can save a lot of AST verbosity for
3181
                    // a bunch of memberwise copies.
3182
3183
0
                    assignList = intermediate.growAggregate(assignList,
3184
0
                                                            intermediate.addAssign(op, subSplitLeft, subSplitRight, loc),
3185
0
                                                            loc);
3186
0
                } else {
3187
0
                    traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3188
0
                }
3189
3190
0
                memberL += (typeL.isBuiltIn() ? 0 : 1);
3191
0
                memberR += (typeR.isBuiltIn() ? 0 : 1);
3192
0
            }
3193
0
        } else {
3194
            // Member copy
3195
0
            assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3196
0
        }
3197
3198
0
    };
3199
3200
0
    TIntermTyped* splitLeft  = left;
3201
0
    TIntermTyped* splitRight = right;
3202
3203
    // If either left or right was a split structure, we must read or write it, but still have to
3204
    // parallel-recurse through the unsplit structure to identify the built-in IO vars.
3205
    // The left can be either a symbol, or an index into a symbol (e.g, array reference)
3206
0
    if (isSplitLeft) {
3207
0
        if (indexesSplit(left)) {
3208
            // Index case: Refer to the indexed symbol, if the left is an index operator.
3209
0
            const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3210
3211
0
            TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3212
3213
0
            splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo,
3214
0
                                              left->getAsBinaryNode()->getRight(), loc);
3215
3216
0
            const TType derefType(splitLeftNonIo->getType(), 0);
3217
0
            splitLeft->setType(derefType);
3218
0
        } else {
3219
            // Symbol case: otherwise, if not indexed, we have the symbol directly.
3220
0
            const TIntermSymbol* symNode = left->getAsSymbolNode();
3221
0
            splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3222
0
        }
3223
0
    }
3224
3225
0
    if (isSplitRight)
3226
0
        splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc);
3227
3228
    // This makes the whole assignment, recursing through subtypes as needed.
3229
0
    traverse(left, right, splitLeft, splitRight, true);
3230
3231
0
    assert(assignList != nullptr);
3232
0
    assignList->setOperator(EOpSequence);
3233
3234
0
    return assignList;
3235
3
}
3236
3237
// An assignment to matrix swizzle must be decomposed into individual assignments.
3238
// These must be selected component-wise from the RHS and stored component-wise
3239
// into the LHS.
3240
TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
3241
                                                            TIntermTyped* right)
3242
0
{
3243
0
    assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
3244
3245
0
    if (op != EOpAssign)
3246
0
        error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
3247
3248
    // isolate the matrix and swizzle nodes
3249
0
    TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
3250
0
    const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
3251
3252
    // if the RHS isn't already a simple vector, let's store into one
3253
0
    TIntermSymbol* vector = right->getAsSymbolNode();
3254
0
    TIntermTyped* vectorAssign = nullptr;
3255
0
    if (vector == nullptr) {
3256
        // create a new intermediate vector variable to assign to
3257
0
        TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
3258
0
        vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
3259
3260
        // assign the right to the new vector
3261
0
        vectorAssign = handleAssign(loc, op, vector, right);
3262
0
    }
3263
3264
    // Assign the vector components to the matrix components.
3265
    // Store this as a sequence, so a single aggregate node represents this
3266
    // entire operation.
3267
0
    TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
3268
0
    TType columnType(matrix->getType(), 0);
3269
0
    TType componentType(columnType, 0);
3270
0
    TType indexType(EbtInt);
3271
0
    for (int i = 0; i < (int)swizzle.size(); i += 2) {
3272
        // the right component, single index into the RHS vector
3273
0
        TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
3274
0
                                    intermediate.addConstantUnion(i/2, loc), loc);
3275
3276
        // the left component, double index into the LHS matrix
3277
0
        TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
3278
0
                                    intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
3279
0
                                                                  indexType, loc),
3280
0
                                    loc);
3281
0
        leftComp->setType(columnType);
3282
0
        leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
3283
0
                                    intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
3284
0
                                                                  indexType, loc),
3285
0
                                    loc);
3286
0
        leftComp->setType(componentType);
3287
3288
        // Add the assignment to the aggregate
3289
0
        result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
3290
0
    }
3291
3292
0
    result->setOp(EOpSequence);
3293
3294
0
    return result;
3295
0
}
3296
3297
//
3298
// HLSL atomic operations have slightly different arguments than
3299
// GLSL/AST/SPIRV.  The semantics are converted below in decomposeIntrinsic.
3300
// This provides the post-decomposition equivalent opcode.
3301
//
3302
TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
3303
0
{
3304
0
    switch (op) {
3305
0
    case EOpInterlockedAdd:             return isImage ? EOpImageAtomicAdd      : EOpAtomicAdd;
3306
0
    case EOpInterlockedAnd:             return isImage ? EOpImageAtomicAnd      : EOpAtomicAnd;
3307
0
    case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
3308
0
    case EOpInterlockedMax:             return isImage ? EOpImageAtomicMax      : EOpAtomicMax;
3309
0
    case EOpInterlockedMin:             return isImage ? EOpImageAtomicMin      : EOpAtomicMin;
3310
0
    case EOpInterlockedOr:              return isImage ? EOpImageAtomicOr       : EOpAtomicOr;
3311
0
    case EOpInterlockedXor:             return isImage ? EOpImageAtomicXor      : EOpAtomicXor;
3312
0
    case EOpInterlockedExchange:        return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
3313
0
    case EOpInterlockedCompareStore:  // TODO: ...
3314
0
    default:
3315
0
        error(loc, "unknown atomic operation", "unknown op", "");
3316
0
        return EOpNull;
3317
0
    }
3318
0
}
3319
3320
//
3321
// Create a combined sampler/texture from separate sampler and texture.
3322
//
3323
TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex,
3324
                                                                TIntermTyped* argSampler)
3325
0
{
3326
0
    TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
3327
3328
0
    txcombine->getSequence().push_back(argTex);
3329
0
    txcombine->getSequence().push_back(argSampler);
3330
3331
0
    TSampler samplerType = argTex->getType().getSampler();
3332
0
    samplerType.combined = true;
3333
3334
    // TODO:
3335
    // This block exists until the spec no longer requires shadow modes on texture objects.
3336
    // It can be deleted after that, along with the shadowTextureVariant member.
3337
0
    {
3338
0
        const bool shadowMode = argSampler->getType().getSampler().shadow;
3339
3340
0
        TIntermSymbol* texSymbol = argTex->getAsSymbolNode();
3341
3342
0
        if (texSymbol == nullptr)
3343
0
            texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3344
3345
0
        if (texSymbol == nullptr) {
3346
0
            error(loc, "unable to find texture symbol", "", "");
3347
0
            return nullptr;
3348
0
        }
3349
3350
        // This forces the texture's shadow state to be the sampler's
3351
        // shadow state.  This depends on downstream optimization to
3352
        // DCE one variant in [shadow, nonshadow] if both are present,
3353
        // or the SPIR-V module would be invalid.
3354
0
        long long newId = texSymbol->getId();
3355
3356
        // Check to see if this texture has been given a shadow mode already.
3357
        // If so, look up the one we already have.
3358
0
        const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId());
3359
3360
0
        if (textureShadowEntry != textureShadowVariant.end())
3361
0
            newId = textureShadowEntry->second->get(shadowMode);
3362
0
        else
3363
0
            textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1);
3364
3365
        // Sometimes we have to create another symbol (if this texture has been seen before,
3366
        // and we haven't created the form for this shadow mode).
3367
0
        if (newId == -1) {
3368
0
            TType texType;
3369
0
            texType.shallowCopy(argTex->getType());
3370
0
            texType.getSampler().shadow = shadowMode;  // set appropriate shadow mode.
3371
0
            globalQualifierFix(loc, texType.getQualifier());
3372
3373
0
            TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType);
3374
3375
0
            trackLinkage(*newTexture);
3376
3377
0
            newId = newTexture->getUniqueId();
3378
0
        }
3379
3380
0
        assert(newId != -1);
3381
3382
0
        if (textureShadowVariant.find(newId) == textureShadowVariant.end())
3383
0
            textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()];
3384
3385
0
        textureShadowVariant[newId]->set(shadowMode, newId);
3386
3387
        // Remember this shadow mode in the texture and the merged type.
3388
0
        argTex->getWritableType().getSampler().shadow = shadowMode;
3389
0
        samplerType.shadow = shadowMode;
3390
3391
0
        texSymbol->switchId(newId);
3392
0
    }
3393
3394
0
    txcombine->setType(TType(samplerType, EvqTemporary));
3395
0
    txcombine->setLoc(loc);
3396
3397
0
    return txcombine;
3398
0
}
3399
3400
// Return true if this a buffer type that has an associated counter buffer.
3401
bool HlslParseContext::hasStructBuffCounter(const TType& type) const
3402
0
{
3403
0
    switch (type.getQualifier().declaredBuiltIn) {
3404
0
    case EbvAppendConsume:       // fall through...
3405
0
    case EbvRWStructuredBuffer:  // ...
3406
0
        return true;
3407
0
    default:
3408
0
        return false; // the other structuredbuffer types do not have a counter.
3409
0
    }
3410
0
}
3411
3412
void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type)
3413
0
{
3414
    // Counter type
3415
0
    TType* counterType = new TType(EbtUint, EvqBuffer);
3416
0
    counterType->setFieldName(intermediate.implicitCounterName);
3417
3418
0
    TTypeList* blockStruct = new TTypeList;
3419
0
    TTypeLoc  member = { counterType, loc };
3420
0
    blockStruct->push_back(member);
3421
3422
0
    TType blockType(blockStruct, "", counterType->getQualifier());
3423
0
    blockType.getQualifier().storage = EvqBuffer;
3424
3425
0
    type.shallowCopy(blockType);
3426
0
    shareStructBufferType(type);
3427
0
}
3428
3429
// declare counter for a structured buffer type
3430
void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
3431
0
{
3432
    // Bail out if not a struct buffer
3433
0
    if (! isStructBufferType(bufferType))
3434
0
        return;
3435
3436
0
    if (! hasStructBuffCounter(bufferType))
3437
0
        return;
3438
3439
0
    TType blockType;
3440
0
    counterBufferType(loc, blockType);
3441
3442
0
    TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str());
3443
3444
    // Counter buffer is not yet in use
3445
0
    structBufferCounter[*blockName] = false;
3446
3447
0
    shareStructBufferType(blockType);
3448
0
    declareBlock(loc, blockType, blockName);
3449
0
}
3450
3451
// return the counter that goes with a given structuredbuffer
3452
TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
3453
0
{
3454
    // Bail out if not a struct buffer
3455
0
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
3456
0
        return nullptr;
3457
3458
0
    const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName()));
3459
3460
    // Mark the counter as being used
3461
0
    structBufferCounter[counterBlockName] = true;
3462
3463
0
    TIntermTyped* counterVar = handleVariable(loc, &counterBlockName);  // find the block structure
3464
0
    TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
3465
3466
0
    TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
3467
0
    counterMember->setType(TType(EbtUint));
3468
0
    return counterMember;
3469
0
}
3470
3471
//
3472
// Decompose structure buffer methods into AST
3473
//
3474
void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3475
0
{
3476
0
    if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
3477
0
        return;
3478
3479
0
    const TOperator op  = node->getAsOperator()->getOp();
3480
0
    TIntermAggregate* argAggregate = arguments->getAsAggregate();
3481
3482
    // Buffer is the object upon which method is called, so always arg 0
3483
0
    TIntermTyped* bufferObj = nullptr;
3484
3485
    // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
3486
0
    if (argAggregate) {
3487
0
        if (argAggregate->getSequence().empty())
3488
0
            return;
3489
0
        if (argAggregate->getSequence()[0])
3490
0
            bufferObj = argAggregate->getSequence()[0]->getAsTyped();
3491
0
    } else {
3492
0
        bufferObj = arguments->getAsSymbolNode();
3493
0
    }
3494
3495
0
    if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr)
3496
0
        return;
3497
3498
    // Some methods require a hidden internal counter, obtained via getStructBufferCounter().
3499
    // This lambda adds something to it and returns the old value.
3500
0
    const auto incDecCounter = [&](int incval) -> TIntermTyped* {
3501
0
        TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast<unsigned int>(incval), loc, true);
3502
0
        TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
3503
3504
0
        if (counter == nullptr)
3505
0
            return nullptr;
3506
3507
0
        TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
3508
0
        counterIncrement->setType(TType(EbtUint, EvqTemporary));
3509
0
        counterIncrement->setLoc(loc);
3510
0
        counterIncrement->getSequence().push_back(counter);
3511
0
        counterIncrement->getSequence().push_back(incrementValue);
3512
3513
0
        return counterIncrement;
3514
0
    };
3515
3516
    // Index to obtain the runtime sized array out of the buffer.
3517
0
    TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
3518
0
    if (argArray == nullptr)
3519
0
        return;  // It might not be a struct buffer method.
3520
3521
0
    switch (op) {
3522
0
    case EOpMethodLoad:
3523
0
        {
3524
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3525
0
            if (argIndex == nullptr) {
3526
0
                error(loc, "invalid index for Load", "", "");
3527
0
                return;
3528
0
            }
3529
3530
0
            const TType& bufferType = bufferObj->getType();
3531
3532
0
            const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn;
3533
3534
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3535
            // buffer then, but that's what it calls itself.
3536
0
            const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer   ||
3537
0
                                              builtInType == EbvRWByteAddressBuffer);
3538
3539
3540
0
            if (isByteAddressBuffer)
3541
0
                argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex,
3542
0
                                                      intermediate.addConstantUnion(2, loc, true),
3543
0
                                                      loc, TType(EbtInt));
3544
3545
            // Index into the array to find the item being loaded.
3546
0
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3547
3548
0
            node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3549
3550
0
            const TType derefType(argArray->getType(), 0);
3551
0
            node->setType(derefType);
3552
0
        }
3553
3554
0
        break;
3555
3556
0
    case EOpMethodLoad2:
3557
0
    case EOpMethodLoad3:
3558
0
    case EOpMethodLoad4:
3559
0
        {
3560
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3561
0
            if (argIndex == nullptr) {
3562
0
                error(loc, "invalid index for vector Load", "", "");
3563
0
                return;
3564
0
            }
3565
3566
0
            TOperator constructOp = EOpNull;
3567
0
            int size = 0;
3568
3569
0
            switch (op) {
3570
0
            case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
3571
0
            case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
3572
0
            case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
3573
0
            default: assert(0);
3574
0
            }
3575
3576
0
            TIntermTyped* body = nullptr;
3577
3578
            // First, we'll store the address in a variable to avoid multiple shifts
3579
            // (we must convert the byte address to an item address)
3580
0
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3581
0
                                                                   intermediate.addConstantUnion(2, loc, true),
3582
0
                                                                   loc, TType(EbtInt));
3583
3584
0
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3585
0
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3586
3587
0
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3588
3589
0
            TIntermTyped* vec = nullptr;
3590
3591
            // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
3592
            // address conversion.
3593
0
            for (int idx=0; idx<size; ++idx) {
3594
0
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3595
3596
                // add index offset
3597
0
                if (idx != 0)
3598
0
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx,
3599
0
                                                           intermediate.addConstantUnion(idx, loc, true),
3600
0
                                                           loc, TType(EbtInt));
3601
3602
0
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3603
0
                                                                                        : EOpIndexIndirect;
3604
3605
0
                TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3606
3607
0
                TType derefType(argArray->getType(), 0);
3608
0
                derefType.getQualifier().makeTemporary();
3609
0
                indexVal->setType(derefType);
3610
3611
0
                vec = intermediate.growAggregate(vec, indexVal);
3612
0
            }
3613
3614
0
            vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
3615
0
            vec->getAsAggregate()->setOperator(constructOp);
3616
3617
0
            body = intermediate.growAggregate(body, vec);
3618
0
            body->setType(vec->getType());
3619
0
            body->getAsAggregate()->setOperator(EOpSequence);
3620
3621
0
            node = body;
3622
0
        }
3623
3624
0
        break;
3625
3626
0
    case EOpMethodStore:
3627
0
    case EOpMethodStore2:
3628
0
    case EOpMethodStore3:
3629
0
    case EOpMethodStore4:
3630
0
        {
3631
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3632
0
            if (argIndex == nullptr) {
3633
0
                error(loc, "invalid index for Store", "", "");
3634
0
                return;
3635
0
            }
3636
0
            TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped();  // value
3637
3638
            // Index into the array to find the item being loaded.
3639
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3640
            // buffer then, but that's what it calls itself).
3641
3642
0
            int size = 0;
3643
3644
0
            switch (op) {
3645
0
            case EOpMethodStore:  size = 1; break;
3646
0
            case EOpMethodStore2: size = 2; break;
3647
0
            case EOpMethodStore3: size = 3; break;
3648
0
            case EOpMethodStore4: size = 4; break;
3649
0
            default: assert(0);
3650
0
            }
3651
3652
0
            TIntermAggregate* body = nullptr;
3653
3654
            // First, we'll store the address in a variable to avoid multiple shifts
3655
            // (we must convert the byte address to an item address)
3656
0
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3657
0
                                                                   intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
3658
3659
0
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3660
0
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3661
3662
0
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3663
3664
0
            for (int idx=0; idx<size; ++idx) {
3665
0
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3666
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3667
3668
                // add index offset
3669
0
                if (idx != 0)
3670
0
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
3671
3672
0
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3673
0
                                                                                        : EOpIndexIndirect;
3674
3675
0
                TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3676
0
                const TType derefType(argArray->getType(), 0);
3677
0
                lValue->setType(derefType);
3678
3679
0
                TIntermTyped* rValue;
3680
0
                if (size == 1) {
3681
0
                    rValue = argValue;
3682
0
                } else {
3683
0
                    rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
3684
0
                    const TType indexType(argValue->getType(), 0);
3685
0
                    rValue->setType(indexType);
3686
0
                }
3687
3688
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3689
3690
0
                body = intermediate.growAggregate(body, assign);
3691
0
            }
3692
3693
0
            body->setOperator(EOpSequence);
3694
0
            node = body;
3695
0
        }
3696
3697
0
        break;
3698
3699
0
    case EOpMethodGetDimensions:
3700
0
        {
3701
0
            const int numArgs = (int)argAggregate->getSequence().size();
3702
0
            TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped();  // out num items
3703
0
            TIntermTyped* argStride   = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr;  // out stride
3704
3705
0
            TIntermAggregate* body = nullptr;
3706
3707
            // Length output:
3708
0
            if (argArray->getType().isSizedArray()) {
3709
0
                const int length = argArray->getType().getOuterArraySize();
3710
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems,
3711
0
                                                              intermediate.addConstantUnion(length, loc, true), loc);
3712
0
                body = intermediate.growAggregate(body, assign, loc);
3713
0
            } else {
3714
0
                TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
3715
0
                                                                               argNumItems->getType());
3716
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
3717
0
                body = intermediate.growAggregate(body, assign, loc);
3718
0
            }
3719
3720
            // Stride output:
3721
0
            if (argStride != nullptr) {
3722
0
                int size;
3723
0
                int stride;
3724
0
                intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking,
3725
0
                                                argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
3726
3727
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride,
3728
0
                                                              intermediate.addConstantUnion(stride, loc, true), loc);
3729
3730
0
                body = intermediate.growAggregate(body, assign);
3731
0
            }
3732
3733
0
            body->setOperator(EOpSequence);
3734
0
            node = body;
3735
0
        }
3736
3737
0
        break;
3738
3739
0
    case EOpInterlockedAdd:
3740
0
    case EOpInterlockedAnd:
3741
0
    case EOpInterlockedExchange:
3742
0
    case EOpInterlockedMax:
3743
0
    case EOpInterlockedMin:
3744
0
    case EOpInterlockedOr:
3745
0
    case EOpInterlockedXor:
3746
0
    case EOpInterlockedCompareExchange:
3747
0
    case EOpInterlockedCompareStore:
3748
0
        {
3749
            // We'll replace the first argument with the block dereference, and let
3750
            // downstream decomposition handle the rest.
3751
3752
0
            TIntermSequence& sequence = argAggregate->getSequence();
3753
3754
0
            TIntermTyped* argIndex     = makeIntegerIndex(sequence[1]->getAsTyped());  // index
3755
0
            if (argIndex == nullptr) {
3756
0
                error(loc, "invalid destination address for interlocked operation", "", "");
3757
0
                return;
3758
0
            }
3759
0
            argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
3760
0
                                                  loc, TType(EbtInt));
3761
3762
0
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3763
0
            TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3764
3765
0
            const TType derefType(argArray->getType(), 0);
3766
0
            element->setType(derefType);
3767
3768
            // Replace the numeric byte offset parameter with array reference.
3769
0
            sequence[1] = element;
3770
0
            sequence.erase(sequence.begin(), sequence.begin()+1);
3771
0
        }
3772
0
        break;
3773
3774
0
    case EOpMethodIncrementCounter:
3775
0
        {
3776
0
            node = incDecCounter(1);
3777
0
            break;
3778
0
        }
3779
3780
0
    case EOpMethodDecrementCounter:
3781
0
        {
3782
0
            TIntermTyped* preIncValue = incDecCounter(-1); // result is original value
3783
0
            node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc,
3784
0
                                              preIncValue->getType());
3785
0
            break;
3786
0
        }
3787
3788
0
    case EOpMethodAppend:
3789
0
        {
3790
0
            TIntermTyped* oldCounter = incDecCounter(1);
3791
3792
0
            TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc);
3793
0
            TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped();
3794
3795
0
            const TType derefType(argArray->getType(), 0);
3796
0
            lValue->setType(derefType);
3797
3798
0
            node = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3799
3800
0
            break;
3801
0
        }
3802
3803
0
    case EOpMethodConsume:
3804
0
        {
3805
0
            TIntermTyped* oldCounter = incDecCounter(-1);
3806
3807
0
            TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter,
3808
0
                                                                  intermediate.addConstantUnion(-1, loc, true), loc,
3809
0
                                                                  oldCounter->getType());
3810
3811
0
            node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc);
3812
3813
0
            const TType derefType(argArray->getType(), 0);
3814
0
            node->setType(derefType);
3815
3816
0
            break;
3817
0
        }
3818
3819
0
    default:
3820
0
        break; // most pass through unchanged
3821
0
    }
3822
0
}
3823
3824
// Create array of standard sample positions for given sample count.
3825
// TODO: remove when a real method to query sample pos exists in SPIR-V.
3826
TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
3827
0
{
3828
0
    struct tSamplePos { float x, y; };
3829
3830
0
    static const tSamplePos pos1[] = {
3831
0
        { 0.0/16.0,  0.0/16.0 },
3832
0
    };
3833
3834
    // standard sample positions for 2, 4, 8, and 16 samples.
3835
0
    static const tSamplePos pos2[] = {
3836
0
        { 4.0/16.0,  4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
3837
0
    };
3838
3839
0
    static const tSamplePos pos4[] = {
3840
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 },
3841
0
    };
3842
3843
0
    static const tSamplePos pos8[] = {
3844
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 },
3845
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 },
3846
0
    };
3847
3848
0
    static const tSamplePos pos16[] = {
3849
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 },
3850
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 },
3851
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 },
3852
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 },
3853
0
    };
3854
3855
0
    const tSamplePos* sampleLoc = nullptr;
3856
0
    int numSamples = count;
3857
3858
0
    switch (count) {
3859
0
    case 2:  sampleLoc = pos2;  break;
3860
0
    case 4:  sampleLoc = pos4;  break;
3861
0
    case 8:  sampleLoc = pos8;  break;
3862
0
    case 16: sampleLoc = pos16; break;
3863
0
    default:
3864
0
        sampleLoc = pos1;
3865
0
        numSamples = 1;
3866
0
    }
3867
3868
0
    TConstUnionArray* values = new TConstUnionArray(numSamples*2);
3869
3870
0
    for (int pos=0; pos<count; ++pos) {
3871
0
        TConstUnion x, y;
3872
0
        x.setDConst(sampleLoc[pos].x);
3873
0
        y.setDConst(sampleLoc[pos].y);
3874
3875
0
        (*values)[pos*2+0] = x;
3876
0
        (*values)[pos*2+1] = y;
3877
0
    }
3878
3879
0
    TType retType(EbtFloat, EvqConst, 2);
3880
3881
0
    if (numSamples != 1) {
3882
0
        TArraySizes* arraySizes = new TArraySizes;
3883
0
        arraySizes->addInnerSize(numSamples);
3884
0
        retType.transferArraySizes(arraySizes);
3885
0
    }
3886
3887
0
    return new TIntermConstantUnion(*values, retType);
3888
0
}
3889
3890
//
3891
// Decompose DX9 and DX10 sample intrinsics & object methods into AST
3892
//
3893
void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3894
0
{
3895
0
    if (node == nullptr || !node->getAsOperator())
3896
0
        return;
3897
3898
    // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it.
3899
0
    const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
3900
0
        result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
3901
3902
0
        TIntermTyped* convertedResult = nullptr;
3903
3904
0
        TType retType;
3905
0
        getTextureReturnType(sampler, retType);
3906
3907
0
        if (retType.isStruct()) {
3908
            // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here)
3909
0
            TIntermAggregate* conversionAggregate = new TIntermAggregate;
3910
0
            convertedResult = conversionAggregate;
3911
3912
            // Convert vector output to return structure.  We will need a temp symbol to copy the results to.
3913
0
            TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType);
3914
3915
            // We also need a temp symbol to hold the result of the texture.  We don't want to re-fetch the
3916
            // sample each time we'll index into the result, so we'll copy to this, and index into the copy.
3917
0
            TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType());
3918
3919
            // Initial copy from texture to our sample result shadow.
3920
0
            TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc),
3921
0
                                                              result, loc);
3922
3923
0
            conversionAggregate->getSequence().push_back(shadowCopy);
3924
3925
0
            unsigned vec4Pos = 0;
3926
3927
0
            for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) {
3928
0
                const TType memberType(retType, m); // dereferenced type of the member we're about to assign.
3929
3930
                // Check for bad struct members.  This should have been caught upstream.  Complain, because
3931
                // wwe don't know what to do with it.  This algorithm could be generalized to handle
3932
                // other things, e.g, sub-structures, but HLSL doesn't allow them.
3933
0
                if (!memberType.isVector() && !memberType.isScalar()) {
3934
0
                    error(loc, "expected: scalar or vector type in texture structure", "", "");
3935
0
                    return nullptr;
3936
0
                }
3937
3938
                // Index into the struct variable to find the member to assign.
3939
0
                TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct,
3940
0
                                                                   intermediate.addSymbol(*structVar, loc),
3941
0
                                                                   intermediate.addConstantUnion(m, loc), loc);
3942
3943
0
                structMember->setType(memberType);
3944
3945
                // Assign each component of (possible) vector in struct member.
3946
0
                for (int component = 0; component < memberType.getVectorSize(); ++component) {
3947
0
                    TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect,
3948
0
                                                                     intermediate.addSymbol(*sampleShadow, loc),
3949
0
                                                                     intermediate.addConstantUnion(vec4Pos++, loc), loc);
3950
0
                    vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1));
3951
3952
0
                    TIntermTyped* memberAssign = nullptr;
3953
3954
0
                    if (memberType.isVector()) {
3955
                        // Vector member: we need to create an access chain to the vector component.
3956
3957
0
                        TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember,
3958
0
                                                                                 intermediate.addConstantUnion(component, loc), loc);
3959
3960
0
                        memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc);
3961
0
                    } else {
3962
                        // Scalar member: we can assign to it directly.
3963
0
                        memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc);
3964
0
                    }
3965
3966
3967
0
                    conversionAggregate->getSequence().push_back(memberAssign);
3968
0
                }
3969
0
            }
3970
3971
            // Add completed variable so the expression results in the whole struct value we just built.
3972
0
            conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc));
3973
3974
            // Make it a sequence.
3975
0
            intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc);
3976
0
        } else {
3977
            // vector clamp the output if template vector type is smaller than sample result.
3978
0
            if (retType.getVectorSize() < node->getVectorSize()) {
3979
                // Too many components.  Construct shorter vector from it.
3980
0
                const TOperator op = intermediate.mapTypeToConstructorOp(retType);
3981
3982
0
                convertedResult = constructBuiltIn(retType, op, result, loc, false);
3983
0
            } else {
3984
                // Enough components.  Use directly.
3985
0
                convertedResult = result;
3986
0
            }
3987
0
        }
3988
3989
0
        convertedResult->setLoc(loc);
3990
0
        return convertedResult;
3991
0
    };
3992
3993
0
    const TOperator op  = node->getAsOperator()->getOp();
3994
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
3995
3996
    // Bail out if not a sampler method.
3997
    // Note though this is odd to do before checking the op, because the op
3998
    // could be something that takes the arguments, and the function in question
3999
    // takes the result of the op.  So, this is not the final word.
4000
0
    if (arguments != nullptr) {
4001
0
        if (argAggregate == nullptr) {
4002
0
            if (arguments->getAsTyped()->getBasicType() != EbtSampler)
4003
0
                return;
4004
0
        } else {
4005
0
            if (argAggregate->getSequence().size() == 0 ||
4006
0
                argAggregate->getSequence()[0] == nullptr ||
4007
0
                argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler)
4008
0
                return;
4009
0
        }
4010
0
    }
4011
4012
0
    switch (op) {
4013
    // **** DX9 intrinsics: ****
4014
0
    case EOpTexture:
4015
0
        {
4016
            // Texture with ddx & ddy is really gradient form in HLSL
4017
0
            if (argAggregate->getSequence().size() == 4)
4018
0
                node->getAsAggregate()->setOperator(EOpTextureGrad);
4019
4020
0
            break;
4021
0
        }
4022
0
    case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different)
4023
0
        {
4024
0
            TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped();   // sampler
4025
0
            TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped();  // coord
4026
4027
0
            assert(argCoord->getVectorSize() == 4);
4028
0
            TIntermTyped *w = intermediate.addConstantUnion(3, loc, true);
4029
0
            TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc);
4030
4031
0
            TOperator constructOp = EOpNull;
4032
0
            const TSampler &sampler = argSamp->getType().getSampler();
4033
0
            int coordSize = 0;
4034
4035
0
            switch (sampler.dim)
4036
0
            {
4037
0
            case Esd1D:   constructOp = EOpConstructFloat; coordSize = 1; break; // 1D
4038
0
            case Esd2D:   constructOp = EOpConstructVec2;  coordSize = 2; break; // 2D
4039
0
            case Esd3D:   constructOp = EOpConstructVec3;  coordSize = 3; break; // 3D
4040
0
            case EsdCube: constructOp = EOpConstructVec3;  coordSize = 3; break; // also 3D
4041
0
            default:
4042
0
                error(loc, "unhandled DX9 texture LoD dimension", "", "");
4043
0
                break;
4044
0
            }
4045
4046
0
            TIntermAggregate *constructCoord = new TIntermAggregate(constructOp);
4047
0
            constructCoord->getSequence().push_back(argCoord);
4048
0
            constructCoord->setLoc(loc);
4049
0
            constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize));
4050
4051
0
            TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod);
4052
0
            tex->getSequence().push_back(argSamp);        // sampler
4053
0
            tex->getSequence().push_back(constructCoord); // coordinate
4054
0
            tex->getSequence().push_back(argLod);         // lod
4055
4056
0
            node = convertReturn(tex, sampler);
4057
4058
0
            break;
4059
0
        }
4060
4061
0
    case EOpTextureBias:
4062
0
        {
4063
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // sampler
4064
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // coord
4065
4066
            // HLSL puts bias in W component of coordinate.  We extract it and add it to
4067
            // the argument list, instead
4068
0
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
4069
0
            TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
4070
4071
0
            TOperator constructOp = EOpNull;
4072
0
            const TSampler& sampler = arg0->getType().getSampler();
4073
4074
0
            switch (sampler.dim) {
4075
0
            case Esd1D:   constructOp = EOpConstructFloat; break; // 1D
4076
0
            case Esd2D:   constructOp = EOpConstructVec2;  break; // 2D
4077
0
            case Esd3D:   constructOp = EOpConstructVec3;  break; // 3D
4078
0
            case EsdCube: constructOp = EOpConstructVec3;  break; // also 3D
4079
0
            default:
4080
0
                error(loc, "unhandled DX9 texture bias dimension", "", "");
4081
0
                break;
4082
0
            }
4083
4084
0
            TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
4085
0
            constructCoord->getSequence().push_back(arg1);
4086
0
            constructCoord->setLoc(loc);
4087
4088
            // The input vector should never be less than 2, since there's always a bias.
4089
            // The max is for safety, and should be a no-op.
4090
0
            constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
4091
4092
0
            TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
4093
0
            tex->getSequence().push_back(arg0);           // sampler
4094
0
            tex->getSequence().push_back(constructCoord); // coordinate
4095
0
            tex->getSequence().push_back(bias);           // bias
4096
4097
0
            node = convertReturn(tex, sampler);
4098
4099
0
            break;
4100
0
        }
4101
4102
    // **** DX10 methods: ****
4103
0
    case EOpMethodSample:     // fall through
4104
0
    case EOpMethodSampleBias: // ...
4105
0
        {
4106
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4107
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4108
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4109
0
            TIntermTyped* argBias   = nullptr;
4110
0
            TIntermTyped* argOffset = nullptr;
4111
0
            const TSampler& sampler = argTex->getType().getSampler();
4112
4113
0
            int nextArg = 3;
4114
4115
0
            if (op == EOpMethodSampleBias)  // SampleBias has a bias arg
4116
0
                argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
4117
4118
0
            TOperator textureOp = EOpTexture;
4119
4120
0
            if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
4121
0
                textureOp = EOpTextureOffset;
4122
0
                argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
4123
0
            }
4124
4125
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4126
4127
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4128
0
            txsample->getSequence().push_back(txcombine);
4129
0
            txsample->getSequence().push_back(argCoord);
4130
4131
0
            if (argOffset != nullptr)
4132
0
                txsample->getSequence().push_back(argOffset);
4133
4134
0
            if (argBias != nullptr)
4135
0
              txsample->getSequence().push_back(argBias);
4136
4137
0
            node = convertReturn(txsample, sampler);
4138
4139
0
            break;
4140
0
        }
4141
4142
0
    case EOpMethodSampleGrad: // ...
4143
0
        {
4144
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4145
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4146
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4147
0
            TIntermTyped* argDDX    = argAggregate->getSequence()[3]->getAsTyped();
4148
0
            TIntermTyped* argDDY    = argAggregate->getSequence()[4]->getAsTyped();
4149
0
            TIntermTyped* argOffset = nullptr;
4150
0
            const TSampler& sampler = argTex->getType().getSampler();
4151
4152
0
            TOperator textureOp = EOpTextureGrad;
4153
4154
0
            if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
4155
0
                textureOp = EOpTextureGradOffset;
4156
0
                argOffset = argAggregate->getSequence()[5]->getAsTyped();
4157
0
            }
4158
4159
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4160
4161
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4162
0
            txsample->getSequence().push_back(txcombine);
4163
0
            txsample->getSequence().push_back(argCoord);
4164
0
            txsample->getSequence().push_back(argDDX);
4165
0
            txsample->getSequence().push_back(argDDY);
4166
4167
0
            if (argOffset != nullptr)
4168
0
                txsample->getSequence().push_back(argOffset);
4169
4170
0
            node = convertReturn(txsample, sampler);
4171
4172
0
            break;
4173
0
        }
4174
4175
0
    case EOpMethodGetDimensions:
4176
0
        {
4177
            // AST returns a vector of results, which we break apart component-wise into
4178
            // separate values to assign to the HLSL method's outputs, ala:
4179
            //  tx . GetDimensions(width, height);
4180
            //      float2 sizeQueryTemp = EOpTextureQuerySize
4181
            //      width = sizeQueryTemp.X;
4182
            //      height = sizeQueryTemp.Y;
4183
4184
0
            TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4185
0
            const TType& texType = argTex->getType();
4186
4187
0
            assert(texType.getBasicType() == EbtSampler);
4188
4189
0
            const TSampler& sampler = texType.getSampler();
4190
0
            const TSamplerDim dim = sampler.dim;
4191
0
            const bool isImage = sampler.isImage();
4192
0
            const bool isMs = sampler.isMultiSample();
4193
0
            const int numArgs = (int)argAggregate->getSequence().size();
4194
4195
0
            int numDims = 0;
4196
4197
0
            switch (dim) {
4198
0
            case Esd1D:     numDims = 1; break; // W
4199
0
            case Esd2D:     numDims = 2; break; // W, H
4200
0
            case Esd3D:     numDims = 3; break; // W, H, D
4201
0
            case EsdCube:   numDims = 2; break; // W, H (cube)
4202
0
            case EsdBuffer: numDims = 1; break; // W (buffers)
4203
0
            case EsdRect:   numDims = 2; break; // W, H (rect)
4204
0
            default:
4205
0
                error(loc, "unhandled DX10 MethodGet dimension", "", "");
4206
0
                break;
4207
0
            }
4208
4209
            // Arrayed adds another dimension for the number of array elements
4210
0
            if (sampler.isArrayed())
4211
0
                ++numDims;
4212
4213
            // Establish whether the method itself is querying mip levels.  This can be false even
4214
            // if the underlying query requires a MIP level, due to the available HLSL method overloads.
4215
0
            const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
4216
4217
            // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
4218
            // True if:
4219
            //   1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
4220
            // or,
4221
            //   2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
4222
0
            const bool mipRequired =
4223
0
                ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
4224
0
                mipQuery; // 2...
4225
4226
            // AST assumes integer return.  Will be converted to float if required.
4227
0
            TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
4228
0
            sizeQuery->getSequence().push_back(argTex);
4229
4230
            // If we're building an LOD query, add the LOD.
4231
0
            if (mipRequired) {
4232
                // If the base HLSL query had no MIP level given, use level 0.
4233
0
                TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
4234
0
                    intermediate.addConstantUnion(0, loc, true);
4235
0
                sizeQuery->getSequence().push_back(queryLod);
4236
0
            }
4237
4238
0
            sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
4239
0
            sizeQuery->setLoc(loc);
4240
4241
            // Return value from size query
4242
0
            TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
4243
0
            tempArg->getWritableType().getQualifier().makeTemporary();
4244
0
            TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
4245
0
                                                                   intermediate.addSymbol(*tempArg, loc),
4246
0
                                                                   sizeQuery, loc);
4247
4248
            // Compound statement for assigning outputs
4249
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
4250
            // Index of first output parameter
4251
0
            const int outParamBase = mipQuery ? 2 : 1;
4252
4253
0
            for (int compNum = 0; compNum < numDims; ++compNum) {
4254
0
                TIntermTyped* indexedOut = nullptr;
4255
0
                TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
4256
4257
0
                if (numDims > 1) {
4258
0
                    TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
4259
0
                    indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
4260
0
                    indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
4261
0
                    indexedOut->setLoc(loc);
4262
0
                } else {
4263
0
                    indexedOut = sizeQueryReturn;
4264
0
                }
4265
4266
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
4267
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
4268
4269
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4270
0
            }
4271
4272
            // handle mip level parameter
4273
0
            if (mipQuery) {
4274
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4275
4276
0
                TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
4277
0
                levelsQuery->getSequence().push_back(argTex);
4278
0
                levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
4279
0
                levelsQuery->setLoc(loc);
4280
4281
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
4282
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4283
0
            }
4284
4285
            // 2DMS formats query # samples, which needs a different query op
4286
0
            if (sampler.isMultiSample()) {
4287
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4288
4289
0
                TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4290
0
                samplesQuery->getSequence().push_back(argTex);
4291
0
                samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4292
0
                samplesQuery->setLoc(loc);
4293
4294
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
4295
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4296
0
            }
4297
4298
0
            compoundStatement->setOperator(EOpSequence);
4299
0
            compoundStatement->setLoc(loc);
4300
0
            compoundStatement->setType(TType(EbtVoid));
4301
4302
0
            node = compoundStatement;
4303
4304
0
            break;
4305
0
        }
4306
4307
0
    case EOpMethodSampleCmp:  // fall through...
4308
0
    case EOpMethodSampleCmpLevelZero:
4309
0
        {
4310
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4311
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4312
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4313
0
            TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
4314
0
            TIntermTyped* argOffset = nullptr;
4315
4316
            // Sampler argument should be a sampler.
4317
0
            if (argSamp->getType().getBasicType() != EbtSampler) {
4318
0
                error(loc, "expected: sampler type", "", "");
4319
0
                return;
4320
0
            }
4321
4322
            // Sampler should be a SamplerComparisonState
4323
0
            if (! argSamp->getType().getSampler().isShadow()) {
4324
0
                error(loc, "expected: SamplerComparisonState", "", "");
4325
0
                return;
4326
0
            }
4327
4328
            // optional offset value
4329
0
            if (argAggregate->getSequence().size() > 4)
4330
0
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4331
4332
0
            const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
4333
4334
            // AST wants comparison value as one of the texture coordinates
4335
0
            TOperator constructOp = EOpNull;
4336
0
            switch (coordDimWithCmpVal) {
4337
            // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
4338
0
            case 2: constructOp = EOpConstructVec2;  break;
4339
0
            case 3: constructOp = EOpConstructVec3;  break;
4340
0
            case 4: constructOp = EOpConstructVec4;  break;
4341
0
            case 5: constructOp = EOpConstructVec4;  break; // cubeArrayShadow, cmp value is separate arg.
4342
0
            default:
4343
0
                error(loc, "unhandled DX10 MethodSample dimension", "", "");
4344
0
                break;
4345
0
            }
4346
4347
0
            TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
4348
0
            coordWithCmp->getSequence().push_back(argCoord);
4349
0
            if (coordDimWithCmpVal != 5) // cube array shadow is special.
4350
0
                coordWithCmp->getSequence().push_back(argCmpVal);
4351
0
            coordWithCmp->setLoc(loc);
4352
0
            coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
4353
4354
0
            TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
4355
0
            if (argOffset != nullptr)
4356
0
                textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
4357
4358
            // Create combined sampler & texture op
4359
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4360
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4361
0
            txsample->getSequence().push_back(txcombine);
4362
0
            txsample->getSequence().push_back(coordWithCmp);
4363
4364
0
            if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
4365
0
                txsample->getSequence().push_back(argCmpVal);
4366
4367
            // the LevelZero form uses 0 as an explicit LOD
4368
0
            if (op == EOpMethodSampleCmpLevelZero)
4369
0
                txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
4370
4371
            // Add offset if present
4372
0
            if (argOffset != nullptr)
4373
0
                txsample->getSequence().push_back(argOffset);
4374
4375
0
            txsample->setType(node->getType());
4376
0
            txsample->setLoc(loc);
4377
0
            node = txsample;
4378
4379
0
            break;
4380
0
        }
4381
4382
0
    case EOpMethodLoad:
4383
0
        {
4384
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4385
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[1]->getAsTyped();
4386
0
            TIntermTyped* argOffset = nullptr;
4387
0
            TIntermTyped* lodComponent = nullptr;
4388
0
            TIntermTyped* coordSwizzle = nullptr;
4389
4390
0
            const TSampler& sampler = argTex->getType().getSampler();
4391
0
            const bool isMS = sampler.isMultiSample();
4392
0
            const bool isBuffer = sampler.dim == EsdBuffer;
4393
0
            const bool isImage = sampler.isImage();
4394
0
            const TBasicType coordBaseType = argCoord->getType().getBasicType();
4395
4396
            // Last component of coordinate is the mip level, for non-MS.  we separate them here:
4397
0
            if (isMS || isBuffer || isImage) {
4398
                // MS, Buffer, and Image have no LOD
4399
0
                coordSwizzle = argCoord;
4400
0
            } else {
4401
                // Extract coordinate
4402
0
                int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
4403
0
                TSwizzleSelectors<TVectorSelector> coordFields;
4404
0
                for (int i = 0; i < swizzleSize; ++i)
4405
0
                    coordFields.push_back(i);
4406
0
                TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
4407
0
                coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
4408
0
                coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
4409
4410
                // Extract LOD
4411
0
                TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
4412
0
                lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
4413
0
                lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
4414
0
            }
4415
4416
0
            const int numArgs    = (int)argAggregate->getSequence().size();
4417
0
            const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
4418
4419
            // Create texel fetch
4420
0
            const TOperator fetchOp = (isImage   ? EOpImageLoad :
4421
0
                                       hasOffset ? EOpTextureFetchOffset :
4422
0
                                       EOpTextureFetch);
4423
0
            TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
4424
4425
            // Build up the fetch
4426
0
            txfetch->getSequence().push_back(argTex);
4427
0
            txfetch->getSequence().push_back(coordSwizzle);
4428
4429
0
            if (isMS) {
4430
                // add 2DMS sample index
4431
0
                TIntermTyped* argSampleIdx  = argAggregate->getSequence()[2]->getAsTyped();
4432
0
                txfetch->getSequence().push_back(argSampleIdx);
4433
0
            } else if (isBuffer) {
4434
                // Nothing else to do for buffers.
4435
0
            } else if (isImage) {
4436
                // Nothing else to do for images.
4437
0
            } else {
4438
                // 2DMS and buffer have no LOD, but everything else does.
4439
0
                txfetch->getSequence().push_back(lodComponent);
4440
0
            }
4441
4442
            // Obtain offset arg, if there is one.
4443
0
            if (hasOffset) {
4444
0
                const int offsetPos  = (isMS ? 3 : 2);
4445
0
                argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
4446
0
                txfetch->getSequence().push_back(argOffset);
4447
0
            }
4448
4449
0
            node = convertReturn(txfetch, sampler);
4450
4451
0
            break;
4452
0
        }
4453
4454
0
    case EOpMethodSampleLevel:
4455
0
        {
4456
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4457
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4458
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4459
0
            TIntermTyped* argLod    = argAggregate->getSequence()[3]->getAsTyped();
4460
0
            TIntermTyped* argOffset = nullptr;
4461
0
            const TSampler& sampler = argTex->getType().getSampler();
4462
4463
0
            const int  numArgs = (int)argAggregate->getSequence().size();
4464
4465
0
            if (numArgs == 5) // offset, if present
4466
0
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4467
4468
0
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
4469
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4470
4471
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4472
4473
0
            txsample->getSequence().push_back(txcombine);
4474
0
            txsample->getSequence().push_back(argCoord);
4475
0
            txsample->getSequence().push_back(argLod);
4476
4477
0
            if (argOffset != nullptr)
4478
0
                txsample->getSequence().push_back(argOffset);
4479
4480
0
            node = convertReturn(txsample, sampler);
4481
4482
0
            break;
4483
0
        }
4484
4485
0
    case EOpMethodGather:
4486
0
        {
4487
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4488
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4489
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4490
0
            TIntermTyped* argOffset = nullptr;
4491
4492
            // Offset is optional
4493
0
            if (argAggregate->getSequence().size() > 3)
4494
0
                argOffset = argAggregate->getSequence()[3]->getAsTyped();
4495
4496
0
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
4497
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4498
4499
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4500
4501
0
            txgather->getSequence().push_back(txcombine);
4502
0
            txgather->getSequence().push_back(argCoord);
4503
            // Offset if not given is implicitly channel 0 (red)
4504
4505
0
            if (argOffset != nullptr)
4506
0
                txgather->getSequence().push_back(argOffset);
4507
4508
0
            txgather->setType(node->getType());
4509
0
            txgather->setLoc(loc);
4510
0
            node = txgather;
4511
4512
0
            break;
4513
0
        }
4514
4515
0
    case EOpMethodGatherRed:      // fall through...
4516
0
    case EOpMethodGatherGreen:    // ...
4517
0
    case EOpMethodGatherBlue:     // ...
4518
0
    case EOpMethodGatherAlpha:    // ...
4519
0
    case EOpMethodGatherCmpRed:   // ...
4520
0
    case EOpMethodGatherCmpGreen: // ...
4521
0
    case EOpMethodGatherCmpBlue:  // ...
4522
0
    case EOpMethodGatherCmpAlpha: // ...
4523
0
        {
4524
0
            int channel = 0;    // the channel we are gathering
4525
0
            int cmpValues = 0;  // 1 if there is a compare value (handier than a bool below)
4526
4527
0
            switch (op) {
4528
0
            case EOpMethodGatherCmpRed:   cmpValues = 1;  [[fallthrough]];
4529
0
            case EOpMethodGatherRed:      channel = 0; break;
4530
0
            case EOpMethodGatherCmpGreen: cmpValues = 1;  [[fallthrough]];
4531
0
            case EOpMethodGatherGreen:    channel = 1; break;
4532
0
            case EOpMethodGatherCmpBlue:  cmpValues = 1;  [[fallthrough]];
4533
0
            case EOpMethodGatherBlue:     channel = 2; break;
4534
0
            case EOpMethodGatherCmpAlpha: cmpValues = 1;  [[fallthrough]];
4535
0
            case EOpMethodGatherAlpha:    channel = 3; break;
4536
0
            default:                      assert(0);   break;
4537
0
            }
4538
4539
            // For now, we have nothing to map the component-wise comparison forms
4540
            // to, because neither GLSL nor SPIR-V has such an opcode.  Issue an
4541
            // unimplemented error instead.  Most of the machinery is here if that
4542
            // should ever become available.  However, red can be passed through
4543
            // to OpImageDrefGather.  G/B/A cannot, because that opcode does not
4544
            // accept a component.
4545
0
            if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
4546
0
                error(loc, "unimplemented: component-level gather compare", "", "");
4547
0
                return;
4548
0
            }
4549
4550
0
            int arg = 0;
4551
4552
0
            TIntermTyped* argTex        = argAggregate->getSequence()[arg++]->getAsTyped();
4553
0
            TIntermTyped* argSamp       = argAggregate->getSequence()[arg++]->getAsTyped();
4554
0
            TIntermTyped* argCoord      = argAggregate->getSequence()[arg++]->getAsTyped();
4555
0
            TIntermTyped* argOffset     = nullptr;
4556
0
            TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
4557
            // TIntermTyped* argStatus     = nullptr; // TODO: residency
4558
0
            TIntermTyped* argCmp        = nullptr;
4559
4560
0
            const TSamplerDim dim = argTex->getType().getSampler().dim;
4561
4562
0
            const int  argSize = (int)argAggregate->getSequence().size();
4563
0
            bool hasStatus     = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
4564
0
            bool hasOffset1    = false;
4565
0
            bool hasOffset4    = false;
4566
4567
            // Sampler argument should be a sampler.
4568
0
            if (argSamp->getType().getBasicType() != EbtSampler) {
4569
0
                error(loc, "expected: sampler type", "", "");
4570
0
                return;
4571
0
            }
4572
4573
            // Cmp forms require SamplerComparisonState
4574
0
            if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) {
4575
0
                error(loc, "expected: SamplerComparisonState", "", "");
4576
0
                return;
4577
0
            }
4578
4579
            // Only 2D forms can have offsets.  Discover if we have 0, 1 or 4 offsets.
4580
0
            if (dim == Esd2D) {
4581
0
                hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
4582
0
                hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
4583
0
            }
4584
4585
0
            assert(!(hasOffset1 && hasOffset4));
4586
4587
0
            TOperator textureOp = EOpTextureGather;
4588
4589
            // Compare forms have compare value
4590
0
            if (cmpValues != 0)
4591
0
                argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4592
4593
            // Some forms have single offset
4594
0
            if (hasOffset1) {
4595
0
                textureOp = EOpTextureGatherOffset;   // single offset form
4596
0
                argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4597
0
            }
4598
4599
            // Some forms have 4 gather offsets
4600
0
            if (hasOffset4) {
4601
0
                textureOp = EOpTextureGatherOffsets;  // note plural, for 4 offset form
4602
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4603
0
                    argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
4604
0
            }
4605
4606
            // Residency status
4607
0
            if (hasStatus) {
4608
                // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
4609
0
                error(loc, "unimplemented: residency status", "", "");
4610
0
                return;
4611
0
            }
4612
4613
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4614
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4615
4616
0
            TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
4617
4618
0
            txgather->getSequence().push_back(txcombine);
4619
0
            txgather->getSequence().push_back(argCoord);
4620
4621
            // AST wants an array of 4 offsets, where HLSL has separate args.  Here
4622
            // we construct an array from the separate args.
4623
0
            if (hasOffset4) {
4624
0
                TType arrayType(EbtInt, EvqTemporary, 2);
4625
0
                TArraySizes* arraySizes = new TArraySizes;
4626
0
                arraySizes->addInnerSize(4);
4627
0
                arrayType.transferArraySizes(arraySizes);
4628
4629
0
                TIntermAggregate* initList = new TIntermAggregate(EOpNull);
4630
4631
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4632
0
                    initList->getSequence().push_back(argOffsets[offsetNum]);
4633
4634
0
                argOffset = addConstructor(loc, initList, arrayType);
4635
0
            }
4636
4637
            // Add comparison value if we have one
4638
0
            if (argCmp != nullptr)
4639
0
                txgather->getSequence().push_back(argCmp);
4640
4641
            // Add offset (either 1, or an array of 4) if we have one
4642
0
            if (argOffset != nullptr)
4643
0
                txgather->getSequence().push_back(argOffset);
4644
4645
            // Add channel value if the sampler is not shadow
4646
0
            if (! argSamp->getType().getSampler().isShadow())
4647
0
                txgather->getSequence().push_back(argChannel);
4648
4649
0
            txgather->setType(node->getType());
4650
0
            txgather->setLoc(loc);
4651
0
            node = txgather;
4652
4653
0
            break;
4654
0
        }
4655
4656
0
    case EOpMethodCalculateLevelOfDetail:
4657
0
    case EOpMethodCalculateLevelOfDetailUnclamped:
4658
0
        {
4659
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4660
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4661
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4662
4663
0
            TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
4664
4665
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4666
0
            txquerylod->getSequence().push_back(txcombine);
4667
0
            txquerylod->getSequence().push_back(argCoord);
4668
4669
0
            TIntermTyped* lodComponent = intermediate.addConstantUnion(
4670
0
                op == EOpMethodCalculateLevelOfDetail ? 0 : 1,
4671
0
                loc, true);
4672
0
            TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
4673
0
            lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
4674
0
            node = lodComponentIdx;
4675
4676
0
            break;
4677
0
        }
4678
4679
0
    case EOpMethodGetSamplePosition:
4680
0
        {
4681
            // TODO: this entire decomposition exists because there is not yet a way to query
4682
            // the sample position directly through SPIR-V.  Instead, we return fixed sample
4683
            // positions for common cases.  *** If the sample positions are set differently,
4684
            // this will be wrong. ***
4685
4686
0
            TIntermTyped* argTex     = argAggregate->getSequence()[0]->getAsTyped();
4687
0
            TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped();
4688
4689
0
            TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4690
0
            samplesQuery->getSequence().push_back(argTex);
4691
0
            samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4692
0
            samplesQuery->setLoc(loc);
4693
4694
0
            TIntermAggregate* compoundStatement = nullptr;
4695
4696
0
            TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint));
4697
0
            outSampleCount->getWritableType().getQualifier().makeTemporary();
4698
0
            TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc),
4699
0
                                                              samplesQuery, loc);
4700
0
            compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4701
4702
0
            TIntermTyped* idxtest[4];
4703
4704
            // Create tests against 2, 4, 8, and 16 sample values
4705
0
            int count = 0;
4706
0
            for (int val = 2; val <= 16; val *= 2)
4707
0
                idxtest[count++] =
4708
0
                    intermediate.addBinaryNode(EOpEqual,
4709
0
                                               intermediate.addSymbol(*outSampleCount, loc),
4710
0
                                               intermediate.addConstantUnion(val, loc),
4711
0
                                               loc, TType(EbtBool));
4712
4713
0
            const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
4714
4715
            // Create index ops into position arrays given sample index.
4716
            // TODO: should it be clamped?
4717
0
            TIntermTyped* index[4];
4718
0
            count = 0;
4719
0
            for (int val = 2; val <= 16; val *= 2) {
4720
0
                index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc);
4721
0
                index[count++]->setType(TType(EbtFloat, EvqTemporary, 2));
4722
0
            }
4723
4724
            // Create expression as:
4725
            // (sampleCount == 2)  ? pos2[idx] :
4726
            // (sampleCount == 4)  ? pos4[idx] :
4727
            // (sampleCount == 8)  ? pos8[idx] :
4728
            // (sampleCount == 16) ? pos16[idx] : float2(0,0);
4729
0
            TIntermTyped* test =
4730
0
                intermediate.addSelection(idxtest[0], index[0],
4731
0
                    intermediate.addSelection(idxtest[1], index[1],
4732
0
                        intermediate.addSelection(idxtest[2], index[2],
4733
0
                            intermediate.addSelection(idxtest[3], index[3],
4734
0
                                                      getSamplePosArray(1), loc), loc), loc), loc);
4735
4736
0
            compoundStatement = intermediate.growAggregate(compoundStatement, test);
4737
0
            compoundStatement->setOperator(EOpSequence);
4738
0
            compoundStatement->setLoc(loc);
4739
0
            compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2));
4740
4741
0
            node = compoundStatement;
4742
4743
0
            break;
4744
0
        }
4745
4746
0
    case EOpSubpassLoad:
4747
0
        {
4748
0
            const TIntermTyped* argSubpass =
4749
0
                argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
4750
0
                arguments->getAsTyped();
4751
4752
0
            const TSampler& sampler = argSubpass->getType().getSampler();
4753
4754
            // subpass load: the multisample form is overloaded.  Here, we convert that to
4755
            // the EOpSubpassLoadMS opcode.
4756
0
            if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
4757
0
                node->getAsOperator()->setOp(EOpSubpassLoadMS);
4758
4759
0
            node = convertReturn(node, sampler);
4760
4761
0
            break;
4762
0
        }
4763
4764
4765
0
    default:
4766
0
        break; // most pass through unchanged
4767
0
    }
4768
0
}
4769
4770
//
4771
// Decompose geometry shader methods
4772
//
4773
void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4774
0
{
4775
0
    if (node == nullptr || !node->getAsOperator())
4776
0
        return;
4777
4778
0
    const TOperator op  = node->getAsOperator()->getOp();
4779
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4780
4781
0
    switch (op) {
4782
0
    case EOpMethodAppend:
4783
0
        if (argAggregate) {
4784
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4785
0
            if (language != EShLangGeometry) {
4786
0
                node = nullptr;
4787
0
                return;
4788
0
            }
4789
4790
0
            TIntermAggregate* sequence = nullptr;
4791
0
            TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
4792
4793
0
            emit->setLoc(loc);
4794
0
            emit->setType(TType(EbtVoid));
4795
4796
0
            TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped();
4797
4798
            // This will be patched in finalization during finalizeAppendMethods()
4799
0
            sequence = intermediate.growAggregate(sequence, data, loc);
4800
0
            sequence = intermediate.growAggregate(sequence, emit);
4801
4802
0
            sequence->setOperator(EOpSequence);
4803
0
            sequence->setLoc(loc);
4804
0
            sequence->setType(TType(EbtVoid));
4805
4806
0
            gsAppends.push_back({sequence, loc});
4807
4808
0
            node = sequence;
4809
0
        }
4810
0
        break;
4811
4812
0
    case EOpMethodRestartStrip:
4813
0
        {
4814
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4815
0
            if (language != EShLangGeometry) {
4816
0
                node = nullptr;
4817
0
                return;
4818
0
            }
4819
4820
0
            TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
4821
0
            cut->setLoc(loc);
4822
0
            cut->setType(TType(EbtVoid));
4823
0
            node = cut;
4824
0
        }
4825
0
        break;
4826
4827
0
    default:
4828
0
        break; // most pass through unchanged
4829
0
    }
4830
0
}
4831
4832
//
4833
// Optionally decompose intrinsics to AST opcodes.
4834
//
4835
void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4836
0
{
4837
    // Helper to find image data for image atomics:
4838
    // OpImageLoad(image[idx])
4839
    // We take the image load apart and add its params to the atomic op aggregate node
4840
0
    const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
4841
0
        TIntermAggregate* loadOp = load->getAsAggregate();
4842
0
        if (loadOp == nullptr) {
4843
0
            error(loc, "unknown image type in atomic operation", "", "");
4844
0
            node = nullptr;
4845
0
            return;
4846
0
        }
4847
4848
0
        atomic->getSequence().push_back(loadOp->getSequence()[0]);
4849
0
        atomic->getSequence().push_back(loadOp->getSequence()[1]);
4850
0
    };
4851
4852
    // Return true if this is an imageLoad, which we will change to an image atomic.
4853
0
    const auto isImageParam = [](TIntermTyped* image) -> bool {
4854
0
        TIntermAggregate* imageAggregate = image->getAsAggregate();
4855
0
        return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
4856
0
    };
4857
4858
0
    const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* {
4859
0
        TSymbol* symbol = symbolTable.find(name);
4860
0
        if (nullptr == symbol) {
4861
0
            type.getQualifier().builtIn = builtin;
4862
4863
0
            TVariable* variable = new TVariable(NewPoolTString(name), type);
4864
4865
0
            symbolTable.insert(*variable);
4866
4867
0
            symbol = symbolTable.find(name);
4868
0
            assert(symbol && "Inserted symbol could not be found!");
4869
0
        }
4870
4871
0
        return intermediate.addSymbol(*(symbol->getAsVariable()), loc);
4872
0
    };
4873
4874
    // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
4875
    // opcodes for compatibility with existing software stacks.
4876
0
    static const bool decomposeHlslIntrinsics = true;
4877
4878
0
    if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
4879
0
        return;
4880
4881
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4882
0
    TIntermUnary* fnUnary = node->getAsUnaryNode();
4883
0
    const TOperator op  = node->getAsOperator()->getOp();
4884
4885
0
    switch (op) {
4886
0
    case EOpGenMul:
4887
0
        {
4888
            // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
4889
            // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
4890
            // we must reverse the operand order here.  Hence, arg0 gets sequence[1], etc.
4891
0
            TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
4892
0
            TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
4893
4894
0
            if (arg0->isVector() && arg1->isVector()) {  // vec * vec
4895
0
                node->getAsAggregate()->setOperator(EOpDot);
4896
0
            } else {
4897
0
                node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
4898
0
            }
4899
4900
0
            break;
4901
0
        }
4902
4903
0
    case EOpRcp:
4904
0
        {
4905
            // rcp(a) -> 1 / a
4906
0
            TIntermTyped* arg0 = fnUnary->getOperand();
4907
0
            TBasicType   type0 = arg0->getBasicType();
4908
0
            TIntermTyped* one  = intermediate.addConstantUnion(1, type0, loc, true);
4909
0
            node  = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
4910
4911
0
            break;
4912
0
        }
4913
4914
0
    case EOpAny: // fall through
4915
0
    case EOpAll:
4916
0
        {
4917
0
            TIntermTyped* typedArg = arguments->getAsTyped();
4918
4919
            // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool.
4920
            // We'll convert here.  Note that for efficiency, we could add a smarter
4921
            // decomposition for some type cases, e.g, maybe by decomposing a dot product.
4922
0
            if (typedArg->getType().getBasicType() != EbtBool) {
4923
0
                const TType boolType(EbtBool, EvqTemporary,
4924
0
                                     typedArg->getVectorSize(),
4925
0
                                     typedArg->getMatrixCols(),
4926
0
                                     typedArg->getMatrixRows(),
4927
0
                                     typedArg->isVector());
4928
4929
0
                typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg);
4930
0
                node->getAsUnaryNode()->setOperand(typedArg);
4931
0
            }
4932
4933
0
            break;
4934
0
        }
4935
4936
0
    case EOpSaturate:
4937
0
        {
4938
            // saturate(a) -> clamp(a,0,1)
4939
0
            TIntermTyped* arg0 = fnUnary->getOperand();
4940
0
            TBasicType   type0 = arg0->getBasicType();
4941
0
            TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
4942
4943
0
            clamp->getSequence().push_back(arg0);
4944
0
            clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
4945
0
            clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
4946
0
            clamp->setLoc(loc);
4947
0
            clamp->setType(node->getType());
4948
0
            clamp->getWritableType().getQualifier().makeTemporary();
4949
0
            node = clamp;
4950
4951
0
            break;
4952
0
        }
4953
4954
0
    case EOpSinCos:
4955
0
        {
4956
            // sincos(a,b,c) -> b = sin(a), c = cos(a)
4957
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
4958
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
4959
0
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
4960
4961
0
            TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
4962
0
            TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
4963
0
            TIntermTyped* sinAssign    = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
4964
0
            TIntermTyped* cosAssign    = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
4965
4966
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
4967
0
            compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
4968
0
            compoundStatement->setOperator(EOpSequence);
4969
0
            compoundStatement->setLoc(loc);
4970
0
            compoundStatement->setType(TType(EbtVoid));
4971
4972
0
            node = compoundStatement;
4973
4974
0
            break;
4975
0
        }
4976
4977
0
    case EOpClip:
4978
0
        {
4979
            // clip(a) -> if (any(a<0)) discard;
4980
0
            TIntermTyped*  arg0 = fnUnary->getOperand();
4981
0
            TBasicType     type0 = arg0->getBasicType();
4982
0
            TIntermTyped*  compareNode = nullptr;
4983
4984
            // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
4985
0
            if (!arg0->isScalar()) {
4986
                // component-wise compare: a < 0
4987
0
                TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
4988
0
                less->getSequence().push_back(arg0);
4989
0
                less->setLoc(loc);
4990
4991
                // make vec or mat of bool matching dimensions of input
4992
0
                less->setType(TType(EbtBool, EvqTemporary,
4993
0
                                    arg0->getType().getVectorSize(),
4994
0
                                    arg0->getType().getMatrixCols(),
4995
0
                                    arg0->getType().getMatrixRows(),
4996
0
                                    arg0->getType().isVector()));
4997
4998
                // calculate # of components for comparison const
4999
0
                const int constComponentCount =
5000
0
                    std::max(arg0->getType().getVectorSize(), 1) *
5001
0
                    std::max(arg0->getType().getMatrixCols(), 1) *
5002
0
                    std::max(arg0->getType().getMatrixRows(), 1);
5003
5004
0
                TConstUnion zero;
5005
0
                if (arg0->getType().isIntegerDomain())
5006
0
                    zero.setDConst(0);
5007
0
                else
5008
0
                    zero.setDConst(0.0);
5009
0
                TConstUnionArray zeros(constComponentCount, zero);
5010
5011
0
                less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
5012
5013
0
                compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
5014
0
            } else {
5015
0
                TIntermTyped* zero;
5016
0
                if (arg0->getType().isIntegerDomain())
5017
0
                    zero = intermediate.addConstantUnion(0, loc, true);
5018
0
                else
5019
0
                    zero = intermediate.addConstantUnion(0.0, type0, loc, true);
5020
0
                compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
5021
0
            }
5022
5023
0
            TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
5024
5025
0
            node = new TIntermSelection(compareNode, killNode, nullptr);
5026
0
            node->setLoc(loc);
5027
5028
0
            break;
5029
0
        }
5030
5031
0
    case EOpLog10:
5032
0
        {
5033
            // log10(a) -> log2(a) * 0.301029995663981  (== 1/log2(10))
5034
0
            TIntermTyped* arg0 = fnUnary->getOperand();
5035
0
            TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
5036
0
            TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
5037
5038
0
            node  = handleBinaryMath(loc, "mul", EOpMul, log2, base);
5039
5040
0
            break;
5041
0
        }
5042
5043
0
    case EOpDst:
5044
0
        {
5045
            // dest.x = 1;
5046
            // dest.y = src0.y * src1.y;
5047
            // dest.z = src0.z;
5048
            // dest.w = src1.w;
5049
5050
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5051
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5052
5053
0
            TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
5054
0
            TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
5055
0
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
5056
5057
0
            TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
5058
0
            TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
5059
0
            TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
5060
0
            TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
5061
5062
0
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5063
5064
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5065
0
            dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
5066
0
            dst->getSequence().push_back(src0z);
5067
0
            dst->getSequence().push_back(src1w);
5068
0
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5069
0
            dst->setLoc(loc);
5070
0
            node = dst;
5071
5072
0
            break;
5073
0
        }
5074
5075
0
    case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
5076
0
    case EOpInterlockedMin: // ...
5077
0
    case EOpInterlockedMax: // ...
5078
0
    case EOpInterlockedAnd: // ...
5079
0
    case EOpInterlockedOr:  // ...
5080
0
    case EOpInterlockedXor: // ...
5081
0
    case EOpInterlockedExchange: // always has output arg
5082
0
        {
5083
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5084
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // value
5085
0
            TIntermTyped* arg2 = nullptr;
5086
5087
0
            if (argAggregate->getSequence().size() > 2)
5088
0
                arg2 = argAggregate->getSequence()[2]->getAsTyped();
5089
5090
0
            const bool isImage = isImageParam(arg0);
5091
0
            const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
5092
0
            TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
5093
0
            atomic->setType(arg0->getType());
5094
0
            atomic->getWritableType().getQualifier().makeTemporary();
5095
0
            atomic->setLoc(loc);
5096
5097
0
            if (isImage) {
5098
                // orig_value = imageAtomicOp(image, loc, data)
5099
0
                imageAtomicParams(atomic, arg0);
5100
0
                atomic->getSequence().push_back(arg1);
5101
5102
0
                if (argAggregate->getSequence().size() > 2) {
5103
0
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5104
0
                } else {
5105
0
                    node = atomic; // no assignment needed, as there was no out var.
5106
0
                }
5107
0
            } else {
5108
                // Normal memory variable:
5109
                // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
5110
0
                if (argAggregate->getSequence().size() > 2) {
5111
                    // optional output param is present.  return value goes to arg2.
5112
0
                    atomic->getSequence().push_back(arg0);
5113
0
                    atomic->getSequence().push_back(arg1);
5114
5115
0
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5116
0
                } else {
5117
                    // Set the matching operator.  Since output is absent, this is all we need to do.
5118
0
                    node->getAsAggregate()->setOperator(atomicOp);
5119
0
                    node->setType(atomic->getType());
5120
0
                }
5121
0
            }
5122
5123
0
            break;
5124
0
        }
5125
5126
0
    case EOpInterlockedCompareExchange:
5127
0
        {
5128
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5129
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // cmp
5130
0
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();  // value
5131
0
            TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped();  // orig
5132
5133
0
            const bool isImage = isImageParam(arg0);
5134
0
            TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
5135
0
            atomic->setLoc(loc);
5136
0
            atomic->setType(arg2->getType());
5137
0
            atomic->getWritableType().getQualifier().makeTemporary();
5138
5139
0
            if (isImage) {
5140
0
                imageAtomicParams(atomic, arg0);
5141
0
            } else {
5142
0
                atomic->getSequence().push_back(arg0);
5143
0
            }
5144
5145
0
            atomic->getSequence().push_back(arg1);
5146
0
            atomic->getSequence().push_back(arg2);
5147
0
            node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
5148
5149
0
            break;
5150
0
        }
5151
5152
0
    case EOpEvaluateAttributeSnapped:
5153
0
        {
5154
            // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
5155
            // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
5156
            //   iU = (iU<<28)>>28
5157
            //   fU = ((float)iU)/16
5158
            // Targets might handle this natively, in which case they can disable
5159
            // decompositions.
5160
5161
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // value
5162
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // offset
5163
5164
0
            TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
5165
0
            TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
5166
0
                                                handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
5167
0
                                                i28);
5168
5169
0
            TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
5170
0
            TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
5171
0
                                                         intermediate.addConversion(EOpConstructFloat,
5172
0
                                                                                    TType(EbtFloat, EvqTemporary, 2), iU),
5173
0
                                                         recip16);
5174
5175
0
            TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
5176
0
            interp->getSequence().push_back(arg0);
5177
0
            interp->getSequence().push_back(floatOffset);
5178
0
            interp->setLoc(loc);
5179
0
            interp->setType(arg0->getType());
5180
0
            interp->getWritableType().getQualifier().makeTemporary();
5181
5182
0
            node = interp;
5183
5184
0
            break;
5185
0
        }
5186
5187
0
    case EOpLit:
5188
0
        {
5189
0
            TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
5190
0
            TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
5191
0
            TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
5192
5193
0
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5194
5195
            // Ambient
5196
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5197
5198
            // Diffuse:
5199
0
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5200
0
            TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
5201
0
            diffuse->getSequence().push_back(n_dot_l);
5202
0
            diffuse->getSequence().push_back(zero);
5203
0
            diffuse->setLoc(loc);
5204
0
            diffuse->setType(TType(EbtFloat));
5205
0
            dst->getSequence().push_back(diffuse);
5206
5207
            // Specular:
5208
0
            TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
5209
0
            min_ndot->getSequence().push_back(n_dot_l);
5210
0
            min_ndot->getSequence().push_back(n_dot_h);
5211
0
            min_ndot->setLoc(loc);
5212
0
            min_ndot->setType(TType(EbtFloat));
5213
5214
0
            TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
5215
0
            TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m);  // n_dot_h * m
5216
5217
0
            dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
5218
5219
            // One:
5220
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5221
5222
0
            dst->setLoc(loc);
5223
0
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5224
0
            node = dst;
5225
0
            break;
5226
0
        }
5227
5228
0
    case EOpAsDouble:
5229
0
        {
5230
            // asdouble accepts two 32 bit ints.  we can use EOpUint64BitsToDouble, but must
5231
            // first construct a uint64.
5232
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5233
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5234
5235
0
            if (arg0->getType().isVector()) { // TODO: ...
5236
0
                error(loc, "double2 conversion not implemented", "asdouble", "");
5237
0
                break;
5238
0
            }
5239
5240
0
            TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
5241
5242
0
            uint64->getSequence().push_back(arg0);
5243
0
            uint64->getSequence().push_back(arg1);
5244
0
            uint64->setType(TType(EbtUint, EvqTemporary, 2));  // convert 2 uints to a uint2
5245
0
            uint64->setLoc(loc);
5246
5247
            // bitcast uint2 to a double
5248
0
            TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
5249
0
            convert->getAsUnaryNode()->setOperand(uint64);
5250
0
            convert->setLoc(loc);
5251
0
            convert->setType(TType(EbtDouble, EvqTemporary));
5252
0
            node = convert;
5253
5254
0
            break;
5255
0
        }
5256
5257
0
    case EOpF16tof32:
5258
0
        {
5259
            // input uvecN with low 16 bits of each component holding a float16.  convert to float32.
5260
0
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5261
0
            TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
5262
0
            const int vecSize = argValue->getType().getVectorSize();
5263
5264
0
            TOperator constructOp = EOpNull;
5265
0
            switch (vecSize) {
5266
0
            case 1: constructOp = EOpNull;          break; // direct use, no construct needed
5267
0
            case 2: constructOp = EOpConstructVec2; break;
5268
0
            case 3: constructOp = EOpConstructVec3; break;
5269
0
            case 4: constructOp = EOpConstructVec4; break;
5270
0
            default: assert(0); break;
5271
0
            }
5272
5273
            // For scalar case, we don't need to construct another type.
5274
0
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5275
5276
0
            if (result) {
5277
0
                result->setType(TType(EbtFloat, EvqTemporary, vecSize));
5278
0
                result->setLoc(loc);
5279
0
            }
5280
5281
0
            for (int idx = 0; idx < vecSize; ++idx) {
5282
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5283
0
                TIntermTyped* component = argValue->getType().isVector() ?
5284
0
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5285
5286
0
                if (component != argValue)
5287
0
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5288
5289
0
                TIntermTyped* unpackOp  = new TIntermUnary(EOpUnpackHalf2x16);
5290
0
                unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
5291
0
                unpackOp->getAsUnaryNode()->setOperand(component);
5292
0
                unpackOp->setLoc(loc);
5293
5294
0
                TIntermTyped* lowOrder  = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
5295
5296
0
                if (result != nullptr) {
5297
0
                    result->getSequence().push_back(lowOrder);
5298
0
                    node = result;
5299
0
                } else {
5300
0
                    node = lowOrder;
5301
0
                }
5302
0
            }
5303
5304
0
            break;
5305
0
        }
5306
5307
0
    case EOpF32tof16:
5308
0
        {
5309
            // input floatN converted to 16 bit float in low order bits of each component of uintN
5310
0
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5311
5312
0
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5313
0
            const int vecSize = argValue->getType().getVectorSize();
5314
5315
0
            TOperator constructOp = EOpNull;
5316
0
            switch (vecSize) {
5317
0
            case 1: constructOp = EOpNull;           break; // direct use, no construct needed
5318
0
            case 2: constructOp = EOpConstructUVec2; break;
5319
0
            case 3: constructOp = EOpConstructUVec3; break;
5320
0
            case 4: constructOp = EOpConstructUVec4; break;
5321
0
            default: assert(0); break;
5322
0
            }
5323
5324
            // For scalar case, we don't need to construct another type.
5325
0
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5326
5327
0
            if (result) {
5328
0
                result->setType(TType(EbtUint, EvqTemporary, vecSize));
5329
0
                result->setLoc(loc);
5330
0
            }
5331
5332
0
            for (int idx = 0; idx < vecSize; ++idx) {
5333
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5334
0
                TIntermTyped* component = argValue->getType().isVector() ?
5335
0
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5336
5337
0
                if (component != argValue)
5338
0
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5339
5340
0
                TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
5341
0
                vec2ComponentAndZero->getSequence().push_back(component);
5342
0
                vec2ComponentAndZero->getSequence().push_back(zero);
5343
0
                vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
5344
0
                vec2ComponentAndZero->setLoc(loc);
5345
5346
0
                TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
5347
0
                packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
5348
0
                packOp->setLoc(loc);
5349
0
                packOp->setType(TType(EbtUint, EvqTemporary));
5350
5351
0
                if (result != nullptr) {
5352
0
                    result->getSequence().push_back(packOp);
5353
0
                    node = result;
5354
0
                } else {
5355
0
                    node = packOp;
5356
0
                }
5357
0
            }
5358
5359
0
            break;
5360
0
        }
5361
5362
0
    case EOpD3DCOLORtoUBYTE4:
5363
0
        {
5364
            // ivec4 ( x.zyxw * 255.001953 );
5365
0
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5366
0
            TSwizzleSelectors<TVectorSelector> selectors;
5367
0
            selectors.push_back(2);
5368
0
            selectors.push_back(1);
5369
0
            selectors.push_back(0);
5370
0
            selectors.push_back(3);
5371
0
            TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
5372
0
            TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
5373
0
            swizzled->setType(arg0->getType());
5374
0
            swizzled->getWritableType().getQualifier().makeTemporary();
5375
5376
0
            TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
5377
0
            TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
5378
0
            rangeConverted->setType(arg0->getType());
5379
0
            rangeConverted->getWritableType().getQualifier().makeTemporary();
5380
5381
0
            node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
5382
0
            node->setLoc(loc);
5383
0
            node->setType(TType(EbtInt, EvqTemporary, 4));
5384
0
            break;
5385
0
        }
5386
5387
0
    case EOpIsFinite:
5388
0
        {
5389
            // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
5390
            // it to !isnan && !isinf
5391
5392
0
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5393
5394
            // We'll make a temporary in case the RHS is cmoplex
5395
0
            TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
5396
0
            tempArg->getWritableType().getQualifier().makeTemporary();
5397
5398
0
            TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
5399
0
                                                                intermediate.addSymbol(*tempArg, loc),
5400
0
                                                                arg0, loc);
5401
5402
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
5403
5404
0
            const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(),
5405
0
                                 arg0->getMatrixRows());
5406
5407
0
            TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
5408
0
            isnan->setType(boolType);
5409
5410
0
            TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
5411
0
            notnan->setType(boolType);
5412
5413
0
            TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
5414
0
            isinf->setType(boolType);
5415
5416
0
            TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
5417
0
            notinf->setType(boolType);
5418
5419
0
            TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
5420
0
            andNode->setType(boolType);
5421
5422
0
            compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
5423
0
            compoundStatement->setOperator(EOpSequence);
5424
0
            compoundStatement->setLoc(loc);
5425
0
            compoundStatement->setType(boolType);
5426
5427
0
            node = compoundStatement;
5428
5429
0
            break;
5430
0
        }
5431
0
    case EOpWaveGetLaneCount:
5432
0
        {
5433
            // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol
5434
            // so that it inhabits the symbol table, but has a user-invalid name
5435
            // in-case some source HLSL defined the symbol also).
5436
0
            TType type(EbtUint, EvqVaryingIn);
5437
0
            node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type);
5438
0
            break;
5439
0
        }
5440
0
    case EOpWaveGetLaneIndex:
5441
0
        {
5442
            // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the
5443
            // symbol so that it inhabits the symbol table, but has a
5444
            // user-invalid name in-case some source HLSL defined the symbol
5445
            // also).
5446
0
            TType type(EbtUint, EvqVaryingIn);
5447
0
            node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type);
5448
0
            break;
5449
0
        }
5450
0
    case EOpWaveActiveCountBits:
5451
0
        {
5452
            // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin
5453
5454
            // uvec4 type.
5455
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5456
5457
            // Get the uvec4 return from subgroupBallot().
5458
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5459
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5460
5461
            // uint type.
5462
0
            TType uintType(EbtUint, EvqTemporary);
5463
5464
0
            node = intermediate.addBuiltInFunctionCall(loc,
5465
0
                EOpSubgroupBallotBitCount, true, res, uintType);
5466
5467
0
            break;
5468
0
        }
5469
0
    case EOpWavePrefixCountBits:
5470
0
        {
5471
            // Mapped to subgroupBallotExclusiveBitCount(subgroupBallot())
5472
            // builtin
5473
5474
            // uvec4 type.
5475
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5476
5477
            // Get the uvec4 return from subgroupBallot().
5478
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5479
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5480
5481
            // uint type.
5482
0
            TType uintType(EbtUint, EvqTemporary);
5483
5484
0
            node = intermediate.addBuiltInFunctionCall(loc,
5485
0
                EOpSubgroupBallotExclusiveBitCount, true, res, uintType);
5486
5487
0
            break;
5488
0
        }
5489
5490
0
    default:
5491
0
        break; // most pass through unchanged
5492
0
    }
5493
0
}
5494
5495
//
5496
// Handle seeing function call syntax in the grammar, which could be any of
5497
//  - .length() method
5498
//  - constructor
5499
//  - a call to a built-in function mapped to an operator
5500
//  - a call to a built-in function that will remain a function call (e.g., texturing)
5501
//  - user function
5502
//  - subroutine call (not implemented yet)
5503
//
5504
TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
5505
7
{
5506
7
    TIntermTyped* result = nullptr;
5507
5508
7
    TOperator op = function->getBuiltInOp();
5509
7
    if (op != EOpNull) {
5510
        //
5511
        // Then this should be a constructor.
5512
        // Don't go through the symbol table for constructors.
5513
        // Their parameters will be verified algorithmically.
5514
        //
5515
7
        TType type(EbtVoid);  // use this to get the type back
5516
7
        if (! constructorError(loc, arguments, *function, op, type)) {
5517
            //
5518
            // It's a constructor, of type 'type'.
5519
            //
5520
7
            result = handleConstructor(loc, arguments, type);
5521
7
            if (result == nullptr) {
5522
0
                error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
5523
0
                return nullptr;
5524
0
            }
5525
7
        }
5526
7
    } else {
5527
        //
5528
        // Find it in the symbol table.
5529
        //
5530
0
        const TFunction* fnCandidate = nullptr;
5531
0
        bool builtIn = false;
5532
0
        int thisDepth = 0;
5533
5534
        // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
5535
        // and clamp the opposite arg.  Since that's complex, we farm it off to a separate method.
5536
        // It doesn't naturally fall out of processing an argument at a time in isolation.
5537
0
        if (function->getName() == "mul")
5538
0
            addGenMulArgumentConversion(loc, *function, arguments);
5539
5540
0
        TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5541
5542
        // TODO: this needs improvement: there's no way at present to look up a signature in
5543
        // the symbol table for an arbitrary type.  This is a temporary hack until that ability exists.
5544
        // It will have false positives, since it doesn't check arg counts or types.
5545
0
        if (arguments) {
5546
            // Check if first argument is struct buffer type.  It may be an aggregate or a symbol, so we
5547
            // look for either case.
5548
5549
0
            TIntermTyped* arg0 = nullptr;
5550
5551
0
            if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0])
5552
0
                arg0 = aggregate->getSequence()[0]->getAsTyped();
5553
0
            else if (arguments->getAsSymbolNode())
5554
0
                arg0 = arguments->getAsSymbolNode();
5555
5556
0
            if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
5557
0
                static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
5558
5559
0
                if (function->getName().length() > methodPrefixSize &&
5560
0
                    isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
5561
0
                    const TString mangle = function->getName() + "(";
5562
0
                    TSymbol* symbol = symbolTable.find(mangle, &builtIn);
5563
5564
0
                    if (symbol)
5565
0
                        fnCandidate = symbol->getAsFunction();
5566
0
                }
5567
0
            }
5568
0
        }
5569
5570
0
        if (fnCandidate == nullptr)
5571
0
            fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments);
5572
5573
0
        if (fnCandidate) {
5574
            // This is a declared function that might map to
5575
            //  - a built-in operator,
5576
            //  - a built-in function not mapped to an operator, or
5577
            //  - a user function.
5578
5579
            // turn an implicit member-function resolution into an explicit call
5580
0
            TString callerName;
5581
0
            if (thisDepth == 0)
5582
0
                callerName = fnCandidate->getMangledName();
5583
0
            else {
5584
                // get the explicit (full) name of the function
5585
0
                assert(currentTypePrefix.size() >= size_t(thisDepth));
5586
0
                callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth];
5587
0
                callerName += fnCandidate->getMangledName();
5588
                // insert the implicit calling argument
5589
0
                pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments);
5590
0
            }
5591
5592
            // Convert 'in' arguments, so that types match.
5593
            // However, skip those that need expansion, that is covered next.
5594
0
            if (arguments)
5595
0
                addInputArgumentConversions(*fnCandidate, arguments);
5596
5597
            // Expand arguments.  Some arguments must physically expand to a different set
5598
            // than what the shader declared and passes.
5599
0
            if (arguments && !builtIn)
5600
0
                expandArguments(loc, *fnCandidate, arguments);
5601
5602
            // Expansion may have changed the form of arguments
5603
0
            aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5604
5605
0
            op = fnCandidate->getBuiltInOp();
5606
0
            if (builtIn && op != EOpNull) {
5607
                // SM 4.0 and above guarantees roundEven semantics for round()
5608
0
                if (!hlslDX9Compatible() && op == EOpRound)
5609
0
                    op = EOpRoundEven;
5610
5611
                // A function call mapped to a built-in operation.
5612
0
                result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments,
5613
0
                                                             fnCandidate->getType());
5614
0
                if (result == nullptr)  {
5615
0
                    error(arguments->getLoc(), " wrong operand type", "Internal Error",
5616
0
                        "built in unary operator function.  Type: %s",
5617
0
                        static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
5618
0
                } else if (result->getAsOperator()) {
5619
0
                    builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
5620
0
                }
5621
0
            } else {
5622
                // This is a function call not mapped to built-in operator.
5623
                // It could still be a built-in function, but only if PureOperatorBuiltins == false.
5624
0
                result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
5625
0
                TIntermAggregate* call = result->getAsAggregate();
5626
0
                call->setName(callerName);
5627
5628
                // this is how we know whether the given function is a built-in function or a user-defined function
5629
                // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
5630
                // if builtIn == true, it's definitely a built-in function with EOpNull
5631
0
                if (! builtIn) {
5632
0
                    call->setUserDefined();
5633
0
                    intermediate.addToCallGraph(infoSink, currentCaller, callerName);
5634
0
                }
5635
0
            }
5636
5637
            // for decompositions, since we want to operate on the function node, not the aggregate holding
5638
            // output conversions.
5639
0
            const TIntermTyped* fnNode = result;
5640
5641
0
            decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
5642
0
            decomposeIntrinsic(loc, result, arguments);           // HLSL->AST intrinsic decompositions
5643
0
            decomposeSampleMethods(loc, result, arguments);       // HLSL->AST sample method decompositions
5644
0
            decomposeGeometryMethods(loc, result, arguments);     // HLSL->AST geometry method decompositions
5645
5646
            // Create the qualifier list, carried in the AST for the call.
5647
            // Because some arguments expand to multiple arguments, the qualifier list will
5648
            // be longer than the formal parameter list.
5649
0
            if (result == fnNode && result->getAsAggregate()) {
5650
0
                TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
5651
0
                for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
5652
0
                    TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
5653
0
                    if (hasStructBuffCounter(*(*fnCandidate)[i].type)) {
5654
                        // add buffer and counter buffer argument qualifier
5655
0
                        qualifierList.push_back(qual);
5656
0
                        qualifierList.push_back(qual);
5657
0
                    } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage,
5658
0
                                             true)) {
5659
                        // add structure member expansion
5660
0
                        for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb)
5661
0
                            qualifierList.push_back(qual);
5662
0
                    } else {
5663
                        // Normal 1:1 case
5664
0
                        qualifierList.push_back(qual);
5665
0
                    }
5666
0
                }
5667
0
            }
5668
5669
            // Convert 'out' arguments.  If it was a constant folded built-in, it won't be an aggregate anymore.
5670
            // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
5671
            // Also, build the qualifier list for user function calls, which are always called with an aggregate.
5672
            // We don't do this is if there has been a decomposition, which will have added its own conversions
5673
            // for output parameters.
5674
0
            if (result == fnNode && result->getAsAggregate())
5675
0
                result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
5676
0
        }
5677
0
    }
5678
5679
    // generic error recovery
5680
    // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to
5681
    //       reduce cascades
5682
7
    if (result == nullptr)
5683
0
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
5684
5685
7
    return result;
5686
7
}
5687
5688
// An initial argument list is difficult: it can be null, or a single node,
5689
// or an aggregate if more than one argument.  Add one to the front, maintaining
5690
// this lack of uniformity.
5691
void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments)
5692
0
{
5693
0
    if (arguments == nullptr)
5694
0
        arguments = front;
5695
0
    else if (arguments->getAsAggregate() != nullptr)
5696
0
        arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front);
5697
0
    else
5698
0
        arguments = intermediate.growAggregate(front, arguments);
5699
0
}
5700
5701
//
5702
// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat.  This is a
5703
// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
5704
// can look at both arguments insert explicit shape changes if required.
5705
//
5706
void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
5707
0
{
5708
0
    TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
5709
5710
0
    if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
5711
        // It really ought to have two arguments.
5712
0
        error(loc, "expected: mul arguments", "", "");
5713
0
        return;
5714
0
    }
5715
5716
0
    TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5717
0
    TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5718
5719
0
    if (arg0->isVector() && arg1->isVector()) {
5720
        // For:
5721
        //    vec * vec: it's handled during intrinsic selection, so while we could do it here,
5722
        //               we can also ignore it, which is easier.
5723
0
    } else if (arg0->isVector() && arg1->isMatrix()) {
5724
        // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
5725
0
        if (arg0->getVectorSize() < arg1->getMatrixCols()) {
5726
            // vec is smaller, so truncate larger mat dimension
5727
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5728
0
                                  0, arg0->getVectorSize(), arg1->getMatrixRows());
5729
0
            arg1 = addConstructor(loc, arg1, truncType);
5730
0
        } else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
5731
            // vec is larger, so truncate vec to mat size
5732
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5733
0
                                  arg1->getMatrixCols());
5734
0
            arg0 = addConstructor(loc, arg0, truncType);
5735
0
        }
5736
0
    } else if (arg0->isMatrix() && arg1->isVector()) {
5737
        // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
5738
0
        if (arg1->getVectorSize() < arg0->getMatrixRows()) {
5739
            // vec is smaller, so truncate larger mat dimension
5740
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5741
0
                                  0, arg0->getMatrixCols(), arg1->getVectorSize());
5742
0
            arg0 = addConstructor(loc, arg0, truncType);
5743
0
        } else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
5744
            // vec is larger, so truncate vec to mat size
5745
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5746
0
                                  arg0->getMatrixRows());
5747
0
            arg1 = addConstructor(loc, arg1, truncType);
5748
0
        }
5749
0
    } else if (arg0->isMatrix() && arg1->isMatrix()) {
5750
        // mat * mat: we clamp the smaller inner dimension to match the other matrix size.
5751
        // Remember, HLSL Mrc = GLSL/SPIRV Mcr.
5752
0
        if (arg0->getMatrixRows() > arg1->getMatrixCols()) {
5753
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5754
0
                                  0, arg0->getMatrixCols(), arg1->getMatrixCols());
5755
0
            arg0 = addConstructor(loc, arg0, truncType);
5756
0
        } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) {
5757
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5758
0
                                  0, arg0->getMatrixRows(), arg1->getMatrixRows());
5759
0
            arg1 = addConstructor(loc, arg1, truncType);
5760
0
        }
5761
0
    } else {
5762
        // It's something with scalars: we'll just leave it alone.  Function selection will handle it
5763
        // downstream.
5764
0
    }
5765
5766
    // Warn if we altered one of the arguments
5767
0
    if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1])
5768
0
        warn(loc, "mul() matrix size mismatch", "", "");
5769
5770
    // Put arguments back.  (They might be unchanged, in which case this is harmless).
5771
0
    argAggregate->getSequence()[0] = arg0;
5772
0
    argAggregate->getSequence()[1] = arg1;
5773
5774
0
    call[0].type = &arg0->getWritableType();
5775
0
    call[1].type = &arg1->getWritableType();
5776
0
}
5777
5778
//
5779
// Add any needed implicit conversions for function-call arguments to input parameters.
5780
//
5781
void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
5782
0
{
5783
0
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5784
5785
    // Replace a single argument with a single argument.
5786
0
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5787
0
        if (function.getParamCount() == 1)
5788
0
            arguments = arg;
5789
0
        else {
5790
0
            if (aggregate == nullptr)
5791
0
                arguments = arg;
5792
0
            else
5793
0
                aggregate->getSequence()[paramNum] = arg;
5794
0
        }
5795
0
    };
5796
5797
    // Process each argument's conversion
5798
0
    for (int param = 0; param < function.getParamCount(); ++param) {
5799
0
        if (! function[param].type->getQualifier().isParamInput())
5800
0
            continue;
5801
5802
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5803
        // is the single argument itself or its children are the arguments.  Only one argument
5804
        // means take 'arguments' itself as the one argument.
5805
0
        TIntermTyped* arg = function.getParamCount() == 1
5806
0
                                   ? arguments->getAsTyped()
5807
0
                                   : (aggregate ?
5808
0
                                        aggregate->getSequence()[param]->getAsTyped() :
5809
0
                                        arguments->getAsTyped());
5810
0
        if (*function[param].type != arg->getType()) {
5811
            // In-qualified arguments just need an extra node added above the argument to
5812
            // convert to the correct type.
5813
0
            TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg);
5814
0
            if (convArg != nullptr)
5815
0
                convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg);
5816
0
            if (convArg != nullptr)
5817
0
                setArg(param, convArg);
5818
0
            else
5819
0
                error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param);
5820
0
        } else {
5821
0
            if (wasFlattened(arg)) {
5822
                // If both formal and calling arg are to be flattened, leave that to argument
5823
                // expansion, not conversion.
5824
0
                if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5825
                    // Will make a two-level subtree.
5826
                    // The deepest will copy member-by-member to build the structure to pass.
5827
                    // The level above that will be a two-operand EOpComma sequence that follows the copy by the
5828
                    // object itself.
5829
0
                    TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type);
5830
0
                    internalAggregate->getWritableType().getQualifier().makeTemporary();
5831
0
                    TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
5832
0
                                                                          internalAggregate->getName(),
5833
0
                                                                          getLanguage(),
5834
0
                                                                          internalAggregate->getType());
5835
0
                    internalSymbolNode->setLoc(arg->getLoc());
5836
                    // This makes the deepest level, the member-wise copy
5837
0
                    TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign,
5838
0
                                                               internalSymbolNode, arg)->getAsAggregate();
5839
5840
                    // Now, pair that with the resulting aggregate.
5841
0
                    assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
5842
0
                    assignAgg->setOperator(EOpComma);
5843
0
                    assignAgg->setType(internalAggregate->getType());
5844
0
                    setArg(param, assignAgg);
5845
0
                }
5846
0
            }
5847
0
        }
5848
0
    }
5849
0
}
5850
5851
//
5852
// Add any needed implicit expansion of calling arguments from what the shader listed to what's
5853
// internally needed for the AST (given the constraints downstream).
5854
//
5855
void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments)
5856
0
{
5857
0
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5858
0
    int functionParamNumberOffset = 0;
5859
5860
    // Replace a single argument with a single argument.
5861
0
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5862
0
        if (function.getParamCount() + functionParamNumberOffset == 1)
5863
0
            arguments = arg;
5864
0
        else {
5865
0
            if (aggregate == nullptr)
5866
0
                arguments = arg;
5867
0
            else
5868
0
                aggregate->getSequence()[paramNum] = arg;
5869
0
        }
5870
0
    };
5871
5872
    // Replace a single argument with a list of arguments
5873
0
    const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) {
5874
0
        if (args.size() == 1)
5875
0
            setArg(paramNum, args.front());
5876
0
        else if (args.size() > 1) {
5877
0
            if (function.getParamCount() + functionParamNumberOffset == 1) {
5878
0
                arguments = intermediate.makeAggregate(args.front());
5879
0
                std::for_each(args.begin() + 1, args.end(),
5880
0
                    [&](TIntermTyped* arg) {
5881
0
                        arguments = intermediate.growAggregate(arguments, arg);
5882
0
                    });
5883
0
            } else {
5884
0
                auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum);
5885
0
                aggregate->getSequence().insert(it, args.begin(), args.end());
5886
0
            }
5887
0
            functionParamNumberOffset += (int)(args.size() - 1);
5888
0
        }
5889
0
    };
5890
5891
    // Process each argument's conversion
5892
0
    for (int param = 0; param < function.getParamCount(); ++param) {
5893
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5894
        // is the single argument itself or its children are the arguments.  Only one argument
5895
        // means take 'arguments' itself as the one argument.
5896
0
        TIntermTyped* arg = function.getParamCount() == 1
5897
0
                                   ? arguments->getAsTyped()
5898
0
                                   : (aggregate ?
5899
0
                                        aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() :
5900
0
                                        arguments->getAsTyped());
5901
5902
0
        if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5903
            // Need to pass the structure members instead of the structure.
5904
0
            TVector<TIntermTyped*> memberArgs;
5905
0
            for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb)
5906
0
                memberArgs.push_back(flattenAccess(arg, memb));
5907
0
            setArgList(param + functionParamNumberOffset, memberArgs);
5908
0
        }
5909
0
    }
5910
5911
    // TODO: if we need both hidden counter args (below) and struct expansion (above)
5912
    // the two algorithms need to be merged: Each assumes the list starts out 1:1 between
5913
    // parameters and arguments.
5914
5915
    // If any argument is a pass-by-reference struct buffer with an associated counter
5916
    // buffer, we have to add another hidden parameter for that counter.
5917
0
    if (aggregate)
5918
0
        addStructBuffArguments(loc, aggregate);
5919
0
}
5920
5921
//
5922
// Add any needed implicit output conversions for function-call arguments.  This
5923
// can require a new tree topology, complicated further by whether the function
5924
// has a return value.
5925
//
5926
// Returns a node of a subtree that evaluates to the return value of the function.
5927
//
5928
TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
5929
0
{
5930
0
    assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
5931
5932
0
    const TSourceLoc& loc = intermNode.getLoc();
5933
5934
0
    TIntermSequence argSequence; // temp sequence for unary node args
5935
5936
0
    if (intermNode.getAsUnaryNode())
5937
0
        argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
5938
5939
0
    TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
5940
5941
0
    const auto needsConversion = [&](int argNum) {
5942
0
        return function[argNum].type->getQualifier().isParamOutput() &&
5943
0
               (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
5944
0
                shouldConvertLValue(arguments[argNum]) ||
5945
0
                wasFlattened(arguments[argNum]->getAsTyped()));
5946
0
    };
5947
5948
    // Will there be any output conversions?
5949
0
    bool outputConversions = false;
5950
0
    for (int i = 0; i < function.getParamCount(); ++i) {
5951
0
        if (needsConversion(i)) {
5952
0
            outputConversions = true;
5953
0
            break;
5954
0
        }
5955
0
    }
5956
5957
0
    if (! outputConversions)
5958
0
        return &intermNode;
5959
5960
    // Setup for the new tree, if needed:
5961
    //
5962
    // Output conversions need a different tree topology.
5963
    // Out-qualified arguments need a temporary of the correct type, with the call
5964
    // followed by an assignment of the temporary to the original argument:
5965
    //     void: function(arg, ...)  ->        (          function(tempArg, ...), arg = tempArg, ...)
5966
    //     ret = function(arg, ...)  ->  ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
5967
    // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
5968
0
    TIntermTyped* conversionTree = nullptr;
5969
0
    TVariable* tempRet = nullptr;
5970
0
    if (intermNode.getBasicType() != EbtVoid) {
5971
        // do the "tempRet = function(...), " bit from above
5972
0
        tempRet = makeInternalVariable("tempReturn", intermNode.getType());
5973
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
5974
0
        conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
5975
0
    } else
5976
0
        conversionTree = &intermNode;
5977
5978
0
    conversionTree = intermediate.makeAggregate(conversionTree);
5979
5980
    // Process each argument's conversion
5981
0
    for (int i = 0; i < function.getParamCount(); ++i) {
5982
0
        if (needsConversion(i)) {
5983
            // Out-qualified arguments needing conversion need to use the topology setup above.
5984
            // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
5985
5986
            // Make a temporary for what the function expects the argument to look like.
5987
0
            TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
5988
0
            tempArg->getWritableType().getQualifier().makeTemporary();
5989
0
            TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
5990
5991
            // This makes the deepest level, the member-wise copy
5992
0
            TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(),
5993
0
                                                    tempArgNode);
5994
0
            tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
5995
0
            conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
5996
5997
            // replace the argument with another node for the same tempArg variable
5998
0
            arguments[i] = intermediate.addSymbol(*tempArg, loc);
5999
0
        }
6000
0
    }
6001
6002
    // Finalize the tree topology (see bigger comment above).
6003
0
    if (tempRet) {
6004
        // do the "..., tempRet" bit from above
6005
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
6006
0
        conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
6007
0
    }
6008
6009
0
    conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
6010
6011
0
    return conversionTree;
6012
0
}
6013
6014
//
6015
// Add any needed "hidden" counter buffer arguments for function calls.
6016
//
6017
// Modifies the 'aggregate' argument if needed.  Otherwise, is no-op.
6018
//
6019
void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate)
6020
0
{
6021
    // See if there are any SB types with counters.
6022
0
    const bool hasStructBuffArg =
6023
0
        std::any_of(aggregate->getSequence().begin(),
6024
0
                    aggregate->getSequence().end(),
6025
0
                    [this](const TIntermNode* node) {
6026
0
                        return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType());
6027
0
                    });
6028
6029
    // Nothing to do, if we didn't find one.
6030
0
    if (! hasStructBuffArg)
6031
0
        return;
6032
6033
0
    TIntermSequence argsWithCounterBuffers;
6034
6035
0
    for (int param = 0; param < int(aggregate->getSequence().size()); ++param) {
6036
0
        argsWithCounterBuffers.push_back(aggregate->getSequence()[param]);
6037
6038
0
        if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) {
6039
0
            const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode();
6040
0
            if (blockSym != nullptr) {
6041
0
                TType counterType;
6042
0
                counterBufferType(loc, counterType);
6043
6044
0
                const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName()));
6045
6046
0
                TVariable* variable = makeInternalVariable(counterBlockName, counterType);
6047
6048
                // Mark this buffer's counter block as being in use
6049
0
                structBufferCounter[counterBlockName] = true;
6050
6051
0
                TIntermSymbol* sym = intermediate.addSymbol(*variable, loc);
6052
0
                argsWithCounterBuffers.push_back(sym);
6053
0
            }
6054
0
        }
6055
0
    }
6056
6057
    // Swap with the temp list we've built up.
6058
0
    aggregate->getSequence().swap(argsWithCounterBuffers);
6059
0
}
6060
6061
6062
//
6063
// Do additional checking of built-in function calls that is not caught
6064
// by normal semantic checks on argument type, extension tagging, etc.
6065
//
6066
// Assumes there has been a semantically correct match to a built-in function prototype.
6067
//
6068
void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
6069
0
{
6070
    // Set up convenience accessors to the argument(s).  There is almost always
6071
    // multiple arguments for the cases below, but when there might be one,
6072
    // check the unaryArg first.
6073
0
    const TIntermSequence* argp = nullptr;   // confusing to use [] syntax on a pointer, so this is to help get a reference
6074
0
    const TIntermTyped* unaryArg = nullptr;
6075
0
    const TIntermTyped* arg0 = nullptr;
6076
0
    if (callNode.getAsAggregate()) {
6077
0
        argp = &callNode.getAsAggregate()->getSequence();
6078
0
        if (argp->size() > 0)
6079
0
            arg0 = (*argp)[0]->getAsTyped();
6080
0
    } else {
6081
0
        assert(callNode.getAsUnaryNode());
6082
0
        unaryArg = callNode.getAsUnaryNode()->getOperand();
6083
0
        arg0 = unaryArg;
6084
0
    }
6085
0
    const TIntermSequence& aggArgs = argp ? *argp : TIntermSequence();  // only valid when unaryArg is nullptr
6086
6087
0
    switch (callNode.getOp()) {
6088
0
    case EOpTextureGather:
6089
0
    case EOpTextureGatherOffset:
6090
0
    case EOpTextureGatherOffsets:
6091
0
    {
6092
        // Figure out which variants are allowed by what extensions,
6093
        // and what arguments must be constant for which situations.
6094
6095
0
        TString featureString = fnCandidate.getName() + "(...)";
6096
0
        const char* feature = featureString.c_str();
6097
0
        int compArg = -1;  // track which argument, if any, is the constant component argument
6098
0
        switch (callNode.getOp()) {
6099
0
        case EOpTextureGather:
6100
            // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
6101
            // otherwise, need GL_ARB_texture_gather.
6102
0
            if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect ||
6103
0
                fnCandidate[0].type->getSampler().shadow) {
6104
0
                if (! fnCandidate[0].type->getSampler().shadow)
6105
0
                    compArg = 2;
6106
0
            }
6107
0
            break;
6108
0
        case EOpTextureGatherOffset:
6109
            // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
6110
0
            if (! fnCandidate[0].type->getSampler().shadow)
6111
0
                compArg = 3;
6112
0
            break;
6113
0
        case EOpTextureGatherOffsets:
6114
0
            if (! fnCandidate[0].type->getSampler().shadow)
6115
0
                compArg = 3;
6116
0
            break;
6117
0
        default:
6118
0
            break;
6119
0
        }
6120
6121
0
        if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
6122
0
            if (aggArgs[compArg]->getAsConstantUnion()) {
6123
0
                int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
6124
0
                if (value < 0 || value > 3)
6125
0
                    error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
6126
0
            } else
6127
0
                error(loc, "must be a compile-time constant:", feature, "component argument");
6128
0
        }
6129
6130
0
        break;
6131
0
    }
6132
6133
0
    case EOpTextureOffset:
6134
0
    case EOpTextureFetchOffset:
6135
0
    case EOpTextureProjOffset:
6136
0
    case EOpTextureLodOffset:
6137
0
    case EOpTextureProjLodOffset:
6138
0
    case EOpTextureGradOffset:
6139
0
    case EOpTextureProjGradOffset:
6140
0
    {
6141
        // Handle texture-offset limits checking
6142
        // Pick which argument has to hold constant offsets
6143
0
        int arg = -1;
6144
0
        switch (callNode.getOp()) {
6145
0
        case EOpTextureOffset:          arg = 2;  break;
6146
0
        case EOpTextureFetchOffset:     arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
6147
0
        case EOpTextureProjOffset:      arg = 2;  break;
6148
0
        case EOpTextureLodOffset:       arg = 3;  break;
6149
0
        case EOpTextureProjLodOffset:   arg = 3;  break;
6150
0
        case EOpTextureGradOffset:      arg = 4;  break;
6151
0
        case EOpTextureProjGradOffset:  arg = 4;  break;
6152
0
        default:
6153
0
            assert(0);
6154
0
            break;
6155
0
        }
6156
6157
0
        if (arg > 0) {
6158
0
            if (aggArgs[arg]->getAsConstantUnion() == nullptr)
6159
0
                error(loc, "argument must be compile-time constant", "texel offset", "");
6160
0
            else {
6161
0
                const TType& type = aggArgs[arg]->getAsTyped()->getType();
6162
0
                for (int c = 0; c < type.getVectorSize(); ++c) {
6163
0
                    int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
6164
0
                    if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
6165
0
                        error(loc, "value is out of range:", "texel offset",
6166
0
                              "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
6167
0
                }
6168
0
            }
6169
0
        }
6170
6171
0
        break;
6172
0
    }
6173
6174
0
    case EOpTextureQuerySamples:
6175
0
    case EOpImageQuerySamples:
6176
0
        break;
6177
6178
0
    case EOpImageAtomicAdd:
6179
0
    case EOpImageAtomicMin:
6180
0
    case EOpImageAtomicMax:
6181
0
    case EOpImageAtomicAnd:
6182
0
    case EOpImageAtomicOr:
6183
0
    case EOpImageAtomicXor:
6184
0
    case EOpImageAtomicExchange:
6185
0
    case EOpImageAtomicCompSwap:
6186
0
        break;
6187
6188
0
    case EOpInterpolateAtCentroid:
6189
0
    case EOpInterpolateAtSample:
6190
0
    case EOpInterpolateAtOffset:
6191
        // TODO(greg-lunarg): Re-enable this check. It currently gives false errors for builtins
6192
        // defined and passed as members of a struct. In this case the storage class is showing to be
6193
        // Function. See glslang #2584
6194
6195
        // Make sure the first argument is an interpolant, or an array element of an interpolant
6196
        // if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
6197
            // It might still be an array element.
6198
            //
6199
            // We could check more, but the semantics of the first argument are already met; the
6200
            // only way to turn an array into a float/vec* is array dereference and swizzle.
6201
            //
6202
            // ES and desktop 4.3 and earlier:  swizzles may not be used
6203
            // desktop 4.4 and later: swizzles may be used
6204
            // const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
6205
            // if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
6206
            //     error(loc, "first argument must be an interpolant, or interpolant-array element",
6207
            //           fnCandidate.getName().c_str(), "");
6208
        // }
6209
0
        break;
6210
6211
0
    default:
6212
0
        break;
6213
0
    }
6214
0
}
6215
6216
//
6217
// Handle seeing something in a grammar production that can be done by calling
6218
// a constructor.
6219
//
6220
// The constructor still must be "handled" by handleFunctionCall(), which will
6221
// then call handleConstructor().
6222
//
6223
TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
6224
331
{
6225
331
    TOperator op = intermediate.mapTypeToConstructorOp(type);
6226
6227
331
    if (op == EOpNull) {
6228
0
        error(loc, "cannot construct this type", type.getBasicString(), "");
6229
0
        return nullptr;
6230
0
    }
6231
6232
331
    TString empty("");
6233
6234
331
    return new TFunction(&empty, type, op);
6235
331
}
6236
6237
//
6238
// Handle seeing a "COLON semantic" at the end of a type declaration,
6239
// by updating the type according to the semantic.
6240
//
6241
void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn,
6242
                                      const TString& upperCase)
6243
0
{
6244
    // Parse and return semantic number.  If limit is 0, it will be ignored.  Otherwise, if the parsed
6245
    // semantic number is >= limit, errorMsg is issued and 0 is returned.
6246
    // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet
6247
    // accept those in lambda functions.
6248
0
    const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int {
6249
0
        size_t pos = semantic.find_last_not_of("0123456789");
6250
0
        if (pos == std::string::npos)
6251
0
            return 0u;
6252
6253
0
        unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1);
6254
6255
0
        if (limit != 0 && semanticNum >= limit) {
6256
0
            error(loc, errorMsg, semantic.c_str(), "");
6257
0
            return 0u;
6258
0
        }
6259
6260
0
        return semanticNum;
6261
0
    };
6262
6263
0
    if (builtIn == EbvNone && hlslDX9Compatible()) {
6264
0
        if (language == EShLangVertex) {
6265
0
            if (qualifier.isParamOutput()) {
6266
0
                if (upperCase == "POSITION") {
6267
0
                    builtIn = EbvPosition;
6268
0
                }
6269
0
                if (upperCase == "PSIZE") {
6270
0
                    builtIn = EbvPointSize;
6271
0
                }
6272
0
            }
6273
0
        } else if (language == EShLangFragment) {
6274
0
            if (qualifier.isParamInput() && upperCase == "VPOS") {
6275
0
                builtIn = EbvFragCoord;
6276
0
            }
6277
0
            if (qualifier.isParamOutput()) {
6278
0
                if (upperCase.compare(0, 5, "COLOR") == 0) {
6279
0
                    qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6280
0
                    nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6281
0
                }
6282
0
                if (upperCase == "DEPTH") {
6283
0
                    builtIn = EbvFragDepth;
6284
0
                }
6285
0
            }
6286
0
        }
6287
0
    }
6288
6289
0
    switch(builtIn) {
6290
0
    case EbvNone:
6291
        // Get location numbers from fragment outputs, instead of
6292
        // auto-assigning them.
6293
0
        if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) {
6294
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6295
0
            nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6296
0
        } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) {
6297
0
            builtIn = EbvClipDistance;
6298
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic");
6299
0
        } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) {
6300
0
            builtIn = EbvCullDistance;
6301
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic");
6302
0
        }
6303
0
        break;
6304
0
    case EbvPosition:
6305
        // adjust for stage in/out
6306
0
        if (language == EShLangFragment)
6307
0
            builtIn = EbvFragCoord;
6308
0
        break;
6309
0
    case EbvFragStencilRef:
6310
0
        error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
6311
0
        break;
6312
0
    case EbvTessLevelInner:
6313
0
    case EbvTessLevelOuter:
6314
0
        qualifier.patch = true;
6315
0
        break;
6316
0
    default:
6317
0
        break;
6318
0
    }
6319
6320
0
    if (qualifier.builtIn == EbvNone)
6321
0
        qualifier.builtIn = builtIn;
6322
0
    qualifier.semanticName = intermediate.addSemanticName(upperCase);
6323
0
}
6324
6325
//
6326
// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
6327
//
6328
// 'location' has the "c[Subcomponent]" part.
6329
// 'component' points to the "component" part, or nullptr if not present.
6330
//
6331
void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
6332
                                        const glslang::TString* component)
6333
0
{
6334
0
    if (location.size() == 0 || location[0] != 'c') {
6335
0
        error(loc, "expected 'c'", "packoffset", "");
6336
0
        return;
6337
0
    }
6338
0
    if (location.size() == 1)
6339
0
        return;
6340
0
    if (! isdigit(location[1])) {
6341
0
        error(loc, "expected number after 'c'", "packoffset", "");
6342
0
        return;
6343
0
    }
6344
6345
0
    qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
6346
0
    if (component != nullptr) {
6347
0
        int componentOffset = 0;
6348
0
        switch ((*component)[0]) {
6349
0
        case 'x': componentOffset =  0; break;
6350
0
        case 'y': componentOffset =  4; break;
6351
0
        case 'z': componentOffset =  8; break;
6352
0
        case 'w': componentOffset = 12; break;
6353
0
        default:
6354
0
            componentOffset = -1;
6355
0
            break;
6356
0
        }
6357
0
        if (componentOffset < 0 || component->size() > 1) {
6358
0
            error(loc, "expected {x, y, z, w} for component", "packoffset", "");
6359
0
            return;
6360
0
        }
6361
0
        qualifier.layoutOffset += componentOffset;
6362
0
    }
6363
0
}
6364
6365
//
6366
// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
6367
//
6368
// 'profile' points to the shader_profile part, or nullptr if not present.
6369
// 'desc' is the type# part.
6370
//
6371
void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
6372
                                      const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
6373
0
{
6374
0
    if (profile != nullptr)
6375
0
        warn(loc, "ignoring shader_profile", "register", "");
6376
6377
0
    if (desc.size() < 1) {
6378
0
        error(loc, "expected register type", "register", "");
6379
0
        return;
6380
0
    }
6381
6382
0
    int regNumber = 0;
6383
0
    if (desc.size() > 1) {
6384
0
        if (isdigit(desc[1]))
6385
0
            regNumber = atoi(desc.substr(1, desc.size()).c_str());
6386
0
        else {
6387
0
            error(loc, "expected register number after register type", "register", "");
6388
0
            return;
6389
0
        }
6390
0
    }
6391
6392
    // more information about register types see
6393
    // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register
6394
0
    const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding();
6395
0
    switch (std::tolower(desc[0])) {
6396
0
    case 'c':
6397
        // c register is the register slot in the global const buffer
6398
        // each slot is a vector of 4 32 bit components
6399
0
        qualifier.layoutOffset = regNumber * 4 * 4;
6400
0
        break;
6401
        // const buffer register slot
6402
0
    case 'b':
6403
        // textrues and structured buffers
6404
0
    case 't':
6405
        // samplers
6406
0
    case 's':
6407
        // uav resources
6408
0
    case 'u':
6409
        // if nothing else has set the binding, do so now
6410
        // (other mechanisms override this one)
6411
0
        if (!qualifier.hasBinding())
6412
0
            qualifier.layoutBinding = regNumber + subComponent;
6413
6414
        // This handles per-register layout sets numbers.  For the global mode which sets
6415
        // every symbol to the same value, see setLinkageLayoutSets().
6416
0
        if ((resourceInfo.size() % 3) == 0) {
6417
            // Apply per-symbol resource set and binding.
6418
0
            for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) {
6419
0
                if (strcmp(desc.c_str(), it[0].c_str()) == 0) {
6420
0
                    qualifier.layoutSet = atoi(it[1].c_str());
6421
0
                    qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent;
6422
0
                    break;
6423
0
                }
6424
0
            }
6425
0
        }
6426
0
        break;
6427
0
    default:
6428
0
        warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
6429
0
        break;
6430
0
    }
6431
6432
    // space
6433
0
    unsigned int setNumber;
6434
0
    const auto crackSpace = [&]() -> bool {
6435
0
        const int spaceLen = 5;
6436
0
        if (spaceDesc->size() < spaceLen + 1)
6437
0
            return false;
6438
0
        if (spaceDesc->compare(0, spaceLen, "space") != 0)
6439
0
            return false;
6440
0
        if (! isdigit((*spaceDesc)[spaceLen]))
6441
0
            return false;
6442
0
        setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
6443
0
        return true;
6444
0
    };
6445
6446
    // if nothing else has set the set, do so now
6447
    // (other mechanisms override this one)
6448
0
    if (spaceDesc && !qualifier.hasSet()) {
6449
0
        if (! crackSpace()) {
6450
0
            error(loc, "expected spaceN", "register", "");
6451
0
            return;
6452
0
        }
6453
0
        qualifier.layoutSet = setNumber;
6454
0
    }
6455
0
}
6456
6457
// Convert to a scalar boolean, or if not allowed by HLSL semantics,
6458
// report an error and return nullptr.
6459
TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition,
6460
                                                             bool mustBeScalar)
6461
17
{
6462
17
    if (mustBeScalar && !condition->getType().isScalarOrVec1()) {
6463
0
        error(loc, "requires a scalar", "conditional expression", "");
6464
0
        return nullptr;
6465
0
    }
6466
6467
17
    return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()),
6468
17
                                      condition);
6469
17
}
6470
6471
//
6472
// Same error message for all places assignments don't work.
6473
//
6474
void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
6475
0
{
6476
0
    error(loc, "", op, "cannot convert from '%s' to '%s'",
6477
0
        right.c_str(), left.c_str());
6478
0
}
6479
6480
//
6481
// Same error message for all places unary operations don't work.
6482
//
6483
void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
6484
0
{
6485
0
    error(loc, " wrong operand type", op,
6486
0
        "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
6487
0
        op, operand.c_str());
6488
0
}
6489
6490
//
6491
// Same error message for all binary operations don't work.
6492
//
6493
void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
6494
0
{
6495
0
    error(loc, " wrong operand types:", op,
6496
0
        "no operation '%s' exists that takes a left-hand operand of type '%s' and "
6497
0
        "a right operand of type '%s' (or there is no acceptable conversion)",
6498
0
        op, left.c_str(), right.c_str());
6499
0
}
6500
6501
//
6502
// A basic type of EbtVoid is a key that the name string was seen in the source, but
6503
// it was not found as a variable in the symbol table.  If so, give the error
6504
// message and insert a dummy variable in the symbol table to prevent future errors.
6505
//
6506
void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
6507
6
{
6508
6
    TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
6509
6
    if (! symbol)
6510
5
        return;
6511
6512
1
    if (symbol->getType().getBasicType() == EbtVoid) {
6513
1
        error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
6514
6515
        // Add to symbol table to prevent future error messages on the same name
6516
1
        if (symbol->getName().size() > 0) {
6517
1
            TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
6518
1
            symbolTable.insert(*fakeVariable);
6519
6520
            // substitute a symbol node for this new variable
6521
1
            nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
6522
1
        }
6523
1
    }
6524
1
}
6525
6526
//
6527
// Both test, and if necessary spit out an error, to see if the node is really
6528
// a constant.
6529
//
6530
void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
6531
0
{
6532
0
    if (node->getQualifier().storage != EvqConst)
6533
0
        error(node->getLoc(), "constant expression required", token, "");
6534
0
}
6535
6536
//
6537
// Both test, and if necessary spit out an error, to see if the node is really
6538
// an integer.
6539
//
6540
void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
6541
0
{
6542
0
    if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
6543
0
        return;
6544
6545
0
    error(node->getLoc(), "scalar integer expression required", token, "");
6546
0
}
6547
6548
//
6549
// Both test, and if necessary spit out an error, to see if we are currently
6550
// globally scoped.
6551
//
6552
void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
6553
0
{
6554
0
    if (! symbolTable.atGlobalLevel())
6555
0
        error(loc, "not allowed in nested scope", token, "");
6556
0
}
6557
6558
bool HlslParseContext::builtInName(const TString& /*identifier*/)
6559
0
{
6560
0
    return false;
6561
0
}
6562
6563
//
6564
// Make sure there is enough data and not too many arguments provided to the
6565
// constructor to build something of the type of the constructor.  Also returns
6566
// the type of the constructor.
6567
//
6568
// Returns true if there was an error in construction.
6569
//
6570
bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
6571
                                        TOperator op, TType& type)
6572
7
{
6573
7
    type.shallowCopy(function.getType());
6574
6575
7
    bool constructingMatrix = false;
6576
7
    switch (op) {
6577
0
    case EOpConstructTextureSampler:
6578
0
        error(loc, "unhandled texture constructor", "constructor", "");
6579
0
        return true;
6580
0
    case EOpConstructMat2x2:
6581
0
    case EOpConstructMat2x3:
6582
0
    case EOpConstructMat2x4:
6583
0
    case EOpConstructMat3x2:
6584
0
    case EOpConstructMat3x3:
6585
0
    case EOpConstructMat3x4:
6586
0
    case EOpConstructMat4x2:
6587
0
    case EOpConstructMat4x3:
6588
0
    case EOpConstructMat4x4:
6589
0
    case EOpConstructDMat2x2:
6590
0
    case EOpConstructDMat2x3:
6591
0
    case EOpConstructDMat2x4:
6592
0
    case EOpConstructDMat3x2:
6593
0
    case EOpConstructDMat3x3:
6594
0
    case EOpConstructDMat3x4:
6595
0
    case EOpConstructDMat4x2:
6596
0
    case EOpConstructDMat4x3:
6597
0
    case EOpConstructDMat4x4:
6598
0
    case EOpConstructIMat2x2:
6599
0
    case EOpConstructIMat2x3:
6600
0
    case EOpConstructIMat2x4:
6601
0
    case EOpConstructIMat3x2:
6602
0
    case EOpConstructIMat3x3:
6603
0
    case EOpConstructIMat3x4:
6604
0
    case EOpConstructIMat4x2:
6605
0
    case EOpConstructIMat4x3:
6606
0
    case EOpConstructIMat4x4:
6607
0
    case EOpConstructUMat2x2:
6608
0
    case EOpConstructUMat2x3:
6609
0
    case EOpConstructUMat2x4:
6610
0
    case EOpConstructUMat3x2:
6611
0
    case EOpConstructUMat3x3:
6612
0
    case EOpConstructUMat3x4:
6613
0
    case EOpConstructUMat4x2:
6614
0
    case EOpConstructUMat4x3:
6615
0
    case EOpConstructUMat4x4:
6616
0
    case EOpConstructBMat2x2:
6617
0
    case EOpConstructBMat2x3:
6618
0
    case EOpConstructBMat2x4:
6619
0
    case EOpConstructBMat3x2:
6620
0
    case EOpConstructBMat3x3:
6621
0
    case EOpConstructBMat3x4:
6622
0
    case EOpConstructBMat4x2:
6623
0
    case EOpConstructBMat4x3:
6624
0
    case EOpConstructBMat4x4:
6625
0
        constructingMatrix = true;
6626
0
        break;
6627
7
    default:
6628
7
        break;
6629
7
    }
6630
6631
    //
6632
    // Walk the arguments for first-pass checks and collection of information.
6633
    //
6634
6635
7
    int size = 0;
6636
7
    bool constType = true;
6637
7
    bool full = false;
6638
7
    bool overFull = false;
6639
7
    bool matrixInMatrix = false;
6640
7
    bool arrayArg = false;
6641
14
    for (int arg = 0; arg < function.getParamCount(); ++arg) {
6642
7
        if (function[arg].type->isArray()) {
6643
0
            if (function[arg].type->isUnsizedArray()) {
6644
                // Can't construct from an unsized array.
6645
0
                error(loc, "array argument must be sized", "constructor", "");
6646
0
                return true;
6647
0
            }
6648
0
            arrayArg = true;
6649
0
        }
6650
7
        if (constructingMatrix && function[arg].type->isMatrix())
6651
0
            matrixInMatrix = true;
6652
6653
        // 'full' will go to true when enough args have been seen.  If we loop
6654
        // again, there is an extra argument.
6655
7
        if (full) {
6656
            // For vectors and matrices, it's okay to have too many components
6657
            // available, but not okay to have unused arguments.
6658
0
            overFull = true;
6659
0
        }
6660
6661
7
        size += function[arg].type->computeNumComponents();
6662
7
        if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
6663
7
            full = true;
6664
6665
7
        if (function[arg].type->getQualifier().storage != EvqConst)
6666
7
            constType = false;
6667
7
    }
6668
6669
7
    if (constType)
6670
0
        type.getQualifier().storage = EvqConst;
6671
6672
7
    if (type.isArray()) {
6673
0
        if (function.getParamCount() == 0) {
6674
0
            error(loc, "array constructor must have at least one argument", "constructor", "");
6675
0
            return true;
6676
0
        }
6677
6678
0
        if (type.isUnsizedArray()) {
6679
            // auto adapt the constructor type to the number of arguments
6680
0
            type.changeOuterArraySize(function.getParamCount());
6681
0
        } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) {
6682
0
            error(loc, "array constructor needs one argument per array element", "constructor", "");
6683
0
            return true;
6684
0
        }
6685
6686
0
        if (type.isArrayOfArrays()) {
6687
            // Types have to match, but we're still making the type.
6688
            // Finish making the type, and the comparison is done later
6689
            // when checking for conversion.
6690
0
            TArraySizes& arraySizes = *type.getArraySizes();
6691
6692
            // At least the dimensionalities have to match.
6693
0
            if (! function[0].type->isArray() ||
6694
0
                arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) {
6695
0
                error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
6696
0
                return true;
6697
0
            }
6698
6699
0
            if (arraySizes.isInnerUnsized()) {
6700
                // "Arrays of arrays ..., and the size for any dimension is optional"
6701
                // That means we need to adopt (from the first argument) the other array sizes into the type.
6702
0
                for (int d = 1; d < arraySizes.getNumDims(); ++d) {
6703
0
                    if (arraySizes.getDimSize(d) == UnsizedArraySize) {
6704
0
                        arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1));
6705
0
                    }
6706
0
                }
6707
0
            }
6708
0
        }
6709
0
    }
6710
6711
    // Some array -> array type casts are okay
6712
7
    if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
6713
0
        !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
6714
0
        type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
6715
0
        return false;
6716
6717
7
    if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
6718
0
        error(loc, "constructing non-array constituent from array argument", "constructor", "");
6719
0
        return true;
6720
0
    }
6721
6722
7
    if (matrixInMatrix && ! type.isArray()) {
6723
0
        return false;
6724
0
    }
6725
6726
7
    if (overFull) {
6727
0
        error(loc, "too many arguments", "constructor", "");
6728
0
        return true;
6729
0
    }
6730
6731
7
    if (op == EOpConstructStruct && ! type.isArray()) {
6732
0
        if (isScalarConstructor(node))
6733
0
            return false;
6734
6735
        // Self-type construction: e.g, we can construct a struct from a single identically typed object.
6736
0
        if (function.getParamCount() == 1 && type == *function[0].type)
6737
0
            return false;
6738
6739
0
        if ((int)type.getStruct()->size() != function.getParamCount()) {
6740
0
            error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
6741
0
            return true;
6742
0
        }
6743
0
    }
6744
6745
7
    if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
6746
7
        (op == EOpConstructStruct && size < type.computeNumComponents())) {
6747
0
        error(loc, "not enough data provided for construction", "constructor", "");
6748
0
        return true;
6749
0
    }
6750
6751
7
    return false;
6752
7
}
6753
6754
// See if 'node', in the context of constructing aggregates, is a scalar argument
6755
// to a constructor.
6756
//
6757
bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
6758
0
{
6759
    // Obviously, it must be a scalar, but an aggregate node might not be fully
6760
    // completed yet: holding a sequence of initializers under an aggregate
6761
    // would not yet be typed, so don't check it's type.  This corresponds to
6762
    // the aggregate operator also not being set yet. (An aggregate operation
6763
    // that legitimately yields a scalar will have a getOp() of that operator,
6764
    // not EOpNull.)
6765
6766
0
    return node->getAsTyped() != nullptr &&
6767
0
           node->getAsTyped()->isScalar() &&
6768
0
           (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
6769
0
}
6770
6771
// Checks to see if a void variable has been declared and raise an error message for such a case
6772
//
6773
// returns true in case of an error
6774
//
6775
bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
6776
15
{
6777
15
    if (basicType == EbtVoid) {
6778
0
        error(loc, "illegal use of type 'void'", identifier.c_str(), "");
6779
0
        return true;
6780
0
    }
6781
6782
15
    return false;
6783
15
}
6784
6785
//
6786
// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
6787
//
6788
void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
6789
0
{
6790
    // move from parameter/unknown qualifiers to pipeline in/out qualifiers
6791
0
    switch (qualifier.storage) {
6792
0
    case EvqIn:
6793
0
        qualifier.storage = EvqVaryingIn;
6794
0
        break;
6795
0
    case EvqOut:
6796
0
        qualifier.storage = EvqVaryingOut;
6797
0
        break;
6798
0
    default:
6799
0
        break;
6800
0
    }
6801
0
}
6802
6803
//
6804
// Merge characteristics of the 'src' qualifier into the 'dst'.
6805
//
6806
void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
6807
0
{
6808
    // Storage qualification
6809
0
    if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
6810
0
        dst.storage = src.storage;
6811
0
    else if ((dst.storage == EvqIn  && src.storage == EvqOut) ||
6812
0
             (dst.storage == EvqOut && src.storage == EvqIn))
6813
0
        dst.storage = EvqInOut;
6814
0
    else if ((dst.storage == EvqIn    && src.storage == EvqConst) ||
6815
0
             (dst.storage == EvqConst && src.storage == EvqIn))
6816
0
        dst.storage = EvqConstReadOnly;
6817
6818
    // Layout qualifiers
6819
0
    mergeObjectLayoutQualifiers(dst, src, false);
6820
6821
    // individual qualifiers
6822
0
#define MERGE_SINGLETON(field) dst.field |= src.field;
6823
0
    MERGE_SINGLETON(invariant);
6824
0
    MERGE_SINGLETON(noContraction);
6825
0
    MERGE_SINGLETON(centroid);
6826
0
    MERGE_SINGLETON(smooth);
6827
0
    MERGE_SINGLETON(flat);
6828
0
    MERGE_SINGLETON(nopersp);
6829
0
    MERGE_SINGLETON(patch);
6830
0
    MERGE_SINGLETON(sample);
6831
0
    MERGE_SINGLETON(coherent);
6832
0
    MERGE_SINGLETON(volatil);
6833
0
    MERGE_SINGLETON(restrict);
6834
0
    MERGE_SINGLETON(readonly);
6835
0
    MERGE_SINGLETON(writeonly);
6836
0
    MERGE_SINGLETON(specConstant);
6837
0
    MERGE_SINGLETON(nonUniform);
6838
0
}
6839
6840
// used to flatten the sampler type space into a single dimension
6841
// correlates with the declaration of defaultSamplerPrecision[]
6842
int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
6843
0
{
6844
0
    int arrayIndex = sampler.arrayed ? 1 : 0;
6845
0
    int shadowIndex = sampler.shadow ? 1 : 0;
6846
0
    int externalIndex = sampler.external ? 1 : 0;
6847
6848
0
    return EsdNumDims *
6849
0
           (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
6850
0
}
6851
6852
//
6853
// Do size checking for an array type's size.
6854
//
6855
void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
6856
0
{
6857
0
    bool isConst = false;
6858
0
    sizePair.size = 1;
6859
0
    sizePair.node = nullptr;
6860
6861
0
    TIntermConstantUnion* constant = expr->getAsConstantUnion();
6862
0
    if (constant) {
6863
        // handle true (non-specialization) constant
6864
0
        sizePair.size = constant->getConstArray()[0].getIConst();
6865
0
        isConst = true;
6866
0
    } else {
6867
        // see if it's a specialization constant instead
6868
0
        if (expr->getQualifier().isSpecConstant()) {
6869
0
            isConst = true;
6870
0
            sizePair.node = expr;
6871
0
            TIntermSymbol* symbol = expr->getAsSymbolNode();
6872
0
            if (symbol && symbol->getConstArray().size() > 0)
6873
0
                sizePair.size = symbol->getConstArray()[0].getIConst();
6874
0
        }
6875
0
    }
6876
6877
0
    if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
6878
0
        error(loc, "array size must be a constant integer expression", "", "");
6879
0
        return;
6880
0
    }
6881
6882
0
    if (sizePair.size <= 0) {
6883
0
        error(loc, "array size must be a positive integer", "", "");
6884
0
        return;
6885
0
    }
6886
0
}
6887
6888
//
6889
// Require array to be completely sized
6890
//
6891
void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
6892
0
{
6893
0
    if (arraySizes.hasUnsized())
6894
0
        error(loc, "array size required", "", "");
6895
0
}
6896
6897
void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
6898
0
{
6899
0
    const TTypeList& structure = *type.getStruct();
6900
0
    for (int m = 0; m < (int)structure.size(); ++m) {
6901
0
        const TType& member = *structure[m].type;
6902
0
        if (member.isArray())
6903
0
            arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
6904
0
    }
6905
0
}
6906
6907
//
6908
// Do all the semantic checking for declaring or redeclaring an array, with and
6909
// without a size, and make the right changes to the symbol table.
6910
//
6911
void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
6912
                                    TSymbol*& symbol, bool track)
6913
8
{
6914
8
    if (symbol == nullptr) {
6915
8
        bool currentScope;
6916
8
        symbol = symbolTable.find(identifier, nullptr, &currentScope);
6917
6918
8
        if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
6919
            // bad shader (errors already reported) trying to redeclare a built-in name as an array
6920
0
            return;
6921
0
        }
6922
8
        if (symbol == nullptr || ! currentScope) {
6923
            //
6924
            // Successfully process a new definition.
6925
            // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
6926
            //
6927
8
            symbol = new TVariable(&identifier, type);
6928
8
            symbolTable.insert(*symbol);
6929
8
            if (track && symbolTable.atGlobalLevel())
6930
8
                trackLinkage(*symbol);
6931
6932
8
            return;
6933
8
        }
6934
0
        if (symbol->getAsAnonMember()) {
6935
0
            error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
6936
0
            symbol = nullptr;
6937
0
            return;
6938
0
        }
6939
0
    }
6940
6941
    //
6942
    // Process a redeclaration.
6943
    //
6944
6945
0
    if (symbol == nullptr) {
6946
0
        error(loc, "array variable name expected", identifier.c_str(), "");
6947
0
        return;
6948
0
    }
6949
6950
    // redeclareBuiltinVariable() should have already done the copyUp()
6951
0
    TType& existingType = symbol->getWritableType();
6952
6953
0
    if (existingType.isSizedArray()) {
6954
        // be more lenient for input arrays to geometry shaders and tessellation control outputs,
6955
        // where the redeclaration is the same size
6956
0
        return;
6957
0
    }
6958
6959
0
    existingType.updateArraySizes(type);
6960
0
}
6961
6962
//
6963
// Enforce non-initializer type/qualifier rules.
6964
//
6965
void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type,
6966
                                    TIntermTyped*& initializer)
6967
15
{
6968
    //
6969
    // Make the qualifier make sense, given that there is an initializer.
6970
    //
6971
15
    if (initializer == nullptr) {
6972
12
        if (type.getQualifier().storage == EvqConst ||
6973
12
            type.getQualifier().storage == EvqConstReadOnly) {
6974
0
            initializer = intermediate.makeAggregate(loc);
6975
0
            warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
6976
0
        }
6977
12
    }
6978
15
}
6979
6980
//
6981
// See if the identifier is a built-in symbol that can be redeclared, and if so,
6982
// copy the symbol table's read-only built-in variable to the current
6983
// global level, where it can be modified based on the passed in type.
6984
//
6985
// Returns nullptr if no redeclaration took place; meaning a normal declaration still
6986
// needs to occur for it, not necessarily an error.
6987
//
6988
// Returns a redeclared and type-modified variable if a redeclared occurred.
6989
//
6990
TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
6991
                                                    const TQualifier& /*qualifier*/,
6992
                                                    const TShaderQualifiers& /*publicType*/)
6993
0
{
6994
0
    if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
6995
0
        return nullptr;
6996
6997
0
    return nullptr;
6998
0
}
6999
7000
//
7001
// Generate index to the array element in a structure buffer (SSBO)
7002
//
7003
TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
7004
2
{
7005
    // Bail out if not a struct buffer
7006
2
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
7007
2
        return nullptr;
7008
7009
    // Runtime sized array is always the last element.
7010
0
    const TTypeList* bufferStruct = buffer->getType().getStruct();
7011
0
    TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
7012
7013
0
    TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
7014
0
    argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
7015
7016
0
    return argArray;
7017
2
}
7018
7019
//
7020
// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
7021
// (template) type.   E.g, StructuredBuffer<MyType> -> MyType.  Else return nullptr.
7022
//
7023
TType* HlslParseContext::getStructBufferContentType(const TType& type) const
7024
2
{
7025
2
    if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer)
7026
2
        return nullptr;
7027
7028
0
    const int memberCount = (int)type.getStruct()->size();
7029
0
    assert(memberCount > 0);
7030
7031
0
    TType* contentType = (*type.getStruct())[memberCount-1].type;
7032
7033
0
    return contentType->isUnsizedArray() ? contentType : nullptr;
7034
2
}
7035
7036
//
7037
// If an existing struct buffer has a sharable type, then share it.
7038
//
7039
void HlslParseContext::shareStructBufferType(TType& type)
7040
0
{
7041
    // PackOffset must be equivalent to share types on a per-member basis.
7042
    // Note: cannot use auto type due to recursion.  Thus, this is a std::function.
7043
0
    const std::function<bool(TType& lhs, TType& rhs)>
7044
0
    compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
7045
0
        if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
7046
0
            return false;
7047
7048
0
        if (lhs.isStruct() != rhs.isStruct())
7049
0
            return false;
7050
7051
0
        if (lhs.getQualifier().builtIn != rhs.getQualifier().builtIn)
7052
0
            return false;
7053
7054
0
        if (lhs.isStruct() && rhs.isStruct()) {
7055
0
            if (lhs.getStruct()->size() != rhs.getStruct()->size())
7056
0
                return false;
7057
7058
0
            for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
7059
0
                if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
7060
0
                    return false;
7061
0
        }
7062
7063
0
        return true;
7064
0
    };
7065
7066
    // We need to compare certain qualifiers in addition to the type.
7067
0
    const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
7068
0
        if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
7069
0
            return false;
7070
7071
        // If both are structures, recursively look for packOffset equality
7072
        // as well as type equality.
7073
0
        return compareQualifiers(lhs, rhs) && lhs == rhs;
7074
0
    };
7075
7076
    // This is an exhaustive O(N) search, but real world shaders have
7077
    // only a small number of these.
7078
0
    for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
7079
        // If the deep structure matches, modulo qualifiers, use it
7080
0
        if (typeEqual(*structBufferTypes[idx], type)) {
7081
0
            type.shallowCopy(*structBufferTypes[idx]);
7082
0
            return;
7083
0
        }
7084
0
    }
7085
7086
    // Otherwise, remember it:
7087
0
    TType* typeCopy = new TType;
7088
0
    typeCopy->shallowCopy(type);
7089
0
    structBufferTypes.push_back(typeCopy);
7090
0
}
7091
7092
void HlslParseContext::paramFix(TType& type)
7093
1.00M
{
7094
1.00M
    switch (type.getQualifier().storage) {
7095
0
    case EvqConst:
7096
0
        type.getQualifier().storage = EvqConstReadOnly;
7097
0
        break;
7098
0
    case EvqGlobal:
7099
909k
    case EvqTemporary:
7100
909k
        type.getQualifier().storage = EvqIn;
7101
909k
        break;
7102
0
    case EvqBuffer:
7103
0
        {
7104
            // SSBO parameter.  These do not go through the declareBlock path since they are fn parameters.
7105
0
            correctUniform(type.getQualifier());
7106
0
            TQualifier bufferQualifier = globalBufferDefaults;
7107
0
            mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
7108
0
            bufferQualifier.storage = type.getQualifier().storage;
7109
0
            bufferQualifier.readonly = type.getQualifier().readonly;
7110
0
            bufferQualifier.coherent = type.getQualifier().coherent;
7111
0
            bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn;
7112
0
            type.getQualifier() = bufferQualifier;
7113
0
            break;
7114
0
        }
7115
97.5k
    default:
7116
97.5k
        break;
7117
1.00M
    }
7118
1.00M
}
7119
7120
void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
7121
3
{
7122
3
    if (type.containsSpecializationSize())
7123
0
        error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
7124
3
}
7125
7126
//
7127
// Layout qualifier stuff.
7128
//
7129
7130
// Put the id's layout qualification into the public type, for qualifiers not having a number set.
7131
// This is before we know any type information for error checking.
7132
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
7133
0
{
7134
0
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7135
7136
0
    if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
7137
0
        qualifier.layoutMatrix = ElmRowMajor;
7138
0
        return;
7139
0
    }
7140
0
    if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
7141
0
        qualifier.layoutMatrix = ElmColumnMajor;
7142
0
        return;
7143
0
    }
7144
0
    if (id == "push_constant") {
7145
0
        requireVulkan(loc, "push_constant");
7146
0
        qualifier.layoutPushConstant = true;
7147
0
        return;
7148
0
    }
7149
0
    if (language == EShLangGeometry || language == EShLangTessEvaluation) {
7150
0
        if (id == TQualifier::getGeometryString(ElgTriangles)) {
7151
            // publicType.shaderQualifiers.geometry = ElgTriangles;
7152
0
            warn(loc, "ignored", id.c_str(), "");
7153
0
            return;
7154
0
        }
7155
0
        if (language == EShLangGeometry) {
7156
0
            if (id == TQualifier::getGeometryString(ElgPoints)) {
7157
                // publicType.shaderQualifiers.geometry = ElgPoints;
7158
0
                warn(loc, "ignored", id.c_str(), "");
7159
0
                return;
7160
0
            }
7161
0
            if (id == TQualifier::getGeometryString(ElgLineStrip)) {
7162
                // publicType.shaderQualifiers.geometry = ElgLineStrip;
7163
0
                warn(loc, "ignored", id.c_str(), "");
7164
0
                return;
7165
0
            }
7166
0
            if (id == TQualifier::getGeometryString(ElgLines)) {
7167
                // publicType.shaderQualifiers.geometry = ElgLines;
7168
0
                warn(loc, "ignored", id.c_str(), "");
7169
0
                return;
7170
0
            }
7171
0
            if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
7172
                // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
7173
0
                warn(loc, "ignored", id.c_str(), "");
7174
0
                return;
7175
0
            }
7176
0
            if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
7177
                // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
7178
0
                warn(loc, "ignored", id.c_str(), "");
7179
0
                return;
7180
0
            }
7181
0
            if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
7182
                // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
7183
0
                warn(loc, "ignored", id.c_str(), "");
7184
0
                return;
7185
0
            }
7186
0
        } else {
7187
0
            assert(language == EShLangTessEvaluation);
7188
7189
            // input primitive
7190
0
            if (id == TQualifier::getGeometryString(ElgTriangles)) {
7191
                // publicType.shaderQualifiers.geometry = ElgTriangles;
7192
0
                warn(loc, "ignored", id.c_str(), "");
7193
0
                return;
7194
0
            }
7195
0
            if (id == TQualifier::getGeometryString(ElgQuads)) {
7196
                // publicType.shaderQualifiers.geometry = ElgQuads;
7197
0
                warn(loc, "ignored", id.c_str(), "");
7198
0
                return;
7199
0
            }
7200
0
            if (id == TQualifier::getGeometryString(ElgIsolines)) {
7201
                // publicType.shaderQualifiers.geometry = ElgIsolines;
7202
0
                warn(loc, "ignored", id.c_str(), "");
7203
0
                return;
7204
0
            }
7205
7206
            // vertex spacing
7207
0
            if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
7208
                // publicType.shaderQualifiers.spacing = EvsEqual;
7209
0
                warn(loc, "ignored", id.c_str(), "");
7210
0
                return;
7211
0
            }
7212
0
            if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
7213
                // publicType.shaderQualifiers.spacing = EvsFractionalEven;
7214
0
                warn(loc, "ignored", id.c_str(), "");
7215
0
                return;
7216
0
            }
7217
0
            if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
7218
                // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
7219
0
                warn(loc, "ignored", id.c_str(), "");
7220
0
                return;
7221
0
            }
7222
7223
            // triangle order
7224
0
            if (id == TQualifier::getVertexOrderString(EvoCw)) {
7225
                // publicType.shaderQualifiers.order = EvoCw;
7226
0
                warn(loc, "ignored", id.c_str(), "");
7227
0
                return;
7228
0
            }
7229
0
            if (id == TQualifier::getVertexOrderString(EvoCcw)) {
7230
                // publicType.shaderQualifiers.order = EvoCcw;
7231
0
                warn(loc, "ignored", id.c_str(), "");
7232
0
                return;
7233
0
            }
7234
7235
            // point mode
7236
0
            if (id == "point_mode") {
7237
                // publicType.shaderQualifiers.pointMode = true;
7238
0
                warn(loc, "ignored", id.c_str(), "");
7239
0
                return;
7240
0
            }
7241
0
        }
7242
0
    }
7243
0
    if (language == EShLangFragment) {
7244
0
        if (id == "origin_upper_left") {
7245
            // publicType.shaderQualifiers.originUpperLeft = true;
7246
0
            warn(loc, "ignored", id.c_str(), "");
7247
0
            return;
7248
0
        }
7249
0
        if (id == "pixel_center_integer") {
7250
            // publicType.shaderQualifiers.pixelCenterInteger = true;
7251
0
            warn(loc, "ignored", id.c_str(), "");
7252
0
            return;
7253
0
        }
7254
0
        if (id == "early_fragment_tests") {
7255
            // publicType.shaderQualifiers.earlyFragmentTests = true;
7256
0
            warn(loc, "ignored", id.c_str(), "");
7257
0
            return;
7258
0
        }
7259
0
        for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
7260
0
            if (id == TQualifier::getLayoutDepthString(depth)) {
7261
                // publicType.shaderQualifiers.layoutDepth = depth;
7262
0
                warn(loc, "ignored", id.c_str(), "");
7263
0
                return;
7264
0
            }
7265
0
        }
7266
0
        if (id.compare(0, 13, "blend_support") == 0) {
7267
0
            bool found = false;
7268
0
            for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
7269
0
                if (id == TQualifier::getBlendEquationString(be)) {
7270
0
                    requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
7271
0
                    intermediate.addBlendEquation(be);
7272
                    // publicType.shaderQualifiers.blendEquation = true;
7273
0
                    warn(loc, "ignored", id.c_str(), "");
7274
0
                    found = true;
7275
0
                    break;
7276
0
                }
7277
0
            }
7278
0
            if (! found)
7279
0
                error(loc, "unknown blend equation", "blend_support", "");
7280
0
            return;
7281
0
        }
7282
0
    }
7283
0
    error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
7284
0
}
7285
7286
// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
7287
// This is before we know any type information for error checking.
7288
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id,
7289
                                          const TIntermTyped* node)
7290
0
{
7291
0
    const char* feature = "layout-id value";
7292
    // const char* nonLiteralFeature = "non-literal layout-id value";
7293
7294
0
    integerCheck(node, feature);
7295
0
    const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
7296
0
    int value = 0;
7297
0
    if (constUnion) {
7298
0
        value = constUnion->getConstArray()[0].getIConst();
7299
0
    }
7300
7301
0
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7302
7303
0
    if (id == "offset") {
7304
0
        qualifier.layoutOffset = value;
7305
0
        return;
7306
0
    } else if (id == "align") {
7307
        // "The specified alignment must be a power of 2, or a compile-time error results."
7308
0
        if (! IsPow2(value))
7309
0
            error(loc, "must be a power of 2", "align", "");
7310
0
        else
7311
0
            qualifier.layoutAlign = value;
7312
0
        return;
7313
0
    } else if (id == "location") {
7314
0
        if ((unsigned int)value >= TQualifier::layoutLocationEnd)
7315
0
            error(loc, "location is too large", id.c_str(), "");
7316
0
        else
7317
0
            qualifier.layoutLocation = value;
7318
0
        return;
7319
0
    } else if (id == "set") {
7320
0
        if ((unsigned int)value >= TQualifier::layoutSetEnd)
7321
0
            error(loc, "set is too large", id.c_str(), "");
7322
0
        else
7323
0
            qualifier.layoutSet = value;
7324
0
        return;
7325
0
    } else if (id == "binding") {
7326
0
        if ((unsigned int)value >= TQualifier::layoutBindingEnd)
7327
0
            error(loc, "binding is too large", id.c_str(), "");
7328
0
        else
7329
0
            qualifier.layoutBinding = value;
7330
0
        return;
7331
0
    } else if (id == "component") {
7332
0
        if ((unsigned)value >= TQualifier::layoutComponentEnd)
7333
0
            error(loc, "component is too large", id.c_str(), "");
7334
0
        else
7335
0
            qualifier.layoutComponent = value;
7336
0
        return;
7337
0
    } else if (id.compare(0, 4, "xfb_") == 0) {
7338
        // "Any shader making any static use (after preprocessing) of any of these
7339
        // *xfb_* qualifiers will cause the shader to be in a transform feedback
7340
        // capturing mode and hence responsible for describing the transform feedback
7341
        // setup."
7342
0
        intermediate.setXfbMode();
7343
0
        if (id == "xfb_buffer") {
7344
            // "It is a compile-time error to specify an *xfb_buffer* that is greater than
7345
            // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
7346
0
            if (value >= resources.maxTransformFeedbackBuffers)
7347
0
                error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d",
7348
0
                      resources.maxTransformFeedbackBuffers);
7349
0
            if (value >= (int)TQualifier::layoutXfbBufferEnd)
7350
0
                error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
7351
0
            else
7352
0
                qualifier.layoutXfbBuffer = value;
7353
0
            return;
7354
0
        } else if (id == "xfb_offset") {
7355
0
            if (value >= (int)TQualifier::layoutXfbOffsetEnd)
7356
0
                error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
7357
0
            else
7358
0
                qualifier.layoutXfbOffset = value;
7359
0
            return;
7360
0
        } else if (id == "xfb_stride") {
7361
            // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
7362
            // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
7363
0
            if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
7364
0
                error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
7365
0
                      resources.maxTransformFeedbackInterleavedComponents);
7366
0
            else if (value >= (int)TQualifier::layoutXfbStrideEnd)
7367
0
                error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
7368
0
            if (value < (int)TQualifier::layoutXfbStrideEnd)
7369
0
                qualifier.layoutXfbStride = value;
7370
0
            return;
7371
0
        }
7372
0
    }
7373
7374
0
    if (id == "input_attachment_index") {
7375
0
        requireVulkan(loc, "input_attachment_index");
7376
0
        if (value >= (int)TQualifier::layoutAttachmentEnd)
7377
0
            error(loc, "attachment index is too large", id.c_str(), "");
7378
0
        else
7379
0
            qualifier.layoutAttachment = value;
7380
0
        return;
7381
0
    }
7382
0
    if (id == "constant_id") {
7383
0
        setSpecConstantId(loc, qualifier, (unsigned)value);
7384
0
        return;
7385
0
    }
7386
7387
0
    switch (language) {
7388
0
    case EShLangVertex:
7389
0
        break;
7390
7391
0
    case EShLangTessControl:
7392
0
        if (id == "vertices") {
7393
0
            if (value == 0)
7394
0
                error(loc, "must be greater than 0", "vertices", "");
7395
0
            else
7396
                // publicType.shaderQualifiers.vertices = value;
7397
0
                warn(loc, "ignored", id.c_str(), "");
7398
0
            return;
7399
0
        }
7400
0
        break;
7401
7402
0
    case EShLangTessEvaluation:
7403
0
        break;
7404
7405
0
    case EShLangGeometry:
7406
0
        if (id == "invocations") {
7407
0
            if (value == 0)
7408
0
                error(loc, "must be at least 1", "invocations", "");
7409
0
            else
7410
                // publicType.shaderQualifiers.invocations = value;
7411
0
                warn(loc, "ignored", id.c_str(), "");
7412
0
            return;
7413
0
        }
7414
0
        if (id == "max_vertices") {
7415
            // publicType.shaderQualifiers.vertices = value;
7416
0
            warn(loc, "ignored", id.c_str(), "");
7417
0
            if (value > resources.maxGeometryOutputVertices)
7418
0
                error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
7419
0
            return;
7420
0
        }
7421
0
        if (id == "stream") {
7422
0
            qualifier.layoutStream = value;
7423
0
            return;
7424
0
        }
7425
0
        break;
7426
7427
0
    case EShLangFragment:
7428
0
        if (id == "index") {
7429
0
            qualifier.layoutIndex = value;
7430
0
            return;
7431
0
        }
7432
0
        break;
7433
7434
0
    case EShLangCompute:
7435
0
        if (id.compare(0, 11, "local_size_") == 0) {
7436
0
            if (id == "local_size_x") {
7437
                // publicType.shaderQualifiers.localSize[0] = value;
7438
0
                warn(loc, "ignored", id.c_str(), "");
7439
0
                return;
7440
0
            }
7441
0
            if (id == "local_size_y") {
7442
                // publicType.shaderQualifiers.localSize[1] = value;
7443
0
                warn(loc, "ignored", id.c_str(), "");
7444
0
                return;
7445
0
            }
7446
0
            if (id == "local_size_z") {
7447
                // publicType.shaderQualifiers.localSize[2] = value;
7448
0
                warn(loc, "ignored", id.c_str(), "");
7449
0
                return;
7450
0
            }
7451
0
            if (spvVersion.spv != 0) {
7452
0
                if (id == "local_size_x_id") {
7453
                    // publicType.shaderQualifiers.localSizeSpecId[0] = value;
7454
0
                    warn(loc, "ignored", id.c_str(), "");
7455
0
                    return;
7456
0
                }
7457
0
                if (id == "local_size_y_id") {
7458
                    // publicType.shaderQualifiers.localSizeSpecId[1] = value;
7459
0
                    warn(loc, "ignored", id.c_str(), "");
7460
0
                    return;
7461
0
                }
7462
0
                if (id == "local_size_z_id") {
7463
                    // publicType.shaderQualifiers.localSizeSpecId[2] = value;
7464
0
                    warn(loc, "ignored", id.c_str(), "");
7465
0
                    return;
7466
0
                }
7467
0
            }
7468
0
        }
7469
0
        break;
7470
7471
0
    default:
7472
0
        break;
7473
0
    }
7474
7475
0
    error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
7476
0
}
7477
7478
void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, unsigned value)
7479
0
{
7480
0
    if (value >= TQualifier::layoutSpecConstantIdEnd) {
7481
0
        error(loc, "specialization-constant id is too large", "constant_id", "");
7482
0
    } else {
7483
0
        qualifier.layoutSpecConstantId = value;
7484
0
        qualifier.specConstant = true;
7485
0
        if (! intermediate.addUsedConstantId(value))
7486
0
            error(loc, "specialization-constant id already used", "constant_id", "");
7487
0
    }
7488
0
    return;
7489
0
}
7490
7491
// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
7492
//
7493
// "More than one layout qualifier may appear in a single declaration.
7494
// Additionally, the same layout-qualifier-name can occur multiple times
7495
// within a layout qualifier or across multiple layout qualifiers in the
7496
// same declaration. When the same layout-qualifier-name occurs
7497
// multiple times, in a single declaration, the last occurrence overrides
7498
// the former occurrence(s).  Further, if such a layout-qualifier-name
7499
// will effect subsequent declarations or other observable behavior, it
7500
// is only the last occurrence that will have any effect, behaving as if
7501
// the earlier occurrence(s) within the declaration are not present.
7502
// This is also true for overriding layout-qualifier-names, where one
7503
// overrides the other (e.g., row_major vs. column_major); only the last
7504
// occurrence has any effect."
7505
//
7506
void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
7507
0
{
7508
0
    if (src.hasMatrix())
7509
0
        dst.layoutMatrix = src.layoutMatrix;
7510
0
    if (src.hasPacking())
7511
0
        dst.layoutPacking = src.layoutPacking;
7512
7513
0
    if (src.hasStream())
7514
0
        dst.layoutStream = src.layoutStream;
7515
7516
0
    if (src.hasFormat())
7517
0
        dst.layoutFormat = src.layoutFormat;
7518
7519
0
    if (src.hasXfbBuffer())
7520
0
        dst.layoutXfbBuffer = src.layoutXfbBuffer;
7521
7522
0
    if (src.hasAlign())
7523
0
        dst.layoutAlign = src.layoutAlign;
7524
7525
0
    if (! inheritOnly) {
7526
0
        if (src.hasLocation())
7527
0
            dst.layoutLocation = src.layoutLocation;
7528
0
        if (src.hasComponent())
7529
0
            dst.layoutComponent = src.layoutComponent;
7530
0
        if (src.hasIndex())
7531
0
            dst.layoutIndex = src.layoutIndex;
7532
7533
0
        if (src.hasOffset())
7534
0
            dst.layoutOffset = src.layoutOffset;
7535
7536
0
        if (src.hasSet())
7537
0
            dst.layoutSet = src.layoutSet;
7538
0
        if (src.layoutBinding != TQualifier::layoutBindingEnd)
7539
0
            dst.layoutBinding = src.layoutBinding;
7540
7541
0
        if (src.hasXfbStride())
7542
0
            dst.layoutXfbStride = src.layoutXfbStride;
7543
0
        if (src.hasXfbOffset())
7544
0
            dst.layoutXfbOffset = src.layoutXfbOffset;
7545
0
        if (src.hasAttachment())
7546
0
            dst.layoutAttachment = src.layoutAttachment;
7547
0
        if (src.hasSpecConstantId())
7548
0
            dst.layoutSpecConstantId = src.layoutSpecConstantId;
7549
7550
0
        if (src.layoutPushConstant)
7551
0
            dst.layoutPushConstant = true;
7552
0
    }
7553
0
}
7554
7555
7556
//
7557
// Look up a function name in the symbol table, and make sure it is a function.
7558
//
7559
// First, look for an exact match.  If there is none, use the generic selector
7560
// TParseContextBase::selectFunction() to find one, parameterized by the
7561
// convertible() and better() predicates defined below.
7562
//
7563
// Return the function symbol if found, otherwise nullptr.
7564
//
7565
const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth,
7566
                                                TIntermTyped*& args)
7567
0
{
7568
0
    if (symbolTable.isFunctionNameVariable(call.getName())) {
7569
0
        error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
7570
0
        return nullptr;
7571
0
    }
7572
7573
    // first, look for an exact match
7574
0
    bool dummyScope;
7575
0
    TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth);
7576
0
    if (symbol)
7577
0
        return symbol->getAsFunction();
7578
7579
    // no exact match, use the generic selector, parameterized by the GLSL rules
7580
7581
    // create list of candidates to send
7582
0
    TVector<const TFunction*> candidateList;
7583
0
    symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
7584
7585
    // These built-in ops can accept any type, so we bypass the argument selection
7586
0
    if (candidateList.size() == 1 && builtIn &&
7587
0
        (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
7588
0
         candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
7589
0
         candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
7590
0
         candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter ||
7591
0
         candidateList[0]->getBuiltInOp() == EOpMethodConsume)) {
7592
0
        return candidateList[0];
7593
0
    }
7594
7595
0
    bool allowOnlyUpConversions = true;
7596
7597
    // can 'from' convert to 'to'?
7598
0
    const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
7599
0
        if (from == to)
7600
0
            return true;
7601
7602
        // no aggregate conversions
7603
0
        if (from.isArray()  || to.isArray() ||
7604
0
            from.isStruct() || to.isStruct())
7605
0
            return false;
7606
7607
0
        switch (op) {
7608
0
        case EOpInterlockedAdd:
7609
0
        case EOpInterlockedAnd:
7610
0
        case EOpInterlockedCompareExchange:
7611
0
        case EOpInterlockedCompareStore:
7612
0
        case EOpInterlockedExchange:
7613
0
        case EOpInterlockedMax:
7614
0
        case EOpInterlockedMin:
7615
0
        case EOpInterlockedOr:
7616
0
        case EOpInterlockedXor:
7617
            // We do not promote the texture or image type for these ocodes.  Normally that would not
7618
            // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
7619
            // stage it's merely e.g, a basic integer type.
7620
            //
7621
            // Instead, we want to promote other arguments, but stay within the same family.  In other
7622
            // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
7623
            // but it is allowed to promote its other arguments.
7624
0
            if (arg == 0)
7625
0
                return false;
7626
0
            break;
7627
0
        case EOpMethodSample:
7628
0
        case EOpMethodSampleBias:
7629
0
        case EOpMethodSampleCmp:
7630
0
        case EOpMethodSampleCmpLevelZero:
7631
0
        case EOpMethodSampleGrad:
7632
0
        case EOpMethodSampleLevel:
7633
0
        case EOpMethodLoad:
7634
0
        case EOpMethodGetDimensions:
7635
0
        case EOpMethodGetSamplePosition:
7636
0
        case EOpMethodGather:
7637
0
        case EOpMethodCalculateLevelOfDetail:
7638
0
        case EOpMethodCalculateLevelOfDetailUnclamped:
7639
0
        case EOpMethodGatherRed:
7640
0
        case EOpMethodGatherGreen:
7641
0
        case EOpMethodGatherBlue:
7642
0
        case EOpMethodGatherAlpha:
7643
0
        case EOpMethodGatherCmp:
7644
0
        case EOpMethodGatherCmpRed:
7645
0
        case EOpMethodGatherCmpGreen:
7646
0
        case EOpMethodGatherCmpBlue:
7647
0
        case EOpMethodGatherCmpAlpha:
7648
0
        case EOpMethodAppend:
7649
0
        case EOpMethodRestartStrip:
7650
            // those are method calls, the object type can not be changed
7651
            // they are equal if the dim and type match (is dim sufficient?)
7652
0
            if (arg == 0)
7653
0
                return from.getSampler().type == to.getSampler().type &&
7654
0
                       from.getSampler().arrayed == to.getSampler().arrayed &&
7655
0
                       from.getSampler().shadow == to.getSampler().shadow &&
7656
0
                       from.getSampler().ms == to.getSampler().ms &&
7657
0
                       from.getSampler().dim == to.getSampler().dim;
7658
0
            break;
7659
0
        default:
7660
0
            break;
7661
0
        }
7662
7663
        // basic types have to be convertible
7664
0
        if (allowOnlyUpConversions)
7665
0
            if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
7666
0
                return false;
7667
7668
        // shapes have to be convertible
7669
0
        if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
7670
0
            (from.isScalarOrVec1() && to.isVector())    ||
7671
0
            (from.isScalarOrVec1() && to.isMatrix())    ||
7672
0
            (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
7673
0
            return true;
7674
7675
        // TODO: what are the matrix rules? they go here
7676
7677
0
        return false;
7678
0
    };
7679
7680
    // Is 'to2' a better conversion than 'to1'?
7681
    // Ties should not be considered as better.
7682
    // Assumes 'convertible' already said true.
7683
0
    const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
7684
        // exact match is always better than mismatch
7685
0
        if (from == to2)
7686
0
            return from != to1;
7687
0
        if (from == to1)
7688
0
            return false;
7689
7690
        // shape changes are always worse
7691
0
        if (from.isScalar() || from.isVector()) {
7692
0
            if (from.getVectorSize() == to2.getVectorSize() &&
7693
0
                from.getVectorSize() != to1.getVectorSize())
7694
0
                return true;
7695
0
            if (from.getVectorSize() == to1.getVectorSize() &&
7696
0
                from.getVectorSize() != to2.getVectorSize())
7697
0
                return false;
7698
0
        }
7699
7700
        // Handle sampler betterness: An exact sampler match beats a non-exact match.
7701
        // (If we just looked at basic type, all EbtSamplers would look the same).
7702
        // If any type is not a sampler, just use the linearize function below.
7703
0
        if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
7704
            // We can ignore the vector size in the comparison.
7705
0
            TSampler to1Sampler = to1.getSampler();
7706
0
            TSampler to2Sampler = to2.getSampler();
7707
7708
0
            to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
7709
7710
0
            if (from.getSampler() == to2Sampler)
7711
0
                return from.getSampler() != to1Sampler;
7712
0
            if (from.getSampler() == to1Sampler)
7713
0
                return false;
7714
0
        }
7715
7716
        // Might or might not be changing shape, which means basic type might
7717
        // or might not match, so within that, the question is how big a
7718
        // basic-type conversion is being done.
7719
        //
7720
        // Use a hierarchy of domains, translated to order of magnitude
7721
        // in a linearized view:
7722
        //   - floating-point vs. integer
7723
        //     - 32 vs. 64 bit (or width in general)
7724
        //       - bool vs. non bool
7725
        //         - signed vs. not signed
7726
0
        const auto linearize = [](const TBasicType& basicType) -> int {
7727
0
            switch (basicType) {
7728
0
            case EbtBool:     return 1;
7729
0
            case EbtInt:      return 10;
7730
0
            case EbtUint:     return 11;
7731
0
            case EbtInt64:    return 20;
7732
0
            case EbtUint64:   return 21;
7733
0
            case EbtFloat:    return 100;
7734
0
            case EbtDouble:   return 110;
7735
0
            default:          return 0;
7736
0
            }
7737
0
        };
7738
7739
0
        return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
7740
0
               abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
7741
0
    };
7742
7743
    // for ambiguity reporting
7744
0
    bool tie = false;
7745
7746
    // send to the generic selector
7747
0
    const TFunction* bestMatch = nullptr;
7748
7749
    // printf has var args and is in the symbol table as "printf()",
7750
    // mangled to "printf("
7751
0
    if (call.getName() == "printf") {
7752
0
        TSymbol* symbol = symbolTable.find("printf(", &builtIn);
7753
0
        if (symbol)
7754
0
            return symbol->getAsFunction();
7755
0
    }
7756
7757
0
    bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7758
7759
0
    if (bestMatch == nullptr) {
7760
        // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
7761
        // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
7762
        // upconversions possible.
7763
0
        allowOnlyUpConversions = false;
7764
0
        bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7765
0
    }
7766
7767
0
    if (bestMatch == nullptr) {
7768
0
        error(loc, "no matching overloaded function found", call.getName().c_str(), "");
7769
0
        return nullptr;
7770
0
    }
7771
7772
    // For built-ins, we can convert across the arguments.  This will happen in several steps:
7773
    // Step 1:  If there's an exact match, use it.
7774
    // Step 2a: Otherwise, get the operator from the best match and promote arguments:
7775
    // Step 2b: reconstruct the TFunction based on the new arg types
7776
    // Step 3:  Re-select after type promotion is applied, to find proper candidate.
7777
0
    if (builtIn) {
7778
        // Step 1: If there's an exact match, use it.
7779
0
        if (call.getMangledName() == bestMatch->getMangledName())
7780
0
            return bestMatch;
7781
7782
        // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
7783
        // are that kind of operator.
7784
0
        if (args != nullptr) {
7785
            // The arg list can be a unary node, or an aggregate.  We have to handle both.
7786
            // We will use the normal promote() facilities, which require an interm node.
7787
0
            TIntermOperator* promote = nullptr;
7788
7789
0
            if (call.getParamCount() == 1) {
7790
0
                promote = new TIntermUnary(bestMatch->getBuiltInOp());
7791
0
                promote->getAsUnaryNode()->setOperand(args->getAsTyped());
7792
0
            } else {
7793
0
                promote = new TIntermAggregate(bestMatch->getBuiltInOp());
7794
0
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7795
0
            }
7796
7797
0
            if (! intermediate.promote(promote))
7798
0
                return nullptr;
7799
7800
            // Obtain the promoted arg list.
7801
0
            if (call.getParamCount() == 1) {
7802
0
                args = promote->getAsUnaryNode()->getOperand();
7803
0
            } else {
7804
0
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7805
0
            }
7806
0
        }
7807
7808
        // Step 2b: reconstruct the TFunction based on the new arg types
7809
0
        TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
7810
7811
0
        if (args->getAsAggregate()) {
7812
            // Handle aggregates: put all args into the new function call
7813
0
            for (int arg = 0; arg < int(args->getAsAggregate()->getSequence().size()); ++arg) {
7814
                // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
7815
0
                TParameter param = { nullptr, new TType, nullptr };
7816
0
                param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
7817
0
                convertedCall.addParameter(param);
7818
0
            }
7819
0
        } else if (args->getAsUnaryNode()) {
7820
            // Handle unaries: put all args into the new function call
7821
0
            TParameter param = { nullptr, new TType, nullptr };
7822
0
            param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
7823
0
            convertedCall.addParameter(param);
7824
0
        } else if (args->getAsTyped()) {
7825
            // Handle bare e.g, floats, not in an aggregate.
7826
0
            TParameter param = { nullptr, new TType, nullptr };
7827
0
            param.type->shallowCopy(args->getAsTyped()->getType());
7828
0
            convertedCall.addParameter(param);
7829
0
        } else {
7830
0
            assert(0); // unknown argument list.
7831
0
            return nullptr;
7832
0
        }
7833
7834
        // Step 3: Re-select after type promotion, to find proper candidate
7835
        // send to the generic selector
7836
0
        bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
7837
7838
        // At this point, there should be no tie.
7839
0
    }
7840
7841
0
    if (tie)
7842
0
        error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
7843
7844
    // Append default parameter values if needed
7845
0
    if (!tie && bestMatch != nullptr) {
7846
0
        for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
7847
0
            handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
7848
0
        }
7849
0
    }
7850
7851
0
    return bestMatch;
7852
0
}
7853
7854
//
7855
// Do everything necessary to handle a typedef declaration, for a single symbol.
7856
//
7857
// 'parseType' is the type part of the declaration (to the left)
7858
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7859
//
7860
void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
7861
0
{
7862
0
    TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
7863
0
    if (! symbolTable.insert(*typeSymbol))
7864
0
        error(loc, "name already defined", "typedef", identifier.c_str());
7865
0
}
7866
7867
// Do everything necessary to handle a struct declaration, including
7868
// making IO aliases because HLSL allows mixed IO in a struct that specializes
7869
// based on the usage (input, output, uniform, none).
7870
void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
7871
0
{
7872
    // If it was named, which means the type can be reused later, add
7873
    // it to the symbol table.  (Unless it's a block, in which
7874
    // case the name is not a type.)
7875
0
    if (type.getBasicType() == EbtBlock || structName.size() == 0)
7876
0
        return;
7877
7878
0
    TVariable* userTypeDef = new TVariable(&structName, type, true);
7879
0
    if (! symbolTable.insert(*userTypeDef)) {
7880
0
        error(loc, "redefinition", structName.c_str(), "struct");
7881
0
        return;
7882
0
    }
7883
7884
    // See if we need IO aliases for the structure typeList
7885
7886
0
    const auto condAlloc = [](bool pred, TTypeList*& list) {
7887
0
        if (pred && list == nullptr)
7888
0
            list = new TTypeList;
7889
0
    };
7890
7891
0
    tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
7892
0
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7893
0
        condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
7894
0
        condAlloc(  hasInput(member->type->getQualifier()), newLists.input);
7895
0
        condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
7896
7897
0
        if (member->type->isStruct()) {
7898
0
            auto it = ioTypeMap.find(member->type->getStruct());
7899
0
            if (it != ioTypeMap.end()) {
7900
0
                condAlloc(it->second.uniform != nullptr, newLists.uniform);
7901
0
                condAlloc(it->second.input   != nullptr, newLists.input);
7902
0
                condAlloc(it->second.output  != nullptr, newLists.output);
7903
0
            }
7904
0
        }
7905
0
    }
7906
0
    if (newLists.uniform == nullptr &&
7907
0
        newLists.input   == nullptr &&
7908
0
        newLists.output  == nullptr) {
7909
        // Won't do any IO caching, clear up the type and get out now.
7910
0
        for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
7911
0
            clearUniformInputOutput(member->type->getQualifier());
7912
0
        return;
7913
0
    }
7914
7915
    // We have IO involved.
7916
7917
    // Make a pure typeList for the symbol table, and cache side copies of IO versions.
7918
0
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7919
0
        const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
7920
0
            if (s != nullptr) {
7921
0
                ioMember.type = new TType;
7922
0
                ioMember.type->shallowCopy(*member->type);
7923
0
                ioMember.type->setStruct(s);
7924
0
            }
7925
0
        };
7926
0
        const auto newMember = [&](TTypeLoc& m) {
7927
0
            if (m.type == nullptr) {
7928
0
                m.type = new TType;
7929
0
                m.type->shallowCopy(*member->type);
7930
0
            }
7931
0
        };
7932
7933
0
        TTypeLoc newUniformMember = { nullptr, member->loc };
7934
0
        TTypeLoc newInputMember   = { nullptr, member->loc };
7935
0
        TTypeLoc newOutputMember  = { nullptr, member->loc };
7936
0
        if (member->type->isStruct()) {
7937
            // swap in an IO child if there is one
7938
0
            auto it = ioTypeMap.find(member->type->getStruct());
7939
0
            if (it != ioTypeMap.end()) {
7940
0
                inheritStruct(it->second.uniform, newUniformMember);
7941
0
                inheritStruct(it->second.input,   newInputMember);
7942
0
                inheritStruct(it->second.output,  newOutputMember);
7943
0
            }
7944
0
        }
7945
0
        if (newLists.uniform) {
7946
0
            newMember(newUniformMember);
7947
7948
            // inherit default matrix layout (changeable via #pragma pack_matrix), if none given.
7949
0
            if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone)
7950
0
                newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
7951
7952
0
            correctUniform(newUniformMember.type->getQualifier());
7953
0
            newLists.uniform->push_back(newUniformMember);
7954
0
        }
7955
0
        if (newLists.input) {
7956
0
            newMember(newInputMember);
7957
0
            correctInput(newInputMember.type->getQualifier());
7958
0
            newLists.input->push_back(newInputMember);
7959
0
        }
7960
0
        if (newLists.output) {
7961
0
            newMember(newOutputMember);
7962
0
            correctOutput(newOutputMember.type->getQualifier());
7963
0
            newLists.output->push_back(newOutputMember);
7964
0
        }
7965
7966
        // make original pure
7967
0
        clearUniformInputOutput(member->type->getQualifier());
7968
0
    }
7969
0
    ioTypeMap[type.getStruct()] = newLists;
7970
0
}
7971
7972
// Lookup a user-type by name.
7973
// If found, fill in the type and return the defining symbol.
7974
// If not found, return nullptr.
7975
TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
7976
673
{
7977
673
    TSymbol* symbol = symbolTable.find(typeName);
7978
673
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
7979
0
        type.shallowCopy(symbol->getType());
7980
0
        return symbol;
7981
0
    } else
7982
673
        return nullptr;
7983
673
}
7984
7985
//
7986
// Do everything necessary to handle a variable (non-block) declaration.
7987
// Either redeclaring a variable, or making a new one, updating the symbol
7988
// table, and all error checking.
7989
//
7990
// Returns a subtree node that computes an initializer, if needed.
7991
// Returns nullptr if there is no code to execute for initialization.
7992
//
7993
// 'parseType' is the type part of the declaration (to the left)
7994
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7995
//
7996
TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type,
7997
                                               TIntermTyped* initializer)
7998
15
{
7999
15
    if (voidErrorCheck(loc, identifier, type.getBasicType()))
8000
0
        return nullptr;
8001
8002
    // Global consts with initializers that are non-const act like EvqGlobal in HLSL.
8003
    // This test is implicitly recursive, because initializers propagate constness
8004
    // up the aggregate node tree during creation.  E.g, for:
8005
    //    { { 1, 2 }, { 3, 4 } }
8006
    // the initializer list is marked EvqConst at the top node, and remains so here.  However:
8007
    //    { 1, { myvar, 2 }, 3 }
8008
    // is not a const intializer, and still becomes EvqGlobal here.
8009
8010
15
    const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
8011
8012
15
    if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
8013
        // Force to global
8014
0
        type.getQualifier().storage = EvqGlobal;
8015
0
    }
8016
8017
    // make const and initialization consistent
8018
15
    fixConstInit(loc, identifier, type, initializer);
8019
8020
    // Check for redeclaration of built-ins and/or attempting to declare a reserved name
8021
15
    TSymbol* symbol = nullptr;
8022
8023
15
    inheritGlobalDefaults(type.getQualifier());
8024
8025
15
    const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true);
8026
8027
    // correct IO in the type
8028
15
    switch (type.getQualifier().storage) {
8029
0
    case EvqGlobal:
8030
6
    case EvqTemporary:
8031
6
        clearUniformInputOutput(type.getQualifier());
8032
6
        break;
8033
8
    case EvqUniform:
8034
8
    case EvqBuffer:
8035
8
        correctUniform(type.getQualifier());
8036
8
        if (type.isStruct()) {
8037
0
            auto it = ioTypeMap.find(type.getStruct());
8038
0
            if (it != ioTypeMap.end())
8039
0
                type.setStruct(it->second.uniform);
8040
0
        }
8041
8042
8
        break;
8043
1
    default:
8044
1
        break;
8045
15
    }
8046
8047
    // Declare the variable
8048
15
    if (type.isArray()) {
8049
        // array case
8050
8
        declareArray(loc, identifier, type, symbol, !flattenVar);
8051
8
    } else {
8052
        // non-array case
8053
7
        if (symbol == nullptr)
8054
7
            symbol = declareNonArray(loc, identifier, type, !flattenVar);
8055
0
        else if (type != symbol->getType())
8056
0
            error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
8057
7
    }
8058
8059
15
    if (symbol == nullptr)
8060
0
        return nullptr;
8061
8062
15
    if (flattenVar)
8063
0
        flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel());
8064
8065
15
    TVariable* variable = symbol->getAsVariable();
8066
8067
15
    if (initializer == nullptr) {
8068
12
        if (intermediate.getDebugInfo())
8069
0
            return executeDeclaration(loc, variable);
8070
12
        else
8071
12
            return nullptr;
8072
12
    }
8073
8074
    // Deal with initializer
8075
3
    if (variable == nullptr) {
8076
0
        error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
8077
0
        return nullptr;
8078
0
    }
8079
3
    return executeInitializer(loc, initializer, variable);
8080
3
}
8081
8082
// Pick up global defaults from the provide global defaults into dst.
8083
void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
8084
15
{
8085
15
    if (dst.storage == EvqVaryingOut) {
8086
0
        if (! dst.hasStream() && language == EShLangGeometry)
8087
0
            dst.layoutStream = globalOutputDefaults.layoutStream;
8088
0
        if (! dst.hasXfbBuffer())
8089
0
            dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
8090
0
    }
8091
15
}
8092
8093
//
8094
// Make an internal-only variable whose name is for debug purposes only
8095
// and won't be searched for.  Callers will only use the return value to use
8096
// the variable, not the name to look it up.  It is okay if the name
8097
// is the same as other names; there won't be any conflict.
8098
//
8099
TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
8100
0
{
8101
0
    TString* nameString = NewPoolTString(name);
8102
0
    TVariable* variable = new TVariable(nameString, type);
8103
0
    symbolTable.makeInternalVariable(*variable);
8104
8105
0
    return variable;
8106
0
}
8107
8108
// Make a symbol node holding a new internal temporary variable.
8109
TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name,
8110
                                                          const TType& type) const
8111
0
{
8112
0
    TVariable* tmpVar = makeInternalVariable(name, type);
8113
0
    tmpVar->getWritableType().getQualifier().makeTemporary();
8114
8115
0
    return intermediate.addSymbol(*tmpVar, loc);
8116
0
}
8117
8118
//
8119
// Declare a non-array variable, the main point being there is no redeclaration
8120
// for resizing allowed.
8121
//
8122
// Return the successfully declared variable.
8123
//
8124
TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
8125
                                             bool track)
8126
7
{
8127
    // make a new variable
8128
7
    TVariable* variable = new TVariable(&identifier, type);
8129
8130
    // add variable to symbol table
8131
7
    if (symbolTable.insert(*variable)) {
8132
7
        if (track && symbolTable.atGlobalLevel())
8133
1
            trackLinkage(*variable);
8134
7
        return variable;
8135
7
    }
8136
8137
0
    error(loc, "redefinition", variable->getName().c_str(), "");
8138
0
    return nullptr;
8139
7
}
8140
8141
// Return a declaration of a temporary variable
8142
//
8143
// This is used to force a variable to be declared in the correct scope
8144
// when debug information is being generated.
8145
8146
TIntermNode* HlslParseContext::executeDeclaration(const TSourceLoc& loc, TVariable* variable)
8147
0
{
8148
  //
8149
  // Identifier must be of type temporary.
8150
  //
8151
0
  TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8152
0
  if (qualifier != EvqTemporary)
8153
0
      return nullptr;
8154
8155
0
  TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8156
0
  return handleDeclare(loc, intermSymbol);
8157
0
}
8158
8159
//
8160
// Handle all types of initializers from the grammar.
8161
//
8162
// Returning nullptr just means there is no code to execute to handle the
8163
// initializer, which will, for example, be the case for constant initializers.
8164
//
8165
// Returns a subtree that accomplished the initialization.
8166
//
8167
TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
8168
3
{
8169
    //
8170
    // Identifier must be of type constant, a global, or a temporary, and
8171
    // starting at version 120, desktop allows uniforms to have initializers.
8172
    //
8173
3
    TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8174
8175
    //
8176
    // If the initializer was from braces { ... }, we convert the whole subtree to a
8177
    // constructor-style subtree, allowing the rest of the code to operate
8178
    // identically for both kinds of initializers.
8179
    //
8180
    //
8181
    // Type can't be deduced from the initializer list, so a skeletal type to
8182
    // follow has to be passed in.  Constness and specialization-constness
8183
    // should be deduced bottom up, not dictated by the skeletal type.
8184
    //
8185
3
    TType skeletalType;
8186
3
    skeletalType.shallowCopy(variable->getType());
8187
3
    skeletalType.getQualifier().makeTemporary();
8188
3
    if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
8189
0
        initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
8190
3
    if (initializer == nullptr) {
8191
        // error recovery; don't leave const without constant values
8192
0
        if (qualifier == EvqConst)
8193
0
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8194
0
        return nullptr;
8195
0
    }
8196
8197
    // Fix outer arrayness if variable is unsized, getting size from the initializer
8198
3
    if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray())
8199
0
        variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
8200
8201
    // Inner arrayness can also get set by an initializer
8202
3
    if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
8203
0
        initializer->getType().getArraySizes()->getNumDims() ==
8204
0
        variable->getType().getArraySizes()->getNumDims()) {
8205
        // adopt unsized sizes from the initializer's sizes
8206
0
        for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
8207
0
            if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) {
8208
0
                variable->getWritableType().getArraySizes()->setDimSize(d,
8209
0
                    initializer->getType().getArraySizes()->getDimSize(d));
8210
0
            }
8211
0
        }
8212
0
    }
8213
8214
    // Uniform and global consts require a constant initializer
8215
3
    if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
8216
0
        error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
8217
0
        variable->getWritableType().getQualifier().storage = EvqTemporary;
8218
0
        return nullptr;
8219
0
    }
8220
8221
    // Const variables require a constant initializer
8222
3
    if (qualifier == EvqConst) {
8223
0
        if (initializer->getType().getQualifier().storage != EvqConst) {
8224
0
            variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
8225
0
            qualifier = EvqConstReadOnly;
8226
0
        }
8227
0
    }
8228
8229
3
    if (qualifier == EvqConst || qualifier == EvqUniform) {
8230
        // Compile-time tagging of the variable with its constant value...
8231
8232
0
        initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
8233
0
        if (initializer != nullptr && variable->getType() != initializer->getType())
8234
0
            initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer);
8235
0
        if (initializer == nullptr || !initializer->getAsConstantUnion() ||
8236
0
                                      variable->getType() != initializer->getType()) {
8237
0
            error(loc, "non-matching or non-convertible constant type for const initializer",
8238
0
                variable->getType().getStorageQualifierString(), "");
8239
0
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8240
0
            return nullptr;
8241
0
        }
8242
8243
0
        variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
8244
3
    } else {
8245
        // normal assigning of a value to a variable...
8246
3
        specializationCheck(loc, initializer->getType(), "initializer");
8247
3
        TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8248
3
        TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
8249
3
        if (initNode == nullptr)
8250
0
            assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
8251
3
        return initNode;
8252
3
    }
8253
8254
0
    return nullptr;
8255
3
}
8256
8257
//
8258
// Reprocess any initializer-list { ... } parts of the initializer.
8259
// Need to hierarchically assign correct types and implicit
8260
// conversions. Will do this mimicking the same process used for
8261
// creating a constructor-style initializer, ensuring we get the
8262
// same form.
8263
//
8264
// Returns a node representing an expression for the initializer list expressed
8265
// as the correct type.
8266
//
8267
// Returns nullptr if there is an error.
8268
//
8269
TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
8270
                                                       TIntermTyped* initializer, TIntermTyped* scalarInit)
8271
0
{
8272
    // Will operate recursively.  Once a subtree is found that is constructor style,
8273
    // everything below it is already good: Only the "top part" of the initializer
8274
    // can be an initializer list, where "top part" can extend for several (or all) levels.
8275
8276
    // see if we have bottomed out in the tree within the initializer-list part
8277
0
    TIntermAggregate* initList = initializer->getAsAggregate();
8278
0
    if (initList == nullptr || initList->getOp() != EOpNull) {
8279
        // We don't have a list, but if it's a scalar and the 'type' is a
8280
        // composite, we need to lengthen below to make it useful.
8281
        // Otherwise, this is an already formed object to initialize with.
8282
0
        if (type.isScalar() || !initializer->getType().isScalar())
8283
0
            return initializer;
8284
0
        else
8285
0
            initList = intermediate.makeAggregate(initializer);
8286
0
    }
8287
8288
    // Of the initializer-list set of nodes, need to process bottom up,
8289
    // so recurse deep, then process on the way up.
8290
8291
    // Go down the tree here...
8292
0
    if (type.isArray()) {
8293
        // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
8294
        // Later on, initializer execution code will deal with array size logic.
8295
0
        TType arrayType;
8296
0
        arrayType.shallowCopy(type);                     // sharing struct stuff is fine
8297
0
        arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
8298
8299
        // edit array sizes to fill in unsized dimensions
8300
0
        if (type.isUnsizedArray())
8301
0
            arrayType.changeOuterArraySize((int)initList->getSequence().size());
8302
8303
        // set unsized array dimensions that can be derived from the initializer's first element
8304
0
        if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
8305
0
            TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
8306
0
            if (firstInit->getType().isArray() &&
8307
0
                arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
8308
0
                for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) {
8309
0
                    if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize)
8310
0
                        arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
8311
0
                }
8312
0
            }
8313
0
        }
8314
8315
        // lengthen list to be long enough
8316
0
        lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
8317
8318
        // recursively process each element
8319
0
        TType elementType(arrayType, 0); // dereferenced type
8320
0
        for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
8321
0
            initList->getSequence()[i] = convertInitializerList(loc, elementType,
8322
0
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8323
0
            if (initList->getSequence()[i] == nullptr)
8324
0
                return nullptr;
8325
0
        }
8326
8327
0
        return addConstructor(loc, initList, arrayType);
8328
0
    } else if (type.isStruct()) {
8329
        // do we have implicit assignments to opaques?
8330
0
        for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) {
8331
0
            if ((*type.getStruct())[i].type->containsOpaque()) {
8332
0
                error(loc, "cannot implicitly initialize opaque members", "initializer list", "");
8333
0
                return nullptr;
8334
0
            }
8335
0
        }
8336
8337
        // lengthen list to be long enough
8338
0
        lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
8339
8340
0
        if (type.getStruct()->size() != initList->getSequence().size()) {
8341
0
            error(loc, "wrong number of structure members", "initializer list", "");
8342
0
            return nullptr;
8343
0
        }
8344
0
        for (size_t i = 0; i < type.getStruct()->size(); ++i) {
8345
0
            initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type,
8346
0
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8347
0
            if (initList->getSequence()[i] == nullptr)
8348
0
                return nullptr;
8349
0
        }
8350
0
    } else if (type.isMatrix()) {
8351
0
        if (type.computeNumComponents() == (int)initList->getSequence().size()) {
8352
            // This means the matrix is initialized component-wise, rather than as
8353
            // a series of rows and columns.  We can just use the list directly as
8354
            // a constructor; no further processing needed.
8355
0
        } else {
8356
            // lengthen list to be long enough
8357
0
            lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
8358
8359
0
            if (type.getMatrixCols() != (int)initList->getSequence().size()) {
8360
0
                error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
8361
0
                return nullptr;
8362
0
            }
8363
0
            TType vectorType(type, 0); // dereferenced type
8364
0
            for (int i = 0; i < type.getMatrixCols(); ++i) {
8365
0
                initList->getSequence()[i] = convertInitializerList(loc, vectorType,
8366
0
                                                                    initList->getSequence()[i]->getAsTyped(), scalarInit);
8367
0
                if (initList->getSequence()[i] == nullptr)
8368
0
                    return nullptr;
8369
0
            }
8370
0
        }
8371
0
    } else if (type.isVector()) {
8372
        // lengthen list to be long enough
8373
0
        lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
8374
8375
        // error check; we're at bottom, so work is finished below
8376
0
        if (type.getVectorSize() != (int)initList->getSequence().size()) {
8377
0
            error(loc, "wrong vector size (or rows in a matrix column):", "initializer list",
8378
0
                  type.getCompleteString().c_str());
8379
0
            return nullptr;
8380
0
        }
8381
0
    } else if (type.isScalar()) {
8382
        // lengthen list to be long enough
8383
0
        lengthenList(loc, initList->getSequence(), 1, scalarInit);
8384
8385
0
        if ((int)initList->getSequence().size() != 1) {
8386
0
            error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
8387
0
            return nullptr;
8388
0
        }
8389
0
    } else {
8390
0
        error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
8391
0
        return nullptr;
8392
0
    }
8393
8394
    // Now that the subtree is processed, process this node as if the
8395
    // initializer list is a set of arguments to a constructor.
8396
0
    TIntermTyped* emulatedConstructorArguments;
8397
0
    if (initList->getSequence().size() == 1)
8398
0
        emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
8399
0
    else
8400
0
        emulatedConstructorArguments = initList;
8401
8402
0
    return addConstructor(loc, emulatedConstructorArguments, type);
8403
0
}
8404
8405
// Lengthen list to be long enough to cover any gap from the current list size
8406
// to 'size'. If the list is longer, do nothing.
8407
// The value to lengthen with is the default for short lists.
8408
//
8409
// By default, lists that are too short due to lack of initializers initialize to zero.
8410
// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
8411
// based on whether something is passed in as 'scalarInit'.
8412
//
8413
// 'scalarInit' must be safe to use each time this is called (no side effects replication).
8414
//
8415
void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
8416
0
{
8417
0
    for (int c = (int)list.size(); c < size; ++c) {
8418
0
        if (scalarInit == nullptr)
8419
0
            list.push_back(intermediate.addConstantUnion(0, loc));
8420
0
        else
8421
0
            list.push_back(scalarInit);
8422
0
    }
8423
0
}
8424
8425
//
8426
// Test for the correctness of the parameters passed to various constructor functions
8427
// and also convert them to the right data type, if allowed and required.
8428
//
8429
// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
8430
//
8431
TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8432
7
{
8433
7
    if (node == nullptr)
8434
0
        return nullptr;
8435
8436
    // Construct identical type
8437
7
    if (type == node->getType())
8438
3
        return node;
8439
8440
    // Handle the idiom "(struct type)<scalar value>"
8441
4
    if (type.isStruct() && isScalarConstructor(node)) {
8442
        // 'node' will almost always get used multiple times, so should not be used directly,
8443
        // it would create a DAG instead of a tree, which might be okay (would
8444
        // like to formalize that for constants and symbols), but if it has
8445
        // side effects, they would get executed multiple times, which is not okay.
8446
0
        if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
8447
0
            TIntermAggregate* seq = intermediate.makeAggregate(loc);
8448
0
            TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
8449
0
            seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
8450
0
            seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
8451
0
            seq->setOp(EOpComma);
8452
0
            seq->setType(type);
8453
0
            return seq;
8454
0
        } else
8455
0
            return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
8456
0
    }
8457
8458
4
    return addConstructor(loc, node, type);
8459
4
}
8460
8461
// Add a constructor, either from the grammar, or other programmatic reasons.
8462
//
8463
// 'node' is what to construct from.
8464
// 'type' is what type to construct.
8465
//
8466
// Returns the constructed object.
8467
// Return nullptr if it can't be done.
8468
//
8469
TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8470
8
{
8471
8
    TIntermAggregate* aggrNode = node->getAsAggregate();
8472
8
    TOperator op = intermediate.mapTypeToConstructorOp(type);
8473
8474
8
    if (op == EOpConstructTextureSampler)
8475
0
        return intermediate.setAggregateOperator(aggrNode, op, type, loc);
8476
8477
8
    TTypeList::const_iterator memberTypes;
8478
8
    if (op == EOpConstructStruct)
8479
0
        memberTypes = type.getStruct()->begin();
8480
8481
8
    TType elementType;
8482
8
    if (type.isArray()) {
8483
0
        TType dereferenced(type, 0);
8484
0
        elementType.shallowCopy(dereferenced);
8485
0
    } else
8486
8
        elementType.shallowCopy(type);
8487
8488
8
    bool singleArg;
8489
8
    if (aggrNode != nullptr) {
8490
0
        if (aggrNode->getOp() != EOpNull)
8491
0
            singleArg = true;
8492
0
        else
8493
0
            singleArg = false;
8494
0
    } else
8495
8
        singleArg = true;
8496
8497
8
    TIntermTyped *newNode;
8498
8
    if (singleArg) {
8499
        // Handle array -> array conversion
8500
        // Constructing an array of one type from an array of another type is allowed,
8501
        // assuming there are enough components available (semantic-checked earlier).
8502
8
        if (type.isArray() && node->isArray())
8503
0
            newNode = convertArray(node, type);
8504
8505
        // If structure constructor or array constructor is being called
8506
        // for only one parameter inside the aggregate, we need to call constructAggregate function once.
8507
8
        else if (type.isArray())
8508
0
            newNode = constructAggregate(node, elementType, 1, node->getLoc());
8509
8
        else if (op == EOpConstructStruct)
8510
0
            newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
8511
8
        else {
8512
            // shape conversion for matrix constructor from scalar.  HLSL semantics are: scalar
8513
            // is replicated into every element of the matrix (not just the diagnonal), so
8514
            // that is handled specially here.
8515
8
            if (type.isMatrix() && node->getType().isScalarOrVec1())
8516
0
                node = intermediate.addShapeConversion(type, node);
8517
8518
8
            newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
8519
8
        }
8520
8521
8
        if (newNode && (type.isArray() || op == EOpConstructStruct))
8522
0
            newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
8523
8524
8
        return newNode;
8525
8
    }
8526
8527
    //
8528
    // Handle list of arguments.
8529
    //
8530
0
    TIntermSequence& sequenceVector = aggrNode->getSequence();    // Stores the information about the parameter to the constructor
8531
    // if the structure constructor contains more than one parameter, then construct
8532
    // each parameter
8533
8534
0
    int paramCount = 0;  // keeps a track of the constructor parameter number being checked
8535
8536
    // for each parameter to the constructor call, check to see if the right type is passed or convert them
8537
    // to the right type if possible (and allowed).
8538
    // for structure constructors, just check if the right type is passed, no conversion is allowed.
8539
8540
0
    for (TIntermSequence::iterator p = sequenceVector.begin();
8541
0
        p != sequenceVector.end(); p++, paramCount++) {
8542
0
        if (type.isArray())
8543
0
            newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
8544
0
        else if (op == EOpConstructStruct)
8545
0
            newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
8546
0
        else
8547
0
            newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
8548
8549
0
        if (newNode)
8550
0
            *p = newNode;
8551
0
        else
8552
0
            return nullptr;
8553
0
    }
8554
8555
0
    TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
8556
8557
0
    return constructor;
8558
0
}
8559
8560
// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
8561
// for the parameter to the constructor (passed to this function). Essentially, it converts
8562
// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
8563
// float, then float is converted to int.
8564
//
8565
// Returns nullptr for an error or the constructed node.
8566
//
8567
TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node,
8568
                                                 const TSourceLoc& loc, bool subset)
8569
8
{
8570
8
    TIntermTyped* newNode;
8571
8
    TOperator basicOp;
8572
8573
    //
8574
    // First, convert types as needed.
8575
    //
8576
8
    switch (op) {
8577
0
    case EOpConstructF16Vec2:
8578
0
    case EOpConstructF16Vec3:
8579
0
    case EOpConstructF16Vec4:
8580
0
    case EOpConstructF16Mat2x2:
8581
0
    case EOpConstructF16Mat2x3:
8582
0
    case EOpConstructF16Mat2x4:
8583
0
    case EOpConstructF16Mat3x2:
8584
0
    case EOpConstructF16Mat3x3:
8585
0
    case EOpConstructF16Mat3x4:
8586
0
    case EOpConstructF16Mat4x2:
8587
0
    case EOpConstructF16Mat4x3:
8588
0
    case EOpConstructF16Mat4x4:
8589
0
    case EOpConstructFloat16:
8590
0
        basicOp = EOpConstructFloat16;
8591
0
        break;
8592
8593
0
    case EOpConstructVec2:
8594
0
    case EOpConstructVec3:
8595
0
    case EOpConstructVec4:
8596
0
    case EOpConstructMat2x2:
8597
0
    case EOpConstructMat2x3:
8598
0
    case EOpConstructMat2x4:
8599
0
    case EOpConstructMat3x2:
8600
0
    case EOpConstructMat3x3:
8601
0
    case EOpConstructMat3x4:
8602
0
    case EOpConstructMat4x2:
8603
0
    case EOpConstructMat4x3:
8604
0
    case EOpConstructMat4x4:
8605
0
    case EOpConstructFloat:
8606
0
        basicOp = EOpConstructFloat;
8607
0
        break;
8608
8609
0
    case EOpConstructDVec2:
8610
0
    case EOpConstructDVec3:
8611
0
    case EOpConstructDVec4:
8612
0
    case EOpConstructDMat2x2:
8613
0
    case EOpConstructDMat2x3:
8614
0
    case EOpConstructDMat2x4:
8615
0
    case EOpConstructDMat3x2:
8616
0
    case EOpConstructDMat3x3:
8617
0
    case EOpConstructDMat3x4:
8618
0
    case EOpConstructDMat4x2:
8619
0
    case EOpConstructDMat4x3:
8620
0
    case EOpConstructDMat4x4:
8621
0
    case EOpConstructDouble:
8622
0
        basicOp = EOpConstructDouble;
8623
0
        break;
8624
8625
0
    case EOpConstructI16Vec2:
8626
0
    case EOpConstructI16Vec3:
8627
0
    case EOpConstructI16Vec4:
8628
0
    case EOpConstructInt16:
8629
0
        basicOp = EOpConstructInt16;
8630
0
        break;
8631
8632
4
    case EOpConstructIVec2:
8633
4
    case EOpConstructIVec3:
8634
4
    case EOpConstructIVec4:
8635
4
    case EOpConstructIMat2x2:
8636
4
    case EOpConstructIMat2x3:
8637
4
    case EOpConstructIMat2x4:
8638
4
    case EOpConstructIMat3x2:
8639
4
    case EOpConstructIMat3x3:
8640
4
    case EOpConstructIMat3x4:
8641
4
    case EOpConstructIMat4x2:
8642
4
    case EOpConstructIMat4x3:
8643
4
    case EOpConstructIMat4x4:
8644
8
    case EOpConstructInt:
8645
8
        basicOp = EOpConstructInt;
8646
8
        break;
8647
8648
0
    case EOpConstructU16Vec2:
8649
0
    case EOpConstructU16Vec3:
8650
0
    case EOpConstructU16Vec4:
8651
0
    case EOpConstructUint16:
8652
0
        basicOp = EOpConstructUint16;
8653
0
        break;
8654
8655
0
    case EOpConstructUVec2:
8656
0
    case EOpConstructUVec3:
8657
0
    case EOpConstructUVec4:
8658
0
    case EOpConstructUMat2x2:
8659
0
    case EOpConstructUMat2x3:
8660
0
    case EOpConstructUMat2x4:
8661
0
    case EOpConstructUMat3x2:
8662
0
    case EOpConstructUMat3x3:
8663
0
    case EOpConstructUMat3x4:
8664
0
    case EOpConstructUMat4x2:
8665
0
    case EOpConstructUMat4x3:
8666
0
    case EOpConstructUMat4x4:
8667
0
    case EOpConstructUint:
8668
0
        basicOp = EOpConstructUint;
8669
0
        break;
8670
8671
0
    case EOpConstructBVec2:
8672
0
    case EOpConstructBVec3:
8673
0
    case EOpConstructBVec4:
8674
0
    case EOpConstructBMat2x2:
8675
0
    case EOpConstructBMat2x3:
8676
0
    case EOpConstructBMat2x4:
8677
0
    case EOpConstructBMat3x2:
8678
0
    case EOpConstructBMat3x3:
8679
0
    case EOpConstructBMat3x4:
8680
0
    case EOpConstructBMat4x2:
8681
0
    case EOpConstructBMat4x3:
8682
0
    case EOpConstructBMat4x4:
8683
0
    case EOpConstructBool:
8684
0
        basicOp = EOpConstructBool;
8685
0
        break;
8686
8687
0
    default:
8688
0
        error(loc, "unsupported construction", "", "");
8689
8690
0
        return nullptr;
8691
8
    }
8692
8
    newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
8693
8
    if (newNode == nullptr) {
8694
0
        error(loc, "can't convert", "constructor", "");
8695
0
        return nullptr;
8696
0
    }
8697
8698
    //
8699
    // Now, if there still isn't an operation to do the construction, and we need one, add one.
8700
    //
8701
8702
    // Otherwise, skip out early.
8703
8
    if (subset || (newNode != node && newNode->getType() == type))
8704
0
        return newNode;
8705
8706
    // setAggregateOperator will insert a new node for the constructor, as needed.
8707
8
    return intermediate.setAggregateOperator(newNode, op, type, loc);
8708
8
}
8709
8710
// Convert the array in node to the requested type, which is also an array.
8711
// Returns nullptr on failure, otherwise returns aggregate holding the list of
8712
// elements needed to construct the array.
8713
TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
8714
0
{
8715
0
    assert(node->isArray() && type.isArray());
8716
0
    if (node->getType().computeNumComponents() < type.computeNumComponents())
8717
0
        return nullptr;
8718
8719
    // TODO: write an argument replicator, for the case the argument should not be
8720
    // executed multiple times, yet multiple copies are needed.
8721
8722
0
    TIntermTyped* constructee = node->getAsTyped();
8723
    // track where we are in consuming the argument
8724
0
    int constructeeElement = 0;
8725
0
    int constructeeComponent = 0;
8726
8727
    // bump up to the next component to consume
8728
0
    const auto getNextComponent = [&]() {
8729
0
        TIntermTyped* component;
8730
0
        component = handleBracketDereference(node->getLoc(), constructee,
8731
0
                                             intermediate.addConstantUnion(constructeeElement, node->getLoc()));
8732
0
        if (component->isVector())
8733
0
            component = handleBracketDereference(node->getLoc(), component,
8734
0
                                                 intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
8735
        // bump component pointer up
8736
0
        ++constructeeComponent;
8737
0
        if (constructeeComponent == constructee->getVectorSize()) {
8738
0
            constructeeComponent = 0;
8739
0
            ++constructeeElement;
8740
0
        }
8741
0
        return component;
8742
0
    };
8743
8744
    // make one subnode per constructed array element
8745
0
    TIntermAggregate* constructor = nullptr;
8746
0
    TType derefType(type, 0);
8747
0
    TType speculativeComponentType(derefType, 0);
8748
0
    TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
8749
0
    TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
8750
0
    TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
8751
0
    for (int e = 0; e < type.getOuterArraySize(); ++e) {
8752
        // construct an element
8753
0
        TIntermTyped* elementArg;
8754
0
        if (type.getVectorSize() == constructee->getVectorSize()) {
8755
            // same element shape
8756
0
            elementArg = handleBracketDereference(node->getLoc(), constructee,
8757
0
                                                  intermediate.addConstantUnion(e, node->getLoc()));
8758
0
        } else {
8759
            // mismatched element shapes
8760
0
            if (type.getVectorSize() == 1)
8761
0
                elementArg = getNextComponent();
8762
0
            else {
8763
                // make a vector
8764
0
                TIntermAggregate* elementConstructee = nullptr;
8765
0
                for (int c = 0; c < type.getVectorSize(); ++c)
8766
0
                    elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
8767
0
                elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
8768
0
            }
8769
0
        }
8770
        // convert basic types
8771
0
        elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
8772
0
        if (elementArg == nullptr)
8773
0
            return nullptr;
8774
        // combine with top-level constructor
8775
0
        constructor = intermediate.growAggregate(constructor, elementArg);
8776
0
    }
8777
8778
0
    return constructor;
8779
0
}
8780
8781
// This function tests for the type of the parameters to the structure or array constructor. Raises
8782
// an error message if the expected type does not match the parameter passed to the constructor.
8783
//
8784
// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
8785
//
8786
TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
8787
                                                   const TSourceLoc& loc)
8788
0
{
8789
    // Handle cases that map more 1:1 between constructor arguments and constructed.
8790
0
    TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
8791
0
    if (converted == nullptr || converted->getType() != type) {
8792
0
        error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
8793
0
            node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
8794
8795
0
        return nullptr;
8796
0
    }
8797
8798
0
    return converted;
8799
0
}
8800
8801
//
8802
// Do everything needed to add an interface block.
8803
//
8804
void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName)
8805
0
{
8806
0
    assert(type.getWritableStruct() != nullptr);
8807
8808
    // Clean up top-level decorations that don't belong.
8809
0
    switch (type.getQualifier().storage) {
8810
0
    case EvqUniform:
8811
0
    case EvqBuffer:
8812
0
        correctUniform(type.getQualifier());
8813
0
        break;
8814
0
    case EvqVaryingIn:
8815
0
        correctInput(type.getQualifier());
8816
0
        break;
8817
0
    case EvqVaryingOut:
8818
0
        correctOutput(type.getQualifier());
8819
0
        break;
8820
0
    default:
8821
0
        break;
8822
0
    }
8823
8824
0
    TTypeList& typeList = *type.getWritableStruct();
8825
    // fix and check for member storage qualifiers and types that don't belong within a block
8826
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8827
0
        TType& memberType = *typeList[member].type;
8828
0
        TQualifier& memberQualifier = memberType.getQualifier();
8829
0
        const TSourceLoc& memberLoc = typeList[member].loc;
8830
0
        globalQualifierFix(memberLoc, memberQualifier);
8831
0
        memberQualifier.storage = type.getQualifier().storage;
8832
8833
0
        if (memberType.isStruct()) {
8834
            // clean up and pick up the right set of decorations
8835
0
            auto it = ioTypeMap.find(memberType.getStruct());
8836
0
            switch (type.getQualifier().storage) {
8837
0
            case EvqUniform:
8838
0
            case EvqBuffer:
8839
0
                correctUniform(type.getQualifier());
8840
0
                if (it != ioTypeMap.end() && it->second.uniform)
8841
0
                    memberType.setStruct(it->second.uniform);
8842
0
                break;
8843
0
            case EvqVaryingIn:
8844
0
                correctInput(type.getQualifier());
8845
0
                if (it != ioTypeMap.end() && it->second.input)
8846
0
                    memberType.setStruct(it->second.input);
8847
0
                break;
8848
0
            case EvqVaryingOut:
8849
0
                correctOutput(type.getQualifier());
8850
0
                if (it != ioTypeMap.end() && it->second.output)
8851
0
                    memberType.setStruct(it->second.output);
8852
0
                break;
8853
0
            default:
8854
0
                break;
8855
0
            }
8856
0
        }
8857
0
    }
8858
8859
    // Make default block qualification, and adjust the member qualifications
8860
8861
0
    TQualifier defaultQualification;
8862
0
    switch (type.getQualifier().storage) {
8863
0
    case EvqUniform:    defaultQualification = globalUniformDefaults;    break;
8864
0
    case EvqBuffer:     defaultQualification = globalBufferDefaults;     break;
8865
0
    case EvqVaryingIn:  defaultQualification = globalInputDefaults;      break;
8866
0
    case EvqVaryingOut: defaultQualification = globalOutputDefaults;     break;
8867
0
    default:            defaultQualification.clear();                    break;
8868
0
    }
8869
8870
    // Special case for "push_constant uniform", which has a default of std430,
8871
    // contrary to normal uniform defaults, and can't have a default tracked for it.
8872
0
    if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
8873
0
        type.getQualifier().layoutPacking = ElpStd430;
8874
8875
    // fix and check for member layout qualifiers
8876
8877
0
    mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
8878
8879
0
    bool memberWithLocation = false;
8880
0
    bool memberWithoutLocation = false;
8881
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8882
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
8883
0
        const TSourceLoc& memberLoc = typeList[member].loc;
8884
0
        if (memberQualifier.hasStream()) {
8885
0
            if (defaultQualification.layoutStream != memberQualifier.layoutStream)
8886
0
                error(memberLoc, "member cannot contradict block", "stream", "");
8887
0
        }
8888
8889
        // "This includes a block's inheritance of the
8890
        // current global default buffer, a block member's inheritance of the block's
8891
        // buffer, and the requirement that any *xfb_buffer* declared on a block
8892
        // member must match the buffer inherited from the block."
8893
0
        if (memberQualifier.hasXfbBuffer()) {
8894
0
            if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
8895
0
                error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
8896
0
        }
8897
8898
0
        if (memberQualifier.hasLocation()) {
8899
0
            switch (type.getQualifier().storage) {
8900
0
            case EvqVaryingIn:
8901
0
            case EvqVaryingOut:
8902
0
                memberWithLocation = true;
8903
0
                break;
8904
0
            default:
8905
0
                break;
8906
0
            }
8907
0
        } else
8908
0
            memberWithoutLocation = true;
8909
8910
0
        TQualifier newMemberQualification = defaultQualification;
8911
0
        mergeQualifiers(newMemberQualification, memberQualifier);
8912
0
        memberQualifier = newMemberQualification;
8913
0
    }
8914
8915
    // Process the members
8916
0
    fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
8917
0
    fixXfbOffsets(type.getQualifier(), typeList);
8918
0
    fixBlockUniformOffsets(type.getQualifier(), typeList);
8919
8920
    // reverse merge, so that currentBlockQualifier now has all layout information
8921
    // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
8922
0
    mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
8923
8924
    //
8925
    // Build and add the interface block as a new type named 'blockName'
8926
    //
8927
8928
    // Use the instance name as the interface name if one exists, else the block name.
8929
0
    const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
8930
8931
0
    TType blockType(&typeList, interfaceName, type.getQualifier());
8932
0
    if (type.isArray())
8933
0
        blockType.transferArraySizes(type.getArraySizes());
8934
8935
    // Add the variable, as anonymous or named instanceName.
8936
    // Make an anonymous variable if no name was provided.
8937
0
    if (instanceName == nullptr)
8938
0
        instanceName = NewPoolTString("");
8939
8940
0
    TVariable& variable = *new TVariable(instanceName, blockType);
8941
0
    if (! symbolTable.insert(variable)) {
8942
0
        if (*instanceName == "")
8943
0
            error(loc, "nameless block contains a member that already has a name at global scope",
8944
0
                  "" /* blockName->c_str() */, "");
8945
0
        else
8946
0
            error(loc, "block instance name redefinition", variable.getName().c_str(), "");
8947
8948
0
        return;
8949
0
    }
8950
8951
    // Save it in the AST for linker use.
8952
0
    if (symbolTable.atGlobalLevel())
8953
0
        trackLinkage(variable);
8954
0
}
8955
8956
//
8957
// "For a block, this process applies to the entire block, or until the first member
8958
// is reached that has a location layout qualifier. When a block member is declared with a location
8959
// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
8960
// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
8961
// until the next member declared with a location qualifier. The values used for locations do not have to be
8962
// declared in increasing order."
8963
void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
8964
0
{
8965
    // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
8966
    // have a location layout qualifier, or a compile-time error results."
8967
0
    if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
8968
0
        error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
8969
0
    else {
8970
0
        if (memberWithLocation) {
8971
            // remove any block-level location and make it per *every* member
8972
0
            int nextLocation = 0;  // by the rule above, initial value is not relevant
8973
0
            if (qualifier.hasAnyLocation()) {
8974
0
                nextLocation = qualifier.layoutLocation;
8975
0
                qualifier.layoutLocation = TQualifier::layoutLocationEnd;
8976
0
                if (qualifier.hasComponent()) {
8977
                    // "It is a compile-time error to apply the *component* qualifier to a ... block"
8978
0
                    error(loc, "cannot apply to a block", "component", "");
8979
0
                }
8980
0
                if (qualifier.hasIndex()) {
8981
0
                    error(loc, "cannot apply to a block", "index", "");
8982
0
                }
8983
0
            }
8984
0
            for (unsigned int member = 0; member < typeList.size(); ++member) {
8985
0
                TQualifier& memberQualifier = typeList[member].type->getQualifier();
8986
0
                const TSourceLoc& memberLoc = typeList[member].loc;
8987
0
                if (! memberQualifier.hasLocation()) {
8988
0
                    if (nextLocation >= (int)TQualifier::layoutLocationEnd)
8989
0
                        error(memberLoc, "location is too large", "location", "");
8990
0
                    memberQualifier.layoutLocation = nextLocation;
8991
0
                    memberQualifier.layoutComponent = 0;
8992
0
                }
8993
0
                nextLocation = memberQualifier.layoutLocation +
8994
0
                               intermediate.computeTypeLocationSize(*typeList[member].type, language);
8995
0
            }
8996
0
        }
8997
0
    }
8998
0
}
8999
9000
void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
9001
0
{
9002
    // "If a block is qualified with xfb_offset, all its
9003
    // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
9004
    // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
9005
    // offsets."
9006
9007
0
    if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
9008
0
        return;
9009
9010
0
    int nextOffset = qualifier.layoutXfbOffset;
9011
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
9012
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
9013
0
        bool contains64BitType = false;
9014
0
        bool contains32BitType = false;
9015
0
        bool contains16BitType = false;
9016
0
        int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType);
9017
        // see if we need to auto-assign an offset to this member
9018
0
        if (! memberQualifier.hasXfbOffset()) {
9019
            // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8"
9020
0
            if (contains64BitType)
9021
0
                RoundToPow2(nextOffset, 8);
9022
0
            else if (contains32BitType)
9023
0
                RoundToPow2(nextOffset, 4);
9024
            // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2"
9025
0
            else if (contains16BitType)
9026
0
                RoundToPow2(nextOffset, 2);
9027
0
            memberQualifier.layoutXfbOffset = nextOffset;
9028
0
        } else
9029
0
            nextOffset = memberQualifier.layoutXfbOffset;
9030
0
        nextOffset += memberSize;
9031
0
    }
9032
9033
    // The above gave all block members an offset, so we can take it off the block now,
9034
    // which will avoid double counting the offset usage.
9035
0
    qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
9036
0
}
9037
9038
// Calculate and save the offset of each block member, using the recursively
9039
// defined block offset rules and the user-provided offset and align.
9040
//
9041
// Also, compute and save the total size of the block. For the block's size, arrayness
9042
// is not taken into account, as each element is backed by a separate buffer.
9043
//
9044
void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
9045
0
{
9046
0
    if (! qualifier.isUniformOrBuffer())
9047
0
        return;
9048
0
    if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
9049
0
        return;
9050
9051
0
    int offset = 0;
9052
0
    int memberSize;
9053
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
9054
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
9055
0
        const TSourceLoc& memberLoc = typeList[member].loc;
9056
9057
        // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
9058
9059
        // modify just the children's view of matrix layout, if there is one for this member
9060
0
        TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
9061
0
        int dummyStride;
9062
0
        int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride,
9063
0
                                                              qualifier.layoutPacking,
9064
0
                                                              subMatrixLayout != ElmNone
9065
0
                                                                  ? subMatrixLayout == ElmRowMajor
9066
0
                                                                  : qualifier.layoutMatrix == ElmRowMajor);
9067
0
        if (memberQualifier.hasOffset()) {
9068
            // "The specified offset must be a multiple
9069
            // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
9070
0
            if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
9071
0
                error(memberLoc, "must be a multiple of the member's alignment", "offset",
9072
0
                    "(layout offset = %d | member alignment = %d)", memberQualifier.layoutOffset, memberAlignment);
9073
9074
            // "The offset qualifier forces the qualified member to start at or after the specified
9075
            // integral-constant expression, which will be its byte offset from the beginning of the buffer.
9076
            // "The actual offset of a member is computed as
9077
            // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
9078
0
            offset = std::max(offset, memberQualifier.layoutOffset);
9079
0
        }
9080
9081
        // "The actual alignment of a member will be the greater of the specified align alignment and the standard
9082
        // (e.g., std140) base alignment for the member's type."
9083
0
        if (memberQualifier.hasAlign())
9084
0
            memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
9085
9086
        // "If the resulting offset is not a multiple of the actual alignment,
9087
        // increase it to the first offset that is a multiple of
9088
        // the actual alignment."
9089
0
        RoundToPow2(offset, memberAlignment);
9090
0
        typeList[member].type->getQualifier().layoutOffset = offset;
9091
0
        offset += memberSize;
9092
0
    }
9093
0
}
9094
9095
// For an identifier that is already declared, add more qualification to it.
9096
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
9097
0
{
9098
0
    TSymbol* symbol = symbolTable.find(identifier);
9099
0
    if (symbol == nullptr) {
9100
0
        error(loc, "identifier not previously declared", identifier.c_str(), "");
9101
0
        return;
9102
0
    }
9103
0
    if (symbol->getAsFunction()) {
9104
0
        error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
9105
0
        return;
9106
0
    }
9107
9108
0
    if (qualifier.isAuxiliary() ||
9109
0
        qualifier.isMemory() ||
9110
0
        qualifier.isInterpolation() ||
9111
0
        qualifier.hasLayout() ||
9112
0
        qualifier.storage != EvqTemporary ||
9113
0
        qualifier.precision != EpqNone) {
9114
0
        error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
9115
0
        return;
9116
0
    }
9117
9118
    // For read-only built-ins, add a new symbol for holding the modified qualifier.
9119
    // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
9120
0
    if (symbol->isReadOnly())
9121
0
        symbol = symbolTable.copyUp(symbol);
9122
9123
0
    if (qualifier.invariant) {
9124
0
        if (intermediate.inIoAccessed(identifier))
9125
0
            error(loc, "cannot change qualification after use", "invariant", "");
9126
0
        symbol->getWritableType().getQualifier().invariant = true;
9127
0
    } else if (qualifier.noContraction) {
9128
0
        if (intermediate.inIoAccessed(identifier))
9129
0
            error(loc, "cannot change qualification after use", "precise", "");
9130
0
        symbol->getWritableType().getQualifier().noContraction = true;
9131
0
    } else if (qualifier.specConstant) {
9132
0
        symbol->getWritableType().getQualifier().makeSpecConstant();
9133
0
        if (qualifier.hasSpecConstantId())
9134
0
            symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
9135
0
    } else
9136
0
        warn(loc, "unknown requalification", "", "");
9137
0
}
9138
9139
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
9140
0
{
9141
0
    for (unsigned int i = 0; i < identifiers.size(); ++i)
9142
0
        addQualifierToExisting(loc, qualifier, *identifiers[i]);
9143
0
}
9144
9145
//
9146
// Update the intermediate for the given input geometry
9147
//
9148
bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9149
1
{
9150
    // these can be declared on non-entry-points, in which case they lose their meaning
9151
1
    if (! parsingEntrypointParameters)
9152
1
        return true;
9153
9154
0
    switch (geometry) {
9155
0
    case ElgPoints:             // fall through
9156
0
    case ElgLines:              // ...
9157
0
    case ElgTriangles:          // ...
9158
0
    case ElgLinesAdjacency:     // ...
9159
0
    case ElgTrianglesAdjacency: // ...
9160
0
        if (! intermediate.setInputPrimitive(geometry)) {
9161
0
            error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9162
0
            return false;
9163
0
        }
9164
0
        break;
9165
9166
0
    default:
9167
0
        error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
9168
0
        return false;
9169
0
    }
9170
9171
0
    return true;
9172
0
}
9173
9174
//
9175
// Update the intermediate for the given output geometry
9176
//
9177
bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9178
0
{
9179
    // If this is not a geometry shader, ignore.  It might be a mixed shader including several stages.
9180
    // Since that's an OK situation, return true for success.
9181
0
    if (language != EShLangGeometry)
9182
0
        return true;
9183
9184
    // these can be declared on non-entry-points, in which case they lose their meaning
9185
0
    if (! parsingEntrypointParameters)
9186
0
        return true;
9187
9188
0
    switch (geometry) {
9189
0
    case ElgPoints:
9190
0
    case ElgLineStrip:
9191
0
    case ElgTriangleStrip:
9192
0
        if (! intermediate.setOutputPrimitive(geometry)) {
9193
0
            error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9194
0
            return false;
9195
0
        }
9196
0
        break;
9197
0
    default:
9198
0
        error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
9199
0
        return false;
9200
0
    }
9201
9202
0
    return true;
9203
0
}
9204
9205
//
9206
// Selection attributes
9207
//
9208
void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
9209
    const TAttributes& attributes)
9210
0
{
9211
0
    if (selection == nullptr)
9212
0
        return;
9213
9214
0
    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 selection", "", "");
9224
0
            break;
9225
0
        }
9226
0
    }
9227
0
}
9228
9229
//
9230
// Switch attributes
9231
//
9232
void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
9233
    const TAttributes& attributes)
9234
0
{
9235
0
    if (selection == nullptr)
9236
0
        return;
9237
9238
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9239
0
        switch (it->name) {
9240
0
        case EatFlatten:
9241
0
            selection->setFlatten();
9242
0
            break;
9243
0
        case EatBranch:
9244
0
            selection->setDontFlatten();
9245
0
            break;
9246
0
        default:
9247
0
            warn(loc, "attribute does not apply to a switch", "", "");
9248
0
            break;
9249
0
        }
9250
0
    }
9251
0
}
9252
9253
//
9254
// Loop attributes
9255
//
9256
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
9257
    const TAttributes& attributes)
9258
0
{
9259
0
    if (loop == nullptr)
9260
0
        return;
9261
9262
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9263
0
        switch (it->name) {
9264
0
        case EatUnroll:
9265
0
            loop->setUnroll();
9266
0
            break;
9267
0
        case EatLoop:
9268
0
            loop->setDontUnroll();
9269
0
            break;
9270
0
        default:
9271
0
            warn(loc, "attribute does not apply to a loop", "", "");
9272
0
            break;
9273
0
        }
9274
0
    }
9275
0
}
9276
9277
//
9278
// Updating default qualifier for the case of a declaration with just a qualifier,
9279
// no type, block, or identifier.
9280
//
9281
void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
9282
0
{
9283
0
    if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
9284
0
        assert(language == EShLangTessControl || language == EShLangGeometry);
9285
        // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
9286
0
    }
9287
0
    if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
9288
0
        if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
9289
0
            error(loc, "cannot change previously set layout value", "invocations", "");
9290
0
    }
9291
0
    if (publicType.shaderQualifiers.geometry != ElgNone) {
9292
0
        if (publicType.qualifier.storage == EvqVaryingIn) {
9293
0
            switch (publicType.shaderQualifiers.geometry) {
9294
0
            case ElgPoints:
9295
0
            case ElgLines:
9296
0
            case ElgLinesAdjacency:
9297
0
            case ElgTriangles:
9298
0
            case ElgTrianglesAdjacency:
9299
0
            case ElgQuads:
9300
0
            case ElgIsolines:
9301
0
                break;
9302
0
            default:
9303
0
                error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9304
0
                      "");
9305
0
            }
9306
0
        } else if (publicType.qualifier.storage == EvqVaryingOut) {
9307
0
            handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
9308
0
        } else
9309
0
            error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9310
0
                  GetStorageQualifierString(publicType.qualifier.storage));
9311
0
    }
9312
0
    if (publicType.shaderQualifiers.spacing != EvsNone)
9313
0
        intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
9314
0
    if (publicType.shaderQualifiers.order != EvoNone)
9315
0
        intermediate.setVertexOrder(publicType.shaderQualifiers.order);
9316
0
    if (publicType.shaderQualifiers.pointMode)
9317
0
        intermediate.setPointMode();
9318
0
    for (int i = 0; i < 3; ++i) {
9319
0
        if (publicType.shaderQualifiers.localSize[i] > 1) {
9320
0
            int max = 0;
9321
0
            switch (i) {
9322
0
            case 0: max = resources.maxComputeWorkGroupSizeX; break;
9323
0
            case 1: max = resources.maxComputeWorkGroupSizeY; break;
9324
0
            case 2: max = resources.maxComputeWorkGroupSizeZ; break;
9325
0
            default: break;
9326
0
            }
9327
0
            if (intermediate.getLocalSize(i) > (unsigned int)max)
9328
0
                error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
9329
9330
            // Fix the existing constant gl_WorkGroupSize with this new information.
9331
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9332
0
            workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
9333
0
        }
9334
0
        if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
9335
0
            intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
9336
            // Set the workgroup built-in variable as a specialization constant
9337
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9338
0
            workGroupSize->getWritableType().getQualifier().specConstant = true;
9339
0
        }
9340
0
    }
9341
0
    if (publicType.shaderQualifiers.earlyFragmentTests)
9342
0
        intermediate.setEarlyFragmentTests();
9343
9344
0
    const TQualifier& qualifier = publicType.qualifier;
9345
9346
0
    switch (qualifier.storage) {
9347
0
    case EvqUniform:
9348
0
        if (qualifier.hasMatrix())
9349
0
            globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
9350
0
        if (qualifier.hasPacking())
9351
0
            globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
9352
0
        break;
9353
0
    case EvqBuffer:
9354
0
        if (qualifier.hasMatrix())
9355
0
            globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
9356
0
        if (qualifier.hasPacking())
9357
0
            globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
9358
0
        break;
9359
0
    case EvqVaryingIn:
9360
0
        break;
9361
0
    case EvqVaryingOut:
9362
0
        if (qualifier.hasStream())
9363
0
            globalOutputDefaults.layoutStream = qualifier.layoutStream;
9364
0
        if (qualifier.hasXfbBuffer())
9365
0
            globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
9366
0
        if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
9367
0
            if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
9368
0
                error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d",
9369
0
                      qualifier.layoutXfbBuffer);
9370
0
        }
9371
0
        break;
9372
0
    default:
9373
0
        error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
9374
0
        return;
9375
0
    }
9376
0
}
9377
9378
//
9379
// Take the sequence of statements that has been built up since the last case/default,
9380
// put it on the list of top-level nodes for the current (inner-most) switch statement,
9381
// and follow that by the case/default we are on now.  (See switch topology comment on
9382
// TIntermSwitch.)
9383
//
9384
void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
9385
0
{
9386
0
    TIntermSequence* switchSequence = switchSequenceStack.back();
9387
9388
0
    if (statements) {
9389
0
        statements->setOperator(EOpSequence);
9390
0
        switchSequence->push_back(statements);
9391
0
    }
9392
0
    if (branchNode) {
9393
        // check all previous cases for the same label (or both are 'default')
9394
0
        for (unsigned int s = 0; s < switchSequence->size(); ++s) {
9395
0
            TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
9396
0
            if (prevBranch) {
9397
0
                TIntermTyped* prevExpression = prevBranch->getExpression();
9398
0
                TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
9399
0
                if (prevExpression == nullptr && newExpression == nullptr)
9400
0
                    error(branchNode->getLoc(), "duplicate label", "default", "");
9401
0
                else if (prevExpression != nullptr &&
9402
0
                    newExpression != nullptr &&
9403
0
                    prevExpression->getAsConstantUnion() &&
9404
0
                    newExpression->getAsConstantUnion() &&
9405
0
                    prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
9406
0
                    newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
9407
0
                    error(branchNode->getLoc(), "duplicated value", "case", "");
9408
0
            }
9409
0
        }
9410
0
        switchSequence->push_back(branchNode);
9411
0
    }
9412
0
}
9413
9414
//
9415
// Turn the top-level node sequence built up of wrapupSwitchSubsequence
9416
// into a switch node.
9417
//
9418
TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
9419
                                         TIntermAggregate* lastStatements, const TAttributes& attributes)
9420
0
{
9421
0
    wrapupSwitchSubsequence(lastStatements, nullptr);
9422
9423
0
    if (expression == nullptr ||
9424
0
        (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
9425
0
        expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
9426
0
        error(loc, "condition must be a scalar integer expression", "switch", "");
9427
9428
    // If there is nothing to do, drop the switch but still execute the expression
9429
0
    TIntermSequence* switchSequence = switchSequenceStack.back();
9430
0
    if (switchSequence->size() == 0)
9431
0
        return expression;
9432
9433
0
    if (lastStatements == nullptr) {
9434
        // emulate a break for error recovery
9435
0
        lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
9436
0
        lastStatements->setOperator(EOpSequence);
9437
0
        switchSequence->push_back(lastStatements);
9438
0
    }
9439
9440
0
    TIntermAggregate* body = new TIntermAggregate(EOpSequence);
9441
0
    body->getSequence() = *switchSequenceStack.back();
9442
0
    body->setLoc(loc);
9443
9444
0
    TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
9445
0
    switchNode->setLoc(loc);
9446
0
    handleSwitchAttributes(loc, switchNode, attributes);
9447
9448
0
    return switchNode;
9449
0
}
9450
9451
// Make a new symbol-table level that is made out of the members of a structure.
9452
// This should be done as an anonymous struct (name is "") so that the symbol table
9453
// finds the members with no explicit reference to a 'this' variable.
9454
void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators)
9455
0
{
9456
    // member variables
9457
0
    TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
9458
0
    symbolTable.pushThis(thisVariable);
9459
9460
    // member functions
9461
0
    for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) {
9462
        // member should have a prefix matching currentTypePrefix.back()
9463
        // but, symbol lookup within the class scope will just use the
9464
        // unprefixed name. Hence, there are two: one fully prefixed and
9465
        // one with no prefix.
9466
0
        TFunction& member = *it->function->clone();
9467
0
        member.removePrefix(currentTypePrefix.back());
9468
0
        symbolTable.insert(member);
9469
0
    }
9470
0
}
9471
9472
// Track levels of class/struct/namespace nesting with a prefix string using
9473
// the type names separated by the scoping operator. E.g., two levels
9474
// would look like:
9475
//
9476
//   outer::inner
9477
//
9478
// The string is empty when at normal global level.
9479
//
9480
void HlslParseContext::pushNamespace(const TString& typeName)
9481
0
{
9482
    // make new type prefix
9483
0
    TString newPrefix;
9484
0
    if (currentTypePrefix.size() > 0)
9485
0
        newPrefix = currentTypePrefix.back();
9486
0
    newPrefix.append(typeName);
9487
0
    newPrefix.append(scopeMangler);
9488
0
    currentTypePrefix.push_back(newPrefix);
9489
0
}
9490
9491
// Opposite of pushNamespace(), see above
9492
void HlslParseContext::popNamespace()
9493
0
{
9494
0
    currentTypePrefix.pop_back();
9495
0
}
9496
9497
// Use the class/struct nesting string to create a global name for
9498
// a member of a class/struct.
9499
void HlslParseContext::getFullNamespaceName(TString*& name) const
9500
448k
{
9501
448k
    if (currentTypePrefix.size() == 0)
9502
448k
        return;
9503
9504
0
    TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
9505
0
    fullName->append(*name);
9506
0
    name = fullName;
9507
0
}
9508
9509
// Helper function to add the namespace scope mangling syntax to a string.
9510
void HlslParseContext::addScopeMangler(TString& name)
9511
0
{
9512
0
    name.append(scopeMangler);
9513
0
}
9514
9515
// Return true if this has uniform-interface like decorations.
9516
bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
9517
0
{
9518
0
    return qualifier.hasUniformLayout() ||
9519
0
           qualifier.layoutPushConstant;
9520
0
}
9521
9522
// Potentially not the opposite of hasUniform(), as if some characteristic is
9523
// ever used for more than one thing (e.g., uniform or input), hasUniform() should
9524
// say it exists, but clearUniform() should leave it in place.
9525
void HlslParseContext::clearUniform(TQualifier& qualifier)
9526
6
{
9527
6
    qualifier.clearUniformLayout();
9528
6
    qualifier.layoutPushConstant = false;
9529
6
}
9530
9531
// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
9532
bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
9533
0
{
9534
0
    switch (qualifier.builtIn) {
9535
0
    case EbvPosition:
9536
0
    case EbvPointSize:
9537
0
        return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
9538
0
    case EbvClipDistance:
9539
0
    case EbvCullDistance:
9540
0
        return language != EShLangVertex && language != EShLangCompute;
9541
0
    case EbvFragCoord:
9542
0
    case EbvFace:
9543
0
    case EbvHelperInvocation:
9544
0
    case EbvLayer:
9545
0
    case EbvPointCoord:
9546
0
    case EbvSampleId:
9547
0
    case EbvSampleMask:
9548
0
    case EbvSamplePosition:
9549
0
    case EbvViewportIndex:
9550
0
        return language == EShLangFragment;
9551
0
    case EbvGlobalInvocationId:
9552
0
    case EbvLocalInvocationIndex:
9553
0
    case EbvLocalInvocationId:
9554
0
    case EbvNumWorkGroups:
9555
0
    case EbvWorkGroupId:
9556
0
    case EbvWorkGroupSize:
9557
0
        return language == EShLangCompute;
9558
0
    case EbvInvocationId:
9559
0
        return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
9560
0
    case EbvPatchVertices:
9561
0
        return language == EShLangTessControl || language == EShLangTessEvaluation;
9562
0
    case EbvInstanceId:
9563
0
    case EbvInstanceIndex:
9564
0
    case EbvVertexId:
9565
0
    case EbvVertexIndex:
9566
0
        return language == EShLangVertex;
9567
0
    case EbvPrimitiveId:
9568
0
        return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl;
9569
0
    case EbvTessLevelInner:
9570
0
    case EbvTessLevelOuter:
9571
0
        return language == EShLangTessEvaluation;
9572
0
    case EbvTessCoord:
9573
0
        return language == EShLangTessEvaluation;
9574
0
    case EbvViewIndex:
9575
0
        return language != EShLangCompute;
9576
0
    default:
9577
0
        return false;
9578
0
    }
9579
0
}
9580
9581
// Return true if there are decorations to preserve for input-like storage.
9582
bool HlslParseContext::hasInput(const TQualifier& qualifier) const
9583
0
{
9584
0
    if (qualifier.hasAnyLocation())
9585
0
        return true;
9586
9587
0
    if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
9588
0
        return true;
9589
9590
0
    if (language == EShLangTessEvaluation && qualifier.patch)
9591
0
        return true;
9592
9593
0
    if (isInputBuiltIn(qualifier))
9594
0
        return true;
9595
9596
0
    return false;
9597
0
}
9598
9599
// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
9600
bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
9601
0
{
9602
0
    switch (qualifier.builtIn) {
9603
0
    case EbvPosition:
9604
0
    case EbvPointSize:
9605
0
    case EbvClipVertex:
9606
0
    case EbvClipDistance:
9607
0
    case EbvCullDistance:
9608
0
        return language != EShLangFragment && language != EShLangCompute;
9609
0
    case EbvFragDepth:
9610
0
    case EbvFragDepthGreater:
9611
0
    case EbvFragDepthLesser:
9612
0
    case EbvSampleMask:
9613
0
        return language == EShLangFragment;
9614
0
    case EbvLayer:
9615
0
    case EbvViewportIndex:
9616
0
        return language == EShLangGeometry || language == EShLangVertex;
9617
0
    case EbvPrimitiveId:
9618
0
        return language == EShLangGeometry;
9619
0
    case EbvTessLevelInner:
9620
0
    case EbvTessLevelOuter:
9621
0
        return language == EShLangTessControl;
9622
0
    default:
9623
0
        return false;
9624
0
    }
9625
0
}
9626
9627
// Return true if there are decorations to preserve for output-like storage.
9628
bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
9629
0
{
9630
0
    if (qualifier.hasAnyLocation())
9631
0
        return true;
9632
9633
0
    if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
9634
0
        return true;
9635
9636
0
    if (language == EShLangTessControl && qualifier.patch)
9637
0
        return true;
9638
9639
0
    if (language == EShLangGeometry && qualifier.hasStream())
9640
0
        return true;
9641
9642
0
    if (isOutputBuiltIn(qualifier))
9643
0
        return true;
9644
9645
0
    return false;
9646
0
}
9647
9648
// Make the IO decorations etc. be appropriate only for an input interface.
9649
void HlslParseContext::correctInput(TQualifier& qualifier)
9650
0
{
9651
0
    clearUniform(qualifier);
9652
0
    if (language == EShLangVertex)
9653
0
        qualifier.clearInterstage();
9654
0
    if (language != EShLangTessEvaluation)
9655
0
        qualifier.patch = false;
9656
0
    if (language != EShLangFragment) {
9657
0
        qualifier.clearInterpolation();
9658
0
        qualifier.sample = false;
9659
0
    }
9660
9661
0
    qualifier.clearStreamLayout();
9662
0
    qualifier.clearXfbLayout();
9663
9664
0
    if (! isInputBuiltIn(qualifier))
9665
0
        qualifier.builtIn = EbvNone;
9666
0
}
9667
9668
// Make the IO decorations etc. be appropriate only for an output interface.
9669
void HlslParseContext::correctOutput(TQualifier& qualifier)
9670
0
{
9671
0
    clearUniform(qualifier);
9672
0
    if (language == EShLangFragment)
9673
0
        qualifier.clearInterstage();
9674
0
    if (language != EShLangGeometry)
9675
0
        qualifier.clearStreamLayout();
9676
0
    if (language == EShLangFragment)
9677
0
        qualifier.clearXfbLayout();
9678
0
    if (language != EShLangTessControl)
9679
0
        qualifier.patch = false;
9680
9681
    // Fixes Test/hlsl.entry-inout.vert (SV_Position will not become a varying).
9682
0
    if (qualifier.builtIn == EbvNone)
9683
0
        qualifier.builtIn = qualifier.declaredBuiltIn;
9684
9685
0
    switch (qualifier.builtIn) {
9686
0
    case EbvFragDepth:
9687
0
        intermediate.setDepthReplacing();
9688
0
        intermediate.setDepth(EldAny);
9689
0
        break;
9690
0
    case EbvFragDepthGreater:
9691
0
        intermediate.setDepthReplacing();
9692
0
        intermediate.setDepth(EldGreater);
9693
0
        qualifier.builtIn = EbvFragDepth;
9694
0
        break;
9695
0
    case EbvFragDepthLesser:
9696
0
        intermediate.setDepthReplacing();
9697
0
        intermediate.setDepth(EldLess);
9698
0
        qualifier.builtIn = EbvFragDepth;
9699
0
        break;
9700
0
    default:
9701
0
        break;
9702
0
    }
9703
9704
0
    if (! isOutputBuiltIn(qualifier))
9705
0
        qualifier.builtIn = EbvNone;
9706
0
}
9707
9708
// Make the IO decorations etc. be appropriate only for uniform type interfaces.
9709
void HlslParseContext::correctUniform(TQualifier& qualifier)
9710
14
{
9711
14
    if (qualifier.declaredBuiltIn == EbvNone)
9712
14
        qualifier.declaredBuiltIn = qualifier.builtIn;
9713
9714
14
    qualifier.builtIn = EbvNone;
9715
14
    qualifier.clearInterstage();
9716
14
    qualifier.clearInterstageLayout();
9717
14
}
9718
9719
// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
9720
void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
9721
6
{
9722
6
    clearUniform(qualifier);
9723
6
    correctUniform(qualifier);
9724
6
}
9725
9726
9727
// Set texture return type.  Returns success (not all types are valid).
9728
bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc)
9729
91.0k
{
9730
    // Seed the output with an invalid index.  We will set it to a valid one if we can.
9731
91.0k
    sampler.structReturnIndex = TSampler::noReturnStruct;
9732
9733
    // Arrays aren't supported.
9734
91.0k
    if (retType.isArray()) {
9735
0
        error(loc, "Arrays not supported in texture template types", "", "");
9736
0
        return false;
9737
0
    }
9738
9739
    // If return type is a vector, remember the vector size in the sampler, and return.
9740
91.0k
    if (retType.isVector() || retType.isScalar()) {
9741
91.0k
        sampler.vectorSize = retType.getVectorSize();
9742
91.0k
        return true;
9743
91.0k
    }
9744
9745
    // If it wasn't a vector, it must be a struct meeting certain requirements.  The requirements
9746
    // are checked below: just check for struct-ness here.
9747
0
    if (!retType.isStruct()) {
9748
0
        error(loc, "Invalid texture template type", "", "");
9749
0
        return false;
9750
0
    }
9751
9752
    // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
9753
0
    if (sampler.isSubpass()) {
9754
0
        error(loc, "Unimplemented: structure template type in subpass input", "", "");
9755
0
        return false;
9756
0
    }
9757
9758
0
    TTypeList* members = retType.getWritableStruct();
9759
9760
    // Check for too many or not enough structure members.
9761
0
    if (members->size() > 4 || members->size() == 0) {
9762
0
        error(loc, "Invalid member count in texture template structure", "", "");
9763
0
        return false;
9764
0
    }
9765
9766
    // Error checking: We must have <= 4 total components, all of the same basic type.
9767
0
    unsigned totalComponents = 0;
9768
0
    for (unsigned m = 0; m < members->size(); ++m) {
9769
        // Check for bad member types
9770
0
        if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) {
9771
0
            error(loc, "Invalid texture template struct member type", "", "");
9772
0
            return false;
9773
0
        }
9774
9775
0
        const unsigned memberVectorSize = (*members)[m].type->getVectorSize();
9776
0
        totalComponents += memberVectorSize;
9777
9778
        // too many total member components
9779
0
        if (totalComponents > 4) {
9780
0
            error(loc, "Too many components in texture template structure type", "", "");
9781
0
            return false;
9782
0
        }
9783
9784
        // All members must be of a common basic type
9785
0
        if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) {
9786
0
            error(loc, "Texture template structure members must same basic type", "", "");
9787
0
            return false;
9788
0
        }
9789
0
    }
9790
9791
    // If the structure in the return type already exists in the table, we'll use it.  Otherwise, we'll make
9792
    // a new entry.  This is a linear search, but it hardly ever happens, and the list cannot be very large.
9793
0
    for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) {
9794
0
        if (textureReturnStruct[idx] == members) {
9795
0
            sampler.structReturnIndex = idx;
9796
0
            return true;
9797
0
        }
9798
0
    }
9799
9800
    // It wasn't found as an existing entry.  See if we have room for a new one.
9801
0
    if (textureReturnStruct.size() >= TSampler::structReturnSlots) {
9802
0
        error(loc, "Texture template struct return slots exceeded", "", "");
9803
0
        return false;
9804
0
    }
9805
9806
    // Insert it in the vector that tracks struct return types.
9807
0
    sampler.structReturnIndex = unsigned(textureReturnStruct.size());
9808
0
    textureReturnStruct.push_back(members);
9809
9810
    // Success!
9811
0
    return true;
9812
0
}
9813
9814
// Return the sampler return type in retType.
9815
void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const
9816
0
{
9817
0
    if (sampler.hasReturnStruct()) {
9818
0
        assert(textureReturnStruct.size() >= sampler.structReturnIndex);
9819
9820
        // We land here if the texture return is a structure.
9821
0
        TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex];
9822
9823
0
        const TType resultType(blockStruct, "");
9824
0
        retType.shallowCopy(resultType);
9825
0
    } else {
9826
        // We land here if the texture return is a vector or scalar.
9827
0
        const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize());
9828
0
        retType.shallowCopy(resultType);
9829
0
    }
9830
0
}
9831
9832
9833
// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type
9834
TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const
9835
0
{
9836
0
    const auto it = builtInTessLinkageSymbols.find(biType);
9837
0
    if (it == builtInTessLinkageSymbols.end())  // if it wasn't declared by the user, return nullptr
9838
0
        return nullptr;
9839
9840
0
    return intermediate.addSymbol(*it->second->getAsVariable());
9841
0
}
9842
9843
// Find the patch constant function (issues error, returns nullptr if not found)
9844
const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc)
9845
0
{
9846
0
    if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
9847
0
        error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
9848
0
        return nullptr;
9849
0
    }
9850
9851
0
    const TString mangledName = patchConstantFunctionName + "(";
9852
9853
    // create list of PCF candidates
9854
0
    TVector<const TFunction*> candidateList;
9855
0
    bool builtIn;
9856
0
    symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
9857
9858
    // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
9859
    // allow any disambiguation of overloads.
9860
0
    if (candidateList.empty()) {
9861
0
        error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
9862
0
        return nullptr;
9863
0
    }
9864
9865
    // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
9866
    // HLSL picks the last one in shader source order.  Since that isn't yet implemented here, error
9867
    // out if there is more than one candidate.
9868
0
    if (candidateList.size() > 1) {
9869
0
        error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
9870
0
        return nullptr;
9871
0
    }
9872
9873
0
    return candidateList[0];
9874
0
}
9875
9876
// Finalization step: Add patch constant function invocation
9877
void HlslParseContext::addPatchConstantInvocation()
9878
207
{
9879
207
    TSourceLoc loc;
9880
207
    loc.init();
9881
9882
    // If there's no patch constant function, or we're not a HS, do nothing.
9883
207
    if (patchConstantFunctionName.empty() || language != EShLangTessControl)
9884
207
        return;
9885
9886
    // Look for built-in variables in a function's parameter list.
9887
0
    const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
9888
0
        for (int p=0; p<function.getParamCount(); ++p) {
9889
0
            TStorageQualifier storage = function[p].type->getQualifier().storage;
9890
9891
0
            if (storage == EvqConstReadOnly) // treated identically to input
9892
0
                storage = EvqIn;
9893
9894
0
            if (function[p].getDeclaredBuiltIn() != EbvNone)
9895
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage));
9896
0
            else
9897
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
9898
0
        }
9899
0
    };
9900
9901
    // If we synthesize a built-in interface variable, we must add it to the linkage.
9902
0
    const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
9903
0
        if (name == nullptr) {
9904
0
            error(loc, "unable to locate patch function parameter name", "", "");
9905
0
            return;
9906
0
        } else {
9907
0
            TVariable& variable = *new TVariable(name, type);
9908
0
            if (! symbolTable.insert(variable)) {
9909
0
                error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
9910
0
                return;
9911
0
            }
9912
9913
0
            globalQualifierFix(loc, variable.getWritableType().getQualifier());
9914
9915
0
            if (symbolNode != nullptr)
9916
0
                *symbolNode = intermediate.addSymbol(variable);
9917
9918
0
            trackLinkage(variable);
9919
0
        }
9920
0
    };
9921
9922
0
    const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) {
9923
0
        const TType& type = *patchConstantFunction[param].type;
9924
0
        const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn();
9925
9926
0
        return type.isSizedArray() && biType == EbvOutputPatch;
9927
0
    };
9928
9929
    // We will perform these steps.  Each is in a scoped block for separation: they could
9930
    // become separate functions to make addPatchConstantInvocation shorter.
9931
    //
9932
    // 1. Union the interfaces, and create built-ins for anything present in the PCF and
9933
    //    declared as a built-in variable that isn't present in the entry point's signature.
9934
    //
9935
    // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main,
9936
    //    or the ones we created.  Matching is based on built-in type.  We may use synthesized
9937
    //    variables from (1) above.
9938
    //
9939
    // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
9940
    //
9941
    // 3. Create a return sequence: copy the return value (if any) from the PCF to a
9942
    //    (non-sanitized) output variable.  In case this may involve multiple copies, such as for
9943
    //    an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
9944
    //    indirections into a complex R-value coming from the call to the PCF.
9945
    //
9946
    // 4. Create a barrier.
9947
    //
9948
    // 5/5B. Call the PCF inside an if test for (invocation id == 0).
9949
9950
0
    TFunction* patchConstantFunctionPtr = const_cast<TFunction*>(findPatchConstantFunction(loc));
9951
9952
0
    if (patchConstantFunctionPtr == nullptr)
9953
0
        return;
9954
9955
0
    TFunction& patchConstantFunction = *patchConstantFunctionPtr;
9956
9957
0
    const int pcfParamCount = patchConstantFunction.getParamCount();
9958
0
    TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
9959
0
    TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
9960
9961
0
    int outPatchParam = -1; // -1 means there isn't one.
9962
9963
    // ================ Step 1A: Union Interfaces ================
9964
    // Our patch constant function.
9965
0
    {
9966
0
        std::set<tInterstageIoData> pcfBuiltIns;  // patch constant function built-ins
9967
0
        std::set<tInterstageIoData> epfBuiltIns;  // entry point function built-ins
9968
9969
0
        assert(entryPointFunction);
9970
0
        assert(entryPointFunctionBody);
9971
9972
0
        findBuiltIns(patchConstantFunction, pcfBuiltIns);
9973
0
        findBuiltIns(*entryPointFunction,   epfBuiltIns);
9974
9975
        // Find the set of built-ins in the PCF that are not present in the entry point.
9976
0
        std::set<tInterstageIoData> notInEntryPoint;
9977
9978
0
        notInEntryPoint = pcfBuiltIns;
9979
9980
        // std::set_difference not usable on unordered containers
9981
0
        for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
9982
0
            notInEntryPoint.erase(*bi);
9983
9984
        // Now we'll add those to the entry and to the linkage.
9985
0
        for (int p=0; p<pcfParamCount; ++p) {
9986
0
            const TBuiltInVariable biType   = patchConstantFunction[p].getDeclaredBuiltIn();
9987
0
            TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
9988
9989
            // Track whether there is an output patch param
9990
0
            if (isOutputPatch(patchConstantFunction, p)) {
9991
0
                if (outPatchParam >= 0) {
9992
                    // Presently we only support one per ctrl pt input.
9993
0
                    error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
9994
0
                    return;
9995
0
                }
9996
0
                outPatchParam = p;
9997
0
            }
9998
9999
0
            if (biType != EbvNone) {
10000
0
                TType* paramType = patchConstantFunction[p].type->clone();
10001
10002
0
                if (storage == EvqConstReadOnly) // treated identically to input
10003
0
                    storage = EvqIn;
10004
10005
                // Presently, the only non-built-in we support is InputPatch, which is treated as
10006
                // a pseudo-built-in.
10007
0
                if (biType == EbvInputPatch) {
10008
0
                    builtInTessLinkageSymbols[biType] = inputPatch;
10009
0
                } else if (biType == EbvOutputPatch) {
10010
                    // Nothing...
10011
0
                } else {
10012
                    // Use the original declaration type for the linkage
10013
0
                    paramType->getQualifier().builtIn = biType;
10014
0
                    if (biType == EbvTessLevelInner || biType == EbvTessLevelOuter)
10015
0
                        paramType->getQualifier().patch = true;
10016
10017
0
                    if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
10018
0
                        addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
10019
0
                }
10020
0
            }
10021
0
        }
10022
10023
        // If we didn't find it because the shader made one, add our own.
10024
0
        if (invocationIdSym == nullptr) {
10025
0
            TType invocationIdType(EbtUint, EvqIn, 1);
10026
0
            TString* invocationIdName = NewPoolTString("InvocationId");
10027
0
            invocationIdType.getQualifier().builtIn = EbvInvocationId;
10028
0
            addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
10029
0
        }
10030
10031
0
        assert(invocationIdSym);
10032
0
    }
10033
10034
0
    TIntermTyped* pcfArguments = nullptr;
10035
0
    TVariable* perCtrlPtVar = nullptr;
10036
10037
    // ================ Step 1B: Argument synthesis ================
10038
    // Create pcfArguments for synthesis of patchconstantfunction invocation
10039
0
    {
10040
0
        for (int p=0; p<pcfParamCount; ++p) {
10041
0
            TIntermTyped* inputArg = nullptr;
10042
10043
0
            if (p == outPatchParam) {
10044
0
                if (perCtrlPtVar == nullptr) {
10045
0
                    perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
10046
0
                                                        *patchConstantFunction[outPatchParam].type);
10047
10048
0
                    perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
10049
0
                }
10050
0
                inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
10051
0
            } else {
10052
                // find which built-in it is
10053
0
                const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
10054
10055
0
                if (biType == EbvInputPatch && inputPatch == nullptr) {
10056
0
                    error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", "");
10057
0
                    return;
10058
0
                }
10059
10060
0
                inputArg = findTessLinkageSymbol(biType);
10061
10062
0
                if (inputArg == nullptr) {
10063
0
                    error(loc, "unable to find patch constant function built-in variable", "", "");
10064
0
                    return;
10065
0
                }
10066
0
            }
10067
10068
0
            if (pcfParamCount == 1)
10069
0
                pcfArguments = inputArg;
10070
0
            else
10071
0
                pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
10072
0
        }
10073
0
    }
10074
10075
    // ================ Step 2: Synthesize call to PCF ================
10076
0
    TIntermAggregate* pcfCallSequence = nullptr;
10077
0
    TIntermTyped* pcfCall = nullptr;
10078
10079
0
    {
10080
        // Create a function call to the patchconstantfunction
10081
0
        if (pcfArguments)
10082
0
            addInputArgumentConversions(patchConstantFunction, pcfArguments);
10083
10084
        // Synthetic call.
10085
0
        pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
10086
0
        pcfCall->getAsAggregate()->setUserDefined();
10087
0
        pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
10088
0
        intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
10089
0
                                    patchConstantFunction.getMangledName());
10090
10091
0
        if (pcfCall->getAsAggregate()) {
10092
0
            TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
10093
0
            for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
10094
0
                TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
10095
0
                qualifierList.push_back(qual);
10096
0
            }
10097
0
            pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
10098
0
        }
10099
0
    }
10100
10101
    // ================ Step 2B: Per Control Point synthesis ================
10102
    // If there is per control point data, we must either emulate that with multiple
10103
    // invocations of the entry point to build up an array, or (TODO:) use a yet
10104
    // unavailable extension to look across the SIMD lanes.  This is the former
10105
    // as a placeholder for the latter.
10106
0
    if (outPatchParam >= 0) {
10107
        // We must introduce a local temp variable of the type wanted by the PCF input.
10108
0
        const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
10109
10110
0
        if (entryPointFunction->getType().getBasicType() == EbtVoid) {
10111
0
            error(loc, "entry point must return a value for use with patch constant function", "", "");
10112
0
            return;
10113
0
        }
10114
10115
        // Create calls to wrapped main to fill in the array.  We will substitute fixed values
10116
        // of invocation ID when calling the wrapped main.
10117
10118
        // This is the type of the each member of the per ctrl point array.
10119
0
        const TType derefType(perCtrlPtVar->getType(), 0);
10120
10121
0
        for (int cpt = 0; cpt < arraySize; ++cpt) {
10122
            // TODO: improve.  substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
10123
            // for this function.
10124
0
            const TString origName = entryPointFunction->getName().substr(1);
10125
0
            TFunction callee(&origName, TType(EbtVoid));
10126
0
            TIntermTyped* callingArgs = nullptr;
10127
10128
0
            for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
10129
0
                TParameter& param = (*entryPointFunction)[i];
10130
0
                TType& paramType = *param.type;
10131
10132
0
                if (paramType.getQualifier().isParamOutput()) {
10133
0
                    error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
10134
0
                    return;
10135
0
                }
10136
10137
0
                if (paramType.getQualifier().isParamInput())  {
10138
0
                    TIntermTyped* arg = nullptr;
10139
0
                    if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) {
10140
                        // substitute invocation ID with the array element ID
10141
0
                        arg = intermediate.addConstantUnion(cpt, loc);
10142
0
                    } else {
10143
0
                        TVariable* argVar = makeInternalVariable(*param.name, *param.type);
10144
0
                        argVar->getWritableType().getQualifier().makeTemporary();
10145
0
                        arg = intermediate.addSymbol(*argVar);
10146
0
                    }
10147
10148
0
                    handleFunctionArgument(&callee, callingArgs, arg);
10149
0
                }
10150
0
            }
10151
10152
            // Call and assign to per ctrl point variable
10153
0
            currentCaller = intermediate.getEntryPointMangledName().c_str();
10154
0
            TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
10155
0
            TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
10156
0
            TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
10157
0
            TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
10158
0
            element->setType(derefType);
10159
0
            element->setLoc(loc);
10160
10161
0
            pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
10162
0
                                                         handleAssign(loc, EOpAssign, element, callReturn));
10163
0
        }
10164
0
    }
10165
10166
    // ================ Step 3: Create return Sequence ================
10167
    // Return sequence: copy PCF result to a temporary, then to shader output variable.
10168
0
    if (pcfCall->getBasicType() != EbtVoid) {
10169
0
        const TType* retType = &patchConstantFunction.getType();  // return type from the PCF
10170
0
        TType outType; // output type that goes with the return type.
10171
0
        outType.shallowCopy(*retType);
10172
10173
        // substitute the output type
10174
0
        const auto newLists = ioTypeMap.find(retType->getStruct());
10175
0
        if (newLists != ioTypeMap.end())
10176
0
            outType.setStruct(newLists->second.output);
10177
10178
        // Substitute the top level type's built-in type
10179
0
        if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
10180
0
            outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
10181
10182
0
        outType.getQualifier().patch = true; // make it a per-patch variable
10183
10184
0
        TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
10185
0
        pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
10186
10187
0
        if (pcfOutput->getType().isStruct())
10188
0
            flatten(*pcfOutput, false);
10189
10190
0
        assignToInterface(*pcfOutput);
10191
10192
0
        TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
10193
10194
        // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
10195
        // repeated calls to the PCF:
10196
0
        TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
10197
0
        pcfCallResult->getWritableType().getQualifier().makeTemporary();
10198
10199
0
        TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
10200
0
        TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
10201
0
        TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym,
10202
0
                                                   intermediate.addSymbol(*pcfCallResult, loc));
10203
10204
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
10205
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
10206
0
    } else {
10207
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
10208
0
    }
10209
10210
    // ================ Step 4: Barrier ================
10211
0
    TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
10212
0
    barrier->setLoc(loc);
10213
0
    barrier->setType(TType(EbtVoid));
10214
0
    epBodySeq.insert(epBodySeq.end(), barrier);
10215
10216
    // ================ Step 5: Test on invocation ID ================
10217
0
    TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
10218
0
    TIntermTyped* cmp =  intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
10219
10220
10221
    // ================ Step 5B: Create if statement on Invocation ID == 0 ================
10222
0
    intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
10223
0
    TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
10224
0
    invocationIdTest->setLoc(loc);
10225
10226
    // add our test sequence before the return.
10227
0
    epBodySeq.insert(epBodySeq.end(), invocationIdTest);
10228
0
}
10229
10230
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
10231
// shader is entirely compiled).
10232
// Preserve order of remaining symbols.
10233
void HlslParseContext::removeUnusedStructBufferCounters()
10234
207
{
10235
207
    const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
10236
207
                                      [this](const TSymbol* sym) {
10237
0
                                          const auto sbcIt = structBufferCounter.find(sym->getName());
10238
0
                                          return sbcIt != structBufferCounter.end() && !sbcIt->second;
10239
0
                                      });
10240
10241
207
    linkageSymbols.erase(endIt, linkageSymbols.end());
10242
207
}
10243
10244
// Finalization step: patch texture shadow modes to match samplers they were combined with
10245
void HlslParseContext::fixTextureShadowModes()
10246
207
{
10247
207
    for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) {
10248
0
        TSampler& sampler = (*symbol)->getWritableType().getSampler();
10249
10250
0
        if (sampler.isTexture()) {
10251
0
            const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId());
10252
0
            if (shadowMode != textureShadowVariant.end()) {
10253
10254
0
                if (shadowMode->second->overloaded())
10255
                    // Texture needs legalization if it's been seen with both shadow and non-shadow modes.
10256
0
                    intermediate.setNeedsLegalization();
10257
10258
0
                sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId());
10259
0
            }
10260
0
        }
10261
0
    }
10262
207
}
10263
10264
// Finalization step: patch append methods to use proper stream output, which isn't known until
10265
// main is parsed, which could happen after the append method is parsed.
10266
void HlslParseContext::finalizeAppendMethods()
10267
207
{
10268
207
    TSourceLoc loc;
10269
207
    loc.init();
10270
10271
    // Nothing to do: bypass test for valid stream output.
10272
207
    if (gsAppends.empty())
10273
207
        return;
10274
10275
0
    if (gsStreamOutput == nullptr) {
10276
0
        error(loc, "unable to find output symbol for Append()", "", "");
10277
0
        return;
10278
0
    }
10279
10280
    // Patch append sequences, now that we know the stream output symbol.
10281
0
    for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) {
10282
0
        append->node->getSequence()[0] =
10283
0
            handleAssign(append->loc, EOpAssign,
10284
0
                         intermediate.addSymbol(*gsStreamOutput, append->loc),
10285
0
                         append->node->getSequence()[0]->getAsTyped());
10286
0
    }
10287
0
}
10288
10289
// post-processing
10290
void HlslParseContext::finish()
10291
207
{
10292
    // Error check: There was a dangling .mips operator.  These are not nested constructs in the grammar, so
10293
    // cannot be detected there.  This is not strictly needed in a non-validating parser; it's just helpful.
10294
207
    if (! mipsOperatorMipArg.empty()) {
10295
0
        error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", "");
10296
0
    }
10297
10298
207
    removeUnusedStructBufferCounters();
10299
207
    addPatchConstantInvocation();
10300
207
    fixTextureShadowModes();
10301
207
    finalizeAppendMethods();
10302
10303
    // Communicate out (esp. for command line) that we formed AST that will make
10304
    // illegal AST SPIR-V and it needs transforms to legalize it.
10305
207
    if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization))
10306
0
        infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize";
10307
10308
207
    TParseContextBase::finish();
10309
207
}
10310
10311
} // end namespace glslang