Coverage Report

Created: 2026-08-31 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/glslang/HLSL/hlslParseHelper.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2017-2018 Google, Inc.
3
// Copyright (C) 2017 LunarG, Inc.
4
//
5
// All rights reserved.
6
//
7
// Redistribution and use in source and binary forms, with or without
8
// modification, are permitted provided that the following conditions
9
// are met:
10
//
11
//    Redistributions of source code must retain the above copyright
12
//    notice, this list of conditions and the following disclaimer.
13
//
14
//    Redistributions in binary form must reproduce the above
15
//    copyright notice, this list of conditions and the following
16
//    disclaimer in the documentation and/or other materials provided
17
//    with the distribution.
18
//
19
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20
//    contributors may be used to endorse or promote products derived
21
//    from this software without specific prior written permission.
22
//
23
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
// POSSIBILITY OF SUCH DAMAGE.
35
//
36
37
#include "hlslParseHelper.h"
38
#include "hlslScanContext.h"
39
#include "hlslGrammar.h"
40
#include "hlslAttributes.h"
41
42
#include "../Include/Common.h"
43
#include "../MachineIndependent/Scan.h"
44
#include "../MachineIndependent/preprocessor/PpContext.h"
45
46
#include <algorithm>
47
#include <functional>
48
#include <cctype>
49
#include <array>
50
#include <set>
51
52
namespace glslang {
53
54
static bool isLayoutSetOutOfRange(int value, bool relaxSetBindingLimits)
55
0
{
56
0
    return value < 0 || (!relaxSetBindingLimits && value >= static_cast<int>(TQualifier::layoutSetEnd));
57
0
}
58
59
static bool isLayoutBindingOutOfRange(int value, bool relaxSetBindingLimits)
60
0
{
61
0
    return value < 0 || (!relaxSetBindingLimits && value >= static_cast<int>(TQualifier::layoutBindingEnd));
62
0
}
63
64
HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
65
                                   int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
66
                                   TInfoSink& infoSink,
67
                                   const TString sourceEntryPointName,
68
                                   bool forwardCompatible, EShMessages messages) :
69
868
    TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink,
70
868
                      forwardCompatible, messages, &sourceEntryPointName),
71
868
    annotationNestingLevel(0),
72
868
    inputPatch(nullptr),
73
868
    nextInLocation(0), nextOutLocation(0),
74
868
    entryPointFunction(nullptr),
75
868
    entryPointFunctionBody(nullptr),
76
868
    gsStreamOutput(nullptr),
77
868
    clipDistanceOutput(nullptr),
78
868
    cullDistanceOutput(nullptr),
79
868
    clipDistanceInput(nullptr),
80
868
    cullDistanceInput(nullptr),
81
868
    parsingEntrypointParameters(false)
82
868
{
83
868
    globalUniformDefaults.clear();
84
868
    globalUniformDefaults.layoutMatrix = ElmRowMajor;
85
868
    globalUniformDefaults.layoutPacking = ElpStd140;
86
87
868
    globalBufferDefaults.clear();
88
868
    globalBufferDefaults.layoutMatrix = ElmRowMajor;
89
868
    globalBufferDefaults.layoutPacking = ElpStd430;
90
91
868
    globalInputDefaults.clear();
92
868
    globalOutputDefaults.clear();
93
94
868
    clipSemanticNSizeIn.fill(0);
95
868
    cullSemanticNSizeIn.fill(0);
96
868
    clipSemanticNSizeOut.fill(0);
97
868
    cullSemanticNSizeOut.fill(0);
98
99
    // "Shaders in the transform
100
    // feedback capturing mode have an initial global default of
101
    //     layout(xfb_buffer = 0) out;"
102
868
    if (language == EShLangVertex ||
103
636
        language == EShLangTessControl ||
104
580
        language == EShLangTessEvaluation ||
105
532
        language == EShLangGeometry)
106
380
        globalOutputDefaults.layoutXfbBuffer = 0;
107
108
868
    if (language == EShLangGeometry)
109
44
        globalOutputDefaults.layoutStream = 0;
110
868
}
111
112
HlslParseContext::~HlslParseContext()
113
868
{
114
868
}
115
116
void HlslParseContext::initializeExtensionBehavior()
117
104
{
118
104
    TParseContextBase::initializeExtensionBehavior();
119
120
    // HLSL allows #line by default.
121
104
    extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
122
104
}
123
124
void HlslParseContext::setLimits(const TBuiltInResource& r)
125
104
{
126
104
    resources = r;
127
104
    intermediate.setLimits(resources);
128
104
}
129
130
//
131
// Parse an array of strings using the parser in HlslRules.
132
//
133
// Returns true for successful acceptance of the shader, false if any errors.
134
//
135
bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
136
86
{
137
86
    currentScanner = &input;
138
86
    ppContext.setInput(input, versionWillBeError);
139
140
86
    HlslScanContext scanContext(*this, ppContext);
141
86
    HlslGrammar grammar(scanContext, *this);
142
86
    if (!grammar.parse()) {
143
        // Print a message formated such that if you click on the message it will take you right to
144
        // the line through most UIs.
145
34
        const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
146
34
        infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column
147
34
                      << ", HLSL parsing failed.\n";
148
34
        ++numErrors;
149
34
        return false;
150
34
    }
151
152
52
    finish();
153
154
52
    return numErrors == 0;
155
86
}
156
157
//
158
// Return true if this l-value node should be converted in some manner.
159
// For instance: turning a load aggregate into a store in an l-value.
160
//
161
bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
162
0
{
163
0
    if (node == nullptr || node->getAsTyped() == nullptr)
164
0
        return false;
165
166
0
    const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
167
0
    const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
168
169
    // If it's a swizzled/indexed aggregate, look at the left node instead.
170
0
    if (lhsAsBinary != nullptr &&
171
0
        (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
172
0
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
173
0
    if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
174
0
        return true;
175
176
0
    return false;
177
0
}
178
179
void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName,
180
                                              TTypeList* newTypeList)
181
0
{
182
0
    newTypeList = nullptr;
183
0
    correctUniform(memberType.getQualifier());
184
0
    if (memberType.isStruct()) {
185
0
        auto it = ioTypeMap.find(memberType.getStruct());
186
0
        if (it != ioTypeMap.end() && it->second.uniform)
187
0
            newTypeList = it->second.uniform;
188
0
    }
189
0
    TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
190
0
}
191
192
//
193
// Return a TLayoutFormat corresponding to the given texture type.
194
//
195
TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
196
2.50k
{
197
2.50k
    if (txType.isStruct()) {
198
        // TODO: implement.
199
0
        error(loc, "unimplemented: structure type in image or buffer", "", "");
200
0
        return ElfNone;
201
0
    }
202
203
2.50k
    const int components = txType.getVectorSize();
204
2.50k
    const TBasicType txBasicType = txType.getBasicType();
205
206
2.50k
    const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
207
2.50k
        if (intermediate.getNoStorageFormat())
208
0
            return ElfNone;
209
210
2.50k
        return components == 1 ? v1 :
211
2.50k
               components == 2 ? v2 : v4;
212
2.50k
    };
213
214
2.50k
    switch (txBasicType) {
215
836
    case EbtFloat: return selectFormat(ElfR32f,  ElfRg32f,  ElfRgba32f);
216
836
    case EbtInt:   return selectFormat(ElfR32i,  ElfRg32i,  ElfRgba32i);
217
836
    case EbtUint:  return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
218
0
    default:
219
0
        error(loc, "unknown basic type in image format", "", "");
220
0
        return ElfNone;
221
2.50k
    }
222
2.50k
}
223
224
//
225
// Both test and if necessary, spit out an error, to see if the node is really
226
// an l-value that can be operated on this way.
227
//
228
// Returns true if there was an error.
229
//
230
bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
231
0
{
232
0
    if (shouldConvertLValue(node)) {
233
        // if we're writing to a texture, it must be an RW form.
234
235
0
        TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
236
0
        TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
237
238
0
        if (!object->getType().getSampler().isImage()) {
239
0
            error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
240
0
            return true;
241
0
        }
242
0
    }
243
244
    // We tolerate samplers as l-values, even though they are nominally
245
    // illegal, because we expect a later optimization to eliminate them.
246
0
    if (node->getType().getBasicType() == EbtSampler) {
247
0
        intermediate.setNeedsLegalization();
248
0
        return false;
249
0
    }
250
251
    // Let the base class check errors
252
0
    return TParseContextBase::lValueErrorCheck(loc, op, node);
253
0
}
254
255
//
256
// This function handles l-value conversions and verifications.  It uses, but is not synonymous
257
// with lValueErrorCheck.  That function accepts an l-value directly, while this one must be
258
// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
259
// series of other image operations.
260
//
261
// Most things are passed through unmodified, except for error checking.
262
//
263
TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
264
0
{
265
0
    if (node == nullptr)
266
0
        return nullptr;
267
268
0
    TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
269
0
    TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
270
0
    TIntermAggregate* sequence = nullptr;
271
272
0
    TIntermTyped* lhs = nodeAsUnary  ? nodeAsUnary->getOperand() :
273
0
                        nodeAsBinary ? nodeAsBinary->getLeft() :
274
0
                        nullptr;
275
276
    // Early bail out if there is no conversion to apply
277
0
    if (!shouldConvertLValue(lhs)) {
278
0
        if (lhs != nullptr)
279
0
            if (lValueErrorCheck(loc, op, lhs))
280
0
                return nullptr;
281
0
        return node;
282
0
    }
283
284
    // *** If we get here, we're going to apply some conversion to an l-value.
285
286
    // Helper to create a load.
287
0
    const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
288
0
        TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
289
0
        loadOp->setLoc(loc);
290
0
        loadOp->getSequence().push_back(object);
291
0
        loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
292
0
        loadOp->setType(derefType);
293
294
0
        sequence = intermediate.growAggregate(sequence,
295
0
                                              intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
296
0
                                              loc);
297
0
    };
298
299
    // Helper to create a store.
300
0
    const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
301
0
        TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
302
0
        storeOp->getSequence().push_back(object);
303
0
        storeOp->getSequence().push_back(coord);
304
0
        storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
305
0
        storeOp->setLoc(loc);
306
0
        storeOp->setType(TType(EbtVoid));
307
308
0
        sequence = intermediate.growAggregate(sequence, storeOp);
309
0
    };
310
311
    // Helper to create an assign.
312
0
    const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
313
0
        sequence = intermediate.growAggregate(sequence,
314
0
                                              intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
315
0
                                              loc);
316
0
    };
317
318
    // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
319
0
    const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
320
        // Add a trailing use of the temp, so the sequence returns the proper value.
321
0
        sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
322
0
        sequence->setOperator(EOpSequence);
323
0
        sequence->setLoc(loc);
324
0
        sequence->setType(derefType);
325
326
0
        return sequence;
327
0
    };
328
329
    // Helper to add unary op
330
0
    const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
331
0
        sequence = intermediate.growAggregate(sequence,
332
0
                                              intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
333
0
                                                                        rhsTmp->getType()),
334
0
                                              loc);
335
0
    };
336
337
    // Return true if swizzle or index writes all components of the given variable.
338
0
    const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
339
0
        if (swizzle == nullptr)  // not a swizzle or index
340
0
            return true;
341
342
        // Track which components are being set.
343
0
        std::array<bool, 4> compIsSet;
344
0
        compIsSet.fill(false);
345
346
0
        const TIntermConstantUnion* asConst     = swizzle->getRight()->getAsConstantUnion();
347
0
        const TIntermAggregate*     asAggregate = swizzle->getRight()->getAsAggregate();
348
349
        // This could be either a direct index, or a swizzle.
350
0
        if (asConst) {
351
0
            compIsSet[asConst->getConstArray()[0].getIConst()] = true;
352
0
        } else if (asAggregate) {
353
0
            const TIntermSequence& seq = asAggregate->getSequence();
354
0
            for (int comp=0; comp<int(seq.size()); ++comp)
355
0
                compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
356
0
        } else {
357
0
            assert(0);
358
0
        }
359
360
        // Return true if all components are being set by the index or swizzle
361
0
        return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
362
0
                           [](bool isSet) { return isSet; } );
363
0
    };
364
365
    // Create swizzle matching input swizzle
366
0
    const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
367
0
        if (swizzle)
368
0
            return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
369
0
        else
370
0
            return var;
371
0
    };
372
373
0
    TIntermBinary*    lhsAsBinary    = lhs->getAsBinaryNode();
374
0
    TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
375
0
    bool lhsIsSwizzle = false;
376
377
    // If it's a swizzled L-value, remember the swizzle, and use the LHS.
378
0
    if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
379
0
        lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
380
0
        lhsIsSwizzle = true;
381
0
    }
382
383
0
    TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
384
0
    TIntermTyped* coord  = lhsAsAggregate->getSequence()[1]->getAsTyped();
385
386
0
    const TSampler& texSampler = object->getType().getSampler();
387
388
0
    TType objDerefType;
389
0
    getTextureReturnType(texSampler, objDerefType);
390
391
0
    if (nodeAsBinary) {
392
0
        TIntermTyped* rhs = nodeAsBinary->getRight();
393
0
        const TOperator assignOp = nodeAsBinary->getOp();
394
395
0
        bool isModifyOp = false;
396
397
0
        switch (assignOp) {
398
0
        case EOpAddAssign:
399
0
        case EOpSubAssign:
400
0
        case EOpMulAssign:
401
0
        case EOpVectorTimesMatrixAssign:
402
0
        case EOpVectorTimesScalarAssign:
403
0
        case EOpMatrixTimesScalarAssign:
404
0
        case EOpMatrixTimesMatrixAssign:
405
0
        case EOpDivAssign:
406
0
        case EOpModAssign:
407
0
        case EOpAndAssign:
408
0
        case EOpInclusiveOrAssign:
409
0
        case EOpExclusiveOrAssign:
410
0
        case EOpLeftShiftAssign:
411
0
        case EOpRightShiftAssign:
412
0
            isModifyOp = true;
413
0
            [[fallthrough]];
414
0
        case EOpAssign:
415
0
            {
416
                // Since this is an lvalue, we'll convert an image load to a sequence like this
417
                // (to still provide the value):
418
                //   OpSequence
419
                //      OpImageStore(object, lhs, rhs)
420
                //      rhs
421
                // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS,
422
                // so we'll convert instead to this:
423
                //   OpSequence
424
                //      rhsTmp = rhs
425
                //      OpImageStore(object, coord, rhsTmp)
426
                //      rhsTmp
427
                // If this is a read-modify-write op, like +=, we issue:
428
                //   OpSequence
429
                //      coordtmp = load's param1
430
                //      rhsTmp = OpImageLoad(object, coordTmp)
431
                //      rhsTmp op= rhs
432
                //      OpImageStore(object, coordTmp, rhsTmp)
433
                //      rhsTmp
434
                //
435
                // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
436
                //    ...
437
                //    rhsTmp.some_swizzle = ...
438
                // For partial writes, an error is generated.
439
440
0
                TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
441
0
                TIntermTyped* coordTmp = coord;
442
443
0
                if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
444
0
                    rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
445
446
                    // Partial updates not yet supported
447
0
                    if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
448
0
                        error(loc, "unimplemented: partial image updates", "", "");
449
0
                    }
450
451
                    // Assign storeTemp = rhs
452
0
                    if (isModifyOp) {
453
                        // We have to make a temp var for the coordinate, to avoid evaluating it twice.
454
0
                        coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
455
0
                        makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
456
0
                        makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
457
0
                    }
458
459
                    // rhsTmp op= rhs.
460
0
                    makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
461
0
                }
462
463
0
                makeStore(object, coordTmp, rhsTmp);         // add a store
464
0
                return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
465
0
            }
466
467
0
        default:
468
0
            break;
469
0
        }
470
0
    }
471
472
0
    if (nodeAsUnary) {
473
0
        const TOperator assignOp = nodeAsUnary->getOp();
474
475
0
        switch (assignOp) {
476
0
        case EOpPreIncrement:
477
0
        case EOpPreDecrement:
478
0
            {
479
                // We turn this into:
480
                //   OpSequence
481
                //      coordtmp = load's param1
482
                //      rhsTmp = OpImageLoad(object, coordTmp)
483
                //      rhsTmp op
484
                //      OpImageStore(object, coordTmp, rhsTmp)
485
                //      rhsTmp
486
487
0
                TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
488
0
                TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
489
490
0
                makeBinary(EOpAssign, coordTmp, coord);           // coordtmp = load[param1]
491
0
                makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
492
0
                makeUnary(assignOp, rhsTmp);                      // op rhsTmp
493
0
                makeStore(object, coordTmp, rhsTmp);              // OpImageStore(object, coordTmp, rhsTmp)
494
0
                return finishSequence(rhsTmp, objDerefType);      // return rhsTmp from sequence
495
0
            }
496
497
0
        case EOpPostIncrement:
498
0
        case EOpPostDecrement:
499
0
            {
500
                // We turn this into:
501
                //   OpSequence
502
                //      coordtmp = load's param1
503
                //      rhsTmp1 = OpImageLoad(object, coordTmp)
504
                //      rhsTmp2 = rhsTmp1
505
                //      rhsTmp2 op
506
                //      OpImageStore(object, coordTmp, rhsTmp2)
507
                //      rhsTmp1 (pre-op value)
508
0
                TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre",  objDerefType);
509
0
                TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
510
0
                TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
511
512
0
                makeBinary(EOpAssign, coordTmp, coord);            // coordtmp = load[param1]
513
0
                makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
514
0
                makeBinary(EOpAssign, rhsTmp2, rhsTmp1);           // rhsTmp2 = rhsTmp1
515
0
                makeUnary(assignOp, rhsTmp2);                      // rhsTmp op
516
0
                makeStore(object, coordTmp, rhsTmp2);              // OpImageStore(object, coordTmp, rhsTmp2)
517
0
                return finishSequence(rhsTmp1, objDerefType);      // return rhsTmp from sequence
518
0
            }
519
520
0
        default:
521
0
            break;
522
0
        }
523
0
    }
524
525
0
    if (lhs)
526
0
        if (lValueErrorCheck(loc, op, lhs))
527
0
            return nullptr;
528
529
0
    return node;
530
0
}
531
532
void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
533
0
{
534
0
    if (pragmaCallback)
535
0
        pragmaCallback(loc.line, tokens);
536
537
0
    if (tokens.size() == 0)
538
0
        return;
539
540
    // These pragmas are case insensitive in HLSL, so we'll compare in lower case.
541
0
    TVector<TString> lowerTokens = tokens;
542
543
0
    for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it)
544
0
        std::transform(it->begin(), it->end(), it->begin(), ::tolower);
545
546
    // Handle pack_matrix
547
0
    if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") {
548
        // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense.
549
        // Row major becomes column major and vice versa.
550
551
0
        if (lowerTokens[2] == "row_major") {
552
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor;
553
0
        } else if (lowerTokens[2] == "column_major") {
554
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
555
0
        } else {
556
            // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major)
557
0
            warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), "");
558
0
            globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
559
0
        }
560
0
        return;
561
0
    }
562
563
    // Handle once
564
0
    if (lowerTokens[0] == "once") {
565
0
        warn(loc, "not implemented", "#pragma once", "");
566
0
        return;
567
0
    }
568
0
}
569
570
//
571
// Look at a '.' matrix selector string and change it into components
572
// for a matrix. There are two types:
573
//
574
//   _21    second row, first column (one based)
575
//   _m21   third row, second column (zero based)
576
//
577
// Returns true if there is no error.
578
//
579
bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
580
                                                  TSwizzleSelectors<TMatrixSelector>& components)
581
0
{
582
0
    int startPos[MaxSwizzleSelectors];
583
0
    int numComps = 0;
584
0
    TString compString = fields;
585
586
    // Find where each component starts,
587
    // recording the first character position after the '_'.
588
0
    for (size_t c = 0; c < compString.size(); ++c) {
589
0
        if (compString[c] == '_') {
590
0
            if (numComps >= MaxSwizzleSelectors) {
591
0
                error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
592
0
                return false;
593
0
            }
594
0
            if (c + 3 > compString.size() ||
595
0
                    ((compString[c+1] == 'm' || compString[c+1] == 'M') && c + 4 > compString.size())) {
596
0
                error(loc, "matrix component swizzle missing", compString.c_str(), "");
597
0
                return false;
598
0
            }
599
0
            startPos[numComps++] = (int)c + 1;
600
0
        }
601
0
    }
602
603
    // Process each component
604
0
    for (int i = 0; i < numComps; ++i) {
605
0
        int pos = startPos[i];
606
0
        int bias = -1;
607
0
        if (compString[pos] == 'm' || compString[pos] == 'M') {
608
0
            bias = 0;
609
0
            ++pos;
610
0
        }
611
0
        TMatrixSelector comp;
612
0
        comp.coord1 = compString[pos+0] - '0' + bias;
613
0
        comp.coord2 = compString[pos+1] - '0' + bias;
614
0
        if (comp.coord1 < 0 || comp.coord1 >= cols) {
615
0
            error(loc, "matrix row component out of range", compString.c_str(), "");
616
0
            return false;
617
0
        }
618
0
        if (comp.coord2 < 0 || comp.coord2 >= rows) {
619
0
            error(loc, "matrix column component out of range", compString.c_str(), "");
620
0
            return false;
621
0
        }
622
0
        components.push_back(comp);
623
0
    }
624
625
0
    return true;
626
0
}
627
628
// If the 'comps' express a column of a matrix,
629
// return the column.  Column means the first coords all match.
630
//
631
// Otherwise, return -1.
632
//
633
int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
634
0
{
635
0
    int col = -1;
636
637
    // right number of comps?
638
0
    if (selector.size() != rows)
639
0
        return -1;
640
641
    // all comps in the same column?
642
    // rows in order?
643
0
    col = selector[0].coord1;
644
0
    for (int i = 0; i < rows; ++i) {
645
0
        if (col != selector[i].coord1)
646
0
            return -1;
647
0
        if (i != selector[i].coord2)
648
0
            return -1;
649
0
    }
650
651
0
    return col;
652
0
}
653
654
//
655
// Handle seeing a variable identifier in the grammar.
656
//
657
TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string)
658
0
{
659
0
    int thisDepth;
660
0
    TSymbol* symbol = symbolTable.find(*string, thisDepth);
661
0
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
662
0
        error(loc, "expected symbol, not user-defined type", string->c_str(), "");
663
0
        return nullptr;
664
0
    }
665
666
0
    const TVariable* variable = nullptr;
667
0
    const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
668
0
    TIntermTyped* node = nullptr;
669
0
    if (anon) {
670
        // It was a member of an anonymous container, which could be a 'this' structure.
671
672
        // Create a subtree for its dereference.
673
0
        if (thisDepth > 0) {
674
0
            variable = getImplicitThis(thisDepth);
675
0
            if (variable == nullptr)
676
0
                error(loc, "cannot access member variables (static member function?)", "this", "");
677
0
        }
678
0
        if (variable == nullptr)
679
0
            variable = anon->getAnonContainer().getAsVariable();
680
681
0
        TIntermTyped* container = intermediate.addSymbol(*variable, loc);
682
0
        TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
683
0
        node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
684
685
0
        node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
686
0
        if (node->getType().hiddenMember())
687
0
            error(loc, "member of nameless block was not redeclared", string->c_str(), "");
688
0
    } else {
689
        // Not a member of an anonymous container.
690
691
        // The symbol table search was done in the lexical phase.
692
        // See if it was a variable.
693
0
        variable = symbol ? symbol->getAsVariable() : nullptr;
694
0
        if (variable) {
695
0
            if ((variable->getType().getBasicType() == EbtBlock ||
696
0
                variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
697
0
                error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
698
0
                variable = nullptr;
699
0
            }
700
0
        } else {
701
0
            if (symbol)
702
0
                error(loc, "variable name expected", string->c_str(), "");
703
0
        }
704
705
        // Recovery, if it wasn't found or was not a variable.
706
0
        if (variable == nullptr) {
707
0
            error(loc, "unknown variable", string->c_str(), "");
708
0
            variable = new TVariable(string, TType(EbtVoid));
709
0
        }
710
711
0
        if (variable->getType().getQualifier().isFrontEndConstant())
712
0
            node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
713
0
        else
714
0
            node = intermediate.addSymbol(*variable, loc);
715
0
    }
716
717
0
    if (variable->getType().getQualifier().isIo())
718
0
        intermediate.addIoAccessed(*string);
719
720
0
    return node;
721
0
}
722
723
//
724
// Handle operator[] on any objects it applies to.  Currently:
725
//    Textures
726
//    Buffers
727
//
728
TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
729
0
{
730
    // handle r-value operator[] on textures and images.  l-values will be processed later.
731
0
    if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
732
0
        const TSampler& sampler = base->getType().getSampler();
733
0
        if (sampler.isImage() || sampler.isTexture()) {
734
0
            if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) {
735
                // The first operator[] to a .mips[] sequence is the mip level.  We'll remember it.
736
0
                mipsOperatorMipArg.back().mipLevel = index;
737
0
                return base;  // next [] index is to the same base.
738
0
            } else {
739
0
                TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
740
741
0
                TType sampReturnType;
742
0
                getTextureReturnType(sampler, sampReturnType);
743
744
0
                load->setType(sampReturnType);
745
0
                load->setLoc(loc);
746
0
                load->getSequence().push_back(base);
747
0
                load->getSequence().push_back(index);
748
749
                // Textures need a MIP.  If we saw one go by, use it.  Otherwise, use zero.
750
0
                if (sampler.isTexture()) {
751
0
                    if (! mipsOperatorMipArg.empty()) {
752
0
                        load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel);
753
0
                        mipsOperatorMipArg.pop_back();
754
0
                    } else {
755
0
                        load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
756
0
                    }
757
0
                }
758
759
0
                return load;
760
0
            }
761
0
        }
762
0
    }
763
764
    // Handle operator[] on structured buffers: this indexes into the array element of the buffer.
765
    // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
766
0
    TIntermTyped* sbArray = indexStructBufferContent(loc, base);
767
0
    if (sbArray != nullptr) {
768
        // Now we'll apply the [] index to that array
769
0
        const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
770
771
0
        TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
772
0
        const TType derefType(sbArray->getType(), 0);
773
0
        element->setType(derefType);
774
0
        return element;
775
0
    }
776
777
0
    return nullptr;
778
0
}
779
780
//
781
// Cast index value to a uint if it isn't already (for operator[], load indexes, etc)
782
TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index)
783
0
{
784
0
    const TBasicType indexBasicType = index->getType().getBasicType();
785
0
    const int vecSize = index->getType().getVectorSize();
786
787
    // We can use int types directly as the index
788
0
    if (indexBasicType == EbtInt || indexBasicType == EbtUint ||
789
0
        indexBasicType == EbtInt64 || indexBasicType == EbtUint64)
790
0
        return index;
791
792
    // Cast index to unsigned integer if it isn't one.
793
0
    return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index);
794
0
}
795
796
//
797
// Handle seeing a base[index] dereference in the grammar.
798
//
799
TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
800
0
{
801
0
    index = makeIntegerIndex(index);
802
803
0
    if (index == nullptr) {
804
0
        error(loc, " unknown index type ", "", "");
805
0
        return nullptr;
806
0
    }
807
808
0
    TIntermTyped* result = handleBracketOperator(loc, base, index);
809
810
0
    if (result != nullptr)
811
0
        return result;  // it was handled as an operator[]
812
813
0
    bool flattened = false;
814
0
    int64_t indexValue = 0;
815
0
    if (index->getQualifier().isFrontEndConstant()) {
816
0
        if (index->getType().contains64BitInt()) {
817
0
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getI64Const();
818
0
        } else if (index->getType().getBasicType() == EbtUint) {
819
0
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getUConst();
820
0
        } else {
821
0
            indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
822
0
        }
823
0
    }
824
825
0
    variableCheck(base);
826
0
    if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
827
0
        if (base->getAsSymbolNode())
828
0
            error(loc, " left of '[' is not of type array, matrix, or vector ",
829
0
                  base->getAsSymbolNode()->getName().c_str(), "");
830
0
        else
831
0
            error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
832
0
    } else if (base->getType().getQualifier().isFrontEndConstant() &&
833
0
               index->getQualifier().isFrontEndConstant()) {
834
        // both base and index are front-end constants
835
0
        checkIndex(loc, base->getType(), indexValue);
836
0
        return intermediate.foldDereference(base, indexValue, loc);
837
0
    } else {
838
        // at least one of base and index is variable...
839
840
0
        if (index->getQualifier().isFrontEndConstant())
841
0
            checkIndex(loc, base->getType(), indexValue);
842
843
0
        if (base->getType().isScalarOrVec1())
844
0
            result = base;
845
0
        else if (base->getAsSymbolNode() && wasFlattened(base)) {
846
0
            if (index->getQualifier().storage != EvqConst)
847
0
                error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
848
849
0
            result = flattenAccess(base, indexValue);
850
0
            flattened = (result != base);
851
0
        } else {
852
0
            if (index->getQualifier().isFrontEndConstant()) {
853
0
                if (base->getType().isUnsizedArray())
854
0
                    base->getWritableType().updateImplicitArraySize(indexValue + 1);
855
0
                else
856
0
                    checkIndex(loc, base->getType(), indexValue);
857
0
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
858
0
            } else
859
0
                result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
860
0
        }
861
0
    }
862
863
0
    if (result == nullptr) {
864
        // Insert dummy error-recovery result
865
0
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
866
0
    } else {
867
        // If the array reference was flattened, it has the correct type.  E.g, if it was
868
        // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
869
        // In that case, we preserve the qualifiers.
870
0
        if (!flattened) {
871
            // Insert valid dereferenced result
872
0
            TType newType(base->getType(), 0);  // dereferenced type
873
0
            if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
874
0
                newType.getQualifier().storage = EvqConst;
875
0
            else
876
0
                newType.getQualifier().storage = EvqTemporary;
877
0
            result->setType(newType);
878
0
        }
879
0
    }
880
881
0
    return result;
882
0
}
883
884
// Handle seeing a binary node with a math operation.
885
TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op,
886
                                                 TIntermTyped* left, TIntermTyped* right)
887
0
{
888
0
    TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
889
0
    if (result == nullptr)
890
0
        binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
891
892
0
    return result;
893
0
}
894
895
// Handle seeing a unary node with a math operation.
896
TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op,
897
                                                TIntermTyped* childNode)
898
0
{
899
0
    TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
900
901
0
    if (result)
902
0
        return result;
903
0
    else
904
0
        unaryOpError(loc, str, childNode->getCompleteString());
905
906
0
    return childNode;
907
0
}
908
//
909
// Return true if the name is a struct buffer method
910
//
911
bool HlslParseContext::isStructBufferMethod(const TString& name) const
912
0
{
913
0
    return
914
0
        name == "GetDimensions"              ||
915
0
        name == "Load"                       ||
916
0
        name == "Load2"                      ||
917
0
        name == "Load3"                      ||
918
0
        name == "Load4"                      ||
919
0
        name == "Store"                      ||
920
0
        name == "Store2"                     ||
921
0
        name == "Store3"                     ||
922
0
        name == "Store4"                     ||
923
0
        name == "InterlockedAdd"             ||
924
0
        name == "InterlockedAnd"             ||
925
0
        name == "InterlockedCompareExchange" ||
926
0
        name == "InterlockedCompareStore"    ||
927
0
        name == "InterlockedExchange"        ||
928
0
        name == "InterlockedMax"             ||
929
0
        name == "InterlockedMin"             ||
930
0
        name == "InterlockedOr"              ||
931
0
        name == "InterlockedXor"             ||
932
0
        name == "IncrementCounter"           ||
933
0
        name == "DecrementCounter"           ||
934
0
        name == "Append"                     ||
935
0
        name == "Consume";
936
0
}
937
938
//
939
// Handle seeing a base.field dereference in the grammar, where 'field' is a
940
// swizzle or member variable.
941
//
942
TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
943
0
{
944
0
    variableCheck(base);
945
946
0
    if (base->isArray()) {
947
0
        error(loc, "cannot apply to an array:", ".", field.c_str());
948
0
        return base;
949
0
    }
950
951
0
    TIntermTyped* result = base;
952
953
0
    if (base->getType().getBasicType() == EbtSampler) {
954
        // Handle .mips[mipid][pos] operation on textures
955
0
        const TSampler& sampler = base->getType().getSampler();
956
0
        if (sampler.isTexture() && field == "mips") {
957
            // Push a null to signify that we expect a mip level under operator[] next.
958
0
            mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr));
959
            // Keep 'result' pointing to 'base', since we expect an operator[] to go by next.
960
0
        } else {
961
0
            if (field == "mips")
962
0
                error(loc, "unexpected texture type for .mips[][] operator:",
963
0
                      base->getType().getCompleteString().c_str(), "");
964
0
            else
965
0
                error(loc, "unexpected operator on texture type:", field.c_str(),
966
0
                      base->getType().getCompleteString().c_str());
967
0
        }
968
0
    } else if (base->isVector() || base->isScalar()) {
969
0
        TSwizzleSelectors<TVectorSelector> selectors;
970
0
        parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
971
972
0
        if (base->isScalar()) {
973
0
            if (selectors.size() == 1)
974
0
                return result;
975
0
            else {
976
0
                TType type(base->getBasicType(), EvqTemporary, selectors.size());
977
0
                return addConstructor(loc, base, type);
978
0
            }
979
0
        }
980
        // Use EOpIndexDirect (below) with vec1.x so that it remains l-value (Test/hlsl.swizzle.vec1.comp)
981
0
        if (base->getVectorSize() == 1 && selectors.size() > 1) {
982
0
            TType scalarType(base->getBasicType(), EvqTemporary, 1);
983
0
            TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
984
0
            return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
985
0
        }
986
987
0
        if (base->getType().getQualifier().isFrontEndConstant())
988
0
            result = intermediate.foldSwizzle(base, selectors, loc);
989
0
        else {
990
0
            if (selectors.size() == 1) {
991
0
                TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
992
0
                result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
993
0
                result->setType(TType(base->getBasicType(), EvqTemporary));
994
0
            } else {
995
0
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
996
0
                result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
997
0
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
998
0
                                selectors.size()));
999
0
            }
1000
0
        }
1001
0
    } else if (base->isMatrix()) {
1002
0
        TSwizzleSelectors<TMatrixSelector> selectors;
1003
0
        if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
1004
0
            return result;
1005
1006
0
        if (selectors.size() == 1) {
1007
            // Representable by m[c][r]
1008
0
            if (base->getType().getQualifier().isFrontEndConstant()) {
1009
0
                result = intermediate.foldDereference(base, selectors[0].coord1, loc);
1010
0
                result = intermediate.foldDereference(result, selectors[0].coord2, loc);
1011
0
            } else {
1012
0
                result = intermediate.addIndex(EOpIndexDirect, base,
1013
0
                                               intermediate.addConstantUnion(selectors[0].coord1, loc),
1014
0
                                               loc);
1015
0
                TType dereferencedCol(base->getType(), 0);
1016
0
                result->setType(dereferencedCol);
1017
0
                result = intermediate.addIndex(EOpIndexDirect, result,
1018
0
                                               intermediate.addConstantUnion(selectors[0].coord2, loc),
1019
0
                                               loc);
1020
0
                TType dereferenced(dereferencedCol, 0);
1021
0
                result->setType(dereferenced);
1022
0
            }
1023
0
        } else {
1024
0
            int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
1025
0
            if (column >= 0) {
1026
                // Representable by m[c]
1027
0
                if (base->getType().getQualifier().isFrontEndConstant())
1028
0
                    result = intermediate.foldDereference(base, column, loc);
1029
0
                else {
1030
0
                    result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc),
1031
0
                                                   loc);
1032
0
                    TType dereferenced(base->getType(), 0);
1033
0
                    result->setType(dereferenced);
1034
0
                }
1035
0
            } else {
1036
                // general case, not a column, not a single component
1037
0
                TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
1038
0
                result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
1039
0
                result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
1040
0
                                      selectors.size()));
1041
0
           }
1042
0
        }
1043
0
    } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
1044
0
        const TTypeList* fields = base->getType().getStruct();
1045
0
        bool fieldFound = false;
1046
0
        int member;
1047
0
        for (member = 0; member < (int)fields->size(); ++member) {
1048
0
            if ((*fields)[member].type->getFieldName() == field) {
1049
0
                fieldFound = true;
1050
0
                break;
1051
0
            }
1052
0
        }
1053
0
        if (fieldFound) {
1054
0
            if (base->getAsSymbolNode() && wasFlattened(base)) {
1055
0
                result = flattenAccess(base, member);
1056
0
            } else {
1057
0
                if (base->getType().getQualifier().storage == EvqConst)
1058
0
                    result = intermediate.foldDereference(base, member, loc);
1059
0
                else {
1060
0
                    TIntermTyped* index = intermediate.addConstantUnion(member, loc);
1061
0
                    result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
1062
0
                    result->setType(*(*fields)[member].type);
1063
0
                }
1064
0
            }
1065
0
        } else
1066
0
            error(loc, "no such field in structure", field.c_str(), "");
1067
0
    } else
1068
0
        error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
1069
1070
0
    return result;
1071
0
}
1072
1073
//
1074
// Return true if the field should be treated as a built-in method.
1075
// Return false otherwise.
1076
//
1077
bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
1078
0
{
1079
0
    if (base == nullptr)
1080
0
        return false;
1081
1082
0
    variableCheck(base);
1083
1084
0
    if (base->getType().getBasicType() == EbtSampler) {
1085
0
        return true;
1086
0
    } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
1087
0
        return true;
1088
0
    } else if (field == "Append" ||
1089
0
               field == "RestartStrip") {
1090
        // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
1091
        // the code is around in the shader source.
1092
0
        return true;
1093
0
    } else
1094
0
        return false;
1095
0
}
1096
1097
// Independently establish a built-in that is a member of a structure.
1098
// 'arraySizes' are what's desired for the independent built-in, whatever
1099
// the higher-level source/expression of them was.
1100
void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
1101
                                    const TQualifier& outerQualifier)
1102
0
{
1103
    // Because of arrays of structs, we might be asked more than once,
1104
    // but the arraySizes passed in should have captured the whole thing
1105
    // the first time.
1106
    // However, clip/cull rely on multiple updates.
1107
0
    if (!isClipOrCullDistance(memberType))
1108
0
        if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) !=
1109
0
            splitBuiltIns.end())
1110
0
            return;
1111
1112
0
    TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType);
1113
1114
0
    if (arraySizes != nullptr && !memberType.isArray())
1115
0
        ioVar->getWritableType().copyArraySizes(*arraySizes);
1116
1117
0
    splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
1118
0
    if (!isClipOrCullDistance(ioVar->getType()))
1119
0
        trackLinkage(*ioVar);
1120
1121
    // Merge qualifier from the user structure
1122
0
    mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
1123
1124
    // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the
1125
    // shader declared them).  This is done after mergeQualifiers(), in case fixBuiltInIoType looks
1126
    // at the qualifier to determine e.g, in or out qualifications.
1127
0
    fixBuiltInIoType(ioVar->getWritableType());
1128
1129
    // But, not location, we're losing that
1130
0
    ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1131
0
}
1132
1133
// Split a type into
1134
//   1. a struct of non-I/O members
1135
//   2. a collection of independent I/O variables
1136
void HlslParseContext::split(const TVariable& variable)
1137
0
{
1138
    // Create a new variable:
1139
0
    const TType& clonedType = *variable.getType().clone();
1140
0
    const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
1141
0
    splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
1142
0
}
1143
1144
// Recursive implementation of split().
1145
// Returns reference to the modified type.
1146
const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
1147
0
{
1148
0
    if (type.isStruct()) {
1149
0
        TTypeList* userStructure = type.getWritableStruct();
1150
0
        for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
1151
0
            if (ioType->type->isBuiltIn()) {
1152
                // move out the built-in
1153
0
                splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
1154
0
                ioType = userStructure->erase(ioType);
1155
0
            } else {
1156
0
                split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier);
1157
0
                ++ioType;
1158
0
            }
1159
0
        }
1160
0
    }
1161
1162
0
    return type;
1163
0
}
1164
1165
// Is this an aggregate that should be flattened?
1166
// Can be applied to intermediate levels of type in a hierarchy.
1167
// Some things like flattening uniform arrays are only about the top level
1168
// of the aggregate, triggered on 'topLevel'.
1169
bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const
1170
0
{
1171
0
    switch (qualifier) {
1172
0
    case EvqVaryingIn:
1173
0
    case EvqVaryingOut:
1174
0
        return type.isStruct() || type.isArray();
1175
0
    case EvqUniform:
1176
0
        return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) ||
1177
0
               (type.isStruct() && type.containsOpaque());
1178
0
    default:
1179
0
        return false;
1180
0
    };
1181
0
}
1182
1183
// Top level variable flattening: construct data
1184
void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed)
1185
0
{
1186
0
    const TType& type = variable.getType();
1187
1188
    // If it's a standalone built-in, there is nothing to flatten
1189
0
    if (type.isBuiltIn() && !type.isStruct())
1190
0
        return;
1191
1192
1193
0
    auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
1194
0
                                                  TFlattenData(type.getQualifier().layoutBinding,
1195
0
                                                               type.getQualifier().layoutLocation)));
1196
1197
0
    if (type.isStruct() && type.getStruct()->size()==0)
1198
0
        return;
1199
    // if flattening arrayed io struct, array each member of dereferenced type
1200
0
    if (arrayed) {
1201
0
        const TType dereferencedType(type, 0);
1202
0
        flatten(variable, dereferencedType, entry.first->second, variable.getName(), linkage,
1203
0
                type.getQualifier(), type.getArraySizes());
1204
0
    } else {
1205
0
        flatten(variable, type, entry.first->second, variable.getName(), linkage,
1206
0
                type.getQualifier(), nullptr);
1207
0
    }
1208
0
}
1209
1210
// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1211
//
1212
// This is mutually recursive with flattenStruct and flattenArray.
1213
// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1214
// members, and later on, we want to turn a path through the tree structure into a final
1215
// location in this linear sequence.
1216
//
1217
// If the tree was N-ary, that can be directly calculated.  However, we are dealing with
1218
// arbitrary numbers - perhaps a struct of 7 members containing an array of 3.  Thus, we must
1219
// build a data structure to allow the sequence of bracket and dot operators on arrays and
1220
// structs to arrive at the proper member.
1221
//
1222
// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1223
// The leaves are the indexes into the flattened member array.
1224
// Each level will have the next location for the Nth item stored sequentially, so for instance:
1225
//
1226
// struct { float2 a[2]; int b; float4 c[3] };
1227
//
1228
// This will produce the following flattened tree:
1229
// Pos: 0  1   2    3  4    5  6   7     8   9  10   11  12 13
1230
//     (3, 7,  8,   5, 6,   0, 1,  2,   11, 12, 13,   3,  4, 5}
1231
//
1232
// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1233
//   (0+2) = 8  -->  (8+1) = 12 -->   12 = 4
1234
//
1235
// so the 4th flattened member in traversal order is ours.
1236
//
1237
int HlslParseContext::flatten(const TVariable& variable, const TType& type,
1238
                              TFlattenData& flattenData, TString name, bool linkage,
1239
                              const TQualifier& outerQualifier,
1240
                              const TArraySizes* builtInArraySizes)
1241
0
{
1242
    // If something is an arrayed struct, the array flattener will recursively call flatten()
1243
    // to then flatten the struct, so this is an "if else": we don't do both.
1244
0
    if (type.isArray())
1245
0
        return flattenArray(variable, type, flattenData, name, linkage, outerQualifier);
1246
0
    else if (type.isStruct())
1247
0
        return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes);
1248
0
    else {
1249
0
        assert(0); // should never happen
1250
0
        return -1;
1251
0
    }
1252
0
}
1253
1254
// Add a single flattened member to the flattened data being tracked for the composite
1255
// Returns true for the final flattening level.
1256
int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData,
1257
                                         const TString& memberName, bool linkage,
1258
                                         const TQualifier& outerQualifier,
1259
                                         const TArraySizes* builtInArraySizes)
1260
0
{
1261
0
    if (!shouldFlatten(type, outerQualifier.storage, false)) {
1262
        // This is as far as we flatten.  Insert the variable.
1263
0
        TVariable* memberVariable = makeInternalVariable(memberName, type);
1264
0
        mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1265
1266
0
        if (flattenData.nextBinding != TQualifier::layoutNotSet)
1267
0
            memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1268
1269
0
        if (memberVariable->getType().isBuiltIn()) {
1270
            // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number)
1271
0
            memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1272
0
        } else {
1273
            // inherited locations must be auto bumped, not replicated
1274
0
            if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
1275
0
                memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
1276
0
                flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language);
1277
0
                nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
1278
0
            }
1279
0
        }
1280
1281
        // Only propagate arraysizes here for arrayed io
1282
0
        if (variable.getType().getQualifier().isArrayedIo(language) && builtInArraySizes != nullptr)
1283
0
            memberVariable->getWritableType().copyArraySizes(*builtInArraySizes);
1284
1285
0
        flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
1286
0
        flattenData.members.push_back(memberVariable);
1287
1288
0
        if (linkage)
1289
0
            trackLinkage(*memberVariable);
1290
1291
0
        return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference
1292
0
    } else {
1293
        // Further recursion required
1294
0
        return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes);
1295
0
    }
1296
0
}
1297
1298
// Figure out the mapping between an aggregate's top members and an
1299
// equivalent set of individual variables.
1300
//
1301
// Assumes shouldFlatten() or equivalent was called first.
1302
int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type,
1303
                                    TFlattenData& flattenData, TString name, bool linkage,
1304
                                    const TQualifier& outerQualifier,
1305
                                    const TArraySizes* builtInArraySizes)
1306
0
{
1307
0
    assert(type.isStruct());
1308
1309
0
    auto members = *type.getStruct();
1310
1311
    // Reserve space for this tree level.
1312
0
    int start = static_cast<int>(flattenData.offsets.size());
1313
0
    int pos = start;
1314
0
    flattenData.offsets.resize(int(pos + members.size()), -1);
1315
1316
0
    for (int member = 0; member < (int)members.size(); ++member) {
1317
0
        TType& dereferencedType = *members[member].type;
1318
0
        if (dereferencedType.isBuiltIn())
1319
0
            splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier);
1320
0
        else {
1321
0
            const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1322
0
                                                name + "." + dereferencedType.getFieldName(),
1323
0
                                                linkage, outerQualifier,
1324
0
                                                builtInArraySizes == nullptr && dereferencedType.isArray()
1325
0
                                                                       ? dereferencedType.getArraySizes()
1326
0
                                                                       : builtInArraySizes);
1327
0
            flattenData.offsets[pos++] = mpos;
1328
0
        }
1329
0
    }
1330
1331
0
    return start;
1332
0
}
1333
1334
// Figure out mapping between an array's members and an
1335
// equivalent set of individual variables.
1336
//
1337
// Assumes shouldFlatten() or equivalent was called first.
1338
int HlslParseContext::flattenArray(const TVariable& variable, const TType& type,
1339
                                   TFlattenData& flattenData, TString name, bool linkage,
1340
                                   const TQualifier& outerQualifier)
1341
0
{
1342
0
    assert(type.isSizedArray());
1343
1344
0
    const int size = type.getOuterArraySize();
1345
0
    const TType dereferencedType(type, 0);
1346
1347
0
    if (name.empty())
1348
0
        name = variable.getName();
1349
1350
    // Reserve space for this tree level.
1351
0
    int start = static_cast<int>(flattenData.offsets.size());
1352
0
    int pos   = start;
1353
0
    flattenData.offsets.resize(int(pos + size), -1);
1354
1355
0
    for (int element=0; element < size; ++element) {
1356
0
        char elementNumBuf[20];  // sufficient for MAXINT
1357
0
        snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
1358
0
        const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1359
0
                                            name + elementNumBuf, linkage, outerQualifier,
1360
0
                                            type.getArraySizes());
1361
1362
0
        flattenData.offsets[pos++] = mpos;
1363
0
    }
1364
1365
0
    return start;
1366
0
}
1367
1368
// Return true if we have flattened this node.
1369
bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1370
0
{
1371
0
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1372
0
           wasFlattened(node->getAsSymbolNode()->getId());
1373
0
}
1374
1375
// Return true if we have split this structure
1376
bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1377
0
{
1378
0
    return node != nullptr && node->getAsSymbolNode() != nullptr &&
1379
0
           wasSplit(node->getAsSymbolNode()->getId());
1380
0
}
1381
1382
// Turn an access into an aggregate that was flattened to instead be
1383
// an access to the individual variable the member was flattened to.
1384
// Assumes wasFlattened() or equivalent was called first.
1385
TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
1386
0
{
1387
0
    const TType dereferencedType(base->getType(), member);  // dereferenced type
1388
0
    const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
1389
0
    TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage,
1390
0
                                            dereferencedType, symbolNode.getFlattenSubset());
1391
1392
0
    return flattened ? flattened : base;
1393
0
}
1394
TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage,
1395
    const TType& dereferencedType, int subset)
1396
0
{
1397
0
    const auto flattenData = flattenMap.find(uniqueId);
1398
1399
0
    if (flattenData == flattenMap.end())
1400
0
        return nullptr;
1401
1402
    // Calculate new cumulative offset from the packed tree
1403
0
    int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member];
1404
1405
0
    TIntermSymbol* subsetSymbol;
1406
0
    if (!shouldFlatten(dereferencedType, outerStorage, false)) {
1407
        // Finished flattening: create symbol for variable
1408
0
        member = flattenData->second.offsets[newSubset];
1409
0
        const TVariable* memberVariable = flattenData->second.members[member];
1410
0
        subsetSymbol = intermediate.addSymbol(*memberVariable);
1411
0
        subsetSymbol->setFlattenSubset(-1);
1412
0
    } else {
1413
1414
        // If this is not the final flattening, accumulate the position and return
1415
        // an object of the partially dereferenced type.
1416
0
        subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", getLanguage(), dereferencedType);
1417
0
        subsetSymbol->setFlattenSubset(newSubset);
1418
0
    }
1419
1420
0
    return subsetSymbol;
1421
0
}
1422
1423
// For finding where the first leaf is in a subtree of a multi-level aggregate
1424
// that is just getting a subset assigned. Follows the same logic as flattenAccess,
1425
// but logically going down the "left-most" tree branch each step of the way.
1426
//
1427
// Returns the offset into the first leaf of the subset.
1428
int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const
1429
0
{
1430
0
    const TIntermSymbol* sym = node.getAsSymbolNode();
1431
0
    if (sym == nullptr)
1432
0
        return 0;
1433
0
    if (!sym->isArray() && !sym->isStruct())
1434
0
        return 0;
1435
0
    int subset = sym->getFlattenSubset();
1436
0
    if (subset == -1)
1437
0
        return 0;
1438
1439
    // Getting this far means a partial aggregate is identified by the flatten subset.
1440
    // Find the first leaf of the subset.
1441
1442
0
    const auto flattenData = flattenMap.find(sym->getId());
1443
0
    if (flattenData == flattenMap.end())
1444
0
        return 0;
1445
1446
0
    return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets);
1447
1448
0
    do {
1449
0
        subset = flattenData->second.offsets[subset];
1450
0
    } while (true);
1451
0
}
1452
// Recursively do the desent
1453
int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const
1454
0
{
1455
0
    if (!type.isArray() && !type.isStruct())
1456
0
        return offsets[subset];
1457
0
    TType derefType(type, 0);
1458
0
    return findSubtreeOffset(derefType, offsets[subset], offsets);
1459
0
}
1460
1461
// Find and return the split IO TVariable for id, or nullptr if none.
1462
TVariable* HlslParseContext::getSplitNonIoVar(long long id) const
1463
0
{
1464
0
    const auto splitNonIoVar = splitNonIoVars.find(id);
1465
0
    if (splitNonIoVar == splitNonIoVars.end())
1466
0
        return nullptr;
1467
1468
0
    return splitNonIoVar->second;
1469
0
}
1470
1471
// Pass through to base class after remembering built-in mappings.
1472
void HlslParseContext::trackLinkage(TSymbol& symbol)
1473
0
{
1474
0
    TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
1475
1476
0
    if (biType != EbvNone)
1477
0
        builtInTessLinkageSymbols[biType] = symbol.clone();
1478
1479
0
    TParseContextBase::trackLinkage(symbol);
1480
0
}
1481
1482
1483
// Returns true if the built-in is a clip or cull distance variable.
1484
bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn)
1485
0
{
1486
0
    return builtIn == EbvClipDistance || builtIn == EbvCullDistance;
1487
0
}
1488
1489
// Some types require fixed array sizes in SPIR-V, but can be scalars or
1490
// arrays of sizes SPIR-V doesn't allow.  For example, tessellation factors.
1491
// This creates the right size.  A conversion is performed when the internal
1492
// type is copied to or from the external type.  This corrects the externally
1493
// facing input or output type to abide downstream semantics.
1494
void HlslParseContext::fixBuiltInIoType(TType& type)
1495
0
{
1496
0
    int requiredArraySize = 0;
1497
0
    int requiredVectorSize = 0;
1498
1499
0
    switch (type.getQualifier().builtIn) {
1500
0
    case EbvTessLevelOuter: requiredArraySize = 4; break;
1501
0
    case EbvTessLevelInner: requiredArraySize = 2; break;
1502
1503
0
    case EbvSampleMask:
1504
0
        {
1505
            // Promote scalar to array of size 1.  Leave existing arrays alone.
1506
0
            if (!type.isArray())
1507
0
                requiredArraySize = 1;
1508
0
            break;
1509
0
        }
1510
1511
0
    case EbvWorkGroupId:        requiredVectorSize = 3; break;
1512
0
    case EbvGlobalInvocationId: requiredVectorSize = 3; break;
1513
0
    case EbvLocalInvocationId:  requiredVectorSize = 3; break;
1514
0
    case EbvTessCoord:          requiredVectorSize = 3; break;
1515
1516
0
    default:
1517
0
        if (isClipOrCullDistance(type)) {
1518
0
            const int loc = type.getQualifier().layoutLocation;
1519
1520
0
            if (type.getQualifier().builtIn == EbvClipDistance) {
1521
0
                if (type.getQualifier().storage == EvqVaryingIn)
1522
0
                    clipSemanticNSizeIn[loc] = type.getVectorSize();
1523
0
                else
1524
0
                    clipSemanticNSizeOut[loc] = type.getVectorSize();
1525
0
            } else {
1526
0
                if (type.getQualifier().storage == EvqVaryingIn)
1527
0
                    cullSemanticNSizeIn[loc] = type.getVectorSize();
1528
0
                else
1529
0
                    cullSemanticNSizeOut[loc] = type.getVectorSize();
1530
0
            }
1531
0
        }
1532
1533
0
        return;
1534
0
    }
1535
1536
    // Alter or set vector size as needed.
1537
0
    if (requiredVectorSize > 0) {
1538
0
        TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize);
1539
0
        newType.getQualifier() = type.getQualifier();
1540
1541
0
        type.shallowCopy(newType);
1542
0
    }
1543
1544
    // Alter or set array size as needed.
1545
0
    if (requiredArraySize > 0) {
1546
0
        if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) {
1547
0
            TArraySizes* arraySizes = new TArraySizes;
1548
0
            arraySizes->addInnerSize(requiredArraySize);
1549
0
            type.transferArraySizes(arraySizes);
1550
0
        }
1551
0
    }
1552
0
}
1553
1554
// Variables that correspond to the user-interface in and out of a stage
1555
// (not the built-in interface) are
1556
//  - assigned locations
1557
//  - registered as a linkage node (part of the stage's external interface).
1558
// Assumes it is called in the order in which locations should be assigned.
1559
void HlslParseContext::assignToInterface(TVariable& variable)
1560
0
{
1561
0
    const auto assignLocation = [&](TVariable& variable) {
1562
0
        TType& type = variable.getWritableType();
1563
0
        if (!type.isStruct() || type.getStruct()->size() > 0) {
1564
0
            TQualifier& qualifier = type.getQualifier();
1565
0
            if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1566
0
                if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) {
1567
                    // Strip off the outer array dimension for those having an extra one.
1568
0
                    int size;
1569
0
                    if (type.isArray() && qualifier.isArrayedIo(language)) {
1570
0
                        TType elementType(type, 0);
1571
0
                        size = intermediate.computeTypeLocationSize(elementType, language);
1572
0
                    } else
1573
0
                        size = intermediate.computeTypeLocationSize(type, language);
1574
1575
0
                    if (qualifier.storage == EvqVaryingIn) {
1576
0
                        variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
1577
0
                        nextInLocation += size;
1578
0
                    } else {
1579
0
                        variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
1580
0
                        nextOutLocation += size;
1581
0
                    }
1582
0
                }
1583
0
                trackLinkage(variable);
1584
0
            }
1585
0
        }
1586
0
    };
1587
1588
0
    if (wasFlattened(variable.getUniqueId())) {
1589
0
        auto& memberList = flattenMap[variable.getUniqueId()].members;
1590
0
        for (auto member = memberList.begin(); member != memberList.end(); ++member)
1591
0
            assignLocation(**member);
1592
0
    } else if (wasSplit(variable.getUniqueId())) {
1593
0
        TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId());
1594
0
        assignLocation(*splitIoVar);
1595
0
    } else {
1596
0
        assignLocation(variable);
1597
0
    }
1598
0
}
1599
1600
//
1601
// Handle seeing a function declarator in the grammar.  This is the precursor
1602
// to recognizing a function prototype or function definition.
1603
//
1604
void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
1605
176k
{
1606
    //
1607
    // Multiple declarations of the same function name are allowed.
1608
    //
1609
    // If this is a definition, the definition production code will check for redefinitions
1610
    // (we don't know at this point if it's a definition or not).
1611
    //
1612
176k
    bool builtIn;
1613
176k
    TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1614
176k
    const TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1615
1616
176k
    if (prototype) {
1617
        // All built-in functions are defined, even though they don't have a body.
1618
        // Count their prototype as a definition instead.
1619
176k
        if (symbolTable.atBuiltInLevel())
1620
176k
            function.setDefined();
1621
0
        else {
1622
0
            if (prevDec && ! builtIn)
1623
0
                symbol->getAsFunction()->setPrototyped();  // need a writable one, but like having prevDec as a const
1624
0
            function.setPrototyped();
1625
0
        }
1626
176k
    }
1627
1628
    // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1629
    // other forms of name collisions.
1630
176k
    if (! symbolTable.insert(function))
1631
0
        error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
1632
176k
}
1633
1634
// For struct buffers with counters, we must pass the counter buffer as hidden parameter.
1635
// This adds the hidden parameter to the parameter list in 'paramNodes' if needed.
1636
// Otherwise, it's a no-op
1637
void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param,
1638
                                                         TIntermAggregate*& paramNodes)
1639
0
{
1640
0
    if (! hasStructBuffCounter(*param.type))
1641
0
        return;
1642
1643
0
    const TString counterBlockName(intermediate.addCounterBufferName(*param.name));
1644
1645
0
    TType counterType;
1646
0
    counterBufferType(loc, counterType);
1647
0
    TVariable *variable = makeInternalVariable(counterBlockName, counterType);
1648
1649
0
    if (! symbolTable.insert(*variable))
1650
0
        error(loc, "redefinition", variable->getName().c_str(), "");
1651
1652
0
    paramNodes = intermediate.growAggregate(paramNodes,
1653
0
                                            intermediate.addSymbol(*variable, loc),
1654
0
                                            loc);
1655
0
}
1656
1657
//
1658
// Handle seeing the function prototype in front of a function definition in the grammar.
1659
// The body is handled after this function returns.
1660
//
1661
// Returns an aggregate of parameter-symbol nodes.
1662
//
1663
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
1664
                                                             const TAttributes& attributes,
1665
                                                             TIntermNode*& entryPointTree)
1666
0
{
1667
0
    currentCaller = function.getMangledName();
1668
0
    TSymbol* symbol = symbolTable.find(function.getMangledName());
1669
0
    TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1670
1671
0
    if (prevDec == nullptr)
1672
0
        error(loc, "can't find function", function.getName().c_str(), "");
1673
    // Note:  'prevDec' could be 'function' if this is the first time we've seen function
1674
    // as it would have just been put in the symbol table.  Otherwise, we're looking up
1675
    // an earlier occurrence.
1676
1677
0
    if (prevDec && prevDec->isDefined()) {
1678
        // Then this function already has a body.
1679
0
        error(loc, "function already has a body", function.getName().c_str(), "");
1680
0
    }
1681
0
    if (prevDec && ! prevDec->isDefined()) {
1682
0
        prevDec->setDefined();
1683
1684
        // Remember the return type for later checking for RETURN statements.
1685
0
        currentFunctionType = &(prevDec->getType());
1686
0
    } else
1687
0
        currentFunctionType = new TType(EbtVoid);
1688
0
    functionReturnsValue = false;
1689
1690
    // Entry points need different I/O and other handling, transform it so the
1691
    // rest of this function doesn't care.
1692
0
    entryPointTree = transformEntryPoint(loc, function, attributes);
1693
1694
    //
1695
    // New symbol table scope for body of function plus its arguments
1696
    //
1697
0
    pushScope();
1698
1699
    //
1700
    // Insert parameters into the symbol table.
1701
    // If the parameter has no name, it's not an error, just don't insert it
1702
    // (could be used for unused args).
1703
    //
1704
    // Also, accumulate the list of parameters into the AST, so lower level code
1705
    // knows where to find parameters.
1706
    //
1707
0
    TIntermAggregate* paramNodes = new TIntermAggregate;
1708
0
    for (int i = 0; i < function.getParamCount(); i++) {
1709
0
        TParameter& param = function[i];
1710
0
        if (param.name != nullptr) {
1711
0
            TVariable *variable = new TVariable(param.name, *param.type);
1712
1713
0
            if (i == 0 && function.hasImplicitThis()) {
1714
                // Anonymous 'this' members are already in a symbol-table level,
1715
                // and we need to know what function parameter to map them to.
1716
0
                symbolTable.makeInternalVariable(*variable);
1717
0
                pushImplicitThis(variable);
1718
0
            }
1719
1720
            // Insert the parameters with name in the symbol table.
1721
0
            if (! symbolTable.insert(*variable))
1722
0
                error(loc, "redefinition", variable->getName().c_str(), "");
1723
1724
            // Add parameters to the AST list.
1725
0
            if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) {
1726
                // Expand the AST parameter nodes (but not the name mangling or symbol table view)
1727
                // for structures that need to be flattened.
1728
0
                flatten(*variable, false);
1729
0
                const TTypeList* structure = variable->getType().getStruct();
1730
0
                for (int mem = 0; mem < (int)structure->size(); ++mem) {
1731
0
                    paramNodes = intermediate.growAggregate(paramNodes,
1732
0
                                                            flattenAccess(variable->getUniqueId(), mem,
1733
0
                                                                          variable->getType().getQualifier().storage,
1734
0
                                                                          *(*structure)[mem].type),
1735
0
                                                            loc);
1736
0
                }
1737
0
            } else {
1738
                // Add the parameter to the AST
1739
0
                paramNodes = intermediate.growAggregate(paramNodes,
1740
0
                                                        intermediate.addSymbol(*variable, loc),
1741
0
                                                        loc);
1742
0
            }
1743
1744
            // Add hidden AST parameter for struct buffer counters, if needed.
1745
0
            addStructBufferHiddenCounterParam(loc, param, paramNodes);
1746
0
        } else
1747
0
            paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
1748
0
    }
1749
0
    if (function.hasIllegalImplicitThis())
1750
0
        pushImplicitThis(nullptr);
1751
1752
0
    intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1753
0
    loopNestingLevel = 0;
1754
0
    controlFlowNestingLevel = 0;
1755
0
    postEntryPointReturn = false;
1756
1757
0
    return paramNodes;
1758
0
}
1759
1760
// Handle all [attrib] attribute for the shader entry point
1761
void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
1762
0
{
1763
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1764
0
        switch (it->name) {
1765
0
        case EatNumThreads:
1766
0
        {
1767
0
            const TIntermSequence& sequence = it->args->getSequence();
1768
            // numthreads has three dimensions (x, y, z); localSize is sized to match,
1769
            // so reject extra arguments rather than indexing past it.
1770
0
            if (sequence.size() > 3) {
1771
0
                error(loc, "expected at most three arguments", "numthreads", "");
1772
0
                break;
1773
0
            }
1774
0
            for (int lid = 0; lid < int(sequence.size()); ++lid)
1775
0
                intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1776
0
            break;
1777
0
        }
1778
0
        case EatInstance: 
1779
0
        {
1780
0
            int invocations;
1781
1782
0
            if (!it->getInt(invocations)) {
1783
0
                error(loc, "invalid instance", "", "");
1784
0
            } else {
1785
0
                if (!intermediate.setInvocations(invocations))
1786
0
                    error(loc, "cannot change previously set instance attribute", "", "");
1787
0
            }
1788
0
            break;
1789
0
        }
1790
0
        case EatMaxVertexCount:
1791
0
        {
1792
0
            int maxVertexCount;
1793
1794
0
            if (! it->getInt(maxVertexCount)) {
1795
0
                error(loc, "invalid maxvertexcount", "", "");
1796
0
            } else {
1797
0
                if (! intermediate.setVertices(maxVertexCount))
1798
0
                    error(loc, "cannot change previously set maxvertexcount attribute", "", "");
1799
0
            }
1800
0
            break;
1801
0
        }
1802
0
        case EatPatchConstantFunc:
1803
0
        {
1804
0
            TString pcfName;
1805
0
            if (! it->getString(pcfName, 0, false)) {
1806
0
                error(loc, "invalid patch constant function", "", "");
1807
0
            } else {
1808
0
                patchConstantFunctionName = pcfName;
1809
0
            }
1810
0
            break;
1811
0
        }
1812
0
        case EatDomain:
1813
0
        {
1814
            // Handle [domain("...")]
1815
0
            TString domainStr;
1816
0
            if (! it->getString(domainStr)) {
1817
0
                error(loc, "invalid domain", "", "");
1818
0
            } else {
1819
0
                TLayoutGeometry domain = ElgNone;
1820
1821
0
                if (domainStr == "tri") {
1822
0
                    domain = ElgTriangles;
1823
0
                } else if (domainStr == "quad") {
1824
0
                    domain = ElgQuads;
1825
0
                } else if (domainStr == "isoline") {
1826
0
                    domain = ElgIsolines;
1827
0
                } else {
1828
0
                    error(loc, "unsupported domain type", domainStr.c_str(), "");
1829
0
                }
1830
1831
0
                if (language == EShLangTessEvaluation) {
1832
0
                    if (! intermediate.setInputPrimitive(domain))
1833
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1834
0
                } else {
1835
0
                    if (! intermediate.setOutputPrimitive(domain))
1836
0
                        error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1837
0
                }
1838
0
            }
1839
0
            break;
1840
0
        }
1841
0
        case EatOutputTopology:
1842
0
        {
1843
            // Handle [outputtopology("...")]
1844
0
            TString topologyStr;
1845
0
            if (! it->getString(topologyStr)) {
1846
0
                error(loc, "invalid outputtopology", "", "");
1847
0
            } else {
1848
0
                TVertexOrder vertexOrder = EvoNone;
1849
0
                TLayoutGeometry primitive = ElgNone;
1850
1851
0
                if (topologyStr == "point") {
1852
0
                    intermediate.setPointMode();
1853
0
                } else if (topologyStr == "line") {
1854
0
                    primitive = ElgIsolines;
1855
0
                } else if (topologyStr == "triangle_cw") {
1856
0
                    vertexOrder = EvoCw;
1857
0
                    primitive = ElgTriangles;
1858
0
                } else if (topologyStr == "triangle_ccw") {
1859
0
                    vertexOrder = EvoCcw;
1860
0
                    primitive = ElgTriangles;
1861
0
                } else {
1862
0
                    error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
1863
0
                }
1864
1865
0
                if (vertexOrder != EvoNone) {
1866
0
                    if (! intermediate.setVertexOrder(vertexOrder)) {
1867
0
                        error(loc, "cannot change previously set outputtopology",
1868
0
                              TQualifier::getVertexOrderString(vertexOrder), "");
1869
0
                    }
1870
0
                }
1871
0
                if (primitive != ElgNone)
1872
0
                    intermediate.setOutputPrimitive(primitive);
1873
0
            }
1874
0
            break;
1875
0
        }
1876
0
        case EatPartitioning:
1877
0
        {
1878
            // Handle [partitioning("...")]
1879
0
            TString partitionStr;
1880
0
            if (! it->getString(partitionStr)) {
1881
0
                error(loc, "invalid partitioning", "", "");
1882
0
            } else {
1883
0
                TVertexSpacing partitioning = EvsNone;
1884
1885
0
                if (partitionStr == "integer") {
1886
0
                    partitioning = EvsEqual;
1887
0
                } else if (partitionStr == "fractional_even") {
1888
0
                    partitioning = EvsFractionalEven;
1889
0
                } else if (partitionStr == "fractional_odd") {
1890
0
                    partitioning = EvsFractionalOdd;
1891
                    //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
1892
0
                } else {
1893
0
                    error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
1894
0
                }
1895
1896
0
                if (! intermediate.setVertexSpacing(partitioning))
1897
0
                    error(loc, "cannot change previously set partitioning",
1898
0
                          TQualifier::getVertexSpacingString(partitioning), "");
1899
0
            }
1900
0
            break;
1901
0
        }
1902
0
        case EatOutputControlPoints:
1903
0
        {
1904
            // Handle [outputcontrolpoints("...")]
1905
0
            int ctrlPoints;
1906
0
            if (! it->getInt(ctrlPoints)) {
1907
0
                error(loc, "invalid outputcontrolpoints", "", "");
1908
0
            } else {
1909
0
                if (! intermediate.setVertices(ctrlPoints)) {
1910
0
                    error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
1911
0
                }
1912
0
            }
1913
0
            break;
1914
0
        }
1915
0
        case EatEarlyDepthStencil:
1916
0
            intermediate.setEarlyFragmentTests();
1917
0
            break;
1918
0
        case EatBuiltIn:
1919
0
        case EatLocation:
1920
            // tolerate these because of dual use of entrypoint and type attributes
1921
0
            break;
1922
0
        default:
1923
0
            warn(loc, "attribute does not apply to entry point", "", "");
1924
0
            break;
1925
0
        }
1926
0
    }
1927
0
}
1928
1929
// Update the given type with any type-like attribute information in the
1930
// attributes.
1931
void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
1932
    bool allowEntry)
1933
571k
{
1934
571k
    if (attributes.size() == 0)
1935
571k
        return;
1936
1937
0
    int value;
1938
0
    TString builtInString;
1939
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1940
0
        switch (it->name) {
1941
0
        case EatLocation:
1942
            // location
1943
0
            if (it->getInt(value))
1944
0
                type.getQualifier().layoutLocation = value;
1945
0
            else
1946
0
                error(loc, "needs a literal integer", "location", "");
1947
0
            break;
1948
0
        case EatBinding:
1949
            // binding
1950
0
            if (it->getInt(value)) {
1951
0
                type.getQualifier().layoutBinding = value;
1952
0
                type.getQualifier().layoutSet = 0;
1953
0
            } else
1954
0
                error(loc, "needs a literal integer", "binding", "");
1955
            // set
1956
0
            if (it->getInt(value, 1))
1957
0
                type.getQualifier().layoutSet = value;
1958
0
            break;
1959
0
        case EatGlobalBinding:
1960
            // global cbuffer binding
1961
0
            if (it->getInt(value))
1962
0
                globalUniformBinding = value;
1963
0
            else
1964
0
                error(loc, "needs a literal integer", "global binding", "");
1965
            // global cbuffer set
1966
0
            if (it->getInt(value, 1))
1967
0
                globalUniformSet = value;
1968
0
            break;
1969
0
        case EatInputAttachment:
1970
            // input attachment
1971
0
            if (it->getInt(value))
1972
0
                type.getQualifier().layoutAttachment = value;
1973
0
            else
1974
0
                error(loc, "needs a literal integer", "input attachment", "");
1975
0
            break;
1976
0
        case EatBuiltIn:
1977
            // PointSize built-in
1978
0
            if (it->getString(builtInString, 0, false)) {
1979
0
                if (builtInString == "PointSize")
1980
0
                    type.getQualifier().builtIn = EbvPointSize;
1981
0
            }
1982
0
            break;
1983
0
        case EatPushConstant:
1984
            // push_constant
1985
0
            type.getQualifier().layoutPushConstant = true;
1986
0
            break;
1987
0
        case EatConstantId:
1988
            // specialization constant
1989
0
            if (type.getQualifier().storage != EvqConst) {
1990
0
                error(loc, "needs a const type", "constant_id", "");
1991
0
                break;
1992
0
            }
1993
0
            if (it->getInt(value)) {
1994
0
                TSourceLoc loc;
1995
0
                loc.init();
1996
0
                setSpecConstantId(loc, type.getQualifier(), (unsigned)value);
1997
0
            }
1998
0
            break;
1999
2000
        // image formats
2001
0
        case EatFormatRgba32f:      type.getQualifier().layoutFormat = ElfRgba32f;      break;
2002
0
        case EatFormatRgba16f:      type.getQualifier().layoutFormat = ElfRgba16f;      break;
2003
0
        case EatFormatR32f:         type.getQualifier().layoutFormat = ElfR32f;         break;
2004
0
        case EatFormatRgba8:        type.getQualifier().layoutFormat = ElfRgba8;        break;
2005
0
        case EatFormatRgba8Snorm:   type.getQualifier().layoutFormat = ElfRgba8Snorm;   break;
2006
0
        case EatFormatRg32f:        type.getQualifier().layoutFormat = ElfRg32f;        break;
2007
0
        case EatFormatRg16f:        type.getQualifier().layoutFormat = ElfRg16f;        break;
2008
0
        case EatFormatR11fG11fB10f: type.getQualifier().layoutFormat = ElfR11fG11fB10f; break;
2009
0
        case EatFormatR16f:         type.getQualifier().layoutFormat = ElfR16f;         break;
2010
0
        case EatFormatRgba16:       type.getQualifier().layoutFormat = ElfRgba16;       break;
2011
0
        case EatFormatRgb10A2:      type.getQualifier().layoutFormat = ElfRgb10A2;      break;
2012
0
        case EatFormatRg16:         type.getQualifier().layoutFormat = ElfRg16;         break;
2013
0
        case EatFormatRg8:          type.getQualifier().layoutFormat = ElfRg8;          break;
2014
0
        case EatFormatR16:          type.getQualifier().layoutFormat = ElfR16;          break;
2015
0
        case EatFormatR8:           type.getQualifier().layoutFormat = ElfR8;           break;
2016
0
        case EatFormatRgba16Snorm:  type.getQualifier().layoutFormat = ElfRgba16Snorm;  break;
2017
0
        case EatFormatRg16Snorm:    type.getQualifier().layoutFormat = ElfRg16Snorm;    break;
2018
0
        case EatFormatRg8Snorm:     type.getQualifier().layoutFormat = ElfRg8Snorm;     break;
2019
0
        case EatFormatR16Snorm:     type.getQualifier().layoutFormat = ElfR16Snorm;     break;
2020
0
        case EatFormatR8Snorm:      type.getQualifier().layoutFormat = ElfR8Snorm;      break;
2021
0
        case EatFormatRgba32i:      type.getQualifier().layoutFormat = ElfRgba32i;      break;
2022
0
        case EatFormatRgba16i:      type.getQualifier().layoutFormat = ElfRgba16i;      break;
2023
0
        case EatFormatRgba8i:       type.getQualifier().layoutFormat = ElfRgba8i;       break;
2024
0
        case EatFormatR32i:         type.getQualifier().layoutFormat = ElfR32i;         break;
2025
0
        case EatFormatRg32i:        type.getQualifier().layoutFormat = ElfRg32i;        break;
2026
0
        case EatFormatRg16i:        type.getQualifier().layoutFormat = ElfRg16i;        break;
2027
0
        case EatFormatRg8i:         type.getQualifier().layoutFormat = ElfRg8i;         break;
2028
0
        case EatFormatR16i:         type.getQualifier().layoutFormat = ElfR16i;         break;
2029
0
        case EatFormatR8i:          type.getQualifier().layoutFormat = ElfR8i;          break;
2030
0
        case EatFormatRgba32ui:     type.getQualifier().layoutFormat = ElfRgba32ui;     break;
2031
0
        case EatFormatRgba16ui:     type.getQualifier().layoutFormat = ElfRgba16ui;     break;
2032
0
        case EatFormatRgba8ui:      type.getQualifier().layoutFormat = ElfRgba8ui;      break;
2033
0
        case EatFormatR32ui:        type.getQualifier().layoutFormat = ElfR32ui;        break;
2034
0
        case EatFormatRgb10a2ui:    type.getQualifier().layoutFormat = ElfRgb10a2ui;    break;
2035
0
        case EatFormatRg32ui:       type.getQualifier().layoutFormat = ElfRg32ui;       break;
2036
0
        case EatFormatRg16ui:       type.getQualifier().layoutFormat = ElfRg16ui;       break;
2037
0
        case EatFormatRg8ui:        type.getQualifier().layoutFormat = ElfRg8ui;        break;
2038
0
        case EatFormatR16ui:        type.getQualifier().layoutFormat = ElfR16ui;        break;
2039
0
        case EatFormatR8ui:         type.getQualifier().layoutFormat = ElfR8ui;         break;
2040
0
        case EatFormatUnknown:      type.getQualifier().layoutFormat = ElfNone;         break;
2041
2042
0
        case EatNonWritable:  type.getQualifier().readonly = true;   break;
2043
0
        case EatNonReadable:  type.getQualifier().writeonly = true;  break;
2044
2045
0
        default:
2046
0
            if (! allowEntry)
2047
0
                warn(loc, "attribute does not apply to a type", "", "");
2048
0
            break;
2049
0
        }
2050
0
    }
2051
0
}
2052
2053
//
2054
// Do all special handling for the entry point, including wrapping
2055
// the shader's entry point with the official entry point that will call it.
2056
//
2057
// The following:
2058
//
2059
//    retType shaderEntryPoint(args...) // shader declared entry point
2060
//    { body }
2061
//
2062
// Becomes
2063
//
2064
//    out retType ret;
2065
//    in iargs<that are input>...;
2066
//    out oargs<that are output> ...;
2067
//
2068
//    void shaderEntryPoint()    // synthesized, but official, entry point
2069
//    {
2070
//        args<that are input> = iargs...;
2071
//        ret = @shaderEntryPoint(args...);
2072
//        oargs = args<that are output>...;
2073
//    }
2074
//    retType @shaderEntryPoint(args...)
2075
//    { body }
2076
//
2077
// The symbol table will still map the original entry point name to the
2078
// the modified function and its new name:
2079
//
2080
//    symbol table:  shaderEntryPoint  ->   @shaderEntryPoint
2081
//
2082
// Returns nullptr if no entry-point tree was built, otherwise, returns
2083
// a subtree that creates the entry point.
2084
//
2085
TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
2086
                                                   const TAttributes& attributes)
2087
0
{
2088
    // Return true if this is a tessellation patch constant function input to a domain shader.
2089
0
    const auto isDsPcfInput = [this](const TType& type) {
2090
0
        return language == EShLangTessEvaluation &&
2091
0
        type.contains([](const TType* t) {
2092
0
                return t->getQualifier().builtIn == EbvTessLevelOuter ||
2093
0
                       t->getQualifier().builtIn == EbvTessLevelInner;
2094
0
            });
2095
0
    };
2096
2097
    // if we aren't in the entry point, fix the IO as such and exit
2098
0
    if (! isEntrypointName(userFunction.getName())) {
2099
0
        remapNonEntryPointIO(userFunction);
2100
0
        return nullptr;
2101
0
    }
2102
2103
0
    entryPointFunction = &userFunction; // needed in finish()
2104
2105
    // Handle entry point attributes
2106
0
    handleEntryPointAttributes(loc, attributes);
2107
2108
    // entry point logic...
2109
2110
    // Move parameters and return value to shader in/out
2111
0
    TVariable* entryPointOutput; // gets created in remapEntryPointIO
2112
0
    TVector<TVariable*> inputs;
2113
0
    TVector<TVariable*> outputs;
2114
0
    remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
2115
2116
    // Further this return/in/out transform by flattening, splitting, and assigning locations
2117
0
    const auto makeVariableInOut = [&](TVariable& variable) {
2118
0
        if (variable.getType().isStruct()) {
2119
0
            bool arrayed = variable.getType().getQualifier().isArrayedIo(language);
2120
0
            flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */, arrayed);
2121
0
        }
2122
        // TODO: flatten arrays too
2123
        // TODO: flatten everything in I/O
2124
        // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed
2125
2126
        // For clip and cull distance, multiple output variables potentially get merged
2127
        // into one in assignClipCullDistance.  That code in assignClipCullDistance
2128
        // handles the interface logic, so we avoid it here in that case.
2129
0
        if (!isClipOrCullDistance(variable.getType()))
2130
0
            assignToInterface(variable);
2131
0
    };
2132
0
    if (entryPointOutput != nullptr)
2133
0
        makeVariableInOut(*entryPointOutput);
2134
0
    for (auto it = inputs.begin(); it != inputs.end(); ++it)
2135
0
        if (!isDsPcfInput((*it)->getType()))  // wait until the end for PCF input (see comment below)
2136
0
            makeVariableInOut(*(*it));
2137
0
    for (auto it = outputs.begin(); it != outputs.end(); ++it)
2138
0
        makeVariableInOut(*(*it));
2139
2140
    // In the domain shader, PCF input must be at the end of the linkage.  That's because in the
2141
    // hull shader there is no ordering: the output comes from the separate PCF, which does not
2142
    // participate in the argument list.  That is always put at the end of the HS linkage, so the
2143
    // input side of the DS must match.  The argument may be in any position in the DS argument list
2144
    // however, so this ensures the linkage is built in the correct order regardless of argument order.
2145
0
    if (language == EShLangTessEvaluation) {
2146
0
        for (auto it = inputs.begin(); it != inputs.end(); ++it)
2147
0
            if (isDsPcfInput((*it)->getType()))
2148
0
                makeVariableInOut(*(*it));
2149
0
    }
2150
2151
    // Add uniform parameters to the $Global uniform block.
2152
0
    TVector<TVariable*> opaque_uniforms;
2153
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2154
0
        TType& paramType = *userFunction[i].type;
2155
0
        TString& paramName = *userFunction[i].name;
2156
0
        if (paramType.getQualifier().storage == EvqUniform) {
2157
0
            if (!paramType.containsOpaque()) {
2158
                // Add it to the global uniform block.
2159
0
                growGlobalUniformBlock(loc, paramType, paramName);
2160
0
            } else {
2161
                // Declare it as a separate variable.
2162
0
                TVariable *var = makeInternalVariable(paramName.c_str(), paramType);
2163
0
                opaque_uniforms.push_back(var);
2164
0
            }
2165
0
        }
2166
0
    }
2167
2168
    // Synthesize the call
2169
2170
0
    pushScope(); // matches the one in handleFunctionBody()
2171
2172
    // new signature
2173
0
    TType voidType(EbtVoid);
2174
0
    TFunction synthEntryPoint(&userFunction.getName(), voidType);
2175
0
    TIntermAggregate* synthParams = new TIntermAggregate();
2176
0
    intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
2177
0
    intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
2178
0
    intermediate.incrementEntryPointCount();
2179
0
    TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
2180
2181
    // change original name
2182
0
    userFunction.addPrefix("@");                         // change the name in the function, but not in the symbol table
2183
2184
    // Copy inputs (shader-in -> calling arg), while building up the call node
2185
0
    TVector<TVariable*> argVars;
2186
0
    TIntermAggregate* synthBody = new TIntermAggregate();
2187
0
    auto inputIt = inputs.begin();
2188
0
    auto opaqueUniformIt = opaque_uniforms.begin();
2189
0
    TIntermTyped* callingArgs = nullptr;
2190
2191
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2192
0
        TParameter& param = userFunction[i];
2193
0
        argVars.push_back(makeInternalVariable(*param.name, *param.type));
2194
0
        argVars.back()->getWritableType().getQualifier().makeTemporary();
2195
2196
        // Track the input patch, which is the only non-builtin supported by hull shader PCF.
2197
0
        if (param.getDeclaredBuiltIn() == EbvInputPatch)
2198
0
            inputPatch = argVars.back();
2199
2200
0
        TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
2201
0
        handleFunctionArgument(&callee, callingArgs, arg);
2202
0
        if (param.type->getQualifier().isParamInput()) {
2203
0
            TIntermTyped* input = intermediate.addSymbol(**inputIt);
2204
0
            if (input->getType().getQualifier().builtIn == EbvFragCoord && intermediate.getDxPositionW()) {
2205
                // Replace FragCoord W with reciprocal
2206
0
                auto pos_xyz = handleDotDereference(loc, input, "xyz");
2207
0
                auto pos_w   = handleDotDereference(loc, input, "w");
2208
0
                auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2209
0
                auto recip_w = intermediate.addBinaryMath(EOpDiv, one, pos_w, loc);
2210
0
                TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
2211
0
                dst->getSequence().push_back(pos_xyz);
2212
0
                dst->getSequence().push_back(recip_w);
2213
0
                dst->setType(TType(EbtFloat, EvqTemporary, 4));
2214
0
                dst->setLoc(loc);
2215
0
                input = dst;
2216
0
            }
2217
0
            intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, input));
2218
0
            inputIt++;
2219
0
        }
2220
0
        if (param.type->getQualifier().storage == EvqUniform) {
2221
0
            if (!param.type->containsOpaque()) {
2222
                // Look it up in the $Global uniform block.
2223
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2224
0
                                                                   handleVariable(loc, param.name)));
2225
0
            } else {
2226
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2227
0
                                                                   intermediate.addSymbol(**opaqueUniformIt)));
2228
0
                ++opaqueUniformIt;
2229
0
            }
2230
0
        }
2231
0
    }
2232
2233
    // Call
2234
0
    currentCaller = synthEntryPoint.getMangledName();
2235
0
    TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
2236
0
    currentCaller = userFunction.getMangledName();
2237
2238
    // Return value
2239
0
    if (entryPointOutput) {
2240
0
        TIntermTyped* returnAssign;
2241
2242
        // For hull shaders, the wrapped entry point return value is written to
2243
        // an array element as indexed by invocation ID, which we might have to make up.
2244
        // This is required to match SPIR-V semantics.
2245
0
        if (language == EShLangTessControl) {
2246
0
            TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
2247
2248
            // If there is no user declared invocation ID, we must make one.
2249
0
            if (invocationIdSym == nullptr) {
2250
0
                TType invocationIdType(EbtUint, EvqIn, 1);
2251
0
                TString* invocationIdName = NewPoolTString("InvocationId");
2252
0
                invocationIdType.getQualifier().builtIn = EbvInvocationId;
2253
2254
0
                TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
2255
2256
0
                globalQualifierFix(loc, variable->getWritableType().getQualifier());
2257
0
                trackLinkage(*variable);
2258
2259
0
                invocationIdSym = intermediate.addSymbol(*variable);
2260
0
            }
2261
2262
0
            TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
2263
0
                                                          invocationIdSym, loc);
2264
2265
            // Set the type of the array element being dereferenced
2266
0
            const TType derefElementType(entryPointOutput->getType(), 0);
2267
0
            element->setType(derefElementType);
2268
2269
0
            returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
2270
0
        } else {
2271
0
            returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
2272
0
        }
2273
0
        intermediate.growAggregate(synthBody, returnAssign);
2274
0
    } else
2275
0
        intermediate.growAggregate(synthBody, callReturn);
2276
2277
    // Output copies
2278
0
    auto outputIt = outputs.begin();
2279
0
    for (int i = 0; i < userFunction.getParamCount(); i++) {
2280
0
        TParameter& param = userFunction[i];
2281
2282
        // GS outputs are via emit, so we do not copy them here.
2283
0
        if (param.type->getQualifier().isParamOutput()) {
2284
0
            if (param.getDeclaredBuiltIn() == EbvGsOutputStream) {
2285
                // GS output stream does not assign outputs here: it's the Append() method
2286
                // which writes to the output, probably multiple times separated by Emit.
2287
                // We merely remember the output to use, here.
2288
0
                gsStreamOutput = *outputIt;
2289
0
            } else {
2290
0
                intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
2291
0
                                                                   intermediate.addSymbol(**outputIt),
2292
0
                                                                   intermediate.addSymbol(*argVars[i])));
2293
0
            }
2294
2295
0
            outputIt++;
2296
0
        }
2297
0
    }
2298
2299
    // Put the pieces together to form a full function subtree
2300
    // for the synthesized entry point.
2301
0
    synthBody->setOperator(EOpSequence);
2302
0
    TIntermNode* synthFunctionDef = synthParams;
2303
0
    handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
2304
2305
0
    entryPointFunctionBody = synthBody;
2306
2307
0
    return synthFunctionDef;
2308
0
}
2309
2310
void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody,
2311
                                          TIntermNode*& node)
2312
0
{
2313
0
    node = intermediate.growAggregate(node, functionBody);
2314
0
    intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
2315
0
    node->getAsAggregate()->setName(function.getMangledName().c_str());
2316
2317
0
    popScope();
2318
0
    if (function.hasImplicitThis())
2319
0
        popImplicitThis();
2320
2321
0
    if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
2322
0
        error(loc, "function does not return a value:", "", function.getName().c_str());
2323
0
}
2324
2325
// AST I/O is done through shader globals declared in the 'in' or 'out'
2326
// storage class.  An HLSL entry point has a return value, input parameters
2327
// and output parameters.  These need to get remapped to the AST I/O.
2328
void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
2329
    TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
2330
0
{
2331
    // We might have in input structure type with no decorations that caused it
2332
    // to look like an input type, yet it has (e.g.) interpolation types that
2333
    // must be modified that turn it into an input type.
2334
    // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
2335
0
    const auto synthesizeEditedInput = [this](TType& type) {
2336
        // True if a type needs to be 'flat'
2337
0
        const auto needsFlat = [](const TType& type) {
2338
0
            return type.containsBasicType(EbtInt) ||
2339
0
                    type.containsBasicType(EbtUint) ||
2340
0
                    type.containsBasicType(EbtInt64) ||
2341
0
                    type.containsBasicType(EbtUint64) ||
2342
0
                    type.containsBasicType(EbtBool) ||
2343
0
                    type.containsBasicType(EbtDouble);
2344
0
        };
2345
2346
0
        if (language == EShLangFragment && needsFlat(type)) {
2347
0
            if (type.isStruct()) {
2348
0
                TTypeList* finalList = nullptr;
2349
0
                auto it = ioTypeMap.find(type.getStruct());
2350
0
                if (it == ioTypeMap.end() || it->second.input == nullptr) {
2351
                    // Getting here means we have no input struct, but we need one.
2352
0
                    auto list = new TTypeList;
2353
0
                    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
2354
0
                        TType* newType = new TType;
2355
0
                        newType->shallowCopy(*member->type);
2356
0
                        TTypeLoc typeLoc = { newType, member->loc };
2357
0
                        list->push_back(typeLoc);
2358
0
                    }
2359
                    // install the new input type
2360
0
                    if (it == ioTypeMap.end()) {
2361
0
                        tIoKinds newLists = { list, nullptr, nullptr };
2362
0
                        ioTypeMap[type.getStruct()] = newLists;
2363
0
                    } else
2364
0
                        it->second.input = list;
2365
0
                    finalList = list;
2366
0
                } else
2367
0
                    finalList = it->second.input;
2368
                // edit for 'flat'
2369
0
                for (auto member = finalList->begin(); member != finalList->end(); ++member) {
2370
0
                    if (needsFlat(*member->type)) {
2371
0
                        member->type->getQualifier().clearInterpolation();
2372
0
                        member->type->getQualifier().flat = true;
2373
0
                    }
2374
0
                }
2375
0
            } else {
2376
0
                type.getQualifier().clearInterpolation();
2377
0
                type.getQualifier().flat = true;
2378
0
            }
2379
0
        }
2380
0
    };
2381
2382
    // Do the actual work to make a type be a shader input or output variable,
2383
    // and clear the original to be non-IO (for use as a normal function parameter/return).
2384
0
    const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
2385
0
        TVariable* ioVariable = makeInternalVariable(name, type);
2386
0
        clearUniformInputOutput(type.getQualifier());
2387
0
        if (type.isStruct()) {
2388
0
            auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
2389
0
            if (newLists != ioTypeMap.end()) {
2390
0
                if (storage == EvqVaryingIn && newLists->second.input)
2391
0
                    ioVariable->getWritableType().setStruct(newLists->second.input);
2392
0
                else if (storage == EvqVaryingOut && newLists->second.output)
2393
0
                    ioVariable->getWritableType().setStruct(newLists->second.output);
2394
0
            }
2395
0
        }
2396
0
        if (storage == EvqVaryingIn) {
2397
0
            correctInput(ioVariable->getWritableType().getQualifier());
2398
0
            if (language == EShLangTessEvaluation)
2399
0
                if (!ioVariable->getType().isArray())
2400
0
                    ioVariable->getWritableType().getQualifier().patch = true;
2401
0
        } else {
2402
0
            correctOutput(ioVariable->getWritableType().getQualifier());
2403
0
        }
2404
0
        ioVariable->getWritableType().getQualifier().storage = storage;
2405
2406
0
        fixBuiltInIoType(ioVariable->getWritableType());
2407
2408
0
        return ioVariable;
2409
0
    };
2410
2411
    // return value is actually a shader-scoped output (out)
2412
0
    if (function.getType().getBasicType() == EbtVoid) {
2413
0
        returnValue = nullptr;
2414
0
    } else {
2415
0
        if (language == EShLangTessControl) {
2416
            // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
2417
            // array in SPIR-V semantics.  We'll write to it indexed by invocation ID.
2418
2419
0
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2420
2421
0
            TType outputType;
2422
0
            outputType.shallowCopy(function.getType());
2423
2424
            // vertices has necessarily already been set when handling entry point attributes.
2425
0
            TArraySizes* arraySizes = new TArraySizes;
2426
0
            arraySizes->addInnerSize(intermediate.getVertices());
2427
0
            outputType.transferArraySizes(arraySizes);
2428
2429
0
            clearUniformInputOutput(function.getWritableType().getQualifier());
2430
0
            returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
2431
0
        } else {
2432
0
            returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2433
0
        }
2434
0
    }
2435
2436
    // parameters are actually shader-scoped inputs and outputs (in or out)
2437
0
    for (int i = 0; i < function.getParamCount(); i++) {
2438
0
        TType& paramType = *function[i].type;
2439
0
        if (paramType.getQualifier().isParamInput()) {
2440
0
            synthesizeEditedInput(paramType);
2441
0
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
2442
0
            inputs.push_back(argAsGlobal);
2443
0
        }
2444
0
        if (paramType.getQualifier().isParamOutput()) {
2445
0
            TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
2446
0
            outputs.push_back(argAsGlobal);
2447
0
        }
2448
0
    }
2449
0
}
2450
2451
// An HLSL function that looks like an entry point, but is not,
2452
// declares entry point IO built-ins, but these have to be undone.
2453
void HlslParseContext::remapNonEntryPointIO(TFunction& function)
2454
0
{
2455
    // return value
2456
0
    if (function.getType().getBasicType() != EbtVoid)
2457
0
        clearUniformInputOutput(function.getWritableType().getQualifier());
2458
2459
    // parameters.
2460
    // References to structuredbuffer types are left unmodified
2461
0
    for (int i = 0; i < function.getParamCount(); i++)
2462
0
        if (!isReference(*function[i].type))
2463
0
            clearUniformInputOutput(function[i].type->getQualifier());
2464
0
}
2465
2466
TIntermNode* HlslParseContext::handleDeclare(const TSourceLoc& loc, TIntermTyped* var)
2467
0
{
2468
0
    return intermediate.addUnaryNode(EOpDeclare, var, loc, TType(EbtVoid));
2469
0
}
2470
2471
// Handle function returns, including type conversions to the function return type
2472
// if necessary.
2473
TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
2474
0
{
2475
0
    functionReturnsValue = true;
2476
2477
0
    if (currentFunctionType->getBasicType() == EbtVoid) {
2478
0
        error(loc, "void function cannot return a value", "return", "");
2479
0
        return intermediate.addBranch(EOpReturn, loc);
2480
0
    } else if (*currentFunctionType != value->getType()) {
2481
0
        value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
2482
0
        if (value && *currentFunctionType != value->getType())
2483
0
            value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value);
2484
0
        if (value == nullptr || *currentFunctionType != value->getType()) {
2485
0
            error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
2486
0
            return value;
2487
0
        }
2488
0
    }
2489
2490
0
    return intermediate.addBranch(EOpReturn, value, loc);
2491
0
}
2492
2493
void HlslParseContext::handleFunctionArgument(TFunction* function,
2494
                                              TIntermTyped*& arguments, TIntermTyped* newArg)
2495
0
{
2496
0
    TParameter param = { nullptr, new TType, nullptr };
2497
0
    param.type->shallowCopy(newArg->getType());
2498
2499
0
    function->addParameter(param);
2500
0
    if (arguments)
2501
0
        arguments = intermediate.growAggregate(arguments, newArg);
2502
0
    else
2503
0
        arguments = newArg;
2504
0
}
2505
2506
// FragCoord may require special loading: we can optionally reciprocate W.
2507
TIntermTyped* HlslParseContext::assignFromFragCoord(const TSourceLoc& loc, TOperator op,
2508
                                                    TIntermTyped* left, TIntermTyped* right)
2509
0
{
2510
    // If we are not asked for reciprocal W, use a plain old assign.
2511
0
    if (!intermediate.getDxPositionW())
2512
0
        return intermediate.addAssign(op, left, right, loc);
2513
2514
    // If we get here, we should reciprocate W.
2515
0
    TIntermAggregate* assignList = nullptr;
2516
2517
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2518
0
    TVariable* rhsTempVar = nullptr;
2519
0
    rhsTempVar = makeInternalVariable("@fragcoord", right->getType());
2520
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2521
2522
0
    {
2523
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2524
0
        assignList = intermediate.growAggregate(assignList,
2525
0
            intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2526
0
    }
2527
2528
    // tmp.w = 1.0 / tmp.w
2529
0
    {
2530
0
        const int W = 3;
2531
2532
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2533
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2534
0
        TIntermTyped* index = intermediate.addConstantUnion(W, loc);
2535
2536
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2537
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2538
2539
0
        const TType derefType(right->getType(), 0);
2540
2541
0
        lhsElement->setType(derefType);
2542
0
        rhsElement->setType(derefType);
2543
2544
0
        auto one     = intermediate.addConstantUnion(1.0, EbtFloat, loc);
2545
0
        auto recip_w = intermediate.addBinaryMath(EOpDiv, one, rhsElement, loc);
2546
2547
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, recip_w, loc));
2548
0
    }
2549
2550
    // Assign the rhs temp (now with W reciprocal) to the final output
2551
0
    {
2552
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2553
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2554
0
    }
2555
2556
0
    assert(assignList != nullptr);
2557
0
    assignList->setOperator(EOpSequence);
2558
2559
0
    return assignList;
2560
0
}
2561
2562
// Position may require special handling: we can optionally invert Y.
2563
// See: https://github.com/KhronosGroup/glslang/issues/1173
2564
//      https://github.com/KhronosGroup/glslang/issues/494
2565
TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
2566
                                               TIntermTyped* left, TIntermTyped* right)
2567
0
{
2568
    // If we are not asked for Y inversion, use a plain old assign.
2569
0
    if (!intermediate.getInvertY())
2570
0
        return intermediate.addAssign(op, left, right, loc);
2571
2572
    // If we get here, we should invert Y.
2573
0
    TIntermAggregate* assignList = nullptr;
2574
2575
    // If this is a complex rvalue, we don't want to dereference it many times.  Create a temporary.
2576
0
    TVariable* rhsTempVar = nullptr;
2577
0
    rhsTempVar = makeInternalVariable("@position", right->getType());
2578
0
    rhsTempVar->getWritableType().getQualifier().makeTemporary();
2579
2580
0
    {
2581
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2582
0
        assignList = intermediate.growAggregate(assignList,
2583
0
                                                intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2584
0
    }
2585
2586
    // pos.y = -pos.y
2587
0
    {
2588
0
        const int Y = 1;
2589
2590
0
        TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2591
0
        TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2592
0
        TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
2593
2594
0
        TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2595
0
        TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2596
2597
0
        const TType derefType(right->getType(), 0);
2598
2599
0
        lhsElement->setType(derefType);
2600
0
        rhsElement->setType(derefType);
2601
2602
0
        TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
2603
2604
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
2605
0
    }
2606
2607
    // Assign the rhs temp (now with Y inversion) to the final output
2608
0
    {
2609
0
        TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2610
0
        assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2611
0
    }
2612
2613
0
    assert(assignList != nullptr);
2614
0
    assignList->setOperator(EOpSequence);
2615
2616
0
    return assignList;
2617
0
}
2618
2619
// Clip and cull distance require special handling due to a semantic mismatch.  In HLSL,
2620
// these can be float scalar, float vector, or arrays of float scalar or float vector.
2621
// In SPIR-V, they are arrays of scalar floats in all cases.  We must copy individual components
2622
// (e.g, both x and y components of a float2) out into the destination float array.
2623
//
2624
// The values are assigned to sequential members of the output array.  The inner dimension
2625
// is vector components.  The outer dimension is array elements.
2626
TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId,
2627
                                                           TIntermTyped* left, TIntermTyped* right)
2628
0
{
2629
0
    switch (language) {
2630
0
    case EShLangFragment:
2631
0
    case EShLangVertex:
2632
0
    case EShLangGeometry:
2633
0
        break;
2634
0
    default:
2635
0
        error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", "");
2636
0
        return nullptr;
2637
0
    }
2638
2639
0
    TVariable** clipCullVar = nullptr;
2640
2641
    // Figure out if we are assigning to, or from, clip or cull distance.
2642
0
    const bool isOutput = isClipOrCullDistance(left->getType());
2643
2644
    // This is the rvalue or lvalue holding the clip or cull distance.
2645
0
    TIntermTyped* clipCullNode = isOutput ? left : right;
2646
    // This is the value going into or out of the clip or cull distance.
2647
0
    TIntermTyped* internalNode = isOutput ? right : left;
2648
2649
0
    const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn;
2650
2651
0
    decltype(clipSemanticNSizeIn)* semanticNSize = nullptr;
2652
2653
    // Refer to either the clip or the cull distance, depending on semantic.
2654
0
    switch (builtInType) {
2655
0
    case EbvClipDistance:
2656
0
        clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput;
2657
0
        semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn;
2658
0
        break;
2659
0
    case EbvCullDistance:
2660
0
        clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput;
2661
0
        semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn;
2662
0
        break;
2663
2664
    // called invalidly: we expected a clip or a cull distance.
2665
    // static compile time problem: should not happen.
2666
0
    default: assert(0); return nullptr;
2667
0
    }
2668
2669
    // This is the offset in the destination array of a given semantic's data
2670
0
    std::array<int, maxClipCullRegs> semanticOffset;
2671
2672
    // Calculate offset of variable of semantic N in destination array
2673
0
    int arrayLoc = 0;
2674
0
    int vecItems = 0;
2675
2676
0
    for (int x = 0; x < maxClipCullRegs; ++x) {
2677
        // See if we overflowed the vec4 packing
2678
0
        if ((vecItems + (*semanticNSize)[x]) > 4) {
2679
0
            arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4
2680
0
            vecItems = 0;
2681
0
        }
2682
2683
0
        semanticOffset[x] = arrayLoc;
2684
0
        vecItems += (*semanticNSize)[x];
2685
0
        arrayLoc += (*semanticNSize)[x];
2686
0
    }
2687
2688
2689
    // It can have up to 2 array dimensions (in the case of geometry shader inputs)
2690
0
    const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes();
2691
0
    const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0;
2692
    // vector sizes:
2693
0
    const int internalVectorSize = internalNode->getType().getVectorSize();
2694
    // array sizes, or 1 if it's not an array:
2695
0
    const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1);
2696
0
    const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1);
2697
2698
    // The created type may be an array of arrays, e.g, for geometry shader inputs.
2699
0
    const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput);
2700
2701
    // If we haven't created the output already, create it now.
2702
0
    if (*clipCullVar == nullptr) {
2703
        // ClipDistance and CullDistance are handled specially in the entry point input/output copy
2704
        // algorithm, because they may need to be unpacked from components of vectors (or a scalar)
2705
        // into a float array, or vice versa.  Here, we make the array the right size and type,
2706
        // which depends on the incoming data, which has several potential dimensions:
2707
        //    * Semantic ID
2708
        //    * vector size
2709
        //    * array size
2710
        // Of those, semantic ID and array size cannot appear simultaneously.
2711
        //
2712
        // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two
2713
        // array dimensions.  The shader's declaration may have one or two array dimensions.  One is always
2714
        // the geometry's dimension.
2715
2716
0
        const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed;
2717
2718
0
        const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1);
2719
0
        const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1;
2720
2721
0
        TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1);
2722
0
        clipCullType.getQualifier() = clipCullNode->getType().getQualifier();
2723
2724
        // Create required array dimension
2725
0
        TArraySizes* arraySizes = new TArraySizes;
2726
0
        if (isImplicitlyArrayed)
2727
0
            arraySizes->addInnerSize(requiredOuterArraySize);
2728
0
        arraySizes->addInnerSize(requiredInnerArraySize);
2729
0
        clipCullType.transferArraySizes(arraySizes);
2730
2731
        // Obtain symbol name: we'll use that for the symbol we introduce.
2732
0
        TIntermSymbol* sym = clipCullNode->getAsSymbolNode();
2733
0
        assert(sym != nullptr);
2734
2735
        // We are moving the semantic ID from the layout location, so it is no longer needed or
2736
        // desired there.
2737
0
        clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
2738
2739
        // Create variable and track its linkage
2740
0
        *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType);
2741
2742
0
        trackLinkage(**clipCullVar);
2743
0
    }
2744
2745
    // Create symbol for the clip or cull variable.
2746
0
    TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar);
2747
2748
    // vector sizes:
2749
0
    const int clipCullVectorSize = clipCullSym->getType().getVectorSize();
2750
2751
    // array sizes, or 1 if it's not an array:
2752
0
    const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes();
2753
0
    const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1;
2754
0
    const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0);
2755
2756
    // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics.
2757
    // fixBuiltInIoType() should have handled that upstream.
2758
0
    assert(clipCullSym->getType().isArray());
2759
0
    assert(clipCullSym->getType().getVectorSize() == 1);
2760
0
    assert(clipCullSym->getType().getBasicType() == EbtFloat);
2761
2762
    // We may be creating multiple sub-assignments.  This is an aggregate to hold them.
2763
    // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
2764
0
    TIntermAggregate* assignList = nullptr;
2765
2766
    // Holds individual component assignments as we make them.
2767
0
    TIntermTyped* clipCullAssign = nullptr;
2768
2769
    // If the types are homomorphic, use a simple assign.  No need to mess about with
2770
    // individual components.
2771
0
    if (clipCullSym->getType().isArray() == internalNode->getType().isArray() &&
2772
0
        clipCullInnerArraySize == internalInnerArraySize &&
2773
0
        clipCullOuterArraySize == internalOuterArraySize &&
2774
0
        clipCullVectorSize == internalVectorSize) {
2775
2776
0
        if (isOutput)
2777
0
            clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc);
2778
0
        else
2779
0
            clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc);
2780
2781
0
        assignList = intermediate.growAggregate(assignList, clipCullAssign);
2782
0
        assignList->setOperator(EOpSequence);
2783
2784
0
        return assignList;
2785
0
    }
2786
2787
    // We are going to copy each component of the internal (per array element if indicated) to sequential
2788
    // array elements of the clipCullSym.  This tracks the lhs element we're writing to as we go along.
2789
    // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above.
2790
0
    int clipCullInnerArrayPos = semanticOffset[semanticId];
2791
0
    int clipCullOuterArrayPos = 0;
2792
2793
    // Lambda to add an index to a node, set the type of the result, and return the new node.
2794
0
    const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* {
2795
0
        const TType derefType(node->getType(), 0);
2796
0
        node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc);
2797
0
        node->setType(derefType);
2798
0
        return node;
2799
0
    };
2800
2801
    // Loop through every component of every element of the internal, and copy to or from the matching external.
2802
0
    for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) {
2803
0
        for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) {
2804
0
            for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) {
2805
                // clip/cull array member to read from / write to:
2806
0
                TIntermTyped* clipCullMember = clipCullSym;
2807
2808
                // If implicitly arrayed, there is an outer array dimension involved
2809
0
                if (isImplicitlyArrayed)
2810
0
                    clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos);
2811
2812
                // Index into proper array position for clip cull member
2813
0
                clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++);
2814
2815
                // if needed, start over with next outer array slice.
2816
0
                if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) {
2817
0
                    clipCullInnerArrayPos = semanticOffset[semanticId];
2818
0
                    ++clipCullOuterArrayPos;
2819
0
                }
2820
2821
                // internal member to read from / write to:
2822
0
                TIntermTyped* internalMember = internalNode;
2823
2824
                // If internal node has outer array dimension, index appropriately.
2825
0
                if (internalArrayDims > 1)
2826
0
                    internalMember = addIndex(internalMember, internalOuterArrayPos);
2827
2828
                // If internal node has inner array dimension, index appropriately.
2829
0
                if (internalArrayDims > 0)
2830
0
                    internalMember = addIndex(internalMember, internalInnerArrayPos);
2831
2832
                // If internal node is a vector, extract the component of interest.
2833
0
                if (internalNode->getType().isVector())
2834
0
                    internalMember = addIndex(internalMember, internalComponent);
2835
2836
                // Create an assignment: output from internal to clip cull, or input from clip cull to internal.
2837
0
                if (isOutput)
2838
0
                    clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc);
2839
0
                else
2840
0
                    clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc);
2841
2842
                // Track assignment in the sequence.
2843
0
                assignList = intermediate.growAggregate(assignList, clipCullAssign);
2844
0
            }
2845
0
        }
2846
0
    }
2847
2848
0
    assert(assignList != nullptr);
2849
0
    assignList->setOperator(EOpSequence);
2850
2851
0
    return assignList;
2852
0
}
2853
2854
// Some simple source assignments need to be flattened to a sequence
2855
// of AST assignments. Catch these and flatten, otherwise, pass through
2856
// to intermediate.addAssign().
2857
//
2858
// Also, assignment to matrix swizzles requires multiple component assignments,
2859
// intercept those as well.
2860
TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
2861
                                             TIntermTyped* right)
2862
0
{
2863
0
    if (left == nullptr || right == nullptr)
2864
0
        return nullptr;
2865
2866
    // writing to opaques will require fixing transforms
2867
0
    if (left->getType().containsOpaque())
2868
0
        intermediate.setNeedsLegalization();
2869
2870
0
    if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
2871
0
        return handleAssignToMatrixSwizzle(loc, op, left, right);
2872
2873
    // Return true if the given node is an index operation into a split variable.
2874
0
    const auto indexesSplit = [this](const TIntermTyped* node) -> bool {
2875
0
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2876
2877
0
        if (binaryNode == nullptr)
2878
0
            return false;
2879
2880
0
        return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) &&
2881
0
               wasSplit(binaryNode->getLeft());
2882
0
    };
2883
2884
    // Return symbol if node is symbol or index ref
2885
0
    const auto getSymbol = [](const TIntermTyped* node) -> const TIntermSymbol* {
2886
0
        const TIntermSymbol* symbolNode = node->getAsSymbolNode();
2887
0
        if (symbolNode != nullptr)
2888
0
            return symbolNode;
2889
2890
0
        const TIntermBinary* binaryNode = node->getAsBinaryNode();
2891
0
        if (binaryNode != nullptr && (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect))
2892
0
            return binaryNode->getLeft()->getAsSymbolNode();
2893
2894
0
        return nullptr;
2895
0
    };
2896
2897
    // Return true if this stage assigns clip position with potentially inverted Y
2898
0
    const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
2899
0
        return node->getType().getQualifier().builtIn == EbvPosition &&
2900
0
               (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
2901
0
    };
2902
2903
0
    const TIntermSymbol* leftSymbol = getSymbol(left);
2904
0
    const TIntermSymbol* rightSymbol = getSymbol(right);
2905
2906
0
    const bool isSplitLeft    = wasSplit(left) || indexesSplit(left);
2907
0
    const bool isSplitRight   = wasSplit(right) || indexesSplit(right);
2908
2909
0
    const bool isFlattenLeft  = wasFlattened(leftSymbol);
2910
0
    const bool isFlattenRight = wasFlattened(rightSymbol);
2911
2912
    // OK to do a single assign if neither side is split or flattened.  Otherwise,
2913
    // fall through to a member-wise copy.
2914
0
    if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) {
2915
        // Clip and cull distance requires more processing.  See comment above assignClipCullDistance.
2916
0
        if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) {
2917
0
            const bool isOutput = isClipOrCullDistance(left->getType());
2918
2919
0
            const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
2920
0
            return assignClipCullDistance(loc, op, semanticId, left, right);
2921
0
        } else if (assignsClipPos(left)) {
2922
            // Position can require special handling: see comment above assignPosition
2923
0
            return assignPosition(loc, op, left, right);
2924
0
        } else if (left->getQualifier().builtIn == EbvSampleMask) {
2925
            // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars
2926
            // in the shader.  Copy the scalar RHS into the LHS array element zero, if that happens.
2927
0
            if (left->isArray() && !right->isArray()) {
2928
0
                const TType derefType(left->getType(), 0);
2929
0
                left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc);
2930
0
                left->setType(derefType);
2931
                // Fall through to add assign.
2932
0
            }
2933
0
        }
2934
2935
0
        return intermediate.addAssign(op, left, right, loc);
2936
0
    }
2937
2938
0
    TIntermAggregate* assignList = nullptr;
2939
0
    const TVector<TVariable*>* leftVariables = nullptr;
2940
0
    const TVector<TVariable*>* rightVariables = nullptr;
2941
2942
    // A temporary to store the right node's value, so we don't keep indirecting into it
2943
    // if it's not a simple symbol.
2944
0
    TVariable* rhsTempVar = nullptr;
2945
2946
    // If the RHS is a simple symbol node, we'll copy it for each member.
2947
0
    TIntermSymbol* cloneSymNode = nullptr;
2948
2949
0
    int memberCount = 0;
2950
2951
    // Track how many items there are to copy.
2952
0
    if (left->getType().isStruct())
2953
0
        memberCount = (int)left->getType().getStruct()->size();
2954
0
    if (left->getType().isArray())
2955
0
        memberCount = left->getType().getCumulativeArraySize();
2956
2957
0
    if (isFlattenLeft)
2958
0
        leftVariables = &flattenMap.find(leftSymbol->getId())->second.members;
2959
2960
0
    if (isFlattenRight) {
2961
0
        rightVariables = &flattenMap.find(rightSymbol->getId())->second.members;
2962
0
    } else {
2963
        // The RHS is not flattened.  There are several cases:
2964
        // 1. 1 item to copy:  Use the RHS directly.
2965
        // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
2966
        // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member.
2967
2968
0
        if (memberCount <= 1) {
2969
            // case 1: we'll use the symbol directly below.  Nothing to do.
2970
0
        } else {
2971
0
            if (right->getAsSymbolNode() != nullptr) {
2972
                // case 2: we'll copy the symbol per iteration below.
2973
0
                cloneSymNode = right->getAsSymbolNode();
2974
0
            } else {
2975
                // case 3: assign to a temp, and indirect into that.
2976
0
                rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
2977
0
                rhsTempVar->getWritableType().getQualifier().makeTemporary();
2978
0
                TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
2979
2980
                // Add this to the aggregate being built.
2981
0
                assignList = intermediate.growAggregate(assignList,
2982
0
                                                        intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
2983
0
            }
2984
0
        }
2985
0
    }
2986
2987
    // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in
2988
    // variables, which is awkward when copying between split and unsplit structures.  This variable tracks
2989
    // array indirections so they can be percolated from outer structs to inner variables.
2990
0
    std::vector <int> arrayElement;
2991
2992
0
    TStorageQualifier leftStorage = left->getType().getQualifier().storage;
2993
0
    TStorageQualifier rightStorage = right->getType().getQualifier().storage;
2994
2995
0
    int leftOffsetStart = findSubtreeOffset(*left);
2996
0
    int rightOffsetStart = findSubtreeOffset(*right);
2997
0
    int leftOffset = leftOffsetStart;
2998
0
    int rightOffset = rightOffsetStart;
2999
3000
0
    const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember,
3001
0
                               bool flattened)
3002
0
                           -> TIntermTyped * {
3003
0
        const bool split     = isLeft ? isSplitLeft   : isSplitRight;
3004
3005
0
        TIntermTyped* subTree;
3006
0
        const TType derefType(type, member);
3007
0
        const TVariable* builtInVar = nullptr;
3008
0
        if ((flattened || split) && derefType.isBuiltIn()) {
3009
0
            auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData(
3010
0
                                                   derefType.getQualifier().builtIn,
3011
0
                                                   isLeft ? leftStorage : rightStorage));
3012
0
            if (splitPair != splitBuiltIns.end())
3013
0
                builtInVar = splitPair->second;
3014
0
        }
3015
0
        if (builtInVar != nullptr) {
3016
            // copy from interstage IO built-in if needed
3017
0
            subTree = intermediate.addSymbol(*builtInVar);
3018
3019
0
            if (subTree->getType().isArray()) {
3020
                // Arrayness of builtIn symbols isn't handled by the normal recursion:
3021
                // it's been extracted and moved to the built-in.
3022
0
                if (!arrayElement.empty()) {
3023
0
                    const TType splitDerefType(subTree->getType(), arrayElement.back());
3024
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3025
0
                                                    intermediate.addConstantUnion(arrayElement.back(), loc), loc);
3026
0
                    subTree->setType(splitDerefType);
3027
0
                } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
3028
                    // This might also be a stage with arrayed outputs, in which case there's an index
3029
                    // operation we should transfer to the output builtin.
3030
3031
0
                    const TType splitDerefType(subTree->getType(), 0);
3032
0
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3033
0
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3034
0
                    subTree->setType(splitDerefType);
3035
0
                }
3036
0
            }
3037
0
        } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
3038
0
            if (isLeft) {
3039
                // offset will cycle through variables for arrayed io
3040
0
                if (leftOffset >= static_cast<int>(leftVariables->size()))
3041
0
                    leftOffset = leftOffsetStart;
3042
0
                subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]);
3043
0
            } else {
3044
                // offset will cycle through variables for arrayed io
3045
0
                if (rightOffset >= static_cast<int>(rightVariables->size()))
3046
0
                    rightOffset = rightOffsetStart;
3047
0
                subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]);
3048
0
            }
3049
3050
            // arrayed io
3051
0
            if (subTree->getType().isArray()) {
3052
0
                if (!arrayElement.empty()) {
3053
0
                    const TType derefType(subTree->getType(), arrayElement.front());
3054
0
                    subTree = intermediate.addIndex(EOpIndexDirect, subTree,
3055
0
                                                    intermediate.addConstantUnion(arrayElement.front(), loc), loc);
3056
0
                    subTree->setType(derefType);
3057
0
                } else {
3058
                    // There's an index operation we should transfer to the output builtin.
3059
0
                    assert(splitNode->getAsOperator() != nullptr &&
3060
0
                           splitNode->getAsOperator()->getOp() == EOpIndexIndirect);
3061
0
                    const TType splitDerefType(subTree->getType(), 0);
3062
0
                    subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
3063
0
                                                    splitNode->getAsBinaryNode()->getRight(), loc);
3064
0
                    subTree->setType(splitDerefType);
3065
0
                }
3066
0
            }
3067
0
        } else {
3068
            // Index operator if it's an aggregate, else EOpNull
3069
0
            const TOperator accessOp = type.isArray()  ? EOpIndexDirect
3070
0
                                     : type.isStruct() ? EOpIndexDirectStruct
3071
0
                                     : EOpNull;
3072
0
            if (accessOp == EOpNull) {
3073
0
                subTree = splitNode;
3074
0
            } else {
3075
0
                subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc),
3076
0
                                                loc);
3077
0
                const TType splitDerefType(splitNode->getType(), splitMember);
3078
0
                subTree->setType(splitDerefType);
3079
0
            }
3080
0
        }
3081
3082
0
        return subTree;
3083
0
    };
3084
3085
    // Use the proper RHS node: a new symbol from a TVariable, copy
3086
    // of an TIntermSymbol node, or sometimes the right node directly.
3087
0
    right = rhsTempVar != nullptr   ? intermediate.addSymbol(*rhsTempVar, loc) :
3088
0
            cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) :
3089
0
            right;
3090
3091
    // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
3092
    // whole thing.  So, we'll resort to an explicit type via std::function.
3093
0
    const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3094
0
                             bool topLevel)>
3095
0
    traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
3096
0
                   bool topLevel) -> void {
3097
        // If we get here, we are assigning to or from a whole array or struct that must be
3098
        // flattened, so have to do member-by-member assignment:
3099
3100
0
        bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel);
3101
0
        bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel);
3102
3103
0
        if ((left->getType().isArray() || right->getType().isArray()) &&
3104
0
              (shouldFlattenSubsetLeft  || isSplitLeft ||
3105
0
               shouldFlattenSubsetRight || isSplitRight)) {
3106
0
            const int elementsL = left->getType().isArray()  ? left->getType().getOuterArraySize()  : 1;
3107
0
            const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
3108
3109
            // The arrays might not be the same size,
3110
            // e.g., if the size has been forced for EbvTessLevelInner/Outer.
3111
0
            const int elementsToCopy = std::min(elementsL, elementsR);
3112
3113
            // array case
3114
0
            for (int element = 0; element < elementsToCopy; ++element) {
3115
0
                arrayElement.push_back(element);
3116
3117
                // Add a new AST symbol node if we have a temp variable holding a complex RHS.
3118
0
                TIntermTyped* subLeft  = getMember(true,  left->getType(),  element, left, element,
3119
0
                                                   shouldFlattenSubsetLeft);
3120
0
                TIntermTyped* subRight = getMember(false, right->getType(), element, right, element,
3121
0
                                                   shouldFlattenSubsetRight);
3122
3123
0
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  element, splitLeft,
3124
0
                                                                       element, shouldFlattenSubsetLeft)
3125
0
                                                           : subLeft;
3126
0
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight,
3127
0
                                                                       element, shouldFlattenSubsetRight)
3128
0
                                                           : subRight;
3129
3130
0
                traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3131
3132
0
                arrayElement.pop_back();
3133
0
            }
3134
0
        } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft  || isSplitLeft ||
3135
0
                                                  shouldFlattenSubsetRight || isSplitRight)) {
3136
            // struct case
3137
0
            const auto& membersL = *left->getType().getStruct();
3138
0
            const auto& membersR = *right->getType().getStruct();
3139
3140
            // These track the members in the split structures corresponding to the same in the unsplit structures,
3141
            // which we traverse in parallel.
3142
0
            int memberL = 0;
3143
0
            int memberR = 0;
3144
3145
            // Handle empty structure assignment
3146
0
            if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
3147
0
                assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3148
3149
0
            for (int member = 0; member < int(membersL.size()); ++member) {
3150
0
                const TType& typeL = *membersL[member].type;
3151
0
                const TType& typeR = *membersR[member].type;
3152
3153
0
                TIntermTyped* subLeft  = getMember(true,  left->getType(), member, left, member,
3154
0
                                                   shouldFlattenSubsetLeft);
3155
0
                TIntermTyped* subRight = getMember(false, right->getType(), member, right, member,
3156
0
                                                   shouldFlattenSubsetRight);
3157
3158
                // If there is no splitting, use the same values to avoid inefficiency.
3159
0
                TIntermTyped* subSplitLeft =  isSplitLeft  ? getMember(true,  left->getType(),  member, splitLeft,
3160
0
                                                                       memberL, shouldFlattenSubsetLeft)
3161
0
                                                           : subLeft;
3162
0
                TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight,
3163
0
                                                                       memberR, shouldFlattenSubsetRight)
3164
0
                                                           : subRight;
3165
3166
0
                if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) {
3167
                    // Clip and cull distance built-in assignment is complex in its own right, and is handled in
3168
                    // a separate function dedicated to that task.  See comment above assignClipCullDistance;
3169
3170
0
                    const bool isOutput = isClipOrCullDistance(subSplitLeft->getType());
3171
3172
                    // Since all clip/cull semantics boil down to the same built-in type, we need to get the
3173
                    // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping.
3174
0
                    const TType derefType((isOutput ? left : right)->getType(), member);
3175
0
                    const int semanticId = derefType.getQualifier().layoutLocation;
3176
3177
0
                    TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId,
3178
0
                                                                              subSplitLeft, subSplitRight);
3179
3180
0
                    assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
3181
0
                } else if (subSplitRight->getType().getQualifier().builtIn == EbvFragCoord) {
3182
                    // FragCoord can require special handling: see comment above assignFromFragCoord
3183
0
                    TIntermTyped* fragCoordAssign = assignFromFragCoord(loc, op, subSplitLeft, subSplitRight);
3184
0
                    assignList = intermediate.growAggregate(assignList, fragCoordAssign, loc);
3185
0
                } else if (assignsClipPos(subSplitLeft)) {
3186
                    // Position can require special handling: see comment above assignPosition
3187
0
                    TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
3188
0
                    assignList = intermediate.growAggregate(assignList, positionAssign, loc);
3189
0
                } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
3190
0
                           !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
3191
                    // If this is the final flattening (no nested types below to flatten)
3192
                    // we'll copy the member, else recurse into the type hierarchy.
3193
                    // However, if splitting the struct, that means we can copy a whole
3194
                    // subtree here IFF it does not itself contain any interstage built-in
3195
                    // IO variables, so we only have to recurse into it if there's something
3196
                    // for splitting to do.  That can save a lot of AST verbosity for
3197
                    // a bunch of memberwise copies.
3198
3199
0
                    assignList = intermediate.growAggregate(assignList,
3200
0
                                                            intermediate.addAssign(op, subSplitLeft, subSplitRight, loc),
3201
0
                                                            loc);
3202
0
                } else {
3203
0
                    traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
3204
0
                }
3205
3206
0
                memberL += (typeL.isBuiltIn() ? 0 : 1);
3207
0
                memberR += (typeR.isBuiltIn() ? 0 : 1);
3208
0
            }
3209
0
        } else {
3210
            // Member copy
3211
0
            assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
3212
0
        }
3213
3214
0
    };
3215
3216
0
    TIntermTyped* splitLeft  = left;
3217
0
    TIntermTyped* splitRight = right;
3218
3219
    // If either left or right was a split structure, we must read or write it, but still have to
3220
    // parallel-recurse through the unsplit structure to identify the built-in IO vars.
3221
    // The left can be either a symbol, or an index into a symbol (e.g, array reference)
3222
0
    if (isSplitLeft) {
3223
0
        if (indexesSplit(left)) {
3224
            // Index case: Refer to the indexed symbol, if the left is an index operator.
3225
0
            const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3226
3227
0
            TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3228
3229
0
            splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo,
3230
0
                                              left->getAsBinaryNode()->getRight(), loc);
3231
3232
0
            const TType derefType(splitLeftNonIo->getType(), 0);
3233
0
            splitLeft->setType(derefType);
3234
0
        } else {
3235
            // Symbol case: otherwise, if not indexed, we have the symbol directly.
3236
0
            const TIntermSymbol* symNode = left->getAsSymbolNode();
3237
0
            splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
3238
0
        }
3239
0
    }
3240
3241
0
    if (isSplitRight)
3242
0
        splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc);
3243
3244
    // This makes the whole assignment, recursing through subtypes as needed.
3245
0
    traverse(left, right, splitLeft, splitRight, true);
3246
3247
0
    assert(assignList != nullptr);
3248
0
    assignList->setOperator(EOpSequence);
3249
3250
0
    return assignList;
3251
0
}
3252
3253
// An assignment to matrix swizzle must be decomposed into individual assignments.
3254
// These must be selected component-wise from the RHS and stored component-wise
3255
// into the LHS.
3256
TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
3257
                                                            TIntermTyped* right)
3258
0
{
3259
0
    assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
3260
3261
0
    if (op != EOpAssign)
3262
0
        error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
3263
3264
    // isolate the matrix and swizzle nodes
3265
0
    TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
3266
0
    const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
3267
3268
    // if the RHS isn't already a simple vector, let's store into one
3269
0
    TIntermSymbol* vector = right->getAsSymbolNode();
3270
0
    TIntermTyped* vectorAssign = nullptr;
3271
0
    if (vector == nullptr) {
3272
        // create a new intermediate vector variable to assign to
3273
0
        TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
3274
0
        vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
3275
3276
        // assign the right to the new vector
3277
0
        vectorAssign = handleAssign(loc, op, vector, right);
3278
0
    }
3279
3280
    // Assign the vector components to the matrix components.
3281
    // Store this as a sequence, so a single aggregate node represents this
3282
    // entire operation.
3283
0
    TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
3284
0
    TType columnType(matrix->getType(), 0);
3285
0
    TType componentType(columnType, 0);
3286
0
    TType indexType(EbtInt);
3287
0
    for (int i = 0; i < (int)swizzle.size(); i += 2) {
3288
        // the right component, single index into the RHS vector
3289
0
        TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
3290
0
                                    intermediate.addConstantUnion(i/2, loc), loc);
3291
3292
        // the left component, double index into the LHS matrix
3293
0
        TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
3294
0
                                    intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
3295
0
                                                                  indexType, loc),
3296
0
                                    loc);
3297
0
        leftComp->setType(columnType);
3298
0
        leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
3299
0
                                    intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
3300
0
                                                                  indexType, loc),
3301
0
                                    loc);
3302
0
        leftComp->setType(componentType);
3303
3304
        // Add the assignment to the aggregate
3305
0
        result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
3306
0
    }
3307
3308
0
    result->setOp(EOpSequence);
3309
3310
0
    return result;
3311
0
}
3312
3313
//
3314
// HLSL atomic operations have slightly different arguments than
3315
// GLSL/AST/SPIRV.  The semantics are converted below in decomposeIntrinsic.
3316
// This provides the post-decomposition equivalent opcode.
3317
//
3318
TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
3319
0
{
3320
0
    switch (op) {
3321
0
    case EOpInterlockedAdd:             return isImage ? EOpImageAtomicAdd      : EOpAtomicAdd;
3322
0
    case EOpInterlockedAnd:             return isImage ? EOpImageAtomicAnd      : EOpAtomicAnd;
3323
0
    case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
3324
0
    case EOpInterlockedMax:             return isImage ? EOpImageAtomicMax      : EOpAtomicMax;
3325
0
    case EOpInterlockedMin:             return isImage ? EOpImageAtomicMin      : EOpAtomicMin;
3326
0
    case EOpInterlockedOr:              return isImage ? EOpImageAtomicOr       : EOpAtomicOr;
3327
0
    case EOpInterlockedXor:             return isImage ? EOpImageAtomicXor      : EOpAtomicXor;
3328
0
    case EOpInterlockedExchange:        return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
3329
0
    case EOpInterlockedCompareStore:  // TODO: ...
3330
0
    default:
3331
0
        error(loc, "unknown atomic operation", "unknown op", "");
3332
0
        return EOpNull;
3333
0
    }
3334
0
}
3335
3336
//
3337
// Create a combined sampler/texture from separate sampler and texture.
3338
//
3339
TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex,
3340
                                                                TIntermTyped* argSampler)
3341
0
{
3342
0
    TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
3343
3344
0
    txcombine->getSequence().push_back(argTex);
3345
0
    txcombine->getSequence().push_back(argSampler);
3346
3347
0
    TSampler samplerType = argTex->getType().getSampler();
3348
0
    samplerType.combined = true;
3349
3350
    // TODO:
3351
    // This block exists until the spec no longer requires shadow modes on texture objects.
3352
    // It can be deleted after that, along with the shadowTextureVariant member.
3353
0
    {
3354
0
        const bool shadowMode = argSampler->getType().getSampler().shadow;
3355
3356
0
        TIntermSymbol* texSymbol = argTex->getAsSymbolNode();
3357
3358
0
        if (texSymbol == nullptr)
3359
0
            texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3360
3361
0
        if (texSymbol == nullptr) {
3362
0
            error(loc, "unable to find texture symbol", "", "");
3363
0
            return nullptr;
3364
0
        }
3365
3366
        // This forces the texture's shadow state to be the sampler's
3367
        // shadow state.  This depends on downstream optimization to
3368
        // DCE one variant in [shadow, nonshadow] if both are present,
3369
        // or the SPIR-V module would be invalid.
3370
0
        long long newId = texSymbol->getId();
3371
3372
        // Check to see if this texture has been given a shadow mode already.
3373
        // If so, look up the one we already have.
3374
0
        const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId());
3375
3376
0
        if (textureShadowEntry != textureShadowVariant.end())
3377
0
            newId = textureShadowEntry->second->get(shadowMode);
3378
0
        else
3379
0
            textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1);
3380
3381
        // Sometimes we have to create another symbol (if this texture has been seen before,
3382
        // and we haven't created the form for this shadow mode).
3383
0
        if (newId == -1) {
3384
0
            TType texType;
3385
0
            texType.shallowCopy(argTex->getType());
3386
0
            texType.getSampler().shadow = shadowMode;  // set appropriate shadow mode.
3387
0
            globalQualifierFix(loc, texType.getQualifier());
3388
3389
0
            TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType);
3390
3391
0
            trackLinkage(*newTexture);
3392
3393
0
            newId = newTexture->getUniqueId();
3394
0
        }
3395
3396
0
        assert(newId != -1);
3397
3398
0
        if (textureShadowVariant.find(newId) == textureShadowVariant.end())
3399
0
            textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()];
3400
3401
0
        textureShadowVariant[newId]->set(shadowMode, newId);
3402
3403
        // Remember this shadow mode in the texture and the merged type.
3404
0
        argTex->getWritableType().getSampler().shadow = shadowMode;
3405
0
        samplerType.shadow = shadowMode;
3406
3407
0
        texSymbol->switchId(newId);
3408
0
    }
3409
3410
0
    txcombine->setType(TType(samplerType, EvqTemporary));
3411
0
    txcombine->setLoc(loc);
3412
3413
0
    return txcombine;
3414
0
}
3415
3416
// Return true if this a buffer type that has an associated counter buffer.
3417
bool HlslParseContext::hasStructBuffCounter(const TType& type) const
3418
0
{
3419
0
    switch (type.getQualifier().declaredBuiltIn) {
3420
0
    case EbvAppendConsume:       // fall through...
3421
0
    case EbvRWStructuredBuffer:  // ...
3422
0
        return true;
3423
0
    default:
3424
0
        return false; // the other structuredbuffer types do not have a counter.
3425
0
    }
3426
0
}
3427
3428
void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type)
3429
0
{
3430
    // Counter type
3431
0
    TType* counterType = new TType(EbtUint, EvqBuffer);
3432
0
    counterType->setFieldName(intermediate.implicitCounterName);
3433
3434
0
    TTypeList* blockStruct = new TTypeList;
3435
0
    TTypeLoc  member = { counterType, loc };
3436
0
    blockStruct->push_back(member);
3437
3438
0
    TType blockType(blockStruct, "", counterType->getQualifier());
3439
0
    blockType.getQualifier().storage = EvqBuffer;
3440
3441
0
    type.shallowCopy(blockType);
3442
0
    shareStructBufferType(type);
3443
0
}
3444
3445
// declare counter for a structured buffer type
3446
void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
3447
0
{
3448
    // Bail out if not a struct buffer
3449
0
    if (! isStructBufferType(bufferType))
3450
0
        return;
3451
3452
0
    if (! hasStructBuffCounter(bufferType))
3453
0
        return;
3454
3455
0
    TType blockType;
3456
0
    counterBufferType(loc, blockType);
3457
3458
0
    TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str());
3459
3460
    // Counter buffer is not yet in use
3461
0
    structBufferCounter[*blockName] = false;
3462
3463
0
    shareStructBufferType(blockType);
3464
0
    declareBlock(loc, blockType, blockName);
3465
0
}
3466
3467
// return the counter that goes with a given structuredbuffer
3468
TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
3469
0
{
3470
    // Bail out if not a struct buffer
3471
0
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
3472
0
        return nullptr;
3473
3474
0
    const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName()));
3475
3476
    // Mark the counter as being used
3477
0
    structBufferCounter[counterBlockName] = true;
3478
3479
0
    TIntermTyped* counterVar = handleVariable(loc, &counterBlockName);  // find the block structure
3480
0
    TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
3481
3482
0
    TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
3483
0
    counterMember->setType(TType(EbtUint));
3484
0
    return counterMember;
3485
0
}
3486
3487
//
3488
// Decompose structure buffer methods into AST
3489
//
3490
void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3491
0
{
3492
0
    if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
3493
0
        return;
3494
3495
0
    const TOperator op  = node->getAsOperator()->getOp();
3496
0
    TIntermAggregate* argAggregate = arguments->getAsAggregate();
3497
3498
    // Buffer is the object upon which method is called, so always arg 0
3499
0
    TIntermTyped* bufferObj = nullptr;
3500
3501
    // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
3502
0
    if (argAggregate) {
3503
0
        if (argAggregate->getSequence().empty())
3504
0
            return;
3505
0
        if (argAggregate->getSequence()[0])
3506
0
            bufferObj = argAggregate->getSequence()[0]->getAsTyped();
3507
0
    } else {
3508
0
        bufferObj = arguments->getAsSymbolNode();
3509
0
    }
3510
3511
0
    if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr)
3512
0
        return;
3513
3514
    // Some methods require a hidden internal counter, obtained via getStructBufferCounter().
3515
    // This lambda adds something to it and returns the old value.
3516
0
    const auto incDecCounter = [&](int incval) -> TIntermTyped* {
3517
0
        TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast<unsigned int>(incval), loc, true);
3518
0
        TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
3519
3520
0
        if (counter == nullptr)
3521
0
            return nullptr;
3522
3523
0
        TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
3524
0
        counterIncrement->setType(TType(EbtUint, EvqTemporary));
3525
0
        counterIncrement->setLoc(loc);
3526
0
        counterIncrement->getSequence().push_back(counter);
3527
0
        counterIncrement->getSequence().push_back(incrementValue);
3528
3529
0
        return counterIncrement;
3530
0
    };
3531
3532
    // Index to obtain the runtime sized array out of the buffer.
3533
0
    TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
3534
0
    if (argArray == nullptr)
3535
0
        return;  // It might not be a struct buffer method.
3536
3537
    // These builtins resolve by name against a zero-parameter prototype, so normal
3538
    // overload resolution never checks the argument count. Validate it here before
3539
    // indexing the argument sequence, otherwise a call with too few arguments reads
3540
    // past the end of the aggregate (or dereferences a null aggregate for a bare
3541
    // buffer object with no arguments).
3542
0
    const int argCount = argAggregate ? (int)argAggregate->getSequence().size() : 1;
3543
0
    int minArgCount = 1; // the buffer object at index 0
3544
0
    switch (op) {
3545
0
    case EOpMethodLoad:
3546
0
    case EOpMethodLoad2:
3547
0
    case EOpMethodLoad3:
3548
0
    case EOpMethodLoad4:
3549
0
    case EOpMethodGetDimensions:
3550
0
    case EOpMethodAppend:
3551
0
    case EOpInterlockedAdd:
3552
0
    case EOpInterlockedAnd:
3553
0
    case EOpInterlockedExchange:
3554
0
    case EOpInterlockedMax:
3555
0
    case EOpInterlockedMin:
3556
0
    case EOpInterlockedOr:
3557
0
    case EOpInterlockedXor:
3558
0
    case EOpInterlockedCompareExchange:
3559
0
    case EOpInterlockedCompareStore:
3560
0
        minArgCount = 2;
3561
0
        break;
3562
0
    case EOpMethodStore:
3563
0
    case EOpMethodStore2:
3564
0
    case EOpMethodStore3:
3565
0
    case EOpMethodStore4:
3566
0
        minArgCount = 3;
3567
0
        break;
3568
0
    default:
3569
0
        break;
3570
0
    }
3571
0
    if (argCount < minArgCount) {
3572
0
        error(loc, "too few arguments to buffer method", "", "");
3573
0
        return;
3574
0
    }
3575
3576
0
    switch (op) {
3577
0
    case EOpMethodLoad:
3578
0
        {
3579
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3580
0
            if (argIndex == nullptr) {
3581
0
                error(loc, "invalid index for Load", "", "");
3582
0
                return;
3583
0
            }
3584
3585
0
            const TType& bufferType = bufferObj->getType();
3586
3587
0
            const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn;
3588
3589
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3590
            // buffer then, but that's what it calls itself.
3591
0
            const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer   ||
3592
0
                                              builtInType == EbvRWByteAddressBuffer);
3593
3594
3595
0
            if (isByteAddressBuffer)
3596
0
                argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex,
3597
0
                                                      intermediate.addConstantUnion(2, loc, true),
3598
0
                                                      loc, TType(EbtInt));
3599
3600
            // Index into the array to find the item being loaded.
3601
0
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3602
3603
0
            node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3604
3605
0
            const TType derefType(argArray->getType(), 0);
3606
0
            node->setType(derefType);
3607
0
        }
3608
3609
0
        break;
3610
3611
0
    case EOpMethodLoad2:
3612
0
    case EOpMethodLoad3:
3613
0
    case EOpMethodLoad4:
3614
0
        {
3615
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3616
0
            if (argIndex == nullptr) {
3617
0
                error(loc, "invalid index for vector Load", "", "");
3618
0
                return;
3619
0
            }
3620
3621
0
            TOperator constructOp = EOpNull;
3622
0
            int size = 0;
3623
3624
0
            switch (op) {
3625
0
            case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
3626
0
            case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
3627
0
            case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
3628
0
            default: assert(0);
3629
0
            }
3630
3631
0
            TIntermTyped* body = nullptr;
3632
3633
            // First, we'll store the address in a variable to avoid multiple shifts
3634
            // (we must convert the byte address to an item address)
3635
0
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3636
0
                                                                   intermediate.addConstantUnion(2, loc, true),
3637
0
                                                                   loc, TType(EbtInt));
3638
3639
0
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3640
0
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3641
3642
0
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3643
3644
0
            TIntermTyped* vec = nullptr;
3645
3646
            // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
3647
            // address conversion.
3648
0
            for (int idx=0; idx<size; ++idx) {
3649
0
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3650
3651
                // add index offset
3652
0
                if (idx != 0)
3653
0
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx,
3654
0
                                                           intermediate.addConstantUnion(idx, loc, true),
3655
0
                                                           loc, TType(EbtInt));
3656
3657
0
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3658
0
                                                                                        : EOpIndexIndirect;
3659
3660
0
                TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3661
3662
0
                TType derefType(argArray->getType(), 0);
3663
0
                derefType.getQualifier().makeTemporary();
3664
0
                indexVal->setType(derefType);
3665
3666
0
                vec = intermediate.growAggregate(vec, indexVal);
3667
0
            }
3668
3669
0
            vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
3670
0
            vec->getAsAggregate()->setOperator(constructOp);
3671
3672
0
            body = intermediate.growAggregate(body, vec);
3673
0
            body->setType(vec->getType());
3674
0
            body->getAsAggregate()->setOperator(EOpSequence);
3675
3676
0
            node = body;
3677
0
        }
3678
3679
0
        break;
3680
3681
0
    case EOpMethodStore:
3682
0
    case EOpMethodStore2:
3683
0
    case EOpMethodStore3:
3684
0
    case EOpMethodStore4:
3685
0
        {
3686
0
            TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped());  // index
3687
0
            if (argIndex == nullptr) {
3688
0
                error(loc, "invalid index for Store", "", "");
3689
0
                return;
3690
0
            }
3691
0
            TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped();  // value
3692
3693
            // Index into the array to find the item being loaded.
3694
            // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3695
            // buffer then, but that's what it calls itself).
3696
3697
0
            int size = 0;
3698
3699
0
            switch (op) {
3700
0
            case EOpMethodStore:  size = 1; break;
3701
0
            case EOpMethodStore2: size = 2; break;
3702
0
            case EOpMethodStore3: size = 3; break;
3703
0
            case EOpMethodStore4: size = 4; break;
3704
0
            default: assert(0);
3705
0
            }
3706
3707
0
            TIntermAggregate* body = nullptr;
3708
3709
            // First, we'll store the address in a variable to avoid multiple shifts
3710
            // (we must convert the byte address to an item address)
3711
0
            TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3712
0
                                                                   intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
3713
3714
0
            TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3715
0
            TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3716
3717
0
            body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3718
3719
0
            for (int idx=0; idx<size; ++idx) {
3720
0
                TIntermTyped* offsetIdx = byteAddrIdxVar;
3721
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3722
3723
                // add index offset
3724
0
                if (idx != 0)
3725
0
                    offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
3726
3727
0
                const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3728
0
                                                                                        : EOpIndexIndirect;
3729
3730
0
                TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3731
0
                const TType derefType(argArray->getType(), 0);
3732
0
                lValue->setType(derefType);
3733
3734
0
                TIntermTyped* rValue;
3735
0
                if (size == 1) {
3736
0
                    rValue = argValue;
3737
0
                } else {
3738
0
                    rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
3739
0
                    const TType indexType(argValue->getType(), 0);
3740
0
                    rValue->setType(indexType);
3741
0
                }
3742
3743
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3744
3745
0
                body = intermediate.growAggregate(body, assign);
3746
0
            }
3747
3748
0
            body->setOperator(EOpSequence);
3749
0
            node = body;
3750
0
        }
3751
3752
0
        break;
3753
3754
0
    case EOpMethodGetDimensions:
3755
0
        {
3756
0
            const int numArgs = (int)argAggregate->getSequence().size();
3757
0
            TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped();  // out num items
3758
0
            TIntermTyped* argStride   = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr;  // out stride
3759
3760
0
            TIntermAggregate* body = nullptr;
3761
3762
            // Length output:
3763
0
            if (argArray->getType().isSizedArray()) {
3764
0
                const int length = argArray->getType().getOuterArraySize();
3765
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems,
3766
0
                                                              intermediate.addConstantUnion(length, loc, true), loc);
3767
0
                body = intermediate.growAggregate(body, assign, loc);
3768
0
            } else {
3769
0
                TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
3770
0
                                                                               argNumItems->getType());
3771
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
3772
0
                body = intermediate.growAggregate(body, assign, loc);
3773
0
            }
3774
3775
            // Stride output:
3776
0
            if (argStride != nullptr) {
3777
0
                int size;
3778
0
                int stride;
3779
0
                intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking,
3780
0
                                                argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
3781
3782
0
                TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride,
3783
0
                                                              intermediate.addConstantUnion(stride, loc, true), loc);
3784
3785
0
                body = intermediate.growAggregate(body, assign);
3786
0
            }
3787
3788
0
            body->setOperator(EOpSequence);
3789
0
            node = body;
3790
0
        }
3791
3792
0
        break;
3793
3794
0
    case EOpInterlockedAdd:
3795
0
    case EOpInterlockedAnd:
3796
0
    case EOpInterlockedExchange:
3797
0
    case EOpInterlockedMax:
3798
0
    case EOpInterlockedMin:
3799
0
    case EOpInterlockedOr:
3800
0
    case EOpInterlockedXor:
3801
0
    case EOpInterlockedCompareExchange:
3802
0
    case EOpInterlockedCompareStore:
3803
0
        {
3804
            // We'll replace the first argument with the block dereference, and let
3805
            // downstream decomposition handle the rest.
3806
3807
0
            TIntermSequence& sequence = argAggregate->getSequence();
3808
3809
0
            TIntermTyped* argIndex     = makeIntegerIndex(sequence[1]->getAsTyped());  // index
3810
0
            if (argIndex == nullptr) {
3811
0
                error(loc, "invalid destination address for interlocked operation", "", "");
3812
0
                return;
3813
0
            }
3814
0
            argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
3815
0
                                                  loc, TType(EbtInt));
3816
3817
0
            const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3818
0
            TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3819
3820
0
            const TType derefType(argArray->getType(), 0);
3821
0
            element->setType(derefType);
3822
3823
            // Replace the numeric byte offset parameter with array reference.
3824
0
            sequence[1] = element;
3825
0
            sequence.erase(sequence.begin(), sequence.begin()+1);
3826
0
        }
3827
0
        break;
3828
3829
0
    case EOpMethodIncrementCounter:
3830
0
        {
3831
0
            node = incDecCounter(1);
3832
0
            break;
3833
0
        }
3834
3835
0
    case EOpMethodDecrementCounter:
3836
0
        {
3837
0
            TIntermTyped* preIncValue = incDecCounter(-1); // result is original value
3838
0
            node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc,
3839
0
                                              preIncValue->getType());
3840
0
            break;
3841
0
        }
3842
3843
0
    case EOpMethodAppend:
3844
0
        {
3845
0
            TIntermTyped* oldCounter = incDecCounter(1);
3846
3847
0
            TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc);
3848
0
            TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped();
3849
3850
0
            const TType derefType(argArray->getType(), 0);
3851
0
            lValue->setType(derefType);
3852
3853
0
            node = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3854
3855
0
            break;
3856
0
        }
3857
3858
0
    case EOpMethodConsume:
3859
0
        {
3860
0
            TIntermTyped* oldCounter = incDecCounter(-1);
3861
3862
0
            TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter,
3863
0
                                                                  intermediate.addConstantUnion(-1, loc, true), loc,
3864
0
                                                                  oldCounter->getType());
3865
3866
0
            node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc);
3867
3868
0
            const TType derefType(argArray->getType(), 0);
3869
0
            node->setType(derefType);
3870
3871
0
            break;
3872
0
        }
3873
3874
0
    default:
3875
0
        break; // most pass through unchanged
3876
0
    }
3877
0
}
3878
3879
// Create array of standard sample positions for given sample count.
3880
// TODO: remove when a real method to query sample pos exists in SPIR-V.
3881
TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
3882
0
{
3883
0
    struct tSamplePos { float x, y; };
3884
3885
0
    static const tSamplePos pos1[] = {
3886
0
        { 0.0/16.0,  0.0/16.0 },
3887
0
    };
3888
3889
    // standard sample positions for 2, 4, 8, and 16 samples.
3890
0
    static const tSamplePos pos2[] = {
3891
0
        { 4.0/16.0,  4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
3892
0
    };
3893
3894
0
    static const tSamplePos pos4[] = {
3895
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 },
3896
0
    };
3897
3898
0
    static const tSamplePos pos8[] = {
3899
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 },
3900
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 },
3901
0
    };
3902
3903
0
    static const tSamplePos pos16[] = {
3904
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 },
3905
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 },
3906
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 },
3907
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 },
3908
0
    };
3909
3910
0
    const tSamplePos* sampleLoc = nullptr;
3911
0
    int numSamples = count;
3912
3913
0
    switch (count) {
3914
0
    case 2:  sampleLoc = pos2;  break;
3915
0
    case 4:  sampleLoc = pos4;  break;
3916
0
    case 8:  sampleLoc = pos8;  break;
3917
0
    case 16: sampleLoc = pos16; break;
3918
0
    default:
3919
0
        sampleLoc = pos1;
3920
0
        numSamples = 1;
3921
0
    }
3922
3923
0
    TConstUnionArray* values = new TConstUnionArray(numSamples*2);
3924
3925
0
    for (int pos=0; pos<count; ++pos) {
3926
0
        TConstUnion x, y;
3927
0
        x.setDConst(sampleLoc[pos].x);
3928
0
        y.setDConst(sampleLoc[pos].y);
3929
3930
0
        (*values)[pos*2+0] = x;
3931
0
        (*values)[pos*2+1] = y;
3932
0
    }
3933
3934
0
    TType retType(EbtFloat, EvqConst, 2);
3935
3936
0
    if (numSamples != 1) {
3937
0
        TArraySizes* arraySizes = new TArraySizes;
3938
0
        arraySizes->addInnerSize(numSamples);
3939
0
        retType.transferArraySizes(arraySizes);
3940
0
    }
3941
3942
0
    return new TIntermConstantUnion(*values, retType);
3943
0
}
3944
3945
//
3946
// Decompose DX9 and DX10 sample intrinsics & object methods into AST
3947
//
3948
void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3949
0
{
3950
0
    if (node == nullptr || !node->getAsOperator())
3951
0
        return;
3952
3953
    // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it.
3954
0
    const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
3955
0
        result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
3956
3957
0
        TIntermTyped* convertedResult = nullptr;
3958
3959
0
        TType retType;
3960
0
        getTextureReturnType(sampler, retType);
3961
3962
0
        if (retType.isStruct()) {
3963
            // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here)
3964
0
            TIntermAggregate* conversionAggregate = new TIntermAggregate;
3965
0
            convertedResult = conversionAggregate;
3966
3967
            // Convert vector output to return structure.  We will need a temp symbol to copy the results to.
3968
0
            TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType);
3969
3970
            // We also need a temp symbol to hold the result of the texture.  We don't want to re-fetch the
3971
            // sample each time we'll index into the result, so we'll copy to this, and index into the copy.
3972
0
            TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType());
3973
3974
            // Initial copy from texture to our sample result shadow.
3975
0
            TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc),
3976
0
                                                              result, loc);
3977
3978
0
            conversionAggregate->getSequence().push_back(shadowCopy);
3979
3980
0
            unsigned vec4Pos = 0;
3981
3982
0
            for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) {
3983
0
                const TType memberType(retType, m); // dereferenced type of the member we're about to assign.
3984
3985
                // Check for bad struct members.  This should have been caught upstream.  Complain, because
3986
                // wwe don't know what to do with it.  This algorithm could be generalized to handle
3987
                // other things, e.g, sub-structures, but HLSL doesn't allow them.
3988
0
                if (!memberType.isVector() && !memberType.isScalar()) {
3989
0
                    error(loc, "expected: scalar or vector type in texture structure", "", "");
3990
0
                    return nullptr;
3991
0
                }
3992
3993
                // Index into the struct variable to find the member to assign.
3994
0
                TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct,
3995
0
                                                                   intermediate.addSymbol(*structVar, loc),
3996
0
                                                                   intermediate.addConstantUnion(m, loc), loc);
3997
3998
0
                structMember->setType(memberType);
3999
4000
                // Assign each component of (possible) vector in struct member.
4001
0
                for (int component = 0; component < memberType.getVectorSize(); ++component) {
4002
0
                    TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect,
4003
0
                                                                     intermediate.addSymbol(*sampleShadow, loc),
4004
0
                                                                     intermediate.addConstantUnion(vec4Pos++, loc), loc);
4005
0
                    vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1));
4006
4007
0
                    TIntermTyped* memberAssign = nullptr;
4008
4009
0
                    if (memberType.isVector()) {
4010
                        // Vector member: we need to create an access chain to the vector component.
4011
4012
0
                        TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember,
4013
0
                                                                                 intermediate.addConstantUnion(component, loc), loc);
4014
4015
0
                        memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc);
4016
0
                    } else {
4017
                        // Scalar member: we can assign to it directly.
4018
0
                        memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc);
4019
0
                    }
4020
4021
4022
0
                    conversionAggregate->getSequence().push_back(memberAssign);
4023
0
                }
4024
0
            }
4025
4026
            // Add completed variable so the expression results in the whole struct value we just built.
4027
0
            conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc));
4028
4029
            // Make it a sequence.
4030
0
            intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc);
4031
0
        } else {
4032
            // vector clamp the output if template vector type is smaller than sample result.
4033
0
            if (retType.getVectorSize() < node->getVectorSize()) {
4034
                // Too many components.  Construct shorter vector from it.
4035
0
                const TOperator op = intermediate.mapTypeToConstructorOp(retType);
4036
4037
0
                convertedResult = constructBuiltIn(retType, op, result, loc, false);
4038
0
            } else {
4039
                // Enough components.  Use directly.
4040
0
                convertedResult = result;
4041
0
            }
4042
0
        }
4043
4044
0
        convertedResult->setLoc(loc);
4045
0
        return convertedResult;
4046
0
    };
4047
4048
0
    const TOperator op  = node->getAsOperator()->getOp();
4049
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4050
4051
    // Bail out if not a sampler method.
4052
    // Note though this is odd to do before checking the op, because the op
4053
    // could be something that takes the arguments, and the function in question
4054
    // takes the result of the op.  So, this is not the final word.
4055
0
    if (arguments != nullptr) {
4056
0
        if (argAggregate == nullptr) {
4057
0
            if (arguments->getAsTyped()->getBasicType() != EbtSampler)
4058
0
                return;
4059
0
        } else {
4060
0
            if (argAggregate->getSequence().size() == 0 ||
4061
0
                argAggregate->getSequence()[0] == nullptr ||
4062
0
                argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler)
4063
0
                return;
4064
0
        }
4065
0
    }
4066
4067
0
    switch (op) {
4068
    // **** DX9 intrinsics: ****
4069
0
    case EOpTexture:
4070
0
        {
4071
            // Texture with ddx & ddy is really gradient form in HLSL
4072
0
            if (argAggregate->getSequence().size() == 4)
4073
0
                node->getAsAggregate()->setOperator(EOpTextureGrad);
4074
4075
0
            break;
4076
0
        }
4077
0
    case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different)
4078
0
        {
4079
0
            TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped();   // sampler
4080
0
            TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped();  // coord
4081
4082
0
            assert(argCoord->getVectorSize() == 4);
4083
0
            TIntermTyped *w = intermediate.addConstantUnion(3, loc, true);
4084
0
            TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc);
4085
4086
0
            TOperator constructOp = EOpNull;
4087
0
            const TSampler &sampler = argSamp->getType().getSampler();
4088
0
            int coordSize = 0;
4089
4090
0
            switch (sampler.dim)
4091
0
            {
4092
0
            case Esd1D:   constructOp = EOpConstructFloat; coordSize = 1; break; // 1D
4093
0
            case Esd2D:   constructOp = EOpConstructVec2;  coordSize = 2; break; // 2D
4094
0
            case Esd3D:   constructOp = EOpConstructVec3;  coordSize = 3; break; // 3D
4095
0
            case EsdCube: constructOp = EOpConstructVec3;  coordSize = 3; break; // also 3D
4096
0
            default:
4097
0
                error(loc, "unhandled DX9 texture LoD dimension", "", "");
4098
0
                break;
4099
0
            }
4100
4101
0
            TIntermAggregate *constructCoord = new TIntermAggregate(constructOp);
4102
0
            constructCoord->getSequence().push_back(argCoord);
4103
0
            constructCoord->setLoc(loc);
4104
0
            constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize));
4105
4106
0
            TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod);
4107
0
            tex->getSequence().push_back(argSamp);        // sampler
4108
0
            tex->getSequence().push_back(constructCoord); // coordinate
4109
0
            tex->getSequence().push_back(argLod);         // lod
4110
4111
0
            node = convertReturn(tex, sampler);
4112
4113
0
            break;
4114
0
        }
4115
4116
0
    case EOpTextureBias:
4117
0
        {
4118
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // sampler
4119
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // coord
4120
4121
            // HLSL puts bias in W component of coordinate.  We extract it and add it to
4122
            // the argument list, instead
4123
0
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
4124
0
            TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
4125
4126
0
            TOperator constructOp = EOpNull;
4127
0
            const TSampler& sampler = arg0->getType().getSampler();
4128
4129
0
            switch (sampler.dim) {
4130
0
            case Esd1D:   constructOp = EOpConstructFloat; break; // 1D
4131
0
            case Esd2D:   constructOp = EOpConstructVec2;  break; // 2D
4132
0
            case Esd3D:   constructOp = EOpConstructVec3;  break; // 3D
4133
0
            case EsdCube: constructOp = EOpConstructVec3;  break; // also 3D
4134
0
            default:
4135
0
                error(loc, "unhandled DX9 texture bias dimension", "", "");
4136
0
                break;
4137
0
            }
4138
4139
0
            TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
4140
0
            constructCoord->getSequence().push_back(arg1);
4141
0
            constructCoord->setLoc(loc);
4142
4143
            // The input vector should never be less than 2, since there's always a bias.
4144
            // The max is for safety, and should be a no-op.
4145
0
            constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
4146
4147
0
            TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
4148
0
            tex->getSequence().push_back(arg0);           // sampler
4149
0
            tex->getSequence().push_back(constructCoord); // coordinate
4150
0
            tex->getSequence().push_back(bias);           // bias
4151
4152
0
            node = convertReturn(tex, sampler);
4153
4154
0
            break;
4155
0
        }
4156
4157
    // **** DX10 methods: ****
4158
0
    case EOpMethodSample:     // fall through
4159
0
    case EOpMethodSampleBias: // ...
4160
0
        {
4161
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4162
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4163
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4164
0
            TIntermTyped* argBias   = nullptr;
4165
0
            TIntermTyped* argOffset = nullptr;
4166
0
            const TSampler& sampler = argTex->getType().getSampler();
4167
4168
0
            int nextArg = 3;
4169
4170
0
            if (op == EOpMethodSampleBias)  // SampleBias has a bias arg
4171
0
                argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
4172
4173
0
            TOperator textureOp = EOpTexture;
4174
4175
0
            if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
4176
0
                textureOp = EOpTextureOffset;
4177
0
                argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
4178
0
            }
4179
4180
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4181
4182
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4183
0
            txsample->getSequence().push_back(txcombine);
4184
0
            txsample->getSequence().push_back(argCoord);
4185
4186
0
            if (argOffset != nullptr)
4187
0
                txsample->getSequence().push_back(argOffset);
4188
4189
0
            if (argBias != nullptr)
4190
0
              txsample->getSequence().push_back(argBias);
4191
4192
0
            node = convertReturn(txsample, sampler);
4193
4194
0
            break;
4195
0
        }
4196
4197
0
    case EOpMethodSampleGrad: // ...
4198
0
        {
4199
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4200
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4201
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4202
0
            TIntermTyped* argDDX    = argAggregate->getSequence()[3]->getAsTyped();
4203
0
            TIntermTyped* argDDY    = argAggregate->getSequence()[4]->getAsTyped();
4204
0
            TIntermTyped* argOffset = nullptr;
4205
0
            const TSampler& sampler = argTex->getType().getSampler();
4206
4207
0
            TOperator textureOp = EOpTextureGrad;
4208
4209
0
            if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
4210
0
                textureOp = EOpTextureGradOffset;
4211
0
                argOffset = argAggregate->getSequence()[5]->getAsTyped();
4212
0
            }
4213
4214
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4215
4216
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4217
0
            txsample->getSequence().push_back(txcombine);
4218
0
            txsample->getSequence().push_back(argCoord);
4219
0
            txsample->getSequence().push_back(argDDX);
4220
0
            txsample->getSequence().push_back(argDDY);
4221
4222
0
            if (argOffset != nullptr)
4223
0
                txsample->getSequence().push_back(argOffset);
4224
4225
0
            node = convertReturn(txsample, sampler);
4226
4227
0
            break;
4228
0
        }
4229
4230
0
    case EOpMethodGetDimensions:
4231
0
        {
4232
            // AST returns a vector of results, which we break apart component-wise into
4233
            // separate values to assign to the HLSL method's outputs, ala:
4234
            //  tx . GetDimensions(width, height);
4235
            //      float2 sizeQueryTemp = EOpTextureQuerySize
4236
            //      width = sizeQueryTemp.X;
4237
            //      height = sizeQueryTemp.Y;
4238
4239
0
            TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4240
0
            const TType& texType = argTex->getType();
4241
4242
0
            assert(texType.getBasicType() == EbtSampler);
4243
4244
0
            const TSampler& sampler = texType.getSampler();
4245
0
            const TSamplerDim dim = sampler.dim;
4246
0
            const bool isImage = sampler.isImage();
4247
0
            const bool isMs = sampler.isMultiSample();
4248
0
            const int numArgs = (int)argAggregate->getSequence().size();
4249
4250
0
            int numDims = 0;
4251
4252
0
            switch (dim) {
4253
0
            case Esd1D:     numDims = 1; break; // W
4254
0
            case Esd2D:     numDims = 2; break; // W, H
4255
0
            case Esd3D:     numDims = 3; break; // W, H, D
4256
0
            case EsdCube:   numDims = 2; break; // W, H (cube)
4257
0
            case EsdBuffer: numDims = 1; break; // W (buffers)
4258
0
            case EsdRect:   numDims = 2; break; // W, H (rect)
4259
0
            default:
4260
0
                error(loc, "unhandled DX10 MethodGet dimension", "", "");
4261
0
                break;
4262
0
            }
4263
4264
            // Arrayed adds another dimension for the number of array elements
4265
0
            if (sampler.isArrayed())
4266
0
                ++numDims;
4267
4268
            // Establish whether the method itself is querying mip levels.  This can be false even
4269
            // if the underlying query requires a MIP level, due to the available HLSL method overloads.
4270
0
            const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
4271
4272
            // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
4273
            // True if:
4274
            //   1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
4275
            // or,
4276
            //   2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
4277
0
            const bool mipRequired =
4278
0
                ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
4279
0
                mipQuery; // 2...
4280
4281
            // AST assumes integer return.  Will be converted to float if required.
4282
0
            TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
4283
0
            sizeQuery->getSequence().push_back(argTex);
4284
4285
            // If we're building an LOD query, add the LOD.
4286
0
            if (mipRequired) {
4287
                // If the base HLSL query had no MIP level given, use level 0.
4288
0
                TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
4289
0
                    intermediate.addConstantUnion(0, loc, true);
4290
0
                sizeQuery->getSequence().push_back(queryLod);
4291
0
            }
4292
4293
0
            sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
4294
0
            sizeQuery->setLoc(loc);
4295
4296
            // Return value from size query
4297
0
            TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
4298
0
            tempArg->getWritableType().getQualifier().makeTemporary();
4299
0
            TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
4300
0
                                                                   intermediate.addSymbol(*tempArg, loc),
4301
0
                                                                   sizeQuery, loc);
4302
4303
            // Compound statement for assigning outputs
4304
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
4305
            // Index of first output parameter
4306
0
            const int outParamBase = mipQuery ? 2 : 1;
4307
4308
0
            for (int compNum = 0; compNum < numDims; ++compNum) {
4309
0
                TIntermTyped* indexedOut = nullptr;
4310
0
                TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
4311
4312
0
                if (numDims > 1) {
4313
0
                    TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
4314
0
                    indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
4315
0
                    indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
4316
0
                    indexedOut->setLoc(loc);
4317
0
                } else {
4318
0
                    indexedOut = sizeQueryReturn;
4319
0
                }
4320
4321
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
4322
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
4323
4324
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4325
0
            }
4326
4327
            // handle mip level parameter
4328
0
            if (mipQuery) {
4329
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4330
4331
0
                TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
4332
0
                levelsQuery->getSequence().push_back(argTex);
4333
0
                levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
4334
0
                levelsQuery->setLoc(loc);
4335
4336
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
4337
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4338
0
            }
4339
4340
            // 2DMS formats query # samples, which needs a different query op
4341
0
            if (sampler.isMultiSample()) {
4342
0
                TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
4343
4344
0
                TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4345
0
                samplesQuery->getSequence().push_back(argTex);
4346
0
                samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4347
0
                samplesQuery->setLoc(loc);
4348
4349
0
                TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
4350
0
                compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4351
0
            }
4352
4353
0
            compoundStatement->setOperator(EOpSequence);
4354
0
            compoundStatement->setLoc(loc);
4355
0
            compoundStatement->setType(TType(EbtVoid));
4356
4357
0
            node = compoundStatement;
4358
4359
0
            break;
4360
0
        }
4361
4362
0
    case EOpMethodSampleCmp:  // fall through...
4363
0
    case EOpMethodSampleCmpLevelZero:
4364
0
        {
4365
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4366
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4367
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4368
0
            TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
4369
0
            TIntermTyped* argOffset = nullptr;
4370
4371
            // Sampler argument should be a sampler.
4372
0
            if (argSamp->getType().getBasicType() != EbtSampler) {
4373
0
                error(loc, "expected: sampler type", "", "");
4374
0
                return;
4375
0
            }
4376
4377
            // Sampler should be a SamplerComparisonState
4378
0
            if (! argSamp->getType().getSampler().isShadow()) {
4379
0
                error(loc, "expected: SamplerComparisonState", "", "");
4380
0
                return;
4381
0
            }
4382
4383
            // optional offset value
4384
0
            if (argAggregate->getSequence().size() > 4)
4385
0
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4386
4387
0
            const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
4388
4389
            // AST wants comparison value as one of the texture coordinates
4390
0
            TOperator constructOp = EOpNull;
4391
0
            switch (coordDimWithCmpVal) {
4392
            // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
4393
0
            case 2: constructOp = EOpConstructVec2;  break;
4394
0
            case 3: constructOp = EOpConstructVec3;  break;
4395
0
            case 4: constructOp = EOpConstructVec4;  break;
4396
0
            case 5: constructOp = EOpConstructVec4;  break; // cubeArrayShadow, cmp value is separate arg.
4397
0
            default:
4398
0
                error(loc, "unhandled DX10 MethodSample dimension", "", "");
4399
0
                break;
4400
0
            }
4401
4402
0
            TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
4403
0
            coordWithCmp->getSequence().push_back(argCoord);
4404
0
            if (coordDimWithCmpVal != 5) // cube array shadow is special.
4405
0
                coordWithCmp->getSequence().push_back(argCmpVal);
4406
0
            coordWithCmp->setLoc(loc);
4407
0
            coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
4408
4409
0
            TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
4410
0
            if (argOffset != nullptr)
4411
0
                textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
4412
4413
            // Create combined sampler & texture op
4414
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4415
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4416
0
            txsample->getSequence().push_back(txcombine);
4417
0
            txsample->getSequence().push_back(coordWithCmp);
4418
4419
0
            if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
4420
0
                txsample->getSequence().push_back(argCmpVal);
4421
4422
            // the LevelZero form uses 0 as an explicit LOD
4423
0
            if (op == EOpMethodSampleCmpLevelZero)
4424
0
                txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
4425
4426
            // Add offset if present
4427
0
            if (argOffset != nullptr)
4428
0
                txsample->getSequence().push_back(argOffset);
4429
4430
0
            txsample->setType(node->getType());
4431
0
            txsample->setLoc(loc);
4432
0
            node = txsample;
4433
4434
0
            break;
4435
0
        }
4436
4437
0
    case EOpMethodLoad:
4438
0
        {
4439
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4440
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[1]->getAsTyped();
4441
0
            TIntermTyped* argOffset = nullptr;
4442
0
            TIntermTyped* lodComponent = nullptr;
4443
0
            TIntermTyped* coordSwizzle = nullptr;
4444
4445
0
            const TSampler& sampler = argTex->getType().getSampler();
4446
0
            const bool isMS = sampler.isMultiSample();
4447
0
            const bool isBuffer = sampler.dim == EsdBuffer;
4448
0
            const bool isImage = sampler.isImage();
4449
0
            const TBasicType coordBaseType = argCoord->getType().getBasicType();
4450
4451
            // Last component of coordinate is the mip level, for non-MS.  we separate them here:
4452
0
            if (isMS || isBuffer || isImage) {
4453
                // MS, Buffer, and Image have no LOD
4454
0
                coordSwizzle = argCoord;
4455
0
            } else {
4456
                // Extract coordinate
4457
0
                int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
4458
0
                TSwizzleSelectors<TVectorSelector> coordFields;
4459
0
                for (int i = 0; i < swizzleSize; ++i)
4460
0
                    coordFields.push_back(i);
4461
0
                TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
4462
0
                coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
4463
0
                coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
4464
4465
                // Extract LOD
4466
0
                TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
4467
0
                lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
4468
0
                lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
4469
0
            }
4470
4471
0
            const int numArgs    = (int)argAggregate->getSequence().size();
4472
0
            const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
4473
4474
            // Create texel fetch
4475
0
            const TOperator fetchOp = (isImage   ? EOpImageLoad :
4476
0
                                       hasOffset ? EOpTextureFetchOffset :
4477
0
                                       EOpTextureFetch);
4478
0
            TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
4479
4480
            // Build up the fetch
4481
0
            txfetch->getSequence().push_back(argTex);
4482
0
            txfetch->getSequence().push_back(coordSwizzle);
4483
4484
0
            if (isMS) {
4485
                // add 2DMS sample index
4486
0
                TIntermTyped* argSampleIdx  = argAggregate->getSequence()[2]->getAsTyped();
4487
0
                txfetch->getSequence().push_back(argSampleIdx);
4488
0
            } else if (isBuffer) {
4489
                // Nothing else to do for buffers.
4490
0
            } else if (isImage) {
4491
                // Nothing else to do for images.
4492
0
            } else {
4493
                // 2DMS and buffer have no LOD, but everything else does.
4494
0
                txfetch->getSequence().push_back(lodComponent);
4495
0
            }
4496
4497
            // Obtain offset arg, if there is one.
4498
0
            if (hasOffset) {
4499
0
                const int offsetPos  = (isMS ? 3 : 2);
4500
0
                argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
4501
0
                txfetch->getSequence().push_back(argOffset);
4502
0
            }
4503
4504
0
            node = convertReturn(txfetch, sampler);
4505
4506
0
            break;
4507
0
        }
4508
4509
0
    case EOpMethodSampleLevel:
4510
0
        {
4511
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4512
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4513
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4514
0
            TIntermTyped* argLod    = argAggregate->getSequence()[3]->getAsTyped();
4515
0
            TIntermTyped* argOffset = nullptr;
4516
0
            const TSampler& sampler = argTex->getType().getSampler();
4517
4518
0
            const int  numArgs = (int)argAggregate->getSequence().size();
4519
4520
0
            if (numArgs == 5) // offset, if present
4521
0
                argOffset = argAggregate->getSequence()[4]->getAsTyped();
4522
4523
0
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
4524
0
            TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4525
4526
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4527
4528
0
            txsample->getSequence().push_back(txcombine);
4529
0
            txsample->getSequence().push_back(argCoord);
4530
0
            txsample->getSequence().push_back(argLod);
4531
4532
0
            if (argOffset != nullptr)
4533
0
                txsample->getSequence().push_back(argOffset);
4534
4535
0
            node = convertReturn(txsample, sampler);
4536
4537
0
            break;
4538
0
        }
4539
4540
0
    case EOpMethodGather:
4541
0
        {
4542
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4543
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4544
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4545
0
            TIntermTyped* argOffset = nullptr;
4546
4547
            // Offset is optional
4548
0
            if (argAggregate->getSequence().size() > 3)
4549
0
                argOffset = argAggregate->getSequence()[3]->getAsTyped();
4550
4551
0
            const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
4552
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4553
4554
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4555
4556
0
            txgather->getSequence().push_back(txcombine);
4557
0
            txgather->getSequence().push_back(argCoord);
4558
            // Offset if not given is implicitly channel 0 (red)
4559
4560
0
            if (argOffset != nullptr)
4561
0
                txgather->getSequence().push_back(argOffset);
4562
4563
0
            txgather->setType(node->getType());
4564
0
            txgather->setLoc(loc);
4565
0
            node = txgather;
4566
4567
0
            break;
4568
0
        }
4569
4570
0
    case EOpMethodGatherRed:      // fall through...
4571
0
    case EOpMethodGatherGreen:    // ...
4572
0
    case EOpMethodGatherBlue:     // ...
4573
0
    case EOpMethodGatherAlpha:    // ...
4574
0
    case EOpMethodGatherCmpRed:   // ...
4575
0
    case EOpMethodGatherCmpGreen: // ...
4576
0
    case EOpMethodGatherCmpBlue:  // ...
4577
0
    case EOpMethodGatherCmpAlpha: // ...
4578
0
        {
4579
0
            int channel = 0;    // the channel we are gathering
4580
0
            int cmpValues = 0;  // 1 if there is a compare value (handier than a bool below)
4581
4582
0
            switch (op) {
4583
0
            case EOpMethodGatherCmpRed:   cmpValues = 1;  [[fallthrough]];
4584
0
            case EOpMethodGatherRed:      channel = 0; break;
4585
0
            case EOpMethodGatherCmpGreen: cmpValues = 1;  [[fallthrough]];
4586
0
            case EOpMethodGatherGreen:    channel = 1; break;
4587
0
            case EOpMethodGatherCmpBlue:  cmpValues = 1;  [[fallthrough]];
4588
0
            case EOpMethodGatherBlue:     channel = 2; break;
4589
0
            case EOpMethodGatherCmpAlpha: cmpValues = 1;  [[fallthrough]];
4590
0
            case EOpMethodGatherAlpha:    channel = 3; break;
4591
0
            default:                      assert(0);   break;
4592
0
            }
4593
4594
            // For now, we have nothing to map the component-wise comparison forms
4595
            // to, because neither GLSL nor SPIR-V has such an opcode.  Issue an
4596
            // unimplemented error instead.  Most of the machinery is here if that
4597
            // should ever become available.  However, red can be passed through
4598
            // to OpImageDrefGather.  G/B/A cannot, because that opcode does not
4599
            // accept a component.
4600
0
            if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
4601
0
                error(loc, "unimplemented: component-level gather compare", "", "");
4602
0
                return;
4603
0
            }
4604
4605
0
            int arg = 0;
4606
4607
0
            TIntermTyped* argTex        = argAggregate->getSequence()[arg++]->getAsTyped();
4608
0
            TIntermTyped* argSamp       = argAggregate->getSequence()[arg++]->getAsTyped();
4609
0
            TIntermTyped* argCoord      = argAggregate->getSequence()[arg++]->getAsTyped();
4610
0
            TIntermTyped* argOffset     = nullptr;
4611
0
            TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
4612
            // TIntermTyped* argStatus     = nullptr; // TODO: residency
4613
0
            TIntermTyped* argCmp        = nullptr;
4614
4615
0
            const TSamplerDim dim = argTex->getType().getSampler().dim;
4616
4617
0
            const int  argSize = (int)argAggregate->getSequence().size();
4618
0
            bool hasStatus     = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
4619
0
            bool hasOffset1    = false;
4620
0
            bool hasOffset4    = false;
4621
4622
            // Sampler argument should be a sampler.
4623
0
            if (argSamp->getType().getBasicType() != EbtSampler) {
4624
0
                error(loc, "expected: sampler type", "", "");
4625
0
                return;
4626
0
            }
4627
4628
            // Cmp forms require SamplerComparisonState
4629
0
            if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) {
4630
0
                error(loc, "expected: SamplerComparisonState", "", "");
4631
0
                return;
4632
0
            }
4633
4634
            // Only 2D forms can have offsets.  Discover if we have 0, 1 or 4 offsets.
4635
0
            if (dim == Esd2D) {
4636
0
                hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
4637
0
                hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
4638
0
            }
4639
4640
0
            assert(!(hasOffset1 && hasOffset4));
4641
4642
0
            TOperator textureOp = EOpTextureGather;
4643
4644
            // Compare forms have compare value
4645
0
            if (cmpValues != 0)
4646
0
                argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4647
4648
            // Some forms have single offset
4649
0
            if (hasOffset1) {
4650
0
                textureOp = EOpTextureGatherOffset;   // single offset form
4651
0
                argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4652
0
            }
4653
4654
            // Some forms have 4 gather offsets
4655
0
            if (hasOffset4) {
4656
0
                textureOp = EOpTextureGatherOffsets;  // note plural, for 4 offset form
4657
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4658
0
                    argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
4659
0
            }
4660
4661
            // Residency status
4662
0
            if (hasStatus) {
4663
                // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
4664
0
                error(loc, "unimplemented: residency status", "", "");
4665
0
                return;
4666
0
            }
4667
4668
0
            TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4669
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4670
4671
0
            TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
4672
4673
0
            txgather->getSequence().push_back(txcombine);
4674
0
            txgather->getSequence().push_back(argCoord);
4675
4676
            // AST wants an array of 4 offsets, where HLSL has separate args.  Here
4677
            // we construct an array from the separate args.
4678
0
            if (hasOffset4) {
4679
0
                TType arrayType(EbtInt, EvqTemporary, 2);
4680
0
                TArraySizes* arraySizes = new TArraySizes;
4681
0
                arraySizes->addInnerSize(4);
4682
0
                arrayType.transferArraySizes(arraySizes);
4683
4684
0
                TIntermAggregate* initList = new TIntermAggregate(EOpNull);
4685
4686
0
                for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4687
0
                    initList->getSequence().push_back(argOffsets[offsetNum]);
4688
4689
0
                argOffset = addConstructor(loc, initList, arrayType);
4690
0
            }
4691
4692
            // Add comparison value if we have one
4693
0
            if (argCmp != nullptr)
4694
0
                txgather->getSequence().push_back(argCmp);
4695
4696
            // Add offset (either 1, or an array of 4) if we have one
4697
0
            if (argOffset != nullptr)
4698
0
                txgather->getSequence().push_back(argOffset);
4699
4700
            // Add channel value if the sampler is not shadow
4701
0
            if (! argSamp->getType().getSampler().isShadow())
4702
0
                txgather->getSequence().push_back(argChannel);
4703
4704
0
            txgather->setType(node->getType());
4705
0
            txgather->setLoc(loc);
4706
0
            node = txgather;
4707
4708
0
            break;
4709
0
        }
4710
4711
0
    case EOpMethodCalculateLevelOfDetail:
4712
0
    case EOpMethodCalculateLevelOfDetailUnclamped:
4713
0
        {
4714
0
            TIntermTyped* argTex    = argAggregate->getSequence()[0]->getAsTyped();
4715
0
            TIntermTyped* argSamp   = argAggregate->getSequence()[1]->getAsTyped();
4716
0
            TIntermTyped* argCoord  = argAggregate->getSequence()[2]->getAsTyped();
4717
4718
0
            TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
4719
4720
0
            TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4721
0
            txquerylod->getSequence().push_back(txcombine);
4722
0
            txquerylod->getSequence().push_back(argCoord);
4723
4724
0
            TIntermTyped* lodComponent = intermediate.addConstantUnion(
4725
0
                op == EOpMethodCalculateLevelOfDetail ? 0 : 1,
4726
0
                loc, true);
4727
0
            TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
4728
0
            lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
4729
0
            node = lodComponentIdx;
4730
4731
0
            break;
4732
0
        }
4733
4734
0
    case EOpMethodGetSamplePosition:
4735
0
        {
4736
            // TODO: this entire decomposition exists because there is not yet a way to query
4737
            // the sample position directly through SPIR-V.  Instead, we return fixed sample
4738
            // positions for common cases.  *** If the sample positions are set differently,
4739
            // this will be wrong. ***
4740
4741
0
            TIntermTyped* argTex     = argAggregate->getSequence()[0]->getAsTyped();
4742
0
            TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped();
4743
4744
0
            TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4745
0
            samplesQuery->getSequence().push_back(argTex);
4746
0
            samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4747
0
            samplesQuery->setLoc(loc);
4748
4749
0
            TIntermAggregate* compoundStatement = nullptr;
4750
4751
0
            TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint));
4752
0
            outSampleCount->getWritableType().getQualifier().makeTemporary();
4753
0
            TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc),
4754
0
                                                              samplesQuery, loc);
4755
0
            compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4756
4757
0
            TIntermTyped* idxtest[4];
4758
4759
            // Create tests against 2, 4, 8, and 16 sample values
4760
0
            int count = 0;
4761
0
            for (int val = 2; val <= 16; val *= 2)
4762
0
                idxtest[count++] =
4763
0
                    intermediate.addBinaryNode(EOpEqual,
4764
0
                                               intermediate.addSymbol(*outSampleCount, loc),
4765
0
                                               intermediate.addConstantUnion(val, loc),
4766
0
                                               loc, TType(EbtBool));
4767
4768
0
            const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
4769
4770
            // Create index ops into position arrays given sample index.
4771
            // TODO: should it be clamped?
4772
0
            TIntermTyped* index[4];
4773
0
            count = 0;
4774
0
            for (int val = 2; val <= 16; val *= 2) {
4775
0
                index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc);
4776
0
                index[count++]->setType(TType(EbtFloat, EvqTemporary, 2));
4777
0
            }
4778
4779
            // Create expression as:
4780
            // (sampleCount == 2)  ? pos2[idx] :
4781
            // (sampleCount == 4)  ? pos4[idx] :
4782
            // (sampleCount == 8)  ? pos8[idx] :
4783
            // (sampleCount == 16) ? pos16[idx] : float2(0,0);
4784
0
            TIntermTyped* test =
4785
0
                intermediate.addSelection(idxtest[0], index[0],
4786
0
                    intermediate.addSelection(idxtest[1], index[1],
4787
0
                        intermediate.addSelection(idxtest[2], index[2],
4788
0
                            intermediate.addSelection(idxtest[3], index[3],
4789
0
                                                      getSamplePosArray(1), loc), loc), loc), loc);
4790
4791
0
            compoundStatement = intermediate.growAggregate(compoundStatement, test);
4792
0
            compoundStatement->setOperator(EOpSequence);
4793
0
            compoundStatement->setLoc(loc);
4794
0
            compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2));
4795
4796
0
            node = compoundStatement;
4797
4798
0
            break;
4799
0
        }
4800
4801
0
    case EOpSubpassLoad:
4802
0
        {
4803
0
            const TIntermTyped* argSubpass =
4804
0
                argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
4805
0
                arguments->getAsTyped();
4806
4807
0
            const TSampler& sampler = argSubpass->getType().getSampler();
4808
4809
            // subpass load: the multisample form is overloaded.  Here, we convert that to
4810
            // the EOpSubpassLoadMS opcode.
4811
0
            if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
4812
0
                node->getAsOperator()->setOp(EOpSubpassLoadMS);
4813
4814
0
            node = convertReturn(node, sampler);
4815
4816
0
            break;
4817
0
        }
4818
4819
4820
0
    default:
4821
0
        break; // most pass through unchanged
4822
0
    }
4823
0
}
4824
4825
//
4826
// Decompose geometry shader methods
4827
//
4828
void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4829
0
{
4830
0
    if (node == nullptr || !node->getAsOperator())
4831
0
        return;
4832
4833
0
    const TOperator op  = node->getAsOperator()->getOp();
4834
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4835
4836
0
    switch (op) {
4837
0
    case EOpMethodAppend:
4838
0
        if (argAggregate) {
4839
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4840
0
            if (language != EShLangGeometry) {
4841
0
                node = nullptr;
4842
0
                return;
4843
0
            }
4844
4845
0
            TIntermAggregate* sequence = nullptr;
4846
0
            TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
4847
4848
0
            emit->setLoc(loc);
4849
0
            emit->setType(TType(EbtVoid));
4850
4851
0
            TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped();
4852
4853
            // This will be patched in finalization during finalizeAppendMethods()
4854
0
            sequence = intermediate.growAggregate(sequence, data, loc);
4855
0
            sequence = intermediate.growAggregate(sequence, emit);
4856
4857
0
            sequence->setOperator(EOpSequence);
4858
0
            sequence->setLoc(loc);
4859
0
            sequence->setType(TType(EbtVoid));
4860
4861
0
            gsAppends.push_back({sequence, loc});
4862
4863
0
            node = sequence;
4864
0
        }
4865
0
        break;
4866
4867
0
    case EOpMethodRestartStrip:
4868
0
        {
4869
            // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4870
0
            if (language != EShLangGeometry) {
4871
0
                node = nullptr;
4872
0
                return;
4873
0
            }
4874
4875
0
            TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
4876
0
            cut->setLoc(loc);
4877
0
            cut->setType(TType(EbtVoid));
4878
0
            node = cut;
4879
0
        }
4880
0
        break;
4881
4882
0
    default:
4883
0
        break; // most pass through unchanged
4884
0
    }
4885
0
}
4886
4887
//
4888
// Optionally decompose intrinsics to AST opcodes.
4889
//
4890
void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4891
0
{
4892
    // Helper to find image data for image atomics:
4893
    // OpImageLoad(image[idx])
4894
    // We take the image load apart and add its params to the atomic op aggregate node
4895
0
    const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
4896
0
        TIntermAggregate* loadOp = load->getAsAggregate();
4897
0
        if (loadOp == nullptr) {
4898
0
            error(loc, "unknown image type in atomic operation", "", "");
4899
0
            node = nullptr;
4900
0
            return;
4901
0
        }
4902
4903
0
        atomic->getSequence().push_back(loadOp->getSequence()[0]);
4904
0
        atomic->getSequence().push_back(loadOp->getSequence()[1]);
4905
0
    };
4906
4907
    // Return true if this is an imageLoad, which we will change to an image atomic.
4908
0
    const auto isImageParam = [](TIntermTyped* image) -> bool {
4909
0
        TIntermAggregate* imageAggregate = image->getAsAggregate();
4910
0
        return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
4911
0
    };
4912
4913
0
    const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* {
4914
0
        TSymbol* symbol = symbolTable.find(name);
4915
0
        if (nullptr == symbol) {
4916
0
            type.getQualifier().builtIn = builtin;
4917
4918
0
            TVariable* variable = new TVariable(NewPoolTString(name), type);
4919
4920
0
            symbolTable.insert(*variable);
4921
4922
0
            symbol = symbolTable.find(name);
4923
0
            assert(symbol && "Inserted symbol could not be found!");
4924
0
        }
4925
4926
0
        return intermediate.addSymbol(*(symbol->getAsVariable()), loc);
4927
0
    };
4928
4929
    // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
4930
    // opcodes for compatibility with existing software stacks.
4931
0
    static const bool decomposeHlslIntrinsics = true;
4932
4933
0
    if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
4934
0
        return;
4935
4936
0
    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4937
0
    TIntermUnary* fnUnary = node->getAsUnaryNode();
4938
0
    const TOperator op  = node->getAsOperator()->getOp();
4939
4940
0
    switch (op) {
4941
0
    case EOpGenMul:
4942
0
        {
4943
            // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
4944
            // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
4945
            // we must reverse the operand order here.  Hence, arg0 gets sequence[1], etc.
4946
0
            TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
4947
0
            TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
4948
4949
0
            if (arg0->isVector() && arg1->isVector()) {  // vec * vec
4950
0
                node->getAsAggregate()->setOperator(EOpDot);
4951
0
            } else {
4952
0
                node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
4953
0
            }
4954
4955
0
            break;
4956
0
        }
4957
4958
0
    case EOpRcp:
4959
0
        {
4960
            // rcp(a) -> 1 / a
4961
0
            TIntermTyped* arg0 = fnUnary->getOperand();
4962
0
            TBasicType   type0 = arg0->getBasicType();
4963
0
            TIntermTyped* one  = intermediate.addConstantUnion(1, type0, loc, true);
4964
0
            node  = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
4965
4966
0
            break;
4967
0
        }
4968
4969
0
    case EOpAny: // fall through
4970
0
    case EOpAll:
4971
0
        {
4972
0
            TIntermTyped* typedArg = arguments->getAsTyped();
4973
4974
            // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool.
4975
            // We'll convert here.  Note that for efficiency, we could add a smarter
4976
            // decomposition for some type cases, e.g, maybe by decomposing a dot product.
4977
0
            if (typedArg->getType().getBasicType() != EbtBool) {
4978
0
                const TType boolType(EbtBool, EvqTemporary,
4979
0
                                     typedArg->getVectorSize(),
4980
0
                                     typedArg->getMatrixCols(),
4981
0
                                     typedArg->getMatrixRows(),
4982
0
                                     typedArg->isVector());
4983
4984
0
                typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg);
4985
0
                node->getAsUnaryNode()->setOperand(typedArg);
4986
0
            }
4987
4988
0
            break;
4989
0
        }
4990
4991
0
    case EOpSaturate:
4992
0
        {
4993
            // saturate(a) -> clamp(a,0,1)
4994
0
            TIntermTyped* arg0 = fnUnary->getOperand();
4995
0
            TBasicType   type0 = arg0->getBasicType();
4996
0
            TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
4997
4998
0
            clamp->getSequence().push_back(arg0);
4999
0
            clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
5000
0
            clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
5001
0
            clamp->setLoc(loc);
5002
0
            clamp->setType(node->getType());
5003
0
            clamp->getWritableType().getQualifier().makeTemporary();
5004
0
            node = clamp;
5005
5006
0
            break;
5007
0
        }
5008
5009
0
    case EOpSinCos:
5010
0
        {
5011
            // sincos(a,b,c) -> b = sin(a), c = cos(a)
5012
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5013
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5014
0
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
5015
5016
0
            TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
5017
0
            TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
5018
0
            TIntermTyped* sinAssign    = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
5019
0
            TIntermTyped* cosAssign    = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
5020
5021
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
5022
0
            compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
5023
0
            compoundStatement->setOperator(EOpSequence);
5024
0
            compoundStatement->setLoc(loc);
5025
0
            compoundStatement->setType(TType(EbtVoid));
5026
5027
0
            node = compoundStatement;
5028
5029
0
            break;
5030
0
        }
5031
5032
0
    case EOpClip:
5033
0
        {
5034
            // clip(a) -> if (any(a<0)) discard;
5035
0
            TIntermTyped*  arg0 = fnUnary->getOperand();
5036
0
            TBasicType     type0 = arg0->getBasicType();
5037
0
            TIntermTyped*  compareNode = nullptr;
5038
5039
            // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
5040
0
            if (!arg0->isScalar()) {
5041
                // component-wise compare: a < 0
5042
0
                TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
5043
0
                less->getSequence().push_back(arg0);
5044
0
                less->setLoc(loc);
5045
5046
                // make vec or mat of bool matching dimensions of input
5047
0
                less->setType(TType(EbtBool, EvqTemporary,
5048
0
                                    arg0->getType().getVectorSize(),
5049
0
                                    arg0->getType().getMatrixCols(),
5050
0
                                    arg0->getType().getMatrixRows(),
5051
0
                                    arg0->getType().isVector()));
5052
5053
                // calculate # of components for comparison const
5054
0
                const int constComponentCount =
5055
0
                    std::max(arg0->getType().getVectorSize(), 1) *
5056
0
                    std::max(arg0->getType().getMatrixCols(), 1) *
5057
0
                    std::max(arg0->getType().getMatrixRows(), 1);
5058
5059
0
                TConstUnion zero;
5060
0
                if (arg0->getType().isIntegerDomain())
5061
0
                    zero.setDConst(0);
5062
0
                else
5063
0
                    zero.setDConst(0.0);
5064
0
                TConstUnionArray zeros(constComponentCount, zero);
5065
5066
0
                less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
5067
5068
0
                compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
5069
0
            } else {
5070
0
                TIntermTyped* zero;
5071
0
                if (arg0->getType().isIntegerDomain())
5072
0
                    zero = intermediate.addConstantUnion(0, loc, true);
5073
0
                else
5074
0
                    zero = intermediate.addConstantUnion(0.0, type0, loc, true);
5075
0
                compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
5076
0
            }
5077
5078
0
            TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
5079
5080
0
            node = new TIntermSelection(compareNode, killNode, nullptr);
5081
0
            node->setLoc(loc);
5082
5083
0
            break;
5084
0
        }
5085
5086
0
    case EOpLog10:
5087
0
        {
5088
            // log10(a) -> log2(a) * 0.301029995663981  (== 1/log2(10))
5089
0
            TIntermTyped* arg0 = fnUnary->getOperand();
5090
0
            TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
5091
0
            TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
5092
5093
0
            node  = handleBinaryMath(loc, "mul", EOpMul, log2, base);
5094
5095
0
            break;
5096
0
        }
5097
5098
0
    case EOpDst:
5099
0
        {
5100
            // dest.x = 1;
5101
            // dest.y = src0.y * src1.y;
5102
            // dest.z = src0.z;
5103
            // dest.w = src1.w;
5104
5105
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5106
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5107
5108
0
            TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
5109
0
            TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
5110
0
            TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
5111
5112
0
            TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
5113
0
            TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
5114
0
            TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
5115
0
            TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
5116
5117
0
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5118
5119
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5120
0
            dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
5121
0
            dst->getSequence().push_back(src0z);
5122
0
            dst->getSequence().push_back(src1w);
5123
0
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5124
0
            dst->setLoc(loc);
5125
0
            node = dst;
5126
5127
0
            break;
5128
0
        }
5129
5130
0
    case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
5131
0
    case EOpInterlockedMin: // ...
5132
0
    case EOpInterlockedMax: // ...
5133
0
    case EOpInterlockedAnd: // ...
5134
0
    case EOpInterlockedOr:  // ...
5135
0
    case EOpInterlockedXor: // ...
5136
0
    case EOpInterlockedExchange: // always has output arg
5137
0
        {
5138
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5139
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // value
5140
0
            TIntermTyped* arg2 = nullptr;
5141
5142
0
            if (argAggregate->getSequence().size() > 2)
5143
0
                arg2 = argAggregate->getSequence()[2]->getAsTyped();
5144
5145
0
            const bool isImage = isImageParam(arg0);
5146
0
            const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
5147
0
            TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
5148
0
            atomic->setType(arg0->getType());
5149
0
            atomic->getWritableType().getQualifier().makeTemporary();
5150
0
            atomic->setLoc(loc);
5151
5152
0
            if (isImage) {
5153
                // orig_value = imageAtomicOp(image, loc, data)
5154
0
                imageAtomicParams(atomic, arg0);
5155
0
                atomic->getSequence().push_back(arg1);
5156
5157
0
                if (argAggregate->getSequence().size() > 2) {
5158
0
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5159
0
                } else {
5160
0
                    node = atomic; // no assignment needed, as there was no out var.
5161
0
                }
5162
0
            } else {
5163
                // Normal memory variable:
5164
                // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
5165
0
                if (argAggregate->getSequence().size() > 2) {
5166
                    // optional output param is present.  return value goes to arg2.
5167
0
                    atomic->getSequence().push_back(arg0);
5168
0
                    atomic->getSequence().push_back(arg1);
5169
5170
0
                    node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
5171
0
                } else {
5172
                    // Set the matching operator.  Since output is absent, this is all we need to do.
5173
0
                    node->getAsAggregate()->setOperator(atomicOp);
5174
0
                    node->setType(atomic->getType());
5175
0
                }
5176
0
            }
5177
5178
0
            break;
5179
0
        }
5180
5181
0
    case EOpInterlockedCompareExchange:
5182
0
        {
5183
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // dest
5184
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // cmp
5185
0
            TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();  // value
5186
0
            TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped();  // orig
5187
5188
0
            const bool isImage = isImageParam(arg0);
5189
0
            TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
5190
0
            atomic->setLoc(loc);
5191
0
            atomic->setType(arg2->getType());
5192
0
            atomic->getWritableType().getQualifier().makeTemporary();
5193
5194
0
            if (isImage) {
5195
0
                imageAtomicParams(atomic, arg0);
5196
0
            } else {
5197
0
                atomic->getSequence().push_back(arg0);
5198
0
            }
5199
5200
0
            atomic->getSequence().push_back(arg1);
5201
0
            atomic->getSequence().push_back(arg2);
5202
0
            node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
5203
5204
0
            break;
5205
0
        }
5206
5207
0
    case EOpEvaluateAttributeSnapped:
5208
0
        {
5209
            // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
5210
            // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
5211
            //   iU = (iU<<28)>>28
5212
            //   fU = ((float)iU)/16
5213
            // Targets might handle this natively, in which case they can disable
5214
            // decompositions.
5215
5216
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();  // value
5217
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();  // offset
5218
5219
0
            TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
5220
0
            TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
5221
0
                                                handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
5222
0
                                                i28);
5223
5224
0
            TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
5225
0
            TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
5226
0
                                                         intermediate.addConversion(EOpConstructFloat,
5227
0
                                                                                    TType(EbtFloat, EvqTemporary, 2), iU),
5228
0
                                                         recip16);
5229
5230
0
            TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
5231
0
            interp->getSequence().push_back(arg0);
5232
0
            interp->getSequence().push_back(floatOffset);
5233
0
            interp->setLoc(loc);
5234
0
            interp->setType(arg0->getType());
5235
0
            interp->getWritableType().getQualifier().makeTemporary();
5236
5237
0
            node = interp;
5238
5239
0
            break;
5240
0
        }
5241
5242
0
    case EOpLit:
5243
0
        {
5244
0
            TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
5245
0
            TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
5246
0
            TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
5247
5248
0
            TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
5249
5250
            // Ambient
5251
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5252
5253
            // Diffuse:
5254
0
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5255
0
            TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
5256
0
            diffuse->getSequence().push_back(n_dot_l);
5257
0
            diffuse->getSequence().push_back(zero);
5258
0
            diffuse->setLoc(loc);
5259
0
            diffuse->setType(TType(EbtFloat));
5260
0
            dst->getSequence().push_back(diffuse);
5261
5262
            // Specular:
5263
0
            TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
5264
0
            min_ndot->getSequence().push_back(n_dot_l);
5265
0
            min_ndot->getSequence().push_back(n_dot_h);
5266
0
            min_ndot->setLoc(loc);
5267
0
            min_ndot->setType(TType(EbtFloat));
5268
5269
0
            TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
5270
0
            TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m);  // n_dot_h * m
5271
5272
0
            dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
5273
5274
            // One:
5275
0
            dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
5276
5277
0
            dst->setLoc(loc);
5278
0
            dst->setType(TType(EbtFloat, EvqTemporary, 4));
5279
0
            node = dst;
5280
0
            break;
5281
0
        }
5282
5283
0
    case EOpAsDouble:
5284
0
        {
5285
            // asdouble accepts two 32 bit ints.  we can use EOpUint64BitsToDouble, but must
5286
            // first construct a uint64.
5287
0
            TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5288
0
            TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5289
5290
0
            if (arg0->getType().isVector()) { // TODO: ...
5291
0
                error(loc, "double2 conversion not implemented", "asdouble", "");
5292
0
                break;
5293
0
            }
5294
5295
0
            TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
5296
5297
0
            uint64->getSequence().push_back(arg0);
5298
0
            uint64->getSequence().push_back(arg1);
5299
0
            uint64->setType(TType(EbtUint, EvqTemporary, 2));  // convert 2 uints to a uint2
5300
0
            uint64->setLoc(loc);
5301
5302
            // bitcast uint2 to a double
5303
0
            TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
5304
0
            convert->getAsUnaryNode()->setOperand(uint64);
5305
0
            convert->setLoc(loc);
5306
0
            convert->setType(TType(EbtDouble, EvqTemporary));
5307
0
            node = convert;
5308
5309
0
            break;
5310
0
        }
5311
5312
0
    case EOpF16tof32:
5313
0
        {
5314
            // input uvecN with low 16 bits of each component holding a float16.  convert to float32.
5315
0
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5316
0
            TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
5317
0
            const int vecSize = argValue->getType().getVectorSize();
5318
5319
0
            TOperator constructOp = EOpNull;
5320
0
            switch (vecSize) {
5321
0
            case 1: constructOp = EOpNull;          break; // direct use, no construct needed
5322
0
            case 2: constructOp = EOpConstructVec2; break;
5323
0
            case 3: constructOp = EOpConstructVec3; break;
5324
0
            case 4: constructOp = EOpConstructVec4; break;
5325
0
            default: assert(0); break;
5326
0
            }
5327
5328
            // For scalar case, we don't need to construct another type.
5329
0
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5330
5331
0
            if (result) {
5332
0
                result->setType(TType(EbtFloat, EvqTemporary, vecSize));
5333
0
                result->setLoc(loc);
5334
0
            }
5335
5336
0
            for (int idx = 0; idx < vecSize; ++idx) {
5337
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5338
0
                TIntermTyped* component = argValue->getType().isVector() ?
5339
0
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5340
5341
0
                if (component != argValue)
5342
0
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5343
5344
0
                TIntermTyped* unpackOp  = new TIntermUnary(EOpUnpackHalf2x16);
5345
0
                unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
5346
0
                unpackOp->getAsUnaryNode()->setOperand(component);
5347
0
                unpackOp->setLoc(loc);
5348
5349
0
                TIntermTyped* lowOrder  = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
5350
5351
0
                if (result != nullptr) {
5352
0
                    result->getSequence().push_back(lowOrder);
5353
0
                    node = result;
5354
0
                } else {
5355
0
                    node = lowOrder;
5356
0
                }
5357
0
            }
5358
5359
0
            break;
5360
0
        }
5361
5362
0
    case EOpF32tof16:
5363
0
        {
5364
            // input floatN converted to 16 bit float in low order bits of each component of uintN
5365
0
            TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
5366
5367
0
            TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
5368
0
            const int vecSize = argValue->getType().getVectorSize();
5369
5370
0
            TOperator constructOp = EOpNull;
5371
0
            switch (vecSize) {
5372
0
            case 1: constructOp = EOpNull;           break; // direct use, no construct needed
5373
0
            case 2: constructOp = EOpConstructUVec2; break;
5374
0
            case 3: constructOp = EOpConstructUVec3; break;
5375
0
            case 4: constructOp = EOpConstructUVec4; break;
5376
0
            default: assert(0); break;
5377
0
            }
5378
5379
            // For scalar case, we don't need to construct another type.
5380
0
            TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5381
5382
0
            if (result) {
5383
0
                result->setType(TType(EbtUint, EvqTemporary, vecSize));
5384
0
                result->setLoc(loc);
5385
0
            }
5386
5387
0
            for (int idx = 0; idx < vecSize; ++idx) {
5388
0
                TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5389
0
                TIntermTyped* component = argValue->getType().isVector() ?
5390
0
                    intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5391
5392
0
                if (component != argValue)
5393
0
                    component->setType(TType(argValue->getBasicType(), EvqTemporary));
5394
5395
0
                TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
5396
0
                vec2ComponentAndZero->getSequence().push_back(component);
5397
0
                vec2ComponentAndZero->getSequence().push_back(zero);
5398
0
                vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
5399
0
                vec2ComponentAndZero->setLoc(loc);
5400
5401
0
                TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
5402
0
                packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
5403
0
                packOp->setLoc(loc);
5404
0
                packOp->setType(TType(EbtUint, EvqTemporary));
5405
5406
0
                if (result != nullptr) {
5407
0
                    result->getSequence().push_back(packOp);
5408
0
                    node = result;
5409
0
                } else {
5410
0
                    node = packOp;
5411
0
                }
5412
0
            }
5413
5414
0
            break;
5415
0
        }
5416
5417
0
    case EOpD3DCOLORtoUBYTE4:
5418
0
        {
5419
            // ivec4 ( x.zyxw * 255.001953 );
5420
0
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5421
0
            TSwizzleSelectors<TVectorSelector> selectors;
5422
0
            selectors.push_back(2);
5423
0
            selectors.push_back(1);
5424
0
            selectors.push_back(0);
5425
0
            selectors.push_back(3);
5426
0
            TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
5427
0
            TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
5428
0
            swizzled->setType(arg0->getType());
5429
0
            swizzled->getWritableType().getQualifier().makeTemporary();
5430
5431
0
            TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
5432
0
            TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
5433
0
            rangeConverted->setType(arg0->getType());
5434
0
            rangeConverted->getWritableType().getQualifier().makeTemporary();
5435
5436
0
            node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
5437
0
            node->setLoc(loc);
5438
0
            node->setType(TType(EbtInt, EvqTemporary, 4));
5439
0
            break;
5440
0
        }
5441
5442
0
    case EOpIsFinite:
5443
0
        {
5444
            // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
5445
            // it to !isnan && !isinf
5446
5447
0
            TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5448
5449
            // We'll make a temporary in case the RHS is cmoplex
5450
0
            TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
5451
0
            tempArg->getWritableType().getQualifier().makeTemporary();
5452
5453
0
            TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
5454
0
                                                                intermediate.addSymbol(*tempArg, loc),
5455
0
                                                                arg0, loc);
5456
5457
0
            TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
5458
5459
0
            const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(),
5460
0
                                 arg0->getMatrixRows());
5461
5462
0
            TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
5463
0
            isnan->setType(boolType);
5464
5465
0
            TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
5466
0
            notnan->setType(boolType);
5467
5468
0
            TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
5469
0
            isinf->setType(boolType);
5470
5471
0
            TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
5472
0
            notinf->setType(boolType);
5473
5474
0
            TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
5475
0
            andNode->setType(boolType);
5476
5477
0
            compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
5478
0
            compoundStatement->setOperator(EOpSequence);
5479
0
            compoundStatement->setLoc(loc);
5480
0
            compoundStatement->setType(boolType);
5481
5482
0
            node = compoundStatement;
5483
5484
0
            break;
5485
0
        }
5486
0
    case EOpWaveGetLaneCount:
5487
0
        {
5488
            // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol
5489
            // so that it inhabits the symbol table, but has a user-invalid name
5490
            // in-case some source HLSL defined the symbol also).
5491
0
            TType type(EbtUint, EvqVaryingIn);
5492
0
            node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type);
5493
0
            break;
5494
0
        }
5495
0
    case EOpWaveGetLaneIndex:
5496
0
        {
5497
            // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the
5498
            // symbol so that it inhabits the symbol table, but has a
5499
            // user-invalid name in-case some source HLSL defined the symbol
5500
            // also).
5501
0
            TType type(EbtUint, EvqVaryingIn);
5502
0
            node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type);
5503
0
            break;
5504
0
        }
5505
0
    case EOpWaveActiveCountBits:
5506
0
        {
5507
            // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin
5508
5509
            // uvec4 type.
5510
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5511
5512
            // Get the uvec4 return from subgroupBallot().
5513
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5514
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5515
5516
            // uint type.
5517
0
            TType uintType(EbtUint, EvqTemporary);
5518
5519
0
            node = intermediate.addBuiltInFunctionCall(loc,
5520
0
                EOpSubgroupBallotBitCount, true, res, uintType);
5521
5522
0
            break;
5523
0
        }
5524
0
    case EOpWavePrefixCountBits:
5525
0
        {
5526
            // Mapped to subgroupBallotExclusiveBitCount(subgroupBallot())
5527
            // builtin
5528
5529
            // uvec4 type.
5530
0
            TType uvec4Type(EbtUint, EvqTemporary, 4);
5531
5532
            // Get the uvec4 return from subgroupBallot().
5533
0
            TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
5534
0
                EOpSubgroupBallot, true, arguments, uvec4Type);
5535
5536
            // uint type.
5537
0
            TType uintType(EbtUint, EvqTemporary);
5538
5539
0
            node = intermediate.addBuiltInFunctionCall(loc,
5540
0
                EOpSubgroupBallotExclusiveBitCount, true, res, uintType);
5541
5542
0
            break;
5543
0
        }
5544
5545
0
    default:
5546
0
        break; // most pass through unchanged
5547
0
    }
5548
0
}
5549
5550
//
5551
// Handle seeing function call syntax in the grammar, which could be any of
5552
//  - .length() method
5553
//  - constructor
5554
//  - a call to a built-in function mapped to an operator
5555
//  - a call to a built-in function that will remain a function call (e.g., texturing)
5556
//  - user function
5557
//  - subroutine call (not implemented yet)
5558
//
5559
TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
5560
0
{
5561
0
    TIntermTyped* result = nullptr;
5562
5563
0
    TOperator op = function->getBuiltInOp();
5564
0
    if (op != EOpNull) {
5565
        //
5566
        // Then this should be a constructor.
5567
        // Don't go through the symbol table for constructors.
5568
        // Their parameters will be verified algorithmically.
5569
        //
5570
0
        TType type(EbtVoid);  // use this to get the type back
5571
0
        if (! constructorError(loc, arguments, *function, op, type)) {
5572
            //
5573
            // It's a constructor, of type 'type'.
5574
            //
5575
0
            result = handleConstructor(loc, arguments, type);
5576
0
            if (result == nullptr) {
5577
0
                error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
5578
0
                return nullptr;
5579
0
            }
5580
0
        }
5581
0
    } else {
5582
        //
5583
        // Find it in the symbol table.
5584
        //
5585
0
        const TFunction* fnCandidate = nullptr;
5586
0
        bool builtIn = false;
5587
0
        int thisDepth = 0;
5588
5589
        // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
5590
        // and clamp the opposite arg.  Since that's complex, we farm it off to a separate method.
5591
        // It doesn't naturally fall out of processing an argument at a time in isolation.
5592
0
        if (function->getName() == "mul")
5593
0
            addGenMulArgumentConversion(loc, *function, arguments);
5594
5595
0
        TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5596
5597
        // TODO: this needs improvement: there's no way at present to look up a signature in
5598
        // the symbol table for an arbitrary type.  This is a temporary hack until that ability exists.
5599
        // It will have false positives, since it doesn't check arg counts or types.
5600
0
        if (arguments) {
5601
            // Check if first argument is struct buffer type.  It may be an aggregate or a symbol, so we
5602
            // look for either case.
5603
5604
0
            TIntermTyped* arg0 = nullptr;
5605
5606
0
            if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0])
5607
0
                arg0 = aggregate->getSequence()[0]->getAsTyped();
5608
0
            else if (arguments->getAsSymbolNode())
5609
0
                arg0 = arguments->getAsSymbolNode();
5610
5611
0
            if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
5612
0
                static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
5613
5614
0
                if (function->getName().length() > methodPrefixSize &&
5615
0
                    isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
5616
0
                    const TString mangle = function->getName() + "(";
5617
0
                    TSymbol* symbol = symbolTable.find(mangle, &builtIn);
5618
5619
0
                    if (symbol)
5620
0
                        fnCandidate = symbol->getAsFunction();
5621
0
                }
5622
0
            }
5623
0
        }
5624
5625
0
        if (fnCandidate == nullptr)
5626
0
            fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments);
5627
5628
0
        if (fnCandidate) {
5629
            // This is a declared function that might map to
5630
            //  - a built-in operator,
5631
            //  - a built-in function not mapped to an operator, or
5632
            //  - a user function.
5633
5634
            // turn an implicit member-function resolution into an explicit call
5635
0
            TString callerName;
5636
0
            if (thisDepth == 0)
5637
0
                callerName = fnCandidate->getMangledName();
5638
0
            else {
5639
                // get the explicit (full) name of the function
5640
0
                assert(currentTypePrefix.size() >= size_t(thisDepth));
5641
0
                callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth];
5642
0
                callerName += fnCandidate->getMangledName();
5643
                // insert the implicit calling argument
5644
0
                pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments);
5645
0
            }
5646
5647
            // Convert 'in' arguments, so that types match.
5648
            // However, skip those that need expansion, that is covered next.
5649
0
            if (arguments)
5650
0
                addInputArgumentConversions(*fnCandidate, arguments);
5651
5652
            // Expand arguments.  Some arguments must physically expand to a different set
5653
            // than what the shader declared and passes.
5654
0
            if (arguments && !builtIn)
5655
0
                expandArguments(loc, *fnCandidate, arguments);
5656
5657
            // Expansion may have changed the form of arguments
5658
0
            aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5659
5660
0
            op = fnCandidate->getBuiltInOp();
5661
0
            if (builtIn && op != EOpNull) {
5662
                // SM 4.0 and above guarantees roundEven semantics for round()
5663
0
                if (!hlslDX9Compatible() && op == EOpRound)
5664
0
                    op = EOpRoundEven;
5665
5666
                // A function call mapped to a built-in operation.
5667
0
                result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments,
5668
0
                                                             fnCandidate->getType());
5669
0
                if (result == nullptr)  {
5670
0
                    error(arguments->getLoc(), " wrong operand type", "Internal Error",
5671
0
                        "built in unary operator function.  Type: %s",
5672
0
                        static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
5673
0
                } else if (result->getAsOperator()) {
5674
0
                    builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
5675
0
                }
5676
0
            } else {
5677
                // This is a function call not mapped to built-in operator.
5678
                // It could still be a built-in function, but only if PureOperatorBuiltins == false.
5679
0
                result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
5680
0
                TIntermAggregate* call = result->getAsAggregate();
5681
0
                call->setName(callerName);
5682
5683
                // this is how we know whether the given function is a built-in function or a user-defined function
5684
                // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
5685
                // if builtIn == true, it's definitely a built-in function with EOpNull
5686
0
                if (! builtIn) {
5687
0
                    call->setUserDefined();
5688
0
                    intermediate.addToCallGraph(infoSink, currentCaller, callerName);
5689
0
                }
5690
0
            }
5691
5692
            // for decompositions, since we want to operate on the function node, not the aggregate holding
5693
            // output conversions.
5694
0
            const TIntermTyped* fnNode = result;
5695
5696
0
            decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
5697
0
            decomposeIntrinsic(loc, result, arguments);           // HLSL->AST intrinsic decompositions
5698
0
            decomposeSampleMethods(loc, result, arguments);       // HLSL->AST sample method decompositions
5699
0
            decomposeGeometryMethods(loc, result, arguments);     // HLSL->AST geometry method decompositions
5700
5701
            // Create the qualifier list, carried in the AST for the call.
5702
            // Because some arguments expand to multiple arguments, the qualifier list will
5703
            // be longer than the formal parameter list.
5704
0
            if (result == fnNode && result->getAsAggregate()) {
5705
0
                TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
5706
0
                for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
5707
0
                    TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
5708
0
                    if (hasStructBuffCounter(*(*fnCandidate)[i].type)) {
5709
                        // add buffer and counter buffer argument qualifier
5710
0
                        qualifierList.push_back(qual);
5711
0
                        qualifierList.push_back(qual);
5712
0
                    } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage,
5713
0
                                             true)) {
5714
                        // add structure member expansion
5715
0
                        for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb)
5716
0
                            qualifierList.push_back(qual);
5717
0
                    } else {
5718
                        // Normal 1:1 case
5719
0
                        qualifierList.push_back(qual);
5720
0
                    }
5721
0
                }
5722
0
            }
5723
5724
            // Convert 'out' arguments.  If it was a constant folded built-in, it won't be an aggregate anymore.
5725
            // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
5726
            // Also, build the qualifier list for user function calls, which are always called with an aggregate.
5727
            // We don't do this is if there has been a decomposition, which will have added its own conversions
5728
            // for output parameters.
5729
0
            if (result == fnNode && result->getAsAggregate())
5730
0
                result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
5731
0
        }
5732
0
    }
5733
5734
    // generic error recovery
5735
    // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to
5736
    //       reduce cascades
5737
0
    if (result == nullptr)
5738
0
        result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
5739
5740
0
    return result;
5741
0
}
5742
5743
// An initial argument list is difficult: it can be null, or a single node,
5744
// or an aggregate if more than one argument.  Add one to the front, maintaining
5745
// this lack of uniformity.
5746
void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments)
5747
0
{
5748
0
    if (arguments == nullptr)
5749
0
        arguments = front;
5750
0
    else if (arguments->getAsAggregate() != nullptr)
5751
0
        arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front);
5752
0
    else
5753
0
        arguments = intermediate.growAggregate(front, arguments);
5754
0
}
5755
5756
//
5757
// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat.  This is a
5758
// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
5759
// can look at both arguments insert explicit shape changes if required.
5760
//
5761
void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
5762
0
{
5763
0
    TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
5764
5765
0
    if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
5766
        // It really ought to have two arguments.
5767
0
        error(loc, "expected: mul arguments", "", "");
5768
0
        return;
5769
0
    }
5770
5771
0
    TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5772
0
    TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5773
5774
0
    if (arg0->isVector() && arg1->isVector()) {
5775
        // For:
5776
        //    vec * vec: it's handled during intrinsic selection, so while we could do it here,
5777
        //               we can also ignore it, which is easier.
5778
0
    } else if (arg0->isVector() && arg1->isMatrix()) {
5779
        // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
5780
0
        if (arg0->getVectorSize() < arg1->getMatrixCols()) {
5781
            // vec is smaller, so truncate larger mat dimension
5782
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5783
0
                                  0, arg0->getVectorSize(), arg1->getMatrixRows());
5784
0
            arg1 = addConstructor(loc, arg1, truncType);
5785
0
        } else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
5786
            // vec is larger, so truncate vec to mat size
5787
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5788
0
                                  arg1->getMatrixCols());
5789
0
            arg0 = addConstructor(loc, arg0, truncType);
5790
0
        }
5791
0
    } else if (arg0->isMatrix() && arg1->isVector()) {
5792
        // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
5793
0
        if (arg1->getVectorSize() < arg0->getMatrixRows()) {
5794
            // vec is smaller, so truncate larger mat dimension
5795
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5796
0
                                  0, arg0->getMatrixCols(), arg1->getVectorSize());
5797
0
            arg0 = addConstructor(loc, arg0, truncType);
5798
0
        } else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
5799
            // vec is larger, so truncate vec to mat size
5800
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5801
0
                                  arg0->getMatrixRows());
5802
0
            arg1 = addConstructor(loc, arg1, truncType);
5803
0
        }
5804
0
    } else if (arg0->isMatrix() && arg1->isMatrix()) {
5805
        // mat * mat: we clamp the smaller inner dimension to match the other matrix size.
5806
        // Remember, HLSL Mrc = GLSL/SPIRV Mcr.
5807
0
        if (arg0->getMatrixRows() > arg1->getMatrixCols()) {
5808
0
            const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5809
0
                                  0, arg0->getMatrixCols(), arg1->getMatrixCols());
5810
0
            arg0 = addConstructor(loc, arg0, truncType);
5811
0
        } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) {
5812
0
            const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5813
0
                                  0, arg0->getMatrixRows(), arg1->getMatrixRows());
5814
0
            arg1 = addConstructor(loc, arg1, truncType);
5815
0
        }
5816
0
    } else {
5817
        // It's something with scalars: we'll just leave it alone.  Function selection will handle it
5818
        // downstream.
5819
0
    }
5820
5821
    // Warn if we altered one of the arguments
5822
0
    if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1])
5823
0
        warn(loc, "mul() matrix size mismatch", "", "");
5824
5825
    // Put arguments back.  (They might be unchanged, in which case this is harmless).
5826
0
    argAggregate->getSequence()[0] = arg0;
5827
0
    argAggregate->getSequence()[1] = arg1;
5828
5829
0
    call[0].type = &arg0->getWritableType();
5830
0
    call[1].type = &arg1->getWritableType();
5831
0
}
5832
5833
//
5834
// Add any needed implicit conversions for function-call arguments to input parameters.
5835
//
5836
void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
5837
0
{
5838
0
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5839
5840
    // Replace a single argument with a single argument.
5841
0
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5842
0
        if (function.getParamCount() == 1)
5843
0
            arguments = arg;
5844
0
        else {
5845
0
            if (aggregate == nullptr)
5846
0
                arguments = arg;
5847
0
            else
5848
0
                aggregate->getSequence()[paramNum] = arg;
5849
0
        }
5850
0
    };
5851
5852
    // Process each argument's conversion
5853
0
    for (int param = 0; param < function.getParamCount(); ++param) {
5854
0
        if (! function[param].type->getQualifier().isParamInput())
5855
0
            continue;
5856
5857
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5858
        // is the single argument itself or its children are the arguments.  Only one argument
5859
        // means take 'arguments' itself as the one argument.
5860
0
        TIntermTyped* arg = function.getParamCount() == 1
5861
0
                                   ? arguments->getAsTyped()
5862
0
                                   : (aggregate ?
5863
0
                                        aggregate->getSequence()[param]->getAsTyped() :
5864
0
                                        arguments->getAsTyped());
5865
0
        if (*function[param].type != arg->getType()) {
5866
            // In-qualified arguments just need an extra node added above the argument to
5867
            // convert to the correct type.
5868
0
            TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg);
5869
0
            if (convArg != nullptr)
5870
0
                convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg);
5871
0
            if (convArg != nullptr)
5872
0
                setArg(param, convArg);
5873
0
            else
5874
0
                error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param);
5875
0
        } else {
5876
0
            if (wasFlattened(arg)) {
5877
                // If both formal and calling arg are to be flattened, leave that to argument
5878
                // expansion, not conversion.
5879
0
                if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5880
                    // Will make a two-level subtree.
5881
                    // The deepest will copy member-by-member to build the structure to pass.
5882
                    // The level above that will be a two-operand EOpComma sequence that follows the copy by the
5883
                    // object itself.
5884
0
                    TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type);
5885
0
                    internalAggregate->getWritableType().getQualifier().makeTemporary();
5886
0
                    TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
5887
0
                                                                          internalAggregate->getName(),
5888
0
                                                                          getLanguage(),
5889
0
                                                                          internalAggregate->getType());
5890
0
                    internalSymbolNode->setLoc(arg->getLoc());
5891
                    // This makes the deepest level, the member-wise copy
5892
0
                    TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign,
5893
0
                                                               internalSymbolNode, arg)->getAsAggregate();
5894
5895
                    // Now, pair that with the resulting aggregate.
5896
0
                    assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
5897
0
                    assignAgg->setOperator(EOpComma);
5898
0
                    assignAgg->setType(internalAggregate->getType());
5899
0
                    setArg(param, assignAgg);
5900
0
                }
5901
0
            }
5902
0
        }
5903
0
    }
5904
0
}
5905
5906
//
5907
// Add any needed implicit expansion of calling arguments from what the shader listed to what's
5908
// internally needed for the AST (given the constraints downstream).
5909
//
5910
void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments)
5911
0
{
5912
0
    TIntermAggregate* aggregate = arguments->getAsAggregate();
5913
0
    int functionParamNumberOffset = 0;
5914
5915
    // Replace a single argument with a single argument.
5916
0
    const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5917
0
        if (function.getParamCount() + functionParamNumberOffset == 1)
5918
0
            arguments = arg;
5919
0
        else {
5920
0
            if (aggregate == nullptr)
5921
0
                arguments = arg;
5922
0
            else
5923
0
                aggregate->getSequence()[paramNum] = arg;
5924
0
        }
5925
0
    };
5926
5927
    // Replace a single argument with a list of arguments
5928
0
    const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) {
5929
0
        if (args.size() == 1)
5930
0
            setArg(paramNum, args.front());
5931
0
        else if (args.size() > 1) {
5932
0
            if (function.getParamCount() + functionParamNumberOffset == 1) {
5933
0
                arguments = intermediate.makeAggregate(args.front());
5934
0
                std::for_each(args.begin() + 1, args.end(),
5935
0
                    [&](TIntermTyped* arg) {
5936
0
                        arguments = intermediate.growAggregate(arguments, arg);
5937
0
                    });
5938
0
            } else {
5939
0
                auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum);
5940
0
                aggregate->getSequence().insert(it, args.begin(), args.end());
5941
0
            }
5942
0
            functionParamNumberOffset += (int)(args.size() - 1);
5943
0
        }
5944
0
    };
5945
5946
    // Process each argument's conversion
5947
0
    for (int param = 0; param < function.getParamCount(); ++param) {
5948
        // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5949
        // is the single argument itself or its children are the arguments.  Only one argument
5950
        // means take 'arguments' itself as the one argument.
5951
0
        TIntermTyped* arg = function.getParamCount() == 1
5952
0
                                   ? arguments->getAsTyped()
5953
0
                                   : (aggregate ?
5954
0
                                        aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() :
5955
0
                                        arguments->getAsTyped());
5956
5957
0
        if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
5958
            // Need to pass the structure members instead of the structure.
5959
0
            TVector<TIntermTyped*> memberArgs;
5960
0
            for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb)
5961
0
                memberArgs.push_back(flattenAccess(arg, memb));
5962
0
            setArgList(param + functionParamNumberOffset, memberArgs);
5963
0
        }
5964
0
    }
5965
5966
    // TODO: if we need both hidden counter args (below) and struct expansion (above)
5967
    // the two algorithms need to be merged: Each assumes the list starts out 1:1 between
5968
    // parameters and arguments.
5969
5970
    // If any argument is a pass-by-reference struct buffer with an associated counter
5971
    // buffer, we have to add another hidden parameter for that counter.
5972
0
    if (aggregate)
5973
0
        addStructBuffArguments(loc, aggregate);
5974
0
}
5975
5976
//
5977
// Add any needed implicit output conversions for function-call arguments.  This
5978
// can require a new tree topology, complicated further by whether the function
5979
// has a return value.
5980
//
5981
// Returns a node of a subtree that evaluates to the return value of the function.
5982
//
5983
TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
5984
0
{
5985
0
    assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
5986
5987
0
    const TSourceLoc& loc = intermNode.getLoc();
5988
5989
0
    TIntermSequence argSequence; // temp sequence for unary node args
5990
5991
0
    if (intermNode.getAsUnaryNode())
5992
0
        argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
5993
5994
0
    TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
5995
5996
0
    const auto needsConversion = [&](int argNum) {
5997
0
        return function[argNum].type->getQualifier().isParamOutput() &&
5998
0
               (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
5999
0
                shouldConvertLValue(arguments[argNum]) ||
6000
0
                wasFlattened(arguments[argNum]->getAsTyped()));
6001
0
    };
6002
6003
    // Will there be any output conversions?
6004
0
    bool outputConversions = false;
6005
0
    for (int i = 0; i < function.getParamCount(); ++i) {
6006
0
        if (needsConversion(i)) {
6007
0
            outputConversions = true;
6008
0
            break;
6009
0
        }
6010
0
    }
6011
6012
0
    if (! outputConversions)
6013
0
        return &intermNode;
6014
6015
    // Setup for the new tree, if needed:
6016
    //
6017
    // Output conversions need a different tree topology.
6018
    // Out-qualified arguments need a temporary of the correct type, with the call
6019
    // followed by an assignment of the temporary to the original argument:
6020
    //     void: function(arg, ...)  ->        (          function(tempArg, ...), arg = tempArg, ...)
6021
    //     ret = function(arg, ...)  ->  ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
6022
    // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
6023
0
    TIntermTyped* conversionTree = nullptr;
6024
0
    TVariable* tempRet = nullptr;
6025
0
    if (intermNode.getBasicType() != EbtVoid) {
6026
        // do the "tempRet = function(...), " bit from above
6027
0
        tempRet = makeInternalVariable("tempReturn", intermNode.getType());
6028
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
6029
0
        conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
6030
0
    } else
6031
0
        conversionTree = &intermNode;
6032
6033
0
    conversionTree = intermediate.makeAggregate(conversionTree);
6034
6035
    // Process each argument's conversion
6036
0
    for (int i = 0; i < function.getParamCount(); ++i) {
6037
0
        if (needsConversion(i)) {
6038
            // Out-qualified arguments needing conversion need to use the topology setup above.
6039
            // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
6040
6041
            // Make a temporary for what the function expects the argument to look like.
6042
0
            TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
6043
0
            tempArg->getWritableType().getQualifier().makeTemporary();
6044
0
            TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
6045
6046
            // This makes the deepest level, the member-wise copy
6047
0
            TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(),
6048
0
                                                    tempArgNode);
6049
0
            tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
6050
0
            conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
6051
6052
            // replace the argument with another node for the same tempArg variable
6053
0
            arguments[i] = intermediate.addSymbol(*tempArg, loc);
6054
0
        }
6055
0
    }
6056
6057
    // Finalize the tree topology (see bigger comment above).
6058
0
    if (tempRet) {
6059
        // do the "..., tempRet" bit from above
6060
0
        TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
6061
0
        conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
6062
0
    }
6063
6064
0
    conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
6065
6066
0
    return conversionTree;
6067
0
}
6068
6069
//
6070
// Add any needed "hidden" counter buffer arguments for function calls.
6071
//
6072
// Modifies the 'aggregate' argument if needed.  Otherwise, is no-op.
6073
//
6074
void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate)
6075
0
{
6076
    // See if there are any SB types with counters.
6077
0
    const bool hasStructBuffArg =
6078
0
        std::any_of(aggregate->getSequence().begin(),
6079
0
                    aggregate->getSequence().end(),
6080
0
                    [this](const TIntermNode* node) {
6081
0
                        return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType());
6082
0
                    });
6083
6084
    // Nothing to do, if we didn't find one.
6085
0
    if (! hasStructBuffArg)
6086
0
        return;
6087
6088
0
    TIntermSequence argsWithCounterBuffers;
6089
6090
0
    for (int param = 0; param < int(aggregate->getSequence().size()); ++param) {
6091
0
        argsWithCounterBuffers.push_back(aggregate->getSequence()[param]);
6092
6093
0
        if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) {
6094
0
            const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode();
6095
0
            if (blockSym != nullptr) {
6096
0
                TType counterType;
6097
0
                counterBufferType(loc, counterType);
6098
6099
0
                const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName()));
6100
6101
0
                TVariable* variable = makeInternalVariable(counterBlockName, counterType);
6102
6103
                // Mark this buffer's counter block as being in use
6104
0
                structBufferCounter[counterBlockName] = true;
6105
6106
0
                TIntermSymbol* sym = intermediate.addSymbol(*variable, loc);
6107
0
                argsWithCounterBuffers.push_back(sym);
6108
0
            }
6109
0
        }
6110
0
    }
6111
6112
    // Swap with the temp list we've built up.
6113
0
    aggregate->getSequence().swap(argsWithCounterBuffers);
6114
0
}
6115
6116
6117
//
6118
// Do additional checking of built-in function calls that is not caught
6119
// by normal semantic checks on argument type, extension tagging, etc.
6120
//
6121
// Assumes there has been a semantically correct match to a built-in function prototype.
6122
//
6123
void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
6124
0
{
6125
    // Set up convenience accessors to the argument(s).  There is almost always
6126
    // multiple arguments for the cases below, but when there might be one,
6127
    // check the unaryArg first.
6128
0
    const TIntermSequence* argp = nullptr;   // confusing to use [] syntax on a pointer, so this is to help get a reference
6129
0
    const TIntermTyped* unaryArg = nullptr;
6130
0
    const TIntermTyped* arg0 = nullptr;
6131
0
    if (callNode.getAsAggregate()) {
6132
0
        argp = &callNode.getAsAggregate()->getSequence();
6133
0
        if (argp->size() > 0)
6134
0
            arg0 = (*argp)[0]->getAsTyped();
6135
0
    } else {
6136
0
        assert(callNode.getAsUnaryNode());
6137
0
        unaryArg = callNode.getAsUnaryNode()->getOperand();
6138
0
        arg0 = unaryArg;
6139
0
    }
6140
0
    const TIntermSequence& aggArgs = argp ? *argp : TIntermSequence();  // only valid when unaryArg is nullptr
6141
6142
0
    switch (callNode.getOp()) {
6143
0
    case EOpTextureGather:
6144
0
    case EOpTextureGatherOffset:
6145
0
    case EOpTextureGatherOffsets:
6146
0
    {
6147
        // Figure out which variants are allowed by what extensions,
6148
        // and what arguments must be constant for which situations.
6149
6150
0
        TString featureString = fnCandidate.getName() + "(...)";
6151
0
        const char* feature = featureString.c_str();
6152
0
        int compArg = -1;  // track which argument, if any, is the constant component argument
6153
0
        switch (callNode.getOp()) {
6154
0
        case EOpTextureGather:
6155
            // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
6156
            // otherwise, need GL_ARB_texture_gather.
6157
0
            if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect ||
6158
0
                fnCandidate[0].type->getSampler().shadow) {
6159
0
                if (! fnCandidate[0].type->getSampler().shadow)
6160
0
                    compArg = 2;
6161
0
            }
6162
0
            break;
6163
0
        case EOpTextureGatherOffset:
6164
            // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
6165
0
            if (! fnCandidate[0].type->getSampler().shadow)
6166
0
                compArg = 3;
6167
0
            break;
6168
0
        case EOpTextureGatherOffsets:
6169
0
            if (! fnCandidate[0].type->getSampler().shadow)
6170
0
                compArg = 3;
6171
0
            break;
6172
0
        default:
6173
0
            break;
6174
0
        }
6175
6176
0
        if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
6177
0
            if (aggArgs[compArg]->getAsConstantUnion()) {
6178
0
                int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
6179
0
                if (value < 0 || value > 3)
6180
0
                    error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
6181
0
            } else
6182
0
                error(loc, "must be a compile-time constant:", feature, "component argument");
6183
0
        }
6184
6185
0
        break;
6186
0
    }
6187
6188
0
    case EOpTextureOffset:
6189
0
    case EOpTextureFetchOffset:
6190
0
    case EOpTextureProjOffset:
6191
0
    case EOpTextureLodOffset:
6192
0
    case EOpTextureProjLodOffset:
6193
0
    case EOpTextureGradOffset:
6194
0
    case EOpTextureProjGradOffset:
6195
0
    {
6196
        // Handle texture-offset limits checking
6197
        // Pick which argument has to hold constant offsets
6198
0
        int arg = -1;
6199
0
        switch (callNode.getOp()) {
6200
0
        case EOpTextureOffset:          arg = 2;  break;
6201
0
        case EOpTextureFetchOffset:     arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
6202
0
        case EOpTextureProjOffset:      arg = 2;  break;
6203
0
        case EOpTextureLodOffset:       arg = 3;  break;
6204
0
        case EOpTextureProjLodOffset:   arg = 3;  break;
6205
0
        case EOpTextureGradOffset:      arg = 4;  break;
6206
0
        case EOpTextureProjGradOffset:  arg = 4;  break;
6207
0
        default:
6208
0
            assert(0);
6209
0
            break;
6210
0
        }
6211
6212
0
        if (arg > 0) {
6213
0
            if (aggArgs[arg]->getAsConstantUnion() == nullptr)
6214
0
                error(loc, "argument must be compile-time constant", "texel offset", "");
6215
0
            else {
6216
0
                const TType& type = aggArgs[arg]->getAsTyped()->getType();
6217
0
                for (int c = 0; c < type.getVectorSize(); ++c) {
6218
0
                    int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
6219
0
                    if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
6220
0
                        error(loc, "value is out of range:", "texel offset",
6221
0
                              "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
6222
0
                }
6223
0
            }
6224
0
        }
6225
6226
0
        break;
6227
0
    }
6228
6229
0
    case EOpTextureQuerySamples:
6230
0
    case EOpImageQuerySamples:
6231
0
        break;
6232
6233
0
    case EOpImageAtomicAdd:
6234
0
    case EOpImageAtomicMin:
6235
0
    case EOpImageAtomicMax:
6236
0
    case EOpImageAtomicAnd:
6237
0
    case EOpImageAtomicOr:
6238
0
    case EOpImageAtomicXor:
6239
0
    case EOpImageAtomicExchange:
6240
0
    case EOpImageAtomicCompSwap:
6241
0
        break;
6242
6243
0
    case EOpInterpolateAtCentroid:
6244
0
    case EOpInterpolateAtSample:
6245
0
    case EOpInterpolateAtOffset:
6246
        // TODO(greg-lunarg): Re-enable this check. It currently gives false errors for builtins
6247
        // defined and passed as members of a struct. In this case the storage class is showing to be
6248
        // Function. See glslang #2584
6249
6250
        // Make sure the first argument is an interpolant, or an array element of an interpolant
6251
        // if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
6252
            // It might still be an array element.
6253
            //
6254
            // We could check more, but the semantics of the first argument are already met; the
6255
            // only way to turn an array into a float/vec* is array dereference and swizzle.
6256
            //
6257
            // ES and desktop 4.3 and earlier:  swizzles may not be used
6258
            // desktop 4.4 and later: swizzles may be used
6259
            // const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
6260
            // if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
6261
            //     error(loc, "first argument must be an interpolant, or interpolant-array element",
6262
            //           fnCandidate.getName().c_str(), "");
6263
        // }
6264
0
        break;
6265
6266
0
    default:
6267
0
        break;
6268
0
    }
6269
0
}
6270
6271
//
6272
// Handle seeing something in a grammar production that can be done by calling
6273
// a constructor.
6274
//
6275
// The constructor still must be "handled" by handleFunctionCall(), which will
6276
// then call handleConstructor().
6277
//
6278
TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
6279
0
{
6280
0
    TOperator op = intermediate.mapTypeToConstructorOp(type);
6281
6282
0
    if (op == EOpNull) {
6283
0
        error(loc, "cannot construct this type", type.getBasicString(), "");
6284
0
        return nullptr;
6285
0
    }
6286
6287
0
    TString empty("");
6288
6289
0
    return new TFunction(&empty, type, op);
6290
0
}
6291
6292
//
6293
// Handle seeing a "COLON semantic" at the end of a type declaration,
6294
// by updating the type according to the semantic.
6295
//
6296
void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn,
6297
                                      const TString& upperCase)
6298
0
{
6299
    // Parse and return semantic number.  If limit is 0, it will be ignored.  Otherwise, if the parsed
6300
    // semantic number is >= limit, errorMsg is issued and 0 is returned.
6301
    // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet
6302
    // accept those in lambda functions.
6303
0
    const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int {
6304
0
        size_t pos = semantic.find_last_not_of("0123456789");
6305
0
        if (pos == std::string::npos)
6306
0
            return 0u;
6307
6308
0
        unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1);
6309
6310
0
        if (limit != 0 && semanticNum >= limit) {
6311
0
            error(loc, errorMsg, semantic.c_str(), "");
6312
0
            return 0u;
6313
0
        }
6314
6315
0
        return semanticNum;
6316
0
    };
6317
6318
0
    if (builtIn == EbvNone && hlslDX9Compatible()) {
6319
0
        if (language == EShLangVertex) {
6320
0
            if (qualifier.isParamOutput()) {
6321
0
                if (upperCase == "POSITION") {
6322
0
                    builtIn = EbvPosition;
6323
0
                }
6324
0
                if (upperCase == "PSIZE") {
6325
0
                    builtIn = EbvPointSize;
6326
0
                }
6327
0
            }
6328
0
        } else if (language == EShLangFragment) {
6329
0
            if (qualifier.isParamInput() && upperCase == "VPOS") {
6330
0
                builtIn = EbvFragCoord;
6331
0
            }
6332
0
            if (qualifier.isParamOutput()) {
6333
0
                if (upperCase.compare(0, 5, "COLOR") == 0) {
6334
0
                    qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6335
0
                    nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6336
0
                }
6337
0
                if (upperCase == "DEPTH") {
6338
0
                    builtIn = EbvFragDepth;
6339
0
                }
6340
0
            }
6341
0
        }
6342
0
    }
6343
6344
0
    switch(builtIn) {
6345
0
    case EbvNone:
6346
        // Get location numbers from fragment outputs, instead of
6347
        // auto-assigning them.
6348
0
        if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) {
6349
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
6350
0
            nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
6351
0
        } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) {
6352
0
            builtIn = EbvClipDistance;
6353
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic");
6354
0
        } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) {
6355
0
            builtIn = EbvCullDistance;
6356
0
            qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic");
6357
0
        }
6358
0
        break;
6359
0
    case EbvPosition:
6360
        // adjust for stage in/out
6361
0
        if (language == EShLangFragment)
6362
0
            builtIn = EbvFragCoord;
6363
0
        break;
6364
0
    case EbvFragStencilRef:
6365
0
        error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
6366
0
        break;
6367
0
    case EbvTessLevelInner:
6368
0
    case EbvTessLevelOuter:
6369
0
        qualifier.patch = true;
6370
0
        break;
6371
0
    default:
6372
0
        break;
6373
0
    }
6374
6375
0
    if (qualifier.builtIn == EbvNone)
6376
0
        qualifier.builtIn = builtIn;
6377
0
    qualifier.semanticName = intermediate.addSemanticName(upperCase);
6378
0
}
6379
6380
//
6381
// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
6382
//
6383
// 'location' has the "c[Subcomponent]" part.
6384
// 'component' points to the "component" part, or nullptr if not present.
6385
//
6386
void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
6387
                                        const glslang::TString* component)
6388
0
{
6389
0
    if (location.size() == 0 || location[0] != 'c') {
6390
0
        error(loc, "expected 'c'", "packoffset", "");
6391
0
        return;
6392
0
    }
6393
0
    if (location.size() == 1)
6394
0
        return;
6395
0
    if (! isdigit(location[1])) {
6396
0
        error(loc, "expected number after 'c'", "packoffset", "");
6397
0
        return;
6398
0
    }
6399
6400
0
    qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
6401
0
    if (component != nullptr) {
6402
0
        int componentOffset = 0;
6403
0
        switch ((*component)[0]) {
6404
0
        case 'x': componentOffset =  0; break;
6405
0
        case 'y': componentOffset =  4; break;
6406
0
        case 'z': componentOffset =  8; break;
6407
0
        case 'w': componentOffset = 12; break;
6408
0
        default:
6409
0
            componentOffset = -1;
6410
0
            break;
6411
0
        }
6412
0
        if (componentOffset < 0 || component->size() > 1) {
6413
0
            error(loc, "expected {x, y, z, w} for component", "packoffset", "");
6414
0
            return;
6415
0
        }
6416
0
        qualifier.layoutOffset += componentOffset;
6417
0
    }
6418
0
}
6419
6420
//
6421
// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
6422
//
6423
// 'profile' points to the shader_profile part, or nullptr if not present.
6424
// 'desc' is the type# part.
6425
//
6426
void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
6427
                                      const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
6428
0
{
6429
0
    if (profile != nullptr)
6430
0
        warn(loc, "ignoring shader_profile", "register", "");
6431
6432
0
    if (desc.size() < 1) {
6433
0
        error(loc, "expected register type", "register", "");
6434
0
        return;
6435
0
    }
6436
6437
0
    int regNumber = 0;
6438
0
    if (desc.size() > 1) {
6439
0
        if (isdigit(desc[1]))
6440
0
            regNumber = atoi(desc.substr(1, desc.size()).c_str());
6441
0
        else {
6442
0
            error(loc, "expected register number after register type", "register", "");
6443
0
            return;
6444
0
        }
6445
0
    }
6446
6447
    // more information about register types see
6448
    // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register
6449
0
    const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding();
6450
0
    switch (std::tolower(desc[0])) {
6451
0
    case 'c':
6452
        // c register is the register slot in the global const buffer
6453
        // each slot is a vector of 4 32 bit components
6454
0
        qualifier.layoutOffset = regNumber * 4 * 4;
6455
0
        break;
6456
        // const buffer register slot
6457
0
    case 'b':
6458
        // textrues and structured buffers
6459
0
    case 't':
6460
        // samplers
6461
0
    case 's':
6462
        // uav resources
6463
0
    case 'u':
6464
        // if nothing else has set the binding, do so now
6465
        // (other mechanisms override this one)
6466
0
        if (!qualifier.hasBinding())
6467
0
            qualifier.layoutBinding = regNumber + subComponent;
6468
6469
        // This handles per-register layout sets numbers.  For the global mode which sets
6470
        // every symbol to the same value, see setLinkageLayoutSets().
6471
0
        if ((resourceInfo.size() % 3) == 0) {
6472
            // Apply per-symbol resource set and binding.
6473
0
            for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) {
6474
0
                if (strcmp(desc.c_str(), it[0].c_str()) == 0) {
6475
0
                    qualifier.layoutSet = atoi(it[1].c_str());
6476
0
                    qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent;
6477
0
                    break;
6478
0
                }
6479
0
            }
6480
0
        }
6481
0
        break;
6482
0
    default:
6483
0
        warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
6484
0
        break;
6485
0
    }
6486
6487
    // space
6488
0
    unsigned int setNumber;
6489
0
    const auto crackSpace = [&]() -> bool {
6490
0
        const int spaceLen = 5;
6491
0
        if (spaceDesc->size() < spaceLen + 1)
6492
0
            return false;
6493
0
        if (spaceDesc->compare(0, spaceLen, "space") != 0)
6494
0
            return false;
6495
0
        if (! isdigit((*spaceDesc)[spaceLen]))
6496
0
            return false;
6497
0
        setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
6498
0
        return true;
6499
0
    };
6500
6501
    // if nothing else has set the set, do so now
6502
    // (other mechanisms override this one)
6503
0
    if (spaceDesc && !qualifier.hasSet()) {
6504
0
        if (! crackSpace()) {
6505
0
            error(loc, "expected spaceN", "register", "");
6506
0
            return;
6507
0
        }
6508
0
        qualifier.layoutSet = setNumber;
6509
0
    }
6510
0
}
6511
6512
// Convert to a scalar boolean, or if not allowed by HLSL semantics,
6513
// report an error and return nullptr.
6514
TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition,
6515
                                                             bool mustBeScalar)
6516
0
{
6517
0
    if (mustBeScalar && !condition->getType().isScalarOrVec1()) {
6518
0
        error(loc, "requires a scalar", "conditional expression", "");
6519
0
        return nullptr;
6520
0
    }
6521
6522
0
    return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()),
6523
0
                                      condition);
6524
0
}
6525
6526
//
6527
// Same error message for all places assignments don't work.
6528
//
6529
void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
6530
0
{
6531
0
    error(loc, "", op, "cannot convert from '%s' to '%s'",
6532
0
        right.c_str(), left.c_str());
6533
0
}
6534
6535
//
6536
// Same error message for all places unary operations don't work.
6537
//
6538
void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
6539
0
{
6540
0
    error(loc, " wrong operand type", op,
6541
0
        "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
6542
0
        op, operand.c_str());
6543
0
}
6544
6545
//
6546
// Same error message for all binary operations don't work.
6547
//
6548
void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
6549
0
{
6550
0
    error(loc, " wrong operand types:", op,
6551
0
        "no operation '%s' exists that takes a left-hand operand of type '%s' and "
6552
0
        "a right operand of type '%s' (or there is no acceptable conversion)",
6553
0
        op, left.c_str(), right.c_str());
6554
0
}
6555
6556
//
6557
// A basic type of EbtVoid is a key that the name string was seen in the source, but
6558
// it was not found as a variable in the symbol table.  If so, give the error
6559
// message and insert a dummy variable in the symbol table to prevent future errors.
6560
//
6561
void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
6562
0
{
6563
0
    TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
6564
0
    if (! symbol)
6565
0
        return;
6566
6567
0
    if (symbol->getType().getBasicType() == EbtVoid) {
6568
0
        error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
6569
6570
        // Add to symbol table to prevent future error messages on the same name
6571
0
        if (symbol->getName().size() > 0) {
6572
0
            TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
6573
0
            symbolTable.insert(*fakeVariable);
6574
6575
            // substitute a symbol node for this new variable
6576
0
            nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
6577
0
        }
6578
0
    }
6579
0
}
6580
6581
//
6582
// Both test, and if necessary spit out an error, to see if the node is really
6583
// a constant.
6584
//
6585
void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
6586
0
{
6587
0
    if (node->getQualifier().storage != EvqConst)
6588
0
        error(node->getLoc(), "constant expression required", token, "");
6589
0
}
6590
6591
//
6592
// Both test, and if necessary spit out an error, to see if the node is really
6593
// an integer.
6594
//
6595
void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
6596
0
{
6597
0
    if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
6598
0
        return;
6599
6600
0
    error(node->getLoc(), "scalar integer expression required", token, "");
6601
0
}
6602
6603
//
6604
// Both test, and if necessary spit out an error, to see if we are currently
6605
// globally scoped.
6606
//
6607
void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
6608
0
{
6609
0
    if (! symbolTable.atGlobalLevel())
6610
0
        error(loc, "not allowed in nested scope", token, "");
6611
0
}
6612
6613
bool HlslParseContext::builtInName(const TString& /*identifier*/)
6614
0
{
6615
0
    return false;
6616
0
}
6617
6618
//
6619
// Make sure there is enough data and not too many arguments provided to the
6620
// constructor to build something of the type of the constructor.  Also returns
6621
// the type of the constructor.
6622
//
6623
// Returns true if there was an error in construction.
6624
//
6625
bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
6626
                                        TOperator op, TType& type)
6627
0
{
6628
0
    type.shallowCopy(function.getType());
6629
6630
0
    bool constructingMatrix = false;
6631
0
    switch (op) {
6632
0
    case EOpConstructTextureSampler:
6633
0
        error(loc, "unhandled texture constructor", "constructor", "");
6634
0
        return true;
6635
0
    case EOpConstructMat2x2:
6636
0
    case EOpConstructMat2x3:
6637
0
    case EOpConstructMat2x4:
6638
0
    case EOpConstructMat3x2:
6639
0
    case EOpConstructMat3x3:
6640
0
    case EOpConstructMat3x4:
6641
0
    case EOpConstructMat4x2:
6642
0
    case EOpConstructMat4x3:
6643
0
    case EOpConstructMat4x4:
6644
0
    case EOpConstructDMat2x2:
6645
0
    case EOpConstructDMat2x3:
6646
0
    case EOpConstructDMat2x4:
6647
0
    case EOpConstructDMat3x2:
6648
0
    case EOpConstructDMat3x3:
6649
0
    case EOpConstructDMat3x4:
6650
0
    case EOpConstructDMat4x2:
6651
0
    case EOpConstructDMat4x3:
6652
0
    case EOpConstructDMat4x4:
6653
0
    case EOpConstructIMat2x2:
6654
0
    case EOpConstructIMat2x3:
6655
0
    case EOpConstructIMat2x4:
6656
0
    case EOpConstructIMat3x2:
6657
0
    case EOpConstructIMat3x3:
6658
0
    case EOpConstructIMat3x4:
6659
0
    case EOpConstructIMat4x2:
6660
0
    case EOpConstructIMat4x3:
6661
0
    case EOpConstructIMat4x4:
6662
0
    case EOpConstructUMat2x2:
6663
0
    case EOpConstructUMat2x3:
6664
0
    case EOpConstructUMat2x4:
6665
0
    case EOpConstructUMat3x2:
6666
0
    case EOpConstructUMat3x3:
6667
0
    case EOpConstructUMat3x4:
6668
0
    case EOpConstructUMat4x2:
6669
0
    case EOpConstructUMat4x3:
6670
0
    case EOpConstructUMat4x4:
6671
0
    case EOpConstructBMat2x2:
6672
0
    case EOpConstructBMat2x3:
6673
0
    case EOpConstructBMat2x4:
6674
0
    case EOpConstructBMat3x2:
6675
0
    case EOpConstructBMat3x3:
6676
0
    case EOpConstructBMat3x4:
6677
0
    case EOpConstructBMat4x2:
6678
0
    case EOpConstructBMat4x3:
6679
0
    case EOpConstructBMat4x4:
6680
0
        constructingMatrix = true;
6681
0
        break;
6682
0
    default:
6683
0
        break;
6684
0
    }
6685
6686
    //
6687
    // Walk the arguments for first-pass checks and collection of information.
6688
    //
6689
6690
0
    int size = 0;
6691
0
    bool constType = true;
6692
0
    bool full = false;
6693
0
    bool overFull = false;
6694
0
    bool matrixInMatrix = false;
6695
0
    bool arrayArg = false;
6696
0
    for (int arg = 0; arg < function.getParamCount(); ++arg) {
6697
0
        if (function[arg].type->isArray()) {
6698
0
            if (function[arg].type->isUnsizedArray()) {
6699
                // Can't construct from an unsized array.
6700
0
                error(loc, "array argument must be sized", "constructor", "");
6701
0
                return true;
6702
0
            }
6703
0
            arrayArg = true;
6704
0
        }
6705
0
        if (constructingMatrix && function[arg].type->isMatrix())
6706
0
            matrixInMatrix = true;
6707
6708
        // 'full' will go to true when enough args have been seen.  If we loop
6709
        // again, there is an extra argument.
6710
0
        if (full) {
6711
            // For vectors and matrices, it's okay to have too many components
6712
            // available, but not okay to have unused arguments.
6713
0
            overFull = true;
6714
0
        }
6715
6716
0
        size += function[arg].type->computeNumComponents();
6717
0
        if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
6718
0
            full = true;
6719
6720
0
        if (function[arg].type->getQualifier().storage != EvqConst)
6721
0
            constType = false;
6722
0
    }
6723
6724
0
    if (constType)
6725
0
        type.getQualifier().storage = EvqConst;
6726
6727
0
    if (type.isArray()) {
6728
0
        if (function.getParamCount() == 0) {
6729
0
            error(loc, "array constructor must have at least one argument", "constructor", "");
6730
0
            return true;
6731
0
        }
6732
6733
0
        if (type.isUnsizedArray()) {
6734
            // auto adapt the constructor type to the number of arguments
6735
0
            type.changeOuterArraySize(function.getParamCount());
6736
0
        } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) {
6737
0
            error(loc, "array constructor needs one argument per array element", "constructor", "");
6738
0
            return true;
6739
0
        }
6740
6741
0
        if (type.isArrayOfArrays()) {
6742
            // Types have to match, but we're still making the type.
6743
            // Finish making the type, and the comparison is done later
6744
            // when checking for conversion.
6745
0
            TArraySizes& arraySizes = *type.getArraySizes();
6746
6747
            // At least the dimensionalities have to match.
6748
0
            if (! function[0].type->isArray() ||
6749
0
                arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) {
6750
0
                error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
6751
0
                return true;
6752
0
            }
6753
6754
0
            if (arraySizes.isInnerUnsized()) {
6755
                // "Arrays of arrays ..., and the size for any dimension is optional"
6756
                // That means we need to adopt (from the first argument) the other array sizes into the type.
6757
0
                for (int d = 1; d < arraySizes.getNumDims(); ++d) {
6758
0
                    if (arraySizes.getDimSize(d) == UnsizedArraySize) {
6759
0
                        arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1));
6760
0
                    }
6761
0
                }
6762
0
            }
6763
0
        }
6764
0
    }
6765
6766
    // Some array -> array type casts are okay
6767
0
    if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
6768
0
        !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
6769
0
        type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
6770
0
        return false;
6771
6772
0
    if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
6773
0
        error(loc, "constructing non-array constituent from array argument", "constructor", "");
6774
0
        return true;
6775
0
    }
6776
6777
0
    if (matrixInMatrix && ! type.isArray()) {
6778
0
        return false;
6779
0
    }
6780
6781
0
    if (overFull) {
6782
0
        error(loc, "too many arguments", "constructor", "");
6783
0
        return true;
6784
0
    }
6785
6786
0
    if (op == EOpConstructStruct && ! type.isArray()) {
6787
0
        if (isScalarConstructor(node))
6788
0
            return false;
6789
6790
        // Self-type construction: e.g, we can construct a struct from a single identically typed object.
6791
0
        if (function.getParamCount() == 1 && type == *function[0].type)
6792
0
            return false;
6793
6794
0
        if ((int)type.getStruct()->size() != function.getParamCount()) {
6795
0
            error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
6796
0
            return true;
6797
0
        }
6798
0
    }
6799
6800
0
    if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
6801
0
        (op == EOpConstructStruct && size < type.computeNumComponents())) {
6802
0
        error(loc, "not enough data provided for construction", "constructor", "");
6803
0
        return true;
6804
0
    }
6805
6806
0
    return false;
6807
0
}
6808
6809
// See if 'node', in the context of constructing aggregates, is a scalar argument
6810
// to a constructor.
6811
//
6812
bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
6813
0
{
6814
    // Obviously, it must be a scalar, but an aggregate node might not be fully
6815
    // completed yet: holding a sequence of initializers under an aggregate
6816
    // would not yet be typed, so don't check it's type.  This corresponds to
6817
    // the aggregate operator also not being set yet. (An aggregate operation
6818
    // that legitimately yields a scalar will have a getOp() of that operator,
6819
    // not EOpNull.)
6820
6821
0
    return node->getAsTyped() != nullptr &&
6822
0
           node->getAsTyped()->isScalar() &&
6823
0
           (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
6824
0
}
6825
6826
// Checks to see if a void variable has been declared and raise an error message for such a case
6827
//
6828
// returns true in case of an error
6829
//
6830
bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
6831
0
{
6832
0
    if (basicType == EbtVoid) {
6833
0
        error(loc, "illegal use of type 'void'", identifier.c_str(), "");
6834
0
        return true;
6835
0
    }
6836
6837
0
    return false;
6838
0
}
6839
6840
//
6841
// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
6842
//
6843
void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
6844
0
{
6845
    // move from parameter/unknown qualifiers to pipeline in/out qualifiers
6846
0
    switch (qualifier.storage) {
6847
0
    case EvqIn:
6848
0
        qualifier.storage = EvqVaryingIn;
6849
0
        break;
6850
0
    case EvqOut:
6851
0
        qualifier.storage = EvqVaryingOut;
6852
0
        break;
6853
0
    default:
6854
0
        break;
6855
0
    }
6856
0
}
6857
6858
//
6859
// Merge characteristics of the 'src' qualifier into the 'dst'.
6860
//
6861
void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
6862
0
{
6863
    // Storage qualification
6864
0
    if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
6865
0
        dst.storage = src.storage;
6866
0
    else if ((dst.storage == EvqIn  && src.storage == EvqOut) ||
6867
0
             (dst.storage == EvqOut && src.storage == EvqIn))
6868
0
        dst.storage = EvqInOut;
6869
0
    else if ((dst.storage == EvqIn    && src.storage == EvqConst) ||
6870
0
             (dst.storage == EvqConst && src.storage == EvqIn))
6871
0
        dst.storage = EvqConstReadOnly;
6872
6873
    // Layout qualifiers
6874
0
    mergeObjectLayoutQualifiers(dst, src, false);
6875
6876
    // individual qualifiers
6877
0
#define MERGE_SINGLETON(field) dst.field |= src.field;
6878
0
    MERGE_SINGLETON(invariant);
6879
0
    MERGE_SINGLETON(noContraction);
6880
0
    MERGE_SINGLETON(centroid);
6881
0
    MERGE_SINGLETON(smooth);
6882
0
    MERGE_SINGLETON(flat);
6883
0
    MERGE_SINGLETON(nopersp);
6884
0
    MERGE_SINGLETON(patch);
6885
0
    MERGE_SINGLETON(sample);
6886
0
    MERGE_SINGLETON(coherent);
6887
0
    MERGE_SINGLETON(volatil);
6888
0
    MERGE_SINGLETON(restrict);
6889
0
    MERGE_SINGLETON(readonly);
6890
0
    MERGE_SINGLETON(writeonly);
6891
0
    MERGE_SINGLETON(specConstant);
6892
0
    MERGE_SINGLETON(nonUniform);
6893
0
}
6894
6895
// used to flatten the sampler type space into a single dimension
6896
// correlates with the declaration of defaultSamplerPrecision[]
6897
int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
6898
0
{
6899
0
    int arrayIndex = sampler.arrayed ? 1 : 0;
6900
0
    int shadowIndex = sampler.shadow ? 1 : 0;
6901
0
    int externalIndex = sampler.external ? 1 : 0;
6902
6903
0
    return EsdNumDims *
6904
0
           (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
6905
0
}
6906
6907
//
6908
// Do size checking for an array type's size.
6909
//
6910
void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
6911
0
{
6912
0
    bool isConst = false;
6913
0
    sizePair.size = 1;
6914
0
    sizePair.node = nullptr;
6915
6916
0
    TIntermConstantUnion* constant = expr->getAsConstantUnion();
6917
0
    if (constant) {
6918
        // handle true (non-specialization) constant
6919
0
        sizePair.size = constant->getConstArray()[0].getIConst();
6920
0
        isConst = true;
6921
0
    } else {
6922
        // see if it's a specialization constant instead
6923
0
        if (expr->getQualifier().isSpecConstant()) {
6924
0
            isConst = true;
6925
0
            sizePair.node = expr;
6926
0
            TIntermSymbol* symbol = expr->getAsSymbolNode();
6927
0
            if (symbol && symbol->getConstArray().size() > 0)
6928
0
                sizePair.size = symbol->getConstArray()[0].getIConst();
6929
0
        }
6930
0
    }
6931
6932
0
    if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
6933
0
        error(loc, "array size must be a constant integer expression", "", "");
6934
0
        return;
6935
0
    }
6936
6937
0
    if (sizePair.size <= 0) {
6938
0
        error(loc, "array size must be a positive integer", "", "");
6939
0
        return;
6940
0
    }
6941
0
}
6942
6943
//
6944
// Require array to be completely sized
6945
//
6946
void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
6947
0
{
6948
0
    if (arraySizes.hasUnsized())
6949
0
        error(loc, "array size required", "", "");
6950
0
}
6951
6952
void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
6953
0
{
6954
0
    const TTypeList& structure = *type.getStruct();
6955
0
    for (int m = 0; m < (int)structure.size(); ++m) {
6956
0
        const TType& member = *structure[m].type;
6957
0
        if (member.isArray())
6958
0
            arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
6959
0
    }
6960
0
}
6961
6962
//
6963
// Do all the semantic checking for declaring or redeclaring an array, with and
6964
// without a size, and make the right changes to the symbol table.
6965
//
6966
void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
6967
                                    TSymbol*& symbol, bool track)
6968
0
{
6969
0
    if (symbol == nullptr) {
6970
0
        bool currentScope;
6971
0
        symbol = symbolTable.find(identifier, nullptr, &currentScope);
6972
6973
0
        if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
6974
            // bad shader (errors already reported) trying to redeclare a built-in name as an array
6975
0
            return;
6976
0
        }
6977
0
        if (symbol == nullptr || ! currentScope) {
6978
            //
6979
            // Successfully process a new definition.
6980
            // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
6981
            //
6982
0
            symbol = new TVariable(&identifier, type);
6983
0
            symbolTable.insert(*symbol);
6984
0
            if (track && symbolTable.atGlobalLevel())
6985
0
                trackLinkage(*symbol);
6986
6987
0
            return;
6988
0
        }
6989
0
        if (symbol->getAsAnonMember()) {
6990
0
            error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
6991
0
            symbol = nullptr;
6992
0
            return;
6993
0
        }
6994
0
    }
6995
6996
    //
6997
    // Process a redeclaration.
6998
    //
6999
7000
0
    if (symbol == nullptr) {
7001
0
        error(loc, "array variable name expected", identifier.c_str(), "");
7002
0
        return;
7003
0
    }
7004
7005
    // redeclareBuiltinVariable() should have already done the copyUp()
7006
0
    TType& existingType = symbol->getWritableType();
7007
7008
0
    if (existingType.isSizedArray()) {
7009
        // be more lenient for input arrays to geometry shaders and tessellation control outputs,
7010
        // where the redeclaration is the same size
7011
0
        return;
7012
0
    }
7013
7014
0
    existingType.updateArraySizes(type);
7015
0
}
7016
7017
//
7018
// Enforce non-initializer type/qualifier rules.
7019
//
7020
void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type,
7021
                                    TIntermTyped*& initializer)
7022
0
{
7023
    //
7024
    // Make the qualifier make sense, given that there is an initializer.
7025
    //
7026
0
    if (initializer == nullptr) {
7027
0
        if (type.getQualifier().storage == EvqConst ||
7028
0
            type.getQualifier().storage == EvqConstReadOnly) {
7029
0
            initializer = intermediate.makeAggregate(loc);
7030
0
            warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
7031
0
        }
7032
0
    }
7033
0
}
7034
7035
//
7036
// See if the identifier is a built-in symbol that can be redeclared, and if so,
7037
// copy the symbol table's read-only built-in variable to the current
7038
// global level, where it can be modified based on the passed in type.
7039
//
7040
// Returns nullptr if no redeclaration took place; meaning a normal declaration still
7041
// needs to occur for it, not necessarily an error.
7042
//
7043
// Returns a redeclared and type-modified variable if a redeclared occurred.
7044
//
7045
TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
7046
                                                    const TQualifier& /*qualifier*/,
7047
                                                    const TShaderQualifiers& /*publicType*/)
7048
0
{
7049
0
    if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
7050
0
        return nullptr;
7051
7052
0
    return nullptr;
7053
0
}
7054
7055
//
7056
// Generate index to the array element in a structure buffer (SSBO)
7057
//
7058
TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
7059
0
{
7060
    // Bail out if not a struct buffer
7061
0
    if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
7062
0
        return nullptr;
7063
7064
    // Runtime sized array is always the last element.
7065
0
    const TTypeList* bufferStruct = buffer->getType().getStruct();
7066
0
    TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
7067
7068
0
    TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
7069
0
    argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
7070
7071
0
    return argArray;
7072
0
}
7073
7074
//
7075
// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
7076
// (template) type.   E.g, StructuredBuffer<MyType> -> MyType.  Else return nullptr.
7077
//
7078
TType* HlslParseContext::getStructBufferContentType(const TType& type) const
7079
0
{
7080
0
    if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer)
7081
0
        return nullptr;
7082
7083
0
    const int memberCount = (int)type.getStruct()->size();
7084
0
    assert(memberCount > 0);
7085
7086
0
    TType* contentType = (*type.getStruct())[memberCount-1].type;
7087
7088
0
    return contentType->isUnsizedArray() ? contentType : nullptr;
7089
0
}
7090
7091
//
7092
// If an existing struct buffer has a sharable type, then share it.
7093
//
7094
void HlslParseContext::shareStructBufferType(TType& type)
7095
0
{
7096
    // PackOffset must be equivalent to share types on a per-member basis.
7097
    // Note: cannot use auto type due to recursion.  Thus, this is a std::function.
7098
0
    const std::function<bool(TType& lhs, TType& rhs)>
7099
0
    compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
7100
0
        if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
7101
0
            return false;
7102
7103
0
        if (lhs.isStruct() != rhs.isStruct())
7104
0
            return false;
7105
7106
0
        if (lhs.getQualifier().builtIn != rhs.getQualifier().builtIn)
7107
0
            return false;
7108
7109
0
        if (lhs.isStruct() && rhs.isStruct()) {
7110
0
            if (lhs.getStruct()->size() != rhs.getStruct()->size())
7111
0
                return false;
7112
7113
0
            for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
7114
0
                if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
7115
0
                    return false;
7116
0
        }
7117
7118
0
        return true;
7119
0
    };
7120
7121
    // We need to compare certain qualifiers in addition to the type.
7122
0
    const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
7123
0
        if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
7124
0
            return false;
7125
7126
        // If both are structures, recursively look for packOffset equality
7127
        // as well as type equality.
7128
0
        return compareQualifiers(lhs, rhs) && lhs == rhs;
7129
0
    };
7130
7131
    // This is an exhaustive O(N) search, but real world shaders have
7132
    // only a small number of these.
7133
0
    for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
7134
        // If the deep structure matches, modulo qualifiers, use it
7135
0
        if (typeEqual(*structBufferTypes[idx], type)) {
7136
0
            type.shallowCopy(*structBufferTypes[idx]);
7137
0
            return;
7138
0
        }
7139
0
    }
7140
7141
    // Otherwise, remember it:
7142
0
    TType* typeCopy = new TType;
7143
0
    typeCopy->shallowCopy(type);
7144
0
    structBufferTypes.push_back(typeCopy);
7145
0
}
7146
7147
void HlslParseContext::paramFix(TType& type)
7148
395k
{
7149
395k
    switch (type.getQualifier().storage) {
7150
0
    case EvqConst:
7151
0
        type.getQualifier().storage = EvqConstReadOnly;
7152
0
        break;
7153
0
    case EvqGlobal:
7154
357k
    case EvqTemporary:
7155
357k
        type.getQualifier().storage = EvqIn;
7156
357k
        break;
7157
0
    case EvqBuffer:
7158
0
        {
7159
            // SSBO parameter.  These do not go through the declareBlock path since they are fn parameters.
7160
0
            correctUniform(type.getQualifier());
7161
0
            TQualifier bufferQualifier = globalBufferDefaults;
7162
0
            mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
7163
0
            bufferQualifier.storage = type.getQualifier().storage;
7164
0
            bufferQualifier.readonly = type.getQualifier().readonly;
7165
0
            bufferQualifier.coherent = type.getQualifier().coherent;
7166
0
            bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn;
7167
0
            type.getQualifier() = bufferQualifier;
7168
0
            break;
7169
0
        }
7170
38.3k
    default:
7171
38.3k
        break;
7172
395k
    }
7173
395k
}
7174
7175
void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
7176
0
{
7177
0
    if (type.containsSpecializationSize())
7178
0
        error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
7179
0
}
7180
7181
//
7182
// Layout qualifier stuff.
7183
//
7184
7185
// Put the id's layout qualification into the public type, for qualifiers not having a number set.
7186
// This is before we know any type information for error checking.
7187
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
7188
0
{
7189
0
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7190
7191
0
    if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
7192
0
        qualifier.layoutMatrix = ElmRowMajor;
7193
0
        return;
7194
0
    }
7195
0
    if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
7196
0
        qualifier.layoutMatrix = ElmColumnMajor;
7197
0
        return;
7198
0
    }
7199
0
    if (id == "push_constant") {
7200
0
        requireVulkan(loc, "push_constant");
7201
0
        qualifier.layoutPushConstant = true;
7202
0
        return;
7203
0
    }
7204
0
    if (language == EShLangGeometry || language == EShLangTessEvaluation) {
7205
0
        if (id == TQualifier::getGeometryString(ElgTriangles)) {
7206
            // publicType.shaderQualifiers.geometry = ElgTriangles;
7207
0
            warn(loc, "ignored", id.c_str(), "");
7208
0
            return;
7209
0
        }
7210
0
        if (language == EShLangGeometry) {
7211
0
            if (id == TQualifier::getGeometryString(ElgPoints)) {
7212
                // publicType.shaderQualifiers.geometry = ElgPoints;
7213
0
                warn(loc, "ignored", id.c_str(), "");
7214
0
                return;
7215
0
            }
7216
0
            if (id == TQualifier::getGeometryString(ElgLineStrip)) {
7217
                // publicType.shaderQualifiers.geometry = ElgLineStrip;
7218
0
                warn(loc, "ignored", id.c_str(), "");
7219
0
                return;
7220
0
            }
7221
0
            if (id == TQualifier::getGeometryString(ElgLines)) {
7222
                // publicType.shaderQualifiers.geometry = ElgLines;
7223
0
                warn(loc, "ignored", id.c_str(), "");
7224
0
                return;
7225
0
            }
7226
0
            if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
7227
                // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
7228
0
                warn(loc, "ignored", id.c_str(), "");
7229
0
                return;
7230
0
            }
7231
0
            if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
7232
                // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
7233
0
                warn(loc, "ignored", id.c_str(), "");
7234
0
                return;
7235
0
            }
7236
0
            if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
7237
                // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
7238
0
                warn(loc, "ignored", id.c_str(), "");
7239
0
                return;
7240
0
            }
7241
0
        } else {
7242
0
            assert(language == EShLangTessEvaluation);
7243
7244
            // input primitive
7245
0
            if (id == TQualifier::getGeometryString(ElgTriangles)) {
7246
                // publicType.shaderQualifiers.geometry = ElgTriangles;
7247
0
                warn(loc, "ignored", id.c_str(), "");
7248
0
                return;
7249
0
            }
7250
0
            if (id == TQualifier::getGeometryString(ElgQuads)) {
7251
                // publicType.shaderQualifiers.geometry = ElgQuads;
7252
0
                warn(loc, "ignored", id.c_str(), "");
7253
0
                return;
7254
0
            }
7255
0
            if (id == TQualifier::getGeometryString(ElgIsolines)) {
7256
                // publicType.shaderQualifiers.geometry = ElgIsolines;
7257
0
                warn(loc, "ignored", id.c_str(), "");
7258
0
                return;
7259
0
            }
7260
7261
            // vertex spacing
7262
0
            if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
7263
                // publicType.shaderQualifiers.spacing = EvsEqual;
7264
0
                warn(loc, "ignored", id.c_str(), "");
7265
0
                return;
7266
0
            }
7267
0
            if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
7268
                // publicType.shaderQualifiers.spacing = EvsFractionalEven;
7269
0
                warn(loc, "ignored", id.c_str(), "");
7270
0
                return;
7271
0
            }
7272
0
            if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
7273
                // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
7274
0
                warn(loc, "ignored", id.c_str(), "");
7275
0
                return;
7276
0
            }
7277
7278
            // triangle order
7279
0
            if (id == TQualifier::getVertexOrderString(EvoCw)) {
7280
                // publicType.shaderQualifiers.order = EvoCw;
7281
0
                warn(loc, "ignored", id.c_str(), "");
7282
0
                return;
7283
0
            }
7284
0
            if (id == TQualifier::getVertexOrderString(EvoCcw)) {
7285
                // publicType.shaderQualifiers.order = EvoCcw;
7286
0
                warn(loc, "ignored", id.c_str(), "");
7287
0
                return;
7288
0
            }
7289
7290
            // point mode
7291
0
            if (id == "point_mode") {
7292
                // publicType.shaderQualifiers.pointMode = true;
7293
0
                warn(loc, "ignored", id.c_str(), "");
7294
0
                return;
7295
0
            }
7296
0
        }
7297
0
    }
7298
0
    if (language == EShLangFragment) {
7299
0
        if (id == "origin_upper_left") {
7300
            // publicType.shaderQualifiers.originUpperLeft = true;
7301
0
            warn(loc, "ignored", id.c_str(), "");
7302
0
            return;
7303
0
        }
7304
0
        if (id == "pixel_center_integer") {
7305
            // publicType.shaderQualifiers.pixelCenterInteger = true;
7306
0
            warn(loc, "ignored", id.c_str(), "");
7307
0
            return;
7308
0
        }
7309
0
        if (id == "early_fragment_tests") {
7310
            // publicType.shaderQualifiers.earlyFragmentTests = true;
7311
0
            warn(loc, "ignored", id.c_str(), "");
7312
0
            return;
7313
0
        }
7314
0
        for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
7315
0
            if (id == TQualifier::getLayoutDepthString(depth)) {
7316
                // publicType.shaderQualifiers.layoutDepth = depth;
7317
0
                warn(loc, "ignored", id.c_str(), "");
7318
0
                return;
7319
0
            }
7320
0
        }
7321
0
        if (id.compare(0, 13, "blend_support") == 0) {
7322
0
            bool found = false;
7323
0
            for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
7324
0
                if (id == TQualifier::getBlendEquationString(be)) {
7325
0
                    requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
7326
0
                    intermediate.addBlendEquation(be);
7327
                    // publicType.shaderQualifiers.blendEquation = true;
7328
0
                    warn(loc, "ignored", id.c_str(), "");
7329
0
                    found = true;
7330
0
                    break;
7331
0
                }
7332
0
            }
7333
0
            if (! found)
7334
0
                error(loc, "unknown blend equation", "blend_support", "");
7335
0
            return;
7336
0
        }
7337
0
    }
7338
0
    error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
7339
0
}
7340
7341
// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
7342
// This is before we know any type information for error checking.
7343
void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id,
7344
                                          const TIntermTyped* node)
7345
0
{
7346
0
    const char* feature = "layout-id value";
7347
    // const char* nonLiteralFeature = "non-literal layout-id value";
7348
7349
0
    integerCheck(node, feature);
7350
0
    const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
7351
0
    int value = 0;
7352
0
    if (constUnion) {
7353
0
        value = constUnion->getConstArray()[0].getIConst();
7354
0
    }
7355
7356
0
    std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7357
7358
0
    if (id == "offset") {
7359
0
        qualifier.layoutOffset = value;
7360
0
        return;
7361
0
    } else if (id == "align") {
7362
        // "The specified alignment must be a power of 2, or a compile-time error results."
7363
0
        if (! IsPow2(value))
7364
0
            error(loc, "must be a power of 2", "align", "");
7365
0
        else
7366
0
            qualifier.layoutAlign = value;
7367
0
        return;
7368
0
    } else if (id == "location") {
7369
0
        if ((unsigned int)value >= TQualifier::layoutLocationEnd)
7370
0
            error(loc, "location is too large", id.c_str(), "");
7371
0
        else
7372
0
            qualifier.layoutLocation = value;
7373
0
        return;
7374
0
    } else if (id == "set") {
7375
0
        if (isLayoutSetOutOfRange(value, relaxSetBindingLimits()))
7376
0
            error(loc, "set is out of range", id.c_str(), "");
7377
0
        else
7378
0
            qualifier.layoutSet = value;
7379
0
        return;
7380
0
    } else if (id == "binding") {
7381
0
        if (isLayoutBindingOutOfRange(value, relaxSetBindingLimits()))
7382
0
            error(loc, "binding is out of range", id.c_str(), "");
7383
0
        else
7384
0
            qualifier.layoutBinding = value;
7385
0
        return;
7386
0
    } else if (id == "component") {
7387
0
        if ((unsigned)value >= TQualifier::layoutComponentEnd)
7388
0
            error(loc, "component is too large", id.c_str(), "");
7389
0
        else
7390
0
            qualifier.layoutComponent = value;
7391
0
        return;
7392
0
    } else if (id.compare(0, 4, "xfb_") == 0) {
7393
        // "Any shader making any static use (after preprocessing) of any of these
7394
        // *xfb_* qualifiers will cause the shader to be in a transform feedback
7395
        // capturing mode and hence responsible for describing the transform feedback
7396
        // setup."
7397
0
        intermediate.setXfbMode();
7398
0
        if (id == "xfb_buffer") {
7399
            // "It is a compile-time error to specify an *xfb_buffer* that is greater than
7400
            // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
7401
0
            if (value >= resources.maxTransformFeedbackBuffers)
7402
0
                error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d",
7403
0
                      resources.maxTransformFeedbackBuffers);
7404
0
            if (value >= (int)TQualifier::layoutXfbBufferEnd)
7405
0
                error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
7406
0
            else
7407
0
                qualifier.layoutXfbBuffer = value;
7408
0
            return;
7409
0
        } else if (id == "xfb_offset") {
7410
0
            if (value >= (int)TQualifier::layoutXfbOffsetEnd)
7411
0
                error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
7412
0
            else
7413
0
                qualifier.layoutXfbOffset = value;
7414
0
            return;
7415
0
        } else if (id == "xfb_stride") {
7416
            // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
7417
            // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
7418
0
            if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
7419
0
                error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
7420
0
                      resources.maxTransformFeedbackInterleavedComponents);
7421
0
            else if (value >= (int)TQualifier::layoutXfbStrideEnd)
7422
0
                error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
7423
0
            if (value < (int)TQualifier::layoutXfbStrideEnd)
7424
0
                qualifier.layoutXfbStride = value;
7425
0
            return;
7426
0
        }
7427
0
    }
7428
7429
0
    if (id == "input_attachment_index") {
7430
0
        requireVulkan(loc, "input_attachment_index");
7431
0
        if (value >= (int)TQualifier::layoutAttachmentEnd)
7432
0
            error(loc, "attachment index is too large", id.c_str(), "");
7433
0
        else
7434
0
            qualifier.layoutAttachment = value;
7435
0
        return;
7436
0
    }
7437
0
    if (id == "constant_id") {
7438
0
        setSpecConstantId(loc, qualifier, (unsigned)value);
7439
0
        return;
7440
0
    }
7441
7442
0
    switch (language) {
7443
0
    case EShLangVertex:
7444
0
        break;
7445
7446
0
    case EShLangTessControl:
7447
0
        if (id == "vertices") {
7448
0
            if (value == 0)
7449
0
                error(loc, "must be greater than 0", "vertices", "");
7450
0
            else
7451
                // publicType.shaderQualifiers.vertices = value;
7452
0
                warn(loc, "ignored", id.c_str(), "");
7453
0
            return;
7454
0
        }
7455
0
        break;
7456
7457
0
    case EShLangTessEvaluation:
7458
0
        break;
7459
7460
0
    case EShLangGeometry:
7461
0
        if (id == "invocations") {
7462
0
            if (value == 0)
7463
0
                error(loc, "must be at least 1", "invocations", "");
7464
0
            else
7465
                // publicType.shaderQualifiers.invocations = value;
7466
0
                warn(loc, "ignored", id.c_str(), "");
7467
0
            return;
7468
0
        }
7469
0
        if (id == "max_vertices") {
7470
            // publicType.shaderQualifiers.vertices = value;
7471
0
            warn(loc, "ignored", id.c_str(), "");
7472
0
            if (value > resources.maxGeometryOutputVertices)
7473
0
                error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
7474
0
            return;
7475
0
        }
7476
0
        if (id == "stream") {
7477
0
            qualifier.layoutStream = value;
7478
0
            return;
7479
0
        }
7480
0
        break;
7481
7482
0
    case EShLangFragment:
7483
0
        if (id == "index") {
7484
0
            qualifier.layoutIndex = value;
7485
0
            return;
7486
0
        }
7487
0
        break;
7488
7489
0
    case EShLangCompute:
7490
0
        if (id.compare(0, 11, "local_size_") == 0) {
7491
0
            if (id == "local_size_x") {
7492
                // publicType.shaderQualifiers.localSize[0] = value;
7493
0
                warn(loc, "ignored", id.c_str(), "");
7494
0
                return;
7495
0
            }
7496
0
            if (id == "local_size_y") {
7497
                // publicType.shaderQualifiers.localSize[1] = value;
7498
0
                warn(loc, "ignored", id.c_str(), "");
7499
0
                return;
7500
0
            }
7501
0
            if (id == "local_size_z") {
7502
                // publicType.shaderQualifiers.localSize[2] = value;
7503
0
                warn(loc, "ignored", id.c_str(), "");
7504
0
                return;
7505
0
            }
7506
0
            if (spvVersion.spv != 0) {
7507
0
                if (id == "local_size_x_id") {
7508
                    // publicType.shaderQualifiers.localSizeSpecId[0] = value;
7509
0
                    warn(loc, "ignored", id.c_str(), "");
7510
0
                    return;
7511
0
                }
7512
0
                if (id == "local_size_y_id") {
7513
                    // publicType.shaderQualifiers.localSizeSpecId[1] = value;
7514
0
                    warn(loc, "ignored", id.c_str(), "");
7515
0
                    return;
7516
0
                }
7517
0
                if (id == "local_size_z_id") {
7518
                    // publicType.shaderQualifiers.localSizeSpecId[2] = value;
7519
0
                    warn(loc, "ignored", id.c_str(), "");
7520
0
                    return;
7521
0
                }
7522
0
            }
7523
0
        }
7524
0
        break;
7525
7526
0
    default:
7527
0
        break;
7528
0
    }
7529
7530
0
    error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
7531
0
}
7532
7533
void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, unsigned value)
7534
0
{
7535
0
    if (value >= TQualifier::layoutSpecConstantIdEnd) {
7536
0
        error(loc, "specialization-constant id is too large", "constant_id", "");
7537
0
    } else {
7538
0
        qualifier.layoutSpecConstantId = value;
7539
0
        qualifier.specConstant = true;
7540
0
        if (! intermediate.addUsedConstantId(value))
7541
0
            error(loc, "specialization-constant id already used", "constant_id", "");
7542
0
    }
7543
0
    return;
7544
0
}
7545
7546
// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
7547
//
7548
// "More than one layout qualifier may appear in a single declaration.
7549
// Additionally, the same layout-qualifier-name can occur multiple times
7550
// within a layout qualifier or across multiple layout qualifiers in the
7551
// same declaration. When the same layout-qualifier-name occurs
7552
// multiple times, in a single declaration, the last occurrence overrides
7553
// the former occurrence(s).  Further, if such a layout-qualifier-name
7554
// will effect subsequent declarations or other observable behavior, it
7555
// is only the last occurrence that will have any effect, behaving as if
7556
// the earlier occurrence(s) within the declaration are not present.
7557
// This is also true for overriding layout-qualifier-names, where one
7558
// overrides the other (e.g., row_major vs. column_major); only the last
7559
// occurrence has any effect."
7560
//
7561
void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
7562
0
{
7563
0
    if (src.hasMatrix())
7564
0
        dst.layoutMatrix = src.layoutMatrix;
7565
0
    if (src.hasPacking())
7566
0
        dst.layoutPacking = src.layoutPacking;
7567
7568
0
    if (src.hasStream())
7569
0
        dst.layoutStream = src.layoutStream;
7570
7571
0
    if (src.hasFormat())
7572
0
        dst.layoutFormat = src.layoutFormat;
7573
7574
0
    if (src.hasXfbBuffer())
7575
0
        dst.layoutXfbBuffer = src.layoutXfbBuffer;
7576
7577
0
    if (src.hasAlign())
7578
0
        dst.layoutAlign = src.layoutAlign;
7579
7580
0
    if (! inheritOnly) {
7581
0
        if (src.hasLocation())
7582
0
            dst.layoutLocation = src.layoutLocation;
7583
0
        if (src.hasComponent())
7584
0
            dst.layoutComponent = src.layoutComponent;
7585
0
        if (src.hasIndex())
7586
0
            dst.layoutIndex = src.layoutIndex;
7587
7588
0
        if (src.hasOffset())
7589
0
            dst.layoutOffset = src.layoutOffset;
7590
7591
0
        if (src.hasSet())
7592
0
            dst.layoutSet = src.layoutSet;
7593
0
        if (src.hasBinding())
7594
0
            dst.layoutBinding = src.layoutBinding;
7595
7596
0
        if (src.hasXfbStride())
7597
0
            dst.layoutXfbStride = src.layoutXfbStride;
7598
0
        if (src.hasXfbOffset())
7599
0
            dst.layoutXfbOffset = src.layoutXfbOffset;
7600
0
        if (src.hasAttachment())
7601
0
            dst.layoutAttachment = src.layoutAttachment;
7602
0
        if (src.hasSpecConstantId())
7603
0
            dst.layoutSpecConstantId = src.layoutSpecConstantId;
7604
7605
0
        if (src.layoutPushConstant)
7606
0
            dst.layoutPushConstant = true;
7607
0
    }
7608
0
}
7609
7610
7611
//
7612
// Look up a function name in the symbol table, and make sure it is a function.
7613
//
7614
// First, look for an exact match.  If there is none, use the generic selector
7615
// TParseContextBase::selectFunction() to find one, parameterized by the
7616
// convertible() and better() predicates defined below.
7617
//
7618
// Return the function symbol if found, otherwise nullptr.
7619
//
7620
const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth,
7621
                                                TIntermTyped*& args)
7622
0
{
7623
0
    if (symbolTable.isFunctionNameVariable(call.getName())) {
7624
0
        error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
7625
0
        return nullptr;
7626
0
    }
7627
7628
    // first, look for an exact match
7629
0
    bool dummyScope;
7630
0
    TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth);
7631
0
    if (symbol)
7632
0
        return symbol->getAsFunction();
7633
7634
    // no exact match, use the generic selector, parameterized by the GLSL rules
7635
7636
    // create list of candidates to send
7637
0
    TVector<const TFunction*> candidateList;
7638
0
    symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
7639
7640
    // These built-in ops can accept any type, so we bypass the argument selection
7641
0
    if (candidateList.size() == 1 && builtIn &&
7642
0
        (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
7643
0
         candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
7644
0
         candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
7645
0
         candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter ||
7646
0
         candidateList[0]->getBuiltInOp() == EOpMethodConsume)) {
7647
0
        return candidateList[0];
7648
0
    }
7649
7650
0
    bool allowOnlyUpConversions = true;
7651
7652
    // can 'from' convert to 'to'?
7653
0
    const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
7654
0
        if (from == to)
7655
0
            return true;
7656
7657
        // no aggregate conversions
7658
0
        if (from.isArray()  || to.isArray() ||
7659
0
            from.isStruct() || to.isStruct())
7660
0
            return false;
7661
7662
0
        switch (op) {
7663
0
        case EOpInterlockedAdd:
7664
0
        case EOpInterlockedAnd:
7665
0
        case EOpInterlockedCompareExchange:
7666
0
        case EOpInterlockedCompareStore:
7667
0
        case EOpInterlockedExchange:
7668
0
        case EOpInterlockedMax:
7669
0
        case EOpInterlockedMin:
7670
0
        case EOpInterlockedOr:
7671
0
        case EOpInterlockedXor:
7672
            // We do not promote the texture or image type for these ocodes.  Normally that would not
7673
            // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
7674
            // stage it's merely e.g, a basic integer type.
7675
            //
7676
            // Instead, we want to promote other arguments, but stay within the same family.  In other
7677
            // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
7678
            // but it is allowed to promote its other arguments.
7679
0
            if (arg == 0)
7680
0
                return false;
7681
0
            break;
7682
0
        case EOpMethodSample:
7683
0
        case EOpMethodSampleBias:
7684
0
        case EOpMethodSampleCmp:
7685
0
        case EOpMethodSampleCmpLevelZero:
7686
0
        case EOpMethodSampleGrad:
7687
0
        case EOpMethodSampleLevel:
7688
0
        case EOpMethodLoad:
7689
0
        case EOpMethodGetDimensions:
7690
0
        case EOpMethodGetSamplePosition:
7691
0
        case EOpMethodGather:
7692
0
        case EOpMethodCalculateLevelOfDetail:
7693
0
        case EOpMethodCalculateLevelOfDetailUnclamped:
7694
0
        case EOpMethodGatherRed:
7695
0
        case EOpMethodGatherGreen:
7696
0
        case EOpMethodGatherBlue:
7697
0
        case EOpMethodGatherAlpha:
7698
0
        case EOpMethodGatherCmp:
7699
0
        case EOpMethodGatherCmpRed:
7700
0
        case EOpMethodGatherCmpGreen:
7701
0
        case EOpMethodGatherCmpBlue:
7702
0
        case EOpMethodGatherCmpAlpha:
7703
0
        case EOpMethodAppend:
7704
0
        case EOpMethodRestartStrip:
7705
            // those are method calls, the object type can not be changed
7706
            // they are equal if the dim and type match (is dim sufficient?)
7707
0
            if (arg == 0)
7708
0
                return from.getSampler().type == to.getSampler().type &&
7709
0
                       from.getSampler().arrayed == to.getSampler().arrayed &&
7710
0
                       from.getSampler().shadow == to.getSampler().shadow &&
7711
0
                       from.getSampler().ms == to.getSampler().ms &&
7712
0
                       from.getSampler().dim == to.getSampler().dim;
7713
0
            break;
7714
0
        default:
7715
0
            break;
7716
0
        }
7717
7718
        // basic types have to be convertible
7719
0
        if (allowOnlyUpConversions)
7720
0
            if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
7721
0
                return false;
7722
7723
        // shapes have to be convertible
7724
0
        if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
7725
0
            (from.isScalarOrVec1() && to.isVector())    ||
7726
0
            (from.isScalarOrVec1() && to.isMatrix())    ||
7727
0
            (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
7728
0
            return true;
7729
7730
        // TODO: what are the matrix rules? they go here
7731
7732
0
        return false;
7733
0
    };
7734
7735
    // Is 'to2' a better conversion than 'to1'?
7736
    // Ties should not be considered as better.
7737
    // Assumes 'convertible' already said true.
7738
0
    const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
7739
        // exact match is always better than mismatch
7740
0
        if (from == to2)
7741
0
            return from != to1;
7742
0
        if (from == to1)
7743
0
            return false;
7744
7745
        // shape changes are always worse
7746
0
        if (from.isScalar() || from.isVector()) {
7747
0
            if (from.getVectorSize() == to2.getVectorSize() &&
7748
0
                from.getVectorSize() != to1.getVectorSize())
7749
0
                return true;
7750
0
            if (from.getVectorSize() == to1.getVectorSize() &&
7751
0
                from.getVectorSize() != to2.getVectorSize())
7752
0
                return false;
7753
0
        }
7754
7755
        // Handle sampler betterness: An exact sampler match beats a non-exact match.
7756
        // (If we just looked at basic type, all EbtSamplers would look the same).
7757
        // If any type is not a sampler, just use the linearize function below.
7758
0
        if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
7759
            // We can ignore the vector size in the comparison.
7760
0
            TSampler to1Sampler = to1.getSampler();
7761
0
            TSampler to2Sampler = to2.getSampler();
7762
7763
0
            to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
7764
7765
0
            if (from.getSampler() == to2Sampler)
7766
0
                return from.getSampler() != to1Sampler;
7767
0
            if (from.getSampler() == to1Sampler)
7768
0
                return false;
7769
0
        }
7770
7771
        // Might or might not be changing shape, which means basic type might
7772
        // or might not match, so within that, the question is how big a
7773
        // basic-type conversion is being done.
7774
        //
7775
        // Use a hierarchy of domains, translated to order of magnitude
7776
        // in a linearized view:
7777
        //   - floating-point vs. integer
7778
        //     - 32 vs. 64 bit (or width in general)
7779
        //       - bool vs. non bool
7780
        //         - signed vs. not signed
7781
0
        const auto linearize = [](const TBasicType& basicType) -> int {
7782
0
            switch (basicType) {
7783
0
            case EbtBool:     return 1;
7784
0
            case EbtInt:      return 10;
7785
0
            case EbtUint:     return 11;
7786
0
            case EbtInt64:    return 20;
7787
0
            case EbtUint64:   return 21;
7788
0
            case EbtFloat:    return 100;
7789
0
            case EbtDouble:   return 110;
7790
0
            default:          return 0;
7791
0
            }
7792
0
        };
7793
7794
0
        return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
7795
0
               abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
7796
0
    };
7797
7798
    // for ambiguity reporting
7799
0
    bool tie = false;
7800
7801
    // send to the generic selector
7802
0
    const TFunction* bestMatch = nullptr;
7803
7804
    // printf has var args and is in the symbol table as "printf()",
7805
    // mangled to "printf("
7806
0
    if (call.getName() == "printf") {
7807
0
        TSymbol* symbol = symbolTable.find("printf(", &builtIn);
7808
0
        if (symbol)
7809
0
            return symbol->getAsFunction();
7810
0
    }
7811
7812
0
    bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7813
7814
0
    if (bestMatch == nullptr) {
7815
        // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
7816
        // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
7817
        // upconversions possible.
7818
0
        allowOnlyUpConversions = false;
7819
0
        bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7820
0
    }
7821
7822
0
    if (bestMatch == nullptr) {
7823
0
        error(loc, "no matching overloaded function found", call.getName().c_str(), "");
7824
0
        return nullptr;
7825
0
    }
7826
7827
    // For built-ins, we can convert across the arguments.  This will happen in several steps:
7828
    // Step 1:  If there's an exact match, use it.
7829
    // Step 2a: Otherwise, get the operator from the best match and promote arguments:
7830
    // Step 2b: reconstruct the TFunction based on the new arg types
7831
    // Step 3:  Re-select after type promotion is applied, to find proper candidate.
7832
0
    if (builtIn) {
7833
        // Step 1: If there's an exact match, use it.
7834
0
        if (call.getMangledName() == bestMatch->getMangledName())
7835
0
            return bestMatch;
7836
7837
        // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
7838
        // are that kind of operator.
7839
0
        if (args != nullptr) {
7840
            // The arg list can be a unary node, or an aggregate.  We have to handle both.
7841
            // We will use the normal promote() facilities, which require an interm node.
7842
0
            TIntermOperator* promote = nullptr;
7843
7844
0
            if (call.getParamCount() == 1) {
7845
0
                promote = new TIntermUnary(bestMatch->getBuiltInOp());
7846
0
                promote->getAsUnaryNode()->setOperand(args->getAsTyped());
7847
0
            } else {
7848
0
                promote = new TIntermAggregate(bestMatch->getBuiltInOp());
7849
0
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7850
0
            }
7851
7852
0
            if (! intermediate.promote(promote))
7853
0
                return nullptr;
7854
7855
            // Obtain the promoted arg list.
7856
0
            if (call.getParamCount() == 1) {
7857
0
                args = promote->getAsUnaryNode()->getOperand();
7858
0
            } else {
7859
0
                promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7860
0
            }
7861
0
        }
7862
7863
        // Step 2b: reconstruct the TFunction based on the new arg types
7864
0
        TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
7865
7866
0
        if (args->getAsAggregate()) {
7867
            // Handle aggregates: put all args into the new function call
7868
0
            for (int arg = 0; arg < int(args->getAsAggregate()->getSequence().size()); ++arg) {
7869
                // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
7870
0
                TParameter param = { nullptr, new TType, nullptr };
7871
0
                param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
7872
0
                convertedCall.addParameter(param);
7873
0
            }
7874
0
        } else if (args->getAsUnaryNode()) {
7875
            // Handle unaries: put all args into the new function call
7876
0
            TParameter param = { nullptr, new TType, nullptr };
7877
0
            param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
7878
0
            convertedCall.addParameter(param);
7879
0
        } else if (args->getAsTyped()) {
7880
            // Handle bare e.g, floats, not in an aggregate.
7881
0
            TParameter param = { nullptr, new TType, nullptr };
7882
0
            param.type->shallowCopy(args->getAsTyped()->getType());
7883
0
            convertedCall.addParameter(param);
7884
0
        } else {
7885
0
            assert(0); // unknown argument list.
7886
0
            return nullptr;
7887
0
        }
7888
7889
        // Step 3: Re-select after type promotion, to find proper candidate
7890
        // send to the generic selector
7891
0
        bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
7892
7893
        // At this point, there should be no tie.
7894
0
    }
7895
7896
0
    if (tie)
7897
0
        error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
7898
7899
    // Append default parameter values if needed
7900
0
    if (!tie && bestMatch != nullptr) {
7901
0
        for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
7902
0
            handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
7903
0
        }
7904
0
    }
7905
7906
0
    return bestMatch;
7907
0
}
7908
7909
//
7910
// Do everything necessary to handle a typedef declaration, for a single symbol.
7911
//
7912
// 'parseType' is the type part of the declaration (to the left)
7913
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7914
//
7915
void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
7916
0
{
7917
0
    TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
7918
0
    if (! symbolTable.insert(*typeSymbol))
7919
0
        error(loc, "name already defined", "typedef", identifier.c_str());
7920
0
}
7921
7922
// Do everything necessary to handle a struct declaration, including
7923
// making IO aliases because HLSL allows mixed IO in a struct that specializes
7924
// based on the usage (input, output, uniform, none).
7925
void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
7926
0
{
7927
    // If it was named, which means the type can be reused later, add
7928
    // it to the symbol table.  (Unless it's a block, in which
7929
    // case the name is not a type.)
7930
0
    if (type.getBasicType() == EbtBlock || structName.size() == 0)
7931
0
        return;
7932
7933
0
    TVariable* userTypeDef = new TVariable(&structName, type, true);
7934
0
    if (! symbolTable.insert(*userTypeDef)) {
7935
0
        error(loc, "redefinition", structName.c_str(), "struct");
7936
0
        return;
7937
0
    }
7938
7939
    // See if we need IO aliases for the structure typeList
7940
7941
0
    const auto condAlloc = [](bool pred, TTypeList*& list) {
7942
0
        if (pred && list == nullptr)
7943
0
            list = new TTypeList;
7944
0
    };
7945
7946
0
    tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
7947
0
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7948
0
        condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
7949
0
        condAlloc(  hasInput(member->type->getQualifier()), newLists.input);
7950
0
        condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
7951
7952
0
        if (member->type->isStruct()) {
7953
0
            auto it = ioTypeMap.find(member->type->getStruct());
7954
0
            if (it != ioTypeMap.end()) {
7955
0
                condAlloc(it->second.uniform != nullptr, newLists.uniform);
7956
0
                condAlloc(it->second.input   != nullptr, newLists.input);
7957
0
                condAlloc(it->second.output  != nullptr, newLists.output);
7958
0
            }
7959
0
        }
7960
0
    }
7961
0
    if (newLists.uniform == nullptr &&
7962
0
        newLists.input   == nullptr &&
7963
0
        newLists.output  == nullptr) {
7964
        // Won't do any IO caching, clear up the type and get out now.
7965
0
        for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
7966
0
            clearUniformInputOutput(member->type->getQualifier());
7967
0
        return;
7968
0
    }
7969
7970
    // We have IO involved.
7971
7972
    // Make a pure typeList for the symbol table, and cache side copies of IO versions.
7973
0
    for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
7974
0
        const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
7975
0
            if (s != nullptr) {
7976
0
                ioMember.type = new TType;
7977
0
                ioMember.type->shallowCopy(*member->type);
7978
0
                ioMember.type->setStruct(s);
7979
0
            }
7980
0
        };
7981
0
        const auto newMember = [&](TTypeLoc& m) {
7982
0
            if (m.type == nullptr) {
7983
0
                m.type = new TType;
7984
0
                m.type->shallowCopy(*member->type);
7985
0
            }
7986
0
        };
7987
7988
0
        TTypeLoc newUniformMember = { nullptr, member->loc };
7989
0
        TTypeLoc newInputMember   = { nullptr, member->loc };
7990
0
        TTypeLoc newOutputMember  = { nullptr, member->loc };
7991
0
        if (member->type->isStruct()) {
7992
            // swap in an IO child if there is one
7993
0
            auto it = ioTypeMap.find(member->type->getStruct());
7994
0
            if (it != ioTypeMap.end()) {
7995
0
                inheritStruct(it->second.uniform, newUniformMember);
7996
0
                inheritStruct(it->second.input,   newInputMember);
7997
0
                inheritStruct(it->second.output,  newOutputMember);
7998
0
            }
7999
0
        }
8000
0
        if (newLists.uniform) {
8001
0
            newMember(newUniformMember);
8002
8003
            // inherit default matrix layout (changeable via #pragma pack_matrix), if none given.
8004
0
            if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone)
8005
0
                newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
8006
8007
0
            correctUniform(newUniformMember.type->getQualifier());
8008
0
            newLists.uniform->push_back(newUniformMember);
8009
0
        }
8010
0
        if (newLists.input) {
8011
0
            newMember(newInputMember);
8012
0
            correctInput(newInputMember.type->getQualifier());
8013
0
            newLists.input->push_back(newInputMember);
8014
0
        }
8015
0
        if (newLists.output) {
8016
0
            newMember(newOutputMember);
8017
0
            correctOutput(newOutputMember.type->getQualifier());
8018
0
            newLists.output->push_back(newOutputMember);
8019
0
        }
8020
8021
        // make original pure
8022
0
        clearUniformInputOutput(member->type->getQualifier());
8023
0
    }
8024
0
    ioTypeMap[type.getStruct()] = newLists;
8025
0
}
8026
8027
// Lookup a user-type by name.
8028
// If found, fill in the type and return the defining symbol.
8029
// If not found, return nullptr.
8030
TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
8031
20
{
8032
20
    TSymbol* symbol = symbolTable.find(typeName);
8033
20
    if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
8034
0
        type.shallowCopy(symbol->getType());
8035
0
        return symbol;
8036
0
    } else
8037
20
        return nullptr;
8038
20
}
8039
8040
//
8041
// Do everything necessary to handle a variable (non-block) declaration.
8042
// Either redeclaring a variable, or making a new one, updating the symbol
8043
// table, and all error checking.
8044
//
8045
// Returns a subtree node that computes an initializer, if needed.
8046
// Returns nullptr if there is no code to execute for initialization.
8047
//
8048
// 'parseType' is the type part of the declaration (to the left)
8049
// 'arraySizes' is the arrayness tagged on the identifier (to the right)
8050
//
8051
TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type,
8052
                                               TIntermTyped* initializer)
8053
0
{
8054
0
    if (voidErrorCheck(loc, identifier, type.getBasicType()))
8055
0
        return nullptr;
8056
8057
    // Global consts with initializers that are non-const act like EvqGlobal in HLSL.
8058
    // This test is implicitly recursive, because initializers propagate constness
8059
    // up the aggregate node tree during creation.  E.g, for:
8060
    //    { { 1, 2 }, { 3, 4 } }
8061
    // the initializer list is marked EvqConst at the top node, and remains so here.  However:
8062
    //    { 1, { myvar, 2 }, 3 }
8063
    // is not a const intializer, and still becomes EvqGlobal here.
8064
8065
0
    const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
8066
8067
0
    if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
8068
        // Force to global
8069
0
        type.getQualifier().storage = EvqGlobal;
8070
0
    }
8071
8072
    // make const and initialization consistent
8073
0
    fixConstInit(loc, identifier, type, initializer);
8074
8075
    // Check for redeclaration of built-ins and/or attempting to declare a reserved name
8076
0
    TSymbol* symbol = nullptr;
8077
8078
0
    inheritGlobalDefaults(type.getQualifier());
8079
8080
0
    const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true);
8081
8082
    // correct IO in the type
8083
0
    switch (type.getQualifier().storage) {
8084
0
    case EvqGlobal:
8085
0
    case EvqTemporary:
8086
0
        clearUniformInputOutput(type.getQualifier());
8087
0
        break;
8088
0
    case EvqUniform:
8089
0
    case EvqBuffer:
8090
0
        correctUniform(type.getQualifier());
8091
0
        if (type.isStruct()) {
8092
0
            auto it = ioTypeMap.find(type.getStruct());
8093
0
            if (it != ioTypeMap.end())
8094
0
                type.setStruct(it->second.uniform);
8095
0
        }
8096
8097
0
        break;
8098
0
    default:
8099
0
        break;
8100
0
    }
8101
8102
    // Declare the variable
8103
0
    if (type.isArray()) {
8104
        // array case
8105
0
        declareArray(loc, identifier, type, symbol, !flattenVar);
8106
0
    } else {
8107
        // non-array case
8108
0
        if (symbol == nullptr)
8109
0
            symbol = declareNonArray(loc, identifier, type, !flattenVar);
8110
0
        else if (type != symbol->getType())
8111
0
            error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
8112
0
    }
8113
8114
0
    if (symbol == nullptr)
8115
0
        return nullptr;
8116
8117
0
    if (flattenVar)
8118
0
        flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel());
8119
8120
0
    TVariable* variable = symbol->getAsVariable();
8121
8122
0
    if (initializer == nullptr) {
8123
0
        if (intermediate.getDebugInfo())
8124
0
            return executeDeclaration(loc, variable);
8125
0
        else
8126
0
            return nullptr;
8127
0
    }
8128
8129
    // Deal with initializer
8130
0
    if (variable == nullptr) {
8131
0
        error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
8132
0
        return nullptr;
8133
0
    }
8134
0
    return executeInitializer(loc, initializer, variable);
8135
0
}
8136
8137
// Pick up global defaults from the provide global defaults into dst.
8138
void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
8139
0
{
8140
0
    if (dst.storage == EvqVaryingOut) {
8141
0
        if (! dst.hasStream() && language == EShLangGeometry)
8142
0
            dst.layoutStream = globalOutputDefaults.layoutStream;
8143
0
        if (! dst.hasXfbBuffer())
8144
0
            dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
8145
0
    }
8146
0
}
8147
8148
//
8149
// Make an internal-only variable whose name is for debug purposes only
8150
// and won't be searched for.  Callers will only use the return value to use
8151
// the variable, not the name to look it up.  It is okay if the name
8152
// is the same as other names; there won't be any conflict.
8153
//
8154
TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
8155
0
{
8156
0
    TString* nameString = NewPoolTString(name);
8157
0
    TVariable* variable = new TVariable(nameString, type);
8158
0
    symbolTable.makeInternalVariable(*variable);
8159
8160
0
    return variable;
8161
0
}
8162
8163
// Make a symbol node holding a new internal temporary variable.
8164
TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name,
8165
                                                          const TType& type) const
8166
0
{
8167
0
    TVariable* tmpVar = makeInternalVariable(name, type);
8168
0
    tmpVar->getWritableType().getQualifier().makeTemporary();
8169
8170
0
    return intermediate.addSymbol(*tmpVar, loc);
8171
0
}
8172
8173
//
8174
// Declare a non-array variable, the main point being there is no redeclaration
8175
// for resizing allowed.
8176
//
8177
// Return the successfully declared variable.
8178
//
8179
TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
8180
                                             bool track)
8181
0
{
8182
    // make a new variable
8183
0
    TVariable* variable = new TVariable(&identifier, type);
8184
8185
    // add variable to symbol table
8186
0
    if (symbolTable.insert(*variable)) {
8187
0
        if (track && symbolTable.atGlobalLevel())
8188
0
            trackLinkage(*variable);
8189
0
        return variable;
8190
0
    }
8191
8192
0
    error(loc, "redefinition", variable->getName().c_str(), "");
8193
0
    return nullptr;
8194
0
}
8195
8196
// Return a declaration of a temporary variable
8197
//
8198
// This is used to force a variable to be declared in the correct scope
8199
// when debug information is being generated.
8200
8201
TIntermNode* HlslParseContext::executeDeclaration(const TSourceLoc& loc, TVariable* variable)
8202
0
{
8203
  //
8204
  // Identifier must be of type temporary.
8205
  //
8206
0
  TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8207
0
  if (qualifier != EvqTemporary)
8208
0
      return nullptr;
8209
8210
0
  TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8211
0
  return handleDeclare(loc, intermSymbol);
8212
0
}
8213
8214
//
8215
// Handle all types of initializers from the grammar.
8216
//
8217
// Returning nullptr just means there is no code to execute to handle the
8218
// initializer, which will, for example, be the case for constant initializers.
8219
//
8220
// Returns a subtree that accomplished the initialization.
8221
//
8222
TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
8223
0
{
8224
    //
8225
    // Identifier must be of type constant, a global, or a temporary, and
8226
    // starting at version 120, desktop allows uniforms to have initializers.
8227
    //
8228
0
    TStorageQualifier qualifier = variable->getType().getQualifier().storage;
8229
8230
    //
8231
    // If the initializer was from braces { ... }, we convert the whole subtree to a
8232
    // constructor-style subtree, allowing the rest of the code to operate
8233
    // identically for both kinds of initializers.
8234
    //
8235
    //
8236
    // Type can't be deduced from the initializer list, so a skeletal type to
8237
    // follow has to be passed in.  Constness and specialization-constness
8238
    // should be deduced bottom up, not dictated by the skeletal type.
8239
    //
8240
0
    TType skeletalType;
8241
0
    skeletalType.shallowCopy(variable->getType());
8242
0
    skeletalType.getQualifier().makeTemporary();
8243
0
    if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
8244
0
        initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
8245
0
    if (initializer == nullptr) {
8246
        // error recovery; don't leave const without constant values
8247
0
        if (qualifier == EvqConst)
8248
0
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8249
0
        return nullptr;
8250
0
    }
8251
8252
    // Fix outer arrayness if variable is unsized, getting size from the initializer
8253
0
    if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray())
8254
0
        variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
8255
8256
    // Inner arrayness can also get set by an initializer
8257
0
    if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
8258
0
        initializer->getType().getArraySizes()->getNumDims() ==
8259
0
        variable->getType().getArraySizes()->getNumDims()) {
8260
        // adopt unsized sizes from the initializer's sizes
8261
0
        for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
8262
0
            if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) {
8263
0
                variable->getWritableType().getArraySizes()->setDimSize(d,
8264
0
                    initializer->getType().getArraySizes()->getDimSize(d));
8265
0
            }
8266
0
        }
8267
0
    }
8268
8269
    // Uniform and global consts require a constant initializer
8270
0
    if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
8271
0
        error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
8272
0
        variable->getWritableType().getQualifier().storage = EvqTemporary;
8273
0
        return nullptr;
8274
0
    }
8275
8276
    // Const variables require a constant initializer
8277
0
    if (qualifier == EvqConst) {
8278
0
        if (initializer->getType().getQualifier().storage != EvqConst) {
8279
0
            variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
8280
0
            qualifier = EvqConstReadOnly;
8281
0
        }
8282
0
    }
8283
8284
0
    if (qualifier == EvqConst || qualifier == EvqUniform) {
8285
        // Compile-time tagging of the variable with its constant value...
8286
8287
0
        initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
8288
0
        if (initializer != nullptr && variable->getType() != initializer->getType())
8289
0
            initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer);
8290
0
        if (initializer == nullptr || !initializer->getAsConstantUnion() ||
8291
0
                                      variable->getType() != initializer->getType()) {
8292
0
            error(loc, "non-matching or non-convertible constant type for const initializer",
8293
0
                variable->getType().getStorageQualifierString(), "");
8294
0
            variable->getWritableType().getQualifier().storage = EvqTemporary;
8295
0
            return nullptr;
8296
0
        }
8297
8298
0
        variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
8299
0
    } else {
8300
        // normal assigning of a value to a variable...
8301
0
        specializationCheck(loc, initializer->getType(), "initializer");
8302
0
        TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
8303
0
        TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
8304
0
        if (initNode == nullptr)
8305
0
            assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
8306
0
        return initNode;
8307
0
    }
8308
8309
0
    return nullptr;
8310
0
}
8311
8312
//
8313
// Reprocess any initializer-list { ... } parts of the initializer.
8314
// Need to hierarchically assign correct types and implicit
8315
// conversions. Will do this mimicking the same process used for
8316
// creating a constructor-style initializer, ensuring we get the
8317
// same form.
8318
//
8319
// Returns a node representing an expression for the initializer list expressed
8320
// as the correct type.
8321
//
8322
// Returns nullptr if there is an error.
8323
//
8324
TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
8325
                                                       TIntermTyped* initializer, TIntermTyped* scalarInit)
8326
0
{
8327
    // Will operate recursively.  Once a subtree is found that is constructor style,
8328
    // everything below it is already good: Only the "top part" of the initializer
8329
    // can be an initializer list, where "top part" can extend for several (or all) levels.
8330
8331
    // see if we have bottomed out in the tree within the initializer-list part
8332
0
    TIntermAggregate* initList = initializer->getAsAggregate();
8333
0
    if (initList == nullptr || initList->getOp() != EOpNull) {
8334
        // We don't have a list, but if it's a scalar and the 'type' is a
8335
        // composite, we need to lengthen below to make it useful.
8336
        // Otherwise, this is an already formed object to initialize with.
8337
0
        if (type.isScalar() || !initializer->getType().isScalar())
8338
0
            return initializer;
8339
0
        else
8340
0
            initList = intermediate.makeAggregate(initializer);
8341
0
    }
8342
8343
    // Of the initializer-list set of nodes, need to process bottom up,
8344
    // so recurse deep, then process on the way up.
8345
8346
    // Go down the tree here...
8347
0
    if (type.isArray()) {
8348
        // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
8349
        // Later on, initializer execution code will deal with array size logic.
8350
0
        TType arrayType;
8351
0
        arrayType.shallowCopy(type);                     // sharing struct stuff is fine
8352
0
        arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
8353
8354
        // edit array sizes to fill in unsized dimensions
8355
0
        if (type.isUnsizedArray())
8356
0
            arrayType.changeOuterArraySize((int)initList->getSequence().size());
8357
8358
        // set unsized array dimensions that can be derived from the initializer's first element
8359
0
        if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
8360
0
            TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
8361
0
            if (firstInit->getType().isArray() &&
8362
0
                arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
8363
0
                for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) {
8364
0
                    if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize)
8365
0
                        arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
8366
0
                }
8367
0
            }
8368
0
        }
8369
8370
        // lengthen list to be long enough
8371
0
        lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
8372
8373
        // recursively process each element
8374
0
        TType elementType(arrayType, 0); // dereferenced type
8375
0
        for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
8376
0
            initList->getSequence()[i] = convertInitializerList(loc, elementType,
8377
0
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8378
0
            if (initList->getSequence()[i] == nullptr)
8379
0
                return nullptr;
8380
0
        }
8381
8382
0
        return addConstructor(loc, initList, arrayType);
8383
0
    } else if (type.isStruct()) {
8384
        // do we have implicit assignments to opaques?
8385
0
        for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) {
8386
0
            if ((*type.getStruct())[i].type->containsOpaque()) {
8387
0
                error(loc, "cannot implicitly initialize opaque members", "initializer list", "");
8388
0
                return nullptr;
8389
0
            }
8390
0
        }
8391
8392
        // lengthen list to be long enough
8393
0
        lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
8394
8395
0
        if (type.getStruct()->size() != initList->getSequence().size()) {
8396
0
            error(loc, "wrong number of structure members", "initializer list", "");
8397
0
            return nullptr;
8398
0
        }
8399
0
        for (size_t i = 0; i < type.getStruct()->size(); ++i) {
8400
0
            initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type,
8401
0
                                                                initList->getSequence()[i]->getAsTyped(), scalarInit);
8402
0
            if (initList->getSequence()[i] == nullptr)
8403
0
                return nullptr;
8404
0
        }
8405
0
    } else if (type.isMatrix()) {
8406
0
        if (type.computeNumComponents() == (int)initList->getSequence().size()) {
8407
            // This means the matrix is initialized component-wise, rather than as
8408
            // a series of rows and columns.  We can just use the list directly as
8409
            // a constructor; no further processing needed.
8410
0
        } else {
8411
            // lengthen list to be long enough
8412
0
            lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
8413
8414
0
            if (type.getMatrixCols() != (int)initList->getSequence().size()) {
8415
0
                error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
8416
0
                return nullptr;
8417
0
            }
8418
0
            TType vectorType(type, 0); // dereferenced type
8419
0
            for (int i = 0; i < type.getMatrixCols(); ++i) {
8420
0
                initList->getSequence()[i] = convertInitializerList(loc, vectorType,
8421
0
                                                                    initList->getSequence()[i]->getAsTyped(), scalarInit);
8422
0
                if (initList->getSequence()[i] == nullptr)
8423
0
                    return nullptr;
8424
0
            }
8425
0
        }
8426
0
    } else if (type.isVector()) {
8427
        // lengthen list to be long enough
8428
0
        lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
8429
8430
        // error check; we're at bottom, so work is finished below
8431
0
        if (type.getVectorSize() != (int)initList->getSequence().size()) {
8432
0
            error(loc, "wrong vector size (or rows in a matrix column):", "initializer list",
8433
0
                  type.getCompleteString().c_str());
8434
0
            return nullptr;
8435
0
        }
8436
0
    } else if (type.isScalar()) {
8437
        // lengthen list to be long enough
8438
0
        lengthenList(loc, initList->getSequence(), 1, scalarInit);
8439
8440
0
        if ((int)initList->getSequence().size() != 1) {
8441
0
            error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
8442
0
            return nullptr;
8443
0
        }
8444
0
    } else {
8445
0
        error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
8446
0
        return nullptr;
8447
0
    }
8448
8449
    // Now that the subtree is processed, process this node as if the
8450
    // initializer list is a set of arguments to a constructor.
8451
0
    TIntermTyped* emulatedConstructorArguments;
8452
0
    if (initList->getSequence().size() == 1)
8453
0
        emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
8454
0
    else
8455
0
        emulatedConstructorArguments = initList;
8456
8457
0
    return addConstructor(loc, emulatedConstructorArguments, type);
8458
0
}
8459
8460
// Lengthen list to be long enough to cover any gap from the current list size
8461
// to 'size'. If the list is longer, do nothing.
8462
// The value to lengthen with is the default for short lists.
8463
//
8464
// By default, lists that are too short due to lack of initializers initialize to zero.
8465
// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
8466
// based on whether something is passed in as 'scalarInit'.
8467
//
8468
// 'scalarInit' must be safe to use each time this is called (no side effects replication).
8469
//
8470
void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
8471
0
{
8472
0
    for (int c = (int)list.size(); c < size; ++c) {
8473
0
        if (scalarInit == nullptr)
8474
0
            list.push_back(intermediate.addConstantUnion(0, loc));
8475
0
        else
8476
0
            list.push_back(scalarInit);
8477
0
    }
8478
0
}
8479
8480
//
8481
// Test for the correctness of the parameters passed to various constructor functions
8482
// and also convert them to the right data type, if allowed and required.
8483
//
8484
// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
8485
//
8486
TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8487
0
{
8488
0
    if (node == nullptr)
8489
0
        return nullptr;
8490
8491
    // Construct identical type
8492
0
    if (type == node->getType())
8493
0
        return node;
8494
8495
    // Handle the idiom "(struct type)<scalar value>"
8496
0
    if (type.isStruct() && isScalarConstructor(node)) {
8497
        // 'node' will almost always get used multiple times, so should not be used directly,
8498
        // it would create a DAG instead of a tree, which might be okay (would
8499
        // like to formalize that for constants and symbols), but if it has
8500
        // side effects, they would get executed multiple times, which is not okay.
8501
0
        if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
8502
0
            TIntermAggregate* seq = intermediate.makeAggregate(loc);
8503
0
            TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
8504
0
            seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
8505
0
            seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
8506
0
            seq->setOp(EOpComma);
8507
0
            seq->setType(type);
8508
0
            return seq;
8509
0
        } else
8510
0
            return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
8511
0
    }
8512
8513
0
    return addConstructor(loc, node, type);
8514
0
}
8515
8516
// Add a constructor, either from the grammar, or other programmatic reasons.
8517
//
8518
// 'node' is what to construct from.
8519
// 'type' is what type to construct.
8520
//
8521
// Returns the constructed object.
8522
// Return nullptr if it can't be done.
8523
//
8524
TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8525
0
{
8526
0
    TIntermAggregate* aggrNode = node->getAsAggregate();
8527
0
    TOperator op = intermediate.mapTypeToConstructorOp(type);
8528
8529
0
    if (op == EOpConstructTextureSampler)
8530
0
        return intermediate.setAggregateOperator(aggrNode, op, type, loc);
8531
8532
0
    TTypeList::const_iterator memberTypes;
8533
0
    if (op == EOpConstructStruct)
8534
0
        memberTypes = type.getStruct()->begin();
8535
8536
0
    TType elementType;
8537
0
    if (type.isArray()) {
8538
0
        TType dereferenced(type, 0);
8539
0
        elementType.shallowCopy(dereferenced);
8540
0
    } else
8541
0
        elementType.shallowCopy(type);
8542
8543
0
    bool singleArg;
8544
0
    if (aggrNode != nullptr) {
8545
0
        if (aggrNode->getOp() != EOpNull)
8546
0
            singleArg = true;
8547
0
        else
8548
0
            singleArg = false;
8549
0
    } else
8550
0
        singleArg = true;
8551
8552
0
    TIntermTyped *newNode;
8553
0
    if (singleArg) {
8554
        // Handle array -> array conversion
8555
        // Constructing an array of one type from an array of another type is allowed,
8556
        // assuming there are enough components available (semantic-checked earlier).
8557
0
        if (type.isArray() && node->isArray())
8558
0
            newNode = convertArray(node, type);
8559
8560
        // If structure constructor or array constructor is being called
8561
        // for only one parameter inside the aggregate, we need to call constructAggregate function once.
8562
0
        else if (type.isArray())
8563
0
            newNode = constructAggregate(node, elementType, 1, node->getLoc());
8564
0
        else if (op == EOpConstructStruct)
8565
0
            newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
8566
0
        else {
8567
            // shape conversion for matrix constructor from scalar.  HLSL semantics are: scalar
8568
            // is replicated into every element of the matrix (not just the diagnonal), so
8569
            // that is handled specially here.
8570
0
            if (type.isMatrix() && node->getType().isScalarOrVec1())
8571
0
                node = intermediate.addShapeConversion(type, node);
8572
8573
0
            newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
8574
0
        }
8575
8576
0
        if (newNode && (type.isArray() || op == EOpConstructStruct))
8577
0
            newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
8578
8579
0
        return newNode;
8580
0
    }
8581
8582
    //
8583
    // Handle list of arguments.
8584
    //
8585
0
    TIntermSequence& sequenceVector = aggrNode->getSequence();    // Stores the information about the parameter to the constructor
8586
    // if the structure constructor contains more than one parameter, then construct
8587
    // each parameter
8588
8589
0
    int paramCount = 0;  // keeps a track of the constructor parameter number being checked
8590
8591
    // for each parameter to the constructor call, check to see if the right type is passed or convert them
8592
    // to the right type if possible (and allowed).
8593
    // for structure constructors, just check if the right type is passed, no conversion is allowed.
8594
8595
0
    for (TIntermSequence::iterator p = sequenceVector.begin();
8596
0
        p != sequenceVector.end(); p++, paramCount++) {
8597
0
        if (type.isArray())
8598
0
            newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
8599
0
        else if (op == EOpConstructStruct)
8600
0
            newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
8601
0
        else
8602
0
            newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
8603
8604
0
        if (newNode)
8605
0
            *p = newNode;
8606
0
        else
8607
0
            return nullptr;
8608
0
    }
8609
8610
0
    TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
8611
8612
0
    return constructor;
8613
0
}
8614
8615
// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
8616
// for the parameter to the constructor (passed to this function). Essentially, it converts
8617
// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
8618
// float, then float is converted to int.
8619
//
8620
// Returns nullptr for an error or the constructed node.
8621
//
8622
TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node,
8623
                                                 const TSourceLoc& loc, bool subset)
8624
0
{
8625
0
    TIntermTyped* newNode;
8626
0
    TOperator basicOp;
8627
8628
    //
8629
    // First, convert types as needed.
8630
    //
8631
0
    switch (op) {
8632
0
    case EOpConstructF16Vec2:
8633
0
    case EOpConstructF16Vec3:
8634
0
    case EOpConstructF16Vec4:
8635
0
    case EOpConstructF16Mat2x2:
8636
0
    case EOpConstructF16Mat2x3:
8637
0
    case EOpConstructF16Mat2x4:
8638
0
    case EOpConstructF16Mat3x2:
8639
0
    case EOpConstructF16Mat3x3:
8640
0
    case EOpConstructF16Mat3x4:
8641
0
    case EOpConstructF16Mat4x2:
8642
0
    case EOpConstructF16Mat4x3:
8643
0
    case EOpConstructF16Mat4x4:
8644
0
    case EOpConstructFloat16:
8645
0
        basicOp = EOpConstructFloat16;
8646
0
        break;
8647
8648
0
    case EOpConstructVec2:
8649
0
    case EOpConstructVec3:
8650
0
    case EOpConstructVec4:
8651
0
    case EOpConstructMat2x2:
8652
0
    case EOpConstructMat2x3:
8653
0
    case EOpConstructMat2x4:
8654
0
    case EOpConstructMat3x2:
8655
0
    case EOpConstructMat3x3:
8656
0
    case EOpConstructMat3x4:
8657
0
    case EOpConstructMat4x2:
8658
0
    case EOpConstructMat4x3:
8659
0
    case EOpConstructMat4x4:
8660
0
    case EOpConstructFloat:
8661
0
        basicOp = EOpConstructFloat;
8662
0
        break;
8663
8664
0
    case EOpConstructDVec2:
8665
0
    case EOpConstructDVec3:
8666
0
    case EOpConstructDVec4:
8667
0
    case EOpConstructDMat2x2:
8668
0
    case EOpConstructDMat2x3:
8669
0
    case EOpConstructDMat2x4:
8670
0
    case EOpConstructDMat3x2:
8671
0
    case EOpConstructDMat3x3:
8672
0
    case EOpConstructDMat3x4:
8673
0
    case EOpConstructDMat4x2:
8674
0
    case EOpConstructDMat4x3:
8675
0
    case EOpConstructDMat4x4:
8676
0
    case EOpConstructDouble:
8677
0
        basicOp = EOpConstructDouble;
8678
0
        break;
8679
8680
0
    case EOpConstructI16Vec2:
8681
0
    case EOpConstructI16Vec3:
8682
0
    case EOpConstructI16Vec4:
8683
0
    case EOpConstructInt16:
8684
0
        basicOp = EOpConstructInt16;
8685
0
        break;
8686
8687
0
    case EOpConstructIVec2:
8688
0
    case EOpConstructIVec3:
8689
0
    case EOpConstructIVec4:
8690
0
    case EOpConstructIMat2x2:
8691
0
    case EOpConstructIMat2x3:
8692
0
    case EOpConstructIMat2x4:
8693
0
    case EOpConstructIMat3x2:
8694
0
    case EOpConstructIMat3x3:
8695
0
    case EOpConstructIMat3x4:
8696
0
    case EOpConstructIMat4x2:
8697
0
    case EOpConstructIMat4x3:
8698
0
    case EOpConstructIMat4x4:
8699
0
    case EOpConstructInt:
8700
0
        basicOp = EOpConstructInt;
8701
0
        break;
8702
8703
0
    case EOpConstructU16Vec2:
8704
0
    case EOpConstructU16Vec3:
8705
0
    case EOpConstructU16Vec4:
8706
0
    case EOpConstructUint16:
8707
0
        basicOp = EOpConstructUint16;
8708
0
        break;
8709
8710
0
    case EOpConstructUVec2:
8711
0
    case EOpConstructUVec3:
8712
0
    case EOpConstructUVec4:
8713
0
    case EOpConstructUMat2x2:
8714
0
    case EOpConstructUMat2x3:
8715
0
    case EOpConstructUMat2x4:
8716
0
    case EOpConstructUMat3x2:
8717
0
    case EOpConstructUMat3x3:
8718
0
    case EOpConstructUMat3x4:
8719
0
    case EOpConstructUMat4x2:
8720
0
    case EOpConstructUMat4x3:
8721
0
    case EOpConstructUMat4x4:
8722
0
    case EOpConstructUint:
8723
0
        basicOp = EOpConstructUint;
8724
0
        break;
8725
8726
0
    case EOpConstructBVec2:
8727
0
    case EOpConstructBVec3:
8728
0
    case EOpConstructBVec4:
8729
0
    case EOpConstructBMat2x2:
8730
0
    case EOpConstructBMat2x3:
8731
0
    case EOpConstructBMat2x4:
8732
0
    case EOpConstructBMat3x2:
8733
0
    case EOpConstructBMat3x3:
8734
0
    case EOpConstructBMat3x4:
8735
0
    case EOpConstructBMat4x2:
8736
0
    case EOpConstructBMat4x3:
8737
0
    case EOpConstructBMat4x4:
8738
0
    case EOpConstructBool:
8739
0
        basicOp = EOpConstructBool;
8740
0
        break;
8741
8742
0
    default:
8743
0
        error(loc, "unsupported construction", "", "");
8744
8745
0
        return nullptr;
8746
0
    }
8747
0
    newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
8748
0
    if (newNode == nullptr) {
8749
0
        error(loc, "can't convert", "constructor", "");
8750
0
        return nullptr;
8751
0
    }
8752
8753
    //
8754
    // Now, if there still isn't an operation to do the construction, and we need one, add one.
8755
    //
8756
8757
    // Otherwise, skip out early.
8758
0
    if (subset || (newNode != node && newNode->getType() == type))
8759
0
        return newNode;
8760
8761
    // setAggregateOperator will insert a new node for the constructor, as needed.
8762
0
    return intermediate.setAggregateOperator(newNode, op, type, loc);
8763
0
}
8764
8765
// Convert the array in node to the requested type, which is also an array.
8766
// Returns nullptr on failure, otherwise returns aggregate holding the list of
8767
// elements needed to construct the array.
8768
TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
8769
0
{
8770
0
    assert(node->isArray() && type.isArray());
8771
0
    if (node->getType().computeNumComponents() < type.computeNumComponents())
8772
0
        return nullptr;
8773
8774
    // TODO: write an argument replicator, for the case the argument should not be
8775
    // executed multiple times, yet multiple copies are needed.
8776
8777
0
    TIntermTyped* constructee = node->getAsTyped();
8778
    // track where we are in consuming the argument
8779
0
    int constructeeElement = 0;
8780
0
    int constructeeComponent = 0;
8781
8782
    // bump up to the next component to consume
8783
0
    const auto getNextComponent = [&]() {
8784
0
        TIntermTyped* component;
8785
0
        component = handleBracketDereference(node->getLoc(), constructee,
8786
0
                                             intermediate.addConstantUnion(constructeeElement, node->getLoc()));
8787
0
        if (component->isVector())
8788
0
            component = handleBracketDereference(node->getLoc(), component,
8789
0
                                                 intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
8790
        // bump component pointer up
8791
0
        ++constructeeComponent;
8792
0
        if (constructeeComponent == constructee->getVectorSize()) {
8793
0
            constructeeComponent = 0;
8794
0
            ++constructeeElement;
8795
0
        }
8796
0
        return component;
8797
0
    };
8798
8799
    // make one subnode per constructed array element
8800
0
    TIntermAggregate* constructor = nullptr;
8801
0
    TType derefType(type, 0);
8802
0
    TType speculativeComponentType(derefType, 0);
8803
0
    TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
8804
0
    TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
8805
0
    TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
8806
0
    for (int e = 0; e < type.getOuterArraySize(); ++e) {
8807
        // construct an element
8808
0
        TIntermTyped* elementArg;
8809
0
        if (type.getVectorSize() == constructee->getVectorSize()) {
8810
            // same element shape
8811
0
            elementArg = handleBracketDereference(node->getLoc(), constructee,
8812
0
                                                  intermediate.addConstantUnion(e, node->getLoc()));
8813
0
        } else {
8814
            // mismatched element shapes
8815
0
            if (type.getVectorSize() == 1)
8816
0
                elementArg = getNextComponent();
8817
0
            else {
8818
                // make a vector
8819
0
                TIntermAggregate* elementConstructee = nullptr;
8820
0
                for (int c = 0; c < type.getVectorSize(); ++c)
8821
0
                    elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
8822
0
                elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
8823
0
            }
8824
0
        }
8825
        // convert basic types
8826
0
        elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
8827
0
        if (elementArg == nullptr)
8828
0
            return nullptr;
8829
        // combine with top-level constructor
8830
0
        constructor = intermediate.growAggregate(constructor, elementArg);
8831
0
    }
8832
8833
0
    return constructor;
8834
0
}
8835
8836
// This function tests for the type of the parameters to the structure or array constructor. Raises
8837
// an error message if the expected type does not match the parameter passed to the constructor.
8838
//
8839
// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
8840
//
8841
TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
8842
                                                   const TSourceLoc& loc)
8843
0
{
8844
    // Handle cases that map more 1:1 between constructor arguments and constructed.
8845
0
    TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
8846
0
    if (converted == nullptr || converted->getType() != type) {
8847
0
        error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
8848
0
            node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
8849
8850
0
        return nullptr;
8851
0
    }
8852
8853
0
    return converted;
8854
0
}
8855
8856
//
8857
// Do everything needed to add an interface block.
8858
//
8859
void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName)
8860
0
{
8861
0
    assert(type.getWritableStruct() != nullptr);
8862
8863
    // Clean up top-level decorations that don't belong.
8864
0
    switch (type.getQualifier().storage) {
8865
0
    case EvqUniform:
8866
0
    case EvqBuffer:
8867
0
        correctUniform(type.getQualifier());
8868
0
        break;
8869
0
    case EvqVaryingIn:
8870
0
        correctInput(type.getQualifier());
8871
0
        break;
8872
0
    case EvqVaryingOut:
8873
0
        correctOutput(type.getQualifier());
8874
0
        break;
8875
0
    default:
8876
0
        break;
8877
0
    }
8878
8879
0
    TTypeList& typeList = *type.getWritableStruct();
8880
    // fix and check for member storage qualifiers and types that don't belong within a block
8881
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8882
0
        TType& memberType = *typeList[member].type;
8883
0
        TQualifier& memberQualifier = memberType.getQualifier();
8884
0
        const TSourceLoc& memberLoc = typeList[member].loc;
8885
0
        globalQualifierFix(memberLoc, memberQualifier);
8886
0
        memberQualifier.storage = type.getQualifier().storage;
8887
8888
0
        if (memberType.isStruct()) {
8889
            // clean up and pick up the right set of decorations
8890
0
            auto it = ioTypeMap.find(memberType.getStruct());
8891
0
            switch (type.getQualifier().storage) {
8892
0
            case EvqUniform:
8893
0
            case EvqBuffer:
8894
0
                correctUniform(type.getQualifier());
8895
0
                if (it != ioTypeMap.end() && it->second.uniform)
8896
0
                    memberType.setStruct(it->second.uniform);
8897
0
                break;
8898
0
            case EvqVaryingIn:
8899
0
                correctInput(type.getQualifier());
8900
0
                if (it != ioTypeMap.end() && it->second.input)
8901
0
                    memberType.setStruct(it->second.input);
8902
0
                break;
8903
0
            case EvqVaryingOut:
8904
0
                correctOutput(type.getQualifier());
8905
0
                if (it != ioTypeMap.end() && it->second.output)
8906
0
                    memberType.setStruct(it->second.output);
8907
0
                break;
8908
0
            default:
8909
0
                break;
8910
0
            }
8911
0
        }
8912
0
    }
8913
8914
    // Make default block qualification, and adjust the member qualifications
8915
8916
0
    TQualifier defaultQualification;
8917
0
    switch (type.getQualifier().storage) {
8918
0
    case EvqUniform:    defaultQualification = globalUniformDefaults;    break;
8919
0
    case EvqBuffer:     defaultQualification = globalBufferDefaults;     break;
8920
0
    case EvqVaryingIn:  defaultQualification = globalInputDefaults;      break;
8921
0
    case EvqVaryingOut: defaultQualification = globalOutputDefaults;     break;
8922
0
    default:            defaultQualification.clear();                    break;
8923
0
    }
8924
8925
    // Special case for "push_constant uniform", which has a default of std430,
8926
    // contrary to normal uniform defaults, and can't have a default tracked for it.
8927
0
    if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
8928
0
        type.getQualifier().layoutPacking = ElpStd430;
8929
8930
    // fix and check for member layout qualifiers
8931
8932
0
    mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
8933
8934
0
    bool memberWithLocation = false;
8935
0
    bool memberWithoutLocation = false;
8936
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
8937
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
8938
0
        const TSourceLoc& memberLoc = typeList[member].loc;
8939
0
        if (memberQualifier.hasStream()) {
8940
0
            if (defaultQualification.layoutStream != memberQualifier.layoutStream)
8941
0
                error(memberLoc, "member cannot contradict block", "stream", "");
8942
0
        }
8943
8944
        // "This includes a block's inheritance of the
8945
        // current global default buffer, a block member's inheritance of the block's
8946
        // buffer, and the requirement that any *xfb_buffer* declared on a block
8947
        // member must match the buffer inherited from the block."
8948
0
        if (memberQualifier.hasXfbBuffer()) {
8949
0
            if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
8950
0
                error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
8951
0
        }
8952
8953
0
        if (memberQualifier.hasLocation()) {
8954
0
            switch (type.getQualifier().storage) {
8955
0
            case EvqVaryingIn:
8956
0
            case EvqVaryingOut:
8957
0
                memberWithLocation = true;
8958
0
                break;
8959
0
            default:
8960
0
                break;
8961
0
            }
8962
0
        } else
8963
0
            memberWithoutLocation = true;
8964
8965
0
        TQualifier newMemberQualification = defaultQualification;
8966
0
        mergeQualifiers(newMemberQualification, memberQualifier);
8967
0
        memberQualifier = newMemberQualification;
8968
0
    }
8969
8970
    // Process the members
8971
0
    fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
8972
0
    fixXfbOffsets(type.getQualifier(), typeList);
8973
0
    fixBlockUniformOffsets(type.getQualifier(), typeList);
8974
8975
    // reverse merge, so that currentBlockQualifier now has all layout information
8976
    // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
8977
0
    mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
8978
8979
    //
8980
    // Build and add the interface block as a new type named 'blockName'
8981
    //
8982
8983
    // Use the instance name as the interface name if one exists, else the block name.
8984
0
    const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
8985
8986
0
    TType blockType(&typeList, interfaceName, type.getQualifier());
8987
0
    if (type.isArray())
8988
0
        blockType.transferArraySizes(type.getArraySizes());
8989
8990
    // Add the variable, as anonymous or named instanceName.
8991
    // Make an anonymous variable if no name was provided.
8992
0
    if (instanceName == nullptr)
8993
0
        instanceName = NewPoolTString("");
8994
8995
0
    TVariable& variable = *new TVariable(instanceName, blockType);
8996
0
    if (! symbolTable.insert(variable)) {
8997
0
        if (*instanceName == "")
8998
0
            error(loc, "nameless block contains a member that already has a name at global scope",
8999
0
                  "" /* blockName->c_str() */, "");
9000
0
        else
9001
0
            error(loc, "block instance name redefinition", variable.getName().c_str(), "");
9002
9003
0
        return;
9004
0
    }
9005
9006
    // Save it in the AST for linker use.
9007
0
    if (symbolTable.atGlobalLevel())
9008
0
        trackLinkage(variable);
9009
0
}
9010
9011
//
9012
// "For a block, this process applies to the entire block, or until the first member
9013
// is reached that has a location layout qualifier. When a block member is declared with a location
9014
// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
9015
// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
9016
// until the next member declared with a location qualifier. The values used for locations do not have to be
9017
// declared in increasing order."
9018
void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
9019
0
{
9020
    // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
9021
    // have a location layout qualifier, or a compile-time error results."
9022
0
    if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
9023
0
        error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
9024
0
    else {
9025
0
        if (memberWithLocation) {
9026
            // remove any block-level location and make it per *every* member
9027
0
            int nextLocation = 0;  // by the rule above, initial value is not relevant
9028
0
            if (qualifier.hasAnyLocation()) {
9029
0
                nextLocation = qualifier.layoutLocation;
9030
0
                qualifier.layoutLocation = TQualifier::layoutLocationEnd;
9031
0
                if (qualifier.hasComponent()) {
9032
                    // "It is a compile-time error to apply the *component* qualifier to a ... block"
9033
0
                    error(loc, "cannot apply to a block", "component", "");
9034
0
                }
9035
0
                if (qualifier.hasIndex()) {
9036
0
                    error(loc, "cannot apply to a block", "index", "");
9037
0
                }
9038
0
            }
9039
0
            for (unsigned int member = 0; member < typeList.size(); ++member) {
9040
0
                TQualifier& memberQualifier = typeList[member].type->getQualifier();
9041
0
                const TSourceLoc& memberLoc = typeList[member].loc;
9042
0
                if (! memberQualifier.hasLocation()) {
9043
0
                    if (nextLocation >= (int)TQualifier::layoutLocationEnd)
9044
0
                        error(memberLoc, "location is too large", "location", "");
9045
0
                    memberQualifier.layoutLocation = nextLocation;
9046
0
                    memberQualifier.layoutComponent = 0;
9047
0
                }
9048
0
                nextLocation = memberQualifier.layoutLocation +
9049
0
                               intermediate.computeTypeLocationSize(*typeList[member].type, language);
9050
0
            }
9051
0
        }
9052
0
    }
9053
0
}
9054
9055
void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
9056
0
{
9057
    // "If a block is qualified with xfb_offset, all its
9058
    // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
9059
    // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
9060
    // offsets."
9061
9062
0
    if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
9063
0
        return;
9064
9065
0
    int nextOffset = qualifier.layoutXfbOffset;
9066
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
9067
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
9068
0
        bool contains64BitType = false;
9069
0
        bool contains32BitType = false;
9070
0
        bool contains16BitType = false;
9071
0
        int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType);
9072
        // see if we need to auto-assign an offset to this member
9073
0
        if (! memberQualifier.hasXfbOffset()) {
9074
            // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8"
9075
0
            if (contains64BitType)
9076
0
                RoundToPow2(nextOffset, 8);
9077
0
            else if (contains32BitType)
9078
0
                RoundToPow2(nextOffset, 4);
9079
            // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2"
9080
0
            else if (contains16BitType)
9081
0
                RoundToPow2(nextOffset, 2);
9082
0
            memberQualifier.layoutXfbOffset = nextOffset;
9083
0
        } else
9084
0
            nextOffset = memberQualifier.layoutXfbOffset;
9085
0
        nextOffset += memberSize;
9086
0
    }
9087
9088
    // The above gave all block members an offset, so we can take it off the block now,
9089
    // which will avoid double counting the offset usage.
9090
0
    qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
9091
0
}
9092
9093
// Calculate and save the offset of each block member, using the recursively
9094
// defined block offset rules and the user-provided offset and align.
9095
//
9096
// Also, compute and save the total size of the block. For the block's size, arrayness
9097
// is not taken into account, as each element is backed by a separate buffer.
9098
//
9099
void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
9100
0
{
9101
0
    if (! qualifier.isUniformOrBuffer())
9102
0
        return;
9103
0
    if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
9104
0
        return;
9105
9106
0
    int offset = 0;
9107
0
    int memberSize;
9108
0
    for (unsigned int member = 0; member < typeList.size(); ++member) {
9109
0
        TQualifier& memberQualifier = typeList[member].type->getQualifier();
9110
0
        const TSourceLoc& memberLoc = typeList[member].loc;
9111
9112
        // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
9113
9114
        // modify just the children's view of matrix layout, if there is one for this member
9115
0
        TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
9116
0
        int dummyStride;
9117
0
        int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride,
9118
0
                                                              qualifier.layoutPacking,
9119
0
                                                              subMatrixLayout != ElmNone
9120
0
                                                                  ? subMatrixLayout == ElmRowMajor
9121
0
                                                                  : qualifier.layoutMatrix == ElmRowMajor);
9122
0
        if (memberQualifier.hasOffset()) {
9123
            // "The specified offset must be a multiple
9124
            // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
9125
0
            if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
9126
0
                error(memberLoc, "must be a multiple of the member's alignment", "offset",
9127
0
                    "(layout offset = %d | member alignment = %d)", memberQualifier.layoutOffset, memberAlignment);
9128
9129
            // "The offset qualifier forces the qualified member to start at or after the specified
9130
            // integral-constant expression, which will be its byte offset from the beginning of the buffer.
9131
            // "The actual offset of a member is computed as
9132
            // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
9133
0
            offset = std::max(offset, memberQualifier.layoutOffset);
9134
0
        }
9135
9136
        // "The actual alignment of a member will be the greater of the specified align alignment and the standard
9137
        // (e.g., std140) base alignment for the member's type."
9138
0
        if (memberQualifier.hasAlign())
9139
0
            memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
9140
9141
        // "If the resulting offset is not a multiple of the actual alignment,
9142
        // increase it to the first offset that is a multiple of
9143
        // the actual alignment."
9144
0
        RoundToPow2(offset, memberAlignment);
9145
0
        typeList[member].type->getQualifier().layoutOffset = offset;
9146
0
        offset += memberSize;
9147
0
    }
9148
0
}
9149
9150
// For an identifier that is already declared, add more qualification to it.
9151
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
9152
0
{
9153
0
    TSymbol* symbol = symbolTable.find(identifier);
9154
0
    if (symbol == nullptr) {
9155
0
        error(loc, "identifier not previously declared", identifier.c_str(), "");
9156
0
        return;
9157
0
    }
9158
0
    if (symbol->getAsFunction()) {
9159
0
        error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
9160
0
        return;
9161
0
    }
9162
9163
0
    if (qualifier.isAuxiliary() ||
9164
0
        qualifier.isMemory() ||
9165
0
        qualifier.isInterpolation() ||
9166
0
        qualifier.hasLayout() ||
9167
0
        qualifier.storage != EvqTemporary ||
9168
0
        qualifier.precision != EpqNone) {
9169
0
        error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
9170
0
        return;
9171
0
    }
9172
9173
    // For read-only built-ins, add a new symbol for holding the modified qualifier.
9174
    // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
9175
0
    if (symbol->isReadOnly())
9176
0
        symbol = symbolTable.copyUp(symbol);
9177
9178
0
    if (qualifier.invariant) {
9179
0
        if (intermediate.inIoAccessed(identifier))
9180
0
            error(loc, "cannot change qualification after use", "invariant", "");
9181
0
        symbol->getWritableType().getQualifier().invariant = true;
9182
0
    } else if (qualifier.noContraction) {
9183
0
        if (intermediate.inIoAccessed(identifier))
9184
0
            error(loc, "cannot change qualification after use", "precise", "");
9185
0
        symbol->getWritableType().getQualifier().noContraction = true;
9186
0
    } else if (qualifier.specConstant) {
9187
0
        symbol->getWritableType().getQualifier().makeSpecConstant();
9188
0
        if (qualifier.hasSpecConstantId())
9189
0
            symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
9190
0
    } else
9191
0
        warn(loc, "unknown requalification", "", "");
9192
0
}
9193
9194
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
9195
0
{
9196
0
    for (unsigned int i = 0; i < identifiers.size(); ++i)
9197
0
        addQualifierToExisting(loc, qualifier, *identifiers[i]);
9198
0
}
9199
9200
//
9201
// Update the intermediate for the given input geometry
9202
//
9203
bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9204
0
{
9205
    // these can be declared on non-entry-points, in which case they lose their meaning
9206
0
    if (! parsingEntrypointParameters)
9207
0
        return true;
9208
9209
0
    switch (geometry) {
9210
0
    case ElgPoints:             // fall through
9211
0
    case ElgLines:              // ...
9212
0
    case ElgTriangles:          // ...
9213
0
    case ElgLinesAdjacency:     // ...
9214
0
    case ElgTrianglesAdjacency: // ...
9215
0
        if (! intermediate.setInputPrimitive(geometry)) {
9216
0
            error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9217
0
            return false;
9218
0
        }
9219
0
        break;
9220
9221
0
    default:
9222
0
        error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
9223
0
        return false;
9224
0
    }
9225
9226
0
    return true;
9227
0
}
9228
9229
//
9230
// Update the intermediate for the given output geometry
9231
//
9232
bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
9233
0
{
9234
    // If this is not a geometry shader, ignore.  It might be a mixed shader including several stages.
9235
    // Since that's an OK situation, return true for success.
9236
0
    if (language != EShLangGeometry)
9237
0
        return true;
9238
9239
    // these can be declared on non-entry-points, in which case they lose their meaning
9240
0
    if (! parsingEntrypointParameters)
9241
0
        return true;
9242
9243
0
    switch (geometry) {
9244
0
    case ElgPoints:
9245
0
    case ElgLineStrip:
9246
0
    case ElgTriangleStrip:
9247
0
        if (! intermediate.setOutputPrimitive(geometry)) {
9248
0
            error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
9249
0
            return false;
9250
0
        }
9251
0
        break;
9252
0
    default:
9253
0
        error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
9254
0
        return false;
9255
0
    }
9256
9257
0
    return true;
9258
0
}
9259
9260
//
9261
// Selection attributes
9262
//
9263
void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
9264
    const TAttributes& attributes)
9265
0
{
9266
0
    if (selection == nullptr)
9267
0
        return;
9268
9269
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9270
0
        switch (it->name) {
9271
0
        case EatFlatten:
9272
0
            selection->setFlatten();
9273
0
            break;
9274
0
        case EatBranch:
9275
0
            selection->setDontFlatten();
9276
0
            break;
9277
0
        default:
9278
0
            warn(loc, "attribute does not apply to a selection", "", "");
9279
0
            break;
9280
0
        }
9281
0
    }
9282
0
}
9283
9284
//
9285
// Switch attributes
9286
//
9287
void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
9288
    const TAttributes& attributes)
9289
0
{
9290
0
    if (selection == nullptr)
9291
0
        return;
9292
9293
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9294
0
        switch (it->name) {
9295
0
        case EatFlatten:
9296
0
            selection->setFlatten();
9297
0
            break;
9298
0
        case EatBranch:
9299
0
            selection->setDontFlatten();
9300
0
            break;
9301
0
        default:
9302
0
            warn(loc, "attribute does not apply to a switch", "", "");
9303
0
            break;
9304
0
        }
9305
0
    }
9306
0
}
9307
9308
//
9309
// Loop attributes
9310
//
9311
void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
9312
    const TAttributes& attributes)
9313
0
{
9314
0
    if (loop == nullptr)
9315
0
        return;
9316
9317
0
    for (auto it = attributes.begin(); it != attributes.end(); ++it) {
9318
0
        switch (it->name) {
9319
0
        case EatUnroll:
9320
0
            loop->setUnroll();
9321
0
            break;
9322
0
        case EatLoop:
9323
0
            loop->setDontUnroll();
9324
0
            break;
9325
0
        default:
9326
0
            warn(loc, "attribute does not apply to a loop", "", "");
9327
0
            break;
9328
0
        }
9329
0
    }
9330
0
}
9331
9332
//
9333
// Updating default qualifier for the case of a declaration with just a qualifier,
9334
// no type, block, or identifier.
9335
//
9336
void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
9337
0
{
9338
0
    if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
9339
0
        assert(language == EShLangTessControl || language == EShLangGeometry);
9340
        // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
9341
0
    }
9342
0
    if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
9343
0
        if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
9344
0
            error(loc, "cannot change previously set layout value", "invocations", "");
9345
0
    }
9346
0
    if (publicType.shaderQualifiers.geometry != ElgNone) {
9347
0
        if (publicType.qualifier.storage == EvqVaryingIn) {
9348
0
            switch (publicType.shaderQualifiers.geometry) {
9349
0
            case ElgPoints:
9350
0
            case ElgLines:
9351
0
            case ElgLinesAdjacency:
9352
0
            case ElgTriangles:
9353
0
            case ElgTrianglesAdjacency:
9354
0
            case ElgQuads:
9355
0
            case ElgIsolines:
9356
0
                break;
9357
0
            default:
9358
0
                error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9359
0
                      "");
9360
0
            }
9361
0
        } else if (publicType.qualifier.storage == EvqVaryingOut) {
9362
0
            handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
9363
0
        } else
9364
0
            error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
9365
0
                  GetStorageQualifierString(publicType.qualifier.storage));
9366
0
    }
9367
0
    if (publicType.shaderQualifiers.spacing != EvsNone)
9368
0
        intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
9369
0
    if (publicType.shaderQualifiers.order != EvoNone)
9370
0
        intermediate.setVertexOrder(publicType.shaderQualifiers.order);
9371
0
    if (publicType.shaderQualifiers.pointMode)
9372
0
        intermediate.setPointMode();
9373
0
    for (int i = 0; i < 3; ++i) {
9374
0
        if (publicType.shaderQualifiers.localSize[i] > 1) {
9375
0
            int max = 0;
9376
0
            switch (i) {
9377
0
            case 0: max = resources.maxComputeWorkGroupSizeX; break;
9378
0
            case 1: max = resources.maxComputeWorkGroupSizeY; break;
9379
0
            case 2: max = resources.maxComputeWorkGroupSizeZ; break;
9380
0
            default: break;
9381
0
            }
9382
0
            if (intermediate.getLocalSize(i) > (unsigned int)max)
9383
0
                error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
9384
9385
            // Fix the existing constant gl_WorkGroupSize with this new information.
9386
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9387
0
            workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
9388
0
        }
9389
0
        if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
9390
0
            intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
9391
            // Set the workgroup built-in variable as a specialization constant
9392
0
            TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
9393
0
            workGroupSize->getWritableType().getQualifier().specConstant = true;
9394
0
        }
9395
0
    }
9396
0
    if (publicType.shaderQualifiers.earlyFragmentTests)
9397
0
        intermediate.setEarlyFragmentTests();
9398
9399
0
    const TQualifier& qualifier = publicType.qualifier;
9400
9401
0
    switch (qualifier.storage) {
9402
0
    case EvqUniform:
9403
0
        if (qualifier.hasMatrix())
9404
0
            globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
9405
0
        if (qualifier.hasPacking())
9406
0
            globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
9407
0
        break;
9408
0
    case EvqBuffer:
9409
0
        if (qualifier.hasMatrix())
9410
0
            globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
9411
0
        if (qualifier.hasPacking())
9412
0
            globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
9413
0
        break;
9414
0
    case EvqVaryingIn:
9415
0
        break;
9416
0
    case EvqVaryingOut:
9417
0
        if (qualifier.hasStream())
9418
0
            globalOutputDefaults.layoutStream = qualifier.layoutStream;
9419
0
        if (qualifier.hasXfbBuffer())
9420
0
            globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
9421
0
        if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
9422
0
            if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
9423
0
                error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d",
9424
0
                      qualifier.layoutXfbBuffer);
9425
0
        }
9426
0
        break;
9427
0
    default:
9428
0
        error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
9429
0
        return;
9430
0
    }
9431
0
}
9432
9433
//
9434
// Take the sequence of statements that has been built up since the last case/default,
9435
// put it on the list of top-level nodes for the current (inner-most) switch statement,
9436
// and follow that by the case/default we are on now.  (See switch topology comment on
9437
// TIntermSwitch.)
9438
//
9439
void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
9440
0
{
9441
0
    TIntermSequence* switchSequence = switchSequenceStack.back();
9442
9443
0
    if (statements) {
9444
0
        statements->setOperator(EOpSequence);
9445
0
        switchSequence->push_back(statements);
9446
0
    }
9447
0
    if (branchNode) {
9448
        // check all previous cases for the same label (or both are 'default')
9449
0
        for (unsigned int s = 0; s < switchSequence->size(); ++s) {
9450
0
            TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
9451
0
            if (prevBranch) {
9452
0
                TIntermTyped* prevExpression = prevBranch->getExpression();
9453
0
                TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
9454
0
                if (prevExpression == nullptr && newExpression == nullptr)
9455
0
                    error(branchNode->getLoc(), "duplicate label", "default", "");
9456
0
                else if (prevExpression != nullptr &&
9457
0
                    newExpression != nullptr &&
9458
0
                    prevExpression->getAsConstantUnion() &&
9459
0
                    newExpression->getAsConstantUnion() &&
9460
0
                    prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
9461
0
                    newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
9462
0
                    error(branchNode->getLoc(), "duplicated value", "case", "");
9463
0
            }
9464
0
        }
9465
0
        switchSequence->push_back(branchNode);
9466
0
    }
9467
0
}
9468
9469
//
9470
// Turn the top-level node sequence built up of wrapupSwitchSubsequence
9471
// into a switch node.
9472
//
9473
TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
9474
                                         TIntermAggregate* lastStatements, const TAttributes& attributes)
9475
0
{
9476
0
    wrapupSwitchSubsequence(lastStatements, nullptr);
9477
9478
0
    if (expression == nullptr ||
9479
0
        (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
9480
0
        expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
9481
0
        error(loc, "condition must be a scalar integer expression", "switch", "");
9482
9483
    // If there is nothing to do, drop the switch but still execute the expression
9484
0
    TIntermSequence* switchSequence = switchSequenceStack.back();
9485
0
    if (switchSequence->size() == 0)
9486
0
        return expression;
9487
9488
0
    if (lastStatements == nullptr) {
9489
        // emulate a break for error recovery
9490
0
        lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
9491
0
        lastStatements->setOperator(EOpSequence);
9492
0
        switchSequence->push_back(lastStatements);
9493
0
    }
9494
9495
0
    TIntermAggregate* body = new TIntermAggregate(EOpSequence);
9496
0
    body->getSequence() = *switchSequenceStack.back();
9497
0
    body->setLoc(loc);
9498
9499
0
    TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
9500
0
    switchNode->setLoc(loc);
9501
0
    handleSwitchAttributes(loc, switchNode, attributes);
9502
9503
0
    return switchNode;
9504
0
}
9505
9506
// Make a new symbol-table level that is made out of the members of a structure.
9507
// This should be done as an anonymous struct (name is "") so that the symbol table
9508
// finds the members with no explicit reference to a 'this' variable.
9509
void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators)
9510
0
{
9511
    // member variables
9512
0
    TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
9513
0
    symbolTable.pushThis(thisVariable);
9514
9515
    // member functions
9516
0
    for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) {
9517
        // member should have a prefix matching currentTypePrefix.back()
9518
        // but, symbol lookup within the class scope will just use the
9519
        // unprefixed name. Hence, there are two: one fully prefixed and
9520
        // one with no prefix.
9521
0
        TFunction& member = *it->function->clone();
9522
0
        member.removePrefix(currentTypePrefix.back());
9523
0
        symbolTable.insert(member);
9524
0
    }
9525
0
}
9526
9527
// Track levels of class/struct/namespace nesting with a prefix string using
9528
// the type names separated by the scoping operator. E.g., two levels
9529
// would look like:
9530
//
9531
//   outer::inner
9532
//
9533
// The string is empty when at normal global level.
9534
//
9535
void HlslParseContext::pushNamespace(const TString& typeName)
9536
0
{
9537
    // make new type prefix
9538
0
    TString newPrefix;
9539
0
    if (currentTypePrefix.size() > 0)
9540
0
        newPrefix = currentTypePrefix.back();
9541
0
    newPrefix.append(typeName);
9542
0
    newPrefix.append(scopeMangler);
9543
0
    currentTypePrefix.push_back(newPrefix);
9544
0
}
9545
9546
// Opposite of pushNamespace(), see above
9547
void HlslParseContext::popNamespace()
9548
0
{
9549
0
    currentTypePrefix.pop_back();
9550
0
}
9551
9552
// Use the class/struct nesting string to create a global name for
9553
// a member of a class/struct.
9554
void HlslParseContext::getFullNamespaceName(TString*& name) const
9555
176k
{
9556
176k
    if (currentTypePrefix.size() == 0)
9557
176k
        return;
9558
9559
0
    TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
9560
0
    fullName->append(*name);
9561
0
    name = fullName;
9562
0
}
9563
9564
// Helper function to add the namespace scope mangling syntax to a string.
9565
void HlslParseContext::addScopeMangler(TString& name)
9566
0
{
9567
0
    name.append(scopeMangler);
9568
0
}
9569
9570
// Return true if this has uniform-interface like decorations.
9571
bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
9572
0
{
9573
0
    return qualifier.hasUniformLayout() ||
9574
0
           qualifier.layoutPushConstant;
9575
0
}
9576
9577
// Potentially not the opposite of hasUniform(), as if some characteristic is
9578
// ever used for more than one thing (e.g., uniform or input), hasUniform() should
9579
// say it exists, but clearUniform() should leave it in place.
9580
void HlslParseContext::clearUniform(TQualifier& qualifier)
9581
0
{
9582
0
    qualifier.clearUniformLayout();
9583
0
    qualifier.layoutPushConstant = false;
9584
0
}
9585
9586
// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
9587
bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
9588
0
{
9589
0
    switch (qualifier.builtIn) {
9590
0
    case EbvPosition:
9591
0
    case EbvPointSize:
9592
0
        return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
9593
0
    case EbvClipDistance:
9594
0
    case EbvCullDistance:
9595
0
        return language != EShLangVertex && language != EShLangCompute;
9596
0
    case EbvFragCoord:
9597
0
    case EbvFace:
9598
0
    case EbvHelperInvocation:
9599
0
    case EbvLayer:
9600
0
    case EbvPointCoord:
9601
0
    case EbvSampleId:
9602
0
    case EbvSampleMask:
9603
0
    case EbvSamplePosition:
9604
0
    case EbvViewportIndex:
9605
0
        return language == EShLangFragment;
9606
0
    case EbvGlobalInvocationId:
9607
0
    case EbvLocalInvocationIndex:
9608
0
    case EbvLocalInvocationId:
9609
0
    case EbvNumWorkGroups:
9610
0
    case EbvWorkGroupId:
9611
0
    case EbvWorkGroupSize:
9612
0
        return language == EShLangCompute;
9613
0
    case EbvInvocationId:
9614
0
        return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
9615
0
    case EbvPatchVertices:
9616
0
        return language == EShLangTessControl || language == EShLangTessEvaluation;
9617
0
    case EbvInstanceId:
9618
0
    case EbvInstanceIndex:
9619
0
    case EbvVertexId:
9620
0
    case EbvVertexIndex:
9621
0
        return language == EShLangVertex;
9622
0
    case EbvPrimitiveId:
9623
0
        return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl;
9624
0
    case EbvTessLevelInner:
9625
0
    case EbvTessLevelOuter:
9626
0
        return language == EShLangTessEvaluation;
9627
0
    case EbvTessCoord:
9628
0
        return language == EShLangTessEvaluation;
9629
0
    case EbvViewIndex:
9630
0
        return language != EShLangCompute;
9631
0
    default:
9632
0
        return false;
9633
0
    }
9634
0
}
9635
9636
// Return true if there are decorations to preserve for input-like storage.
9637
bool HlslParseContext::hasInput(const TQualifier& qualifier) const
9638
0
{
9639
0
    if (qualifier.hasAnyLocation())
9640
0
        return true;
9641
9642
0
    if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
9643
0
        return true;
9644
9645
0
    if (language == EShLangTessEvaluation && qualifier.patch)
9646
0
        return true;
9647
9648
0
    if (isInputBuiltIn(qualifier))
9649
0
        return true;
9650
9651
0
    return false;
9652
0
}
9653
9654
// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
9655
bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
9656
0
{
9657
0
    switch (qualifier.builtIn) {
9658
0
    case EbvPosition:
9659
0
    case EbvPointSize:
9660
0
    case EbvClipVertex:
9661
0
    case EbvClipDistance:
9662
0
    case EbvCullDistance:
9663
0
        return language != EShLangFragment && language != EShLangCompute;
9664
0
    case EbvFragDepth:
9665
0
    case EbvFragDepthGreater:
9666
0
    case EbvFragDepthLesser:
9667
0
    case EbvSampleMask:
9668
0
        return language == EShLangFragment;
9669
0
    case EbvLayer:
9670
0
    case EbvViewportIndex:
9671
0
        return language == EShLangGeometry || language == EShLangVertex;
9672
0
    case EbvPrimitiveId:
9673
0
        return language == EShLangGeometry;
9674
0
    case EbvTessLevelInner:
9675
0
    case EbvTessLevelOuter:
9676
0
        return language == EShLangTessControl;
9677
0
    default:
9678
0
        return false;
9679
0
    }
9680
0
}
9681
9682
// Return true if there are decorations to preserve for output-like storage.
9683
bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
9684
0
{
9685
0
    if (qualifier.hasAnyLocation())
9686
0
        return true;
9687
9688
0
    if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
9689
0
        return true;
9690
9691
0
    if (language == EShLangTessControl && qualifier.patch)
9692
0
        return true;
9693
9694
0
    if (language == EShLangGeometry && qualifier.hasStream())
9695
0
        return true;
9696
9697
0
    if (isOutputBuiltIn(qualifier))
9698
0
        return true;
9699
9700
0
    return false;
9701
0
}
9702
9703
// Make the IO decorations etc. be appropriate only for an input interface.
9704
void HlslParseContext::correctInput(TQualifier& qualifier)
9705
0
{
9706
0
    clearUniform(qualifier);
9707
0
    if (language == EShLangVertex)
9708
0
        qualifier.clearInterstage();
9709
0
    if (language != EShLangTessEvaluation)
9710
0
        qualifier.patch = false;
9711
0
    if (language != EShLangFragment) {
9712
0
        qualifier.clearInterpolation();
9713
0
        qualifier.sample = false;
9714
0
    }
9715
9716
0
    qualifier.clearStreamLayout();
9717
0
    qualifier.clearXfbLayout();
9718
9719
0
    if (! isInputBuiltIn(qualifier))
9720
0
        qualifier.builtIn = EbvNone;
9721
0
}
9722
9723
// Make the IO decorations etc. be appropriate only for an output interface.
9724
void HlslParseContext::correctOutput(TQualifier& qualifier)
9725
0
{
9726
0
    clearUniform(qualifier);
9727
0
    if (language == EShLangFragment)
9728
0
        qualifier.clearInterstage();
9729
0
    if (language != EShLangGeometry)
9730
0
        qualifier.clearStreamLayout();
9731
0
    if (language == EShLangFragment)
9732
0
        qualifier.clearXfbLayout();
9733
0
    if (language != EShLangTessControl)
9734
0
        qualifier.patch = false;
9735
9736
    // Fixes Test/hlsl.entry-inout.vert (SV_Position will not become a varying).
9737
0
    if (qualifier.builtIn == EbvNone)
9738
0
        qualifier.builtIn = qualifier.declaredBuiltIn;
9739
9740
0
    switch (qualifier.builtIn) {
9741
0
    case EbvFragDepth:
9742
0
        intermediate.setDepthReplacing();
9743
0
        intermediate.setDepth(EldAny);
9744
0
        break;
9745
0
    case EbvFragDepthGreater:
9746
0
        intermediate.setDepthReplacing();
9747
0
        intermediate.setDepth(EldGreater);
9748
0
        qualifier.builtIn = EbvFragDepth;
9749
0
        break;
9750
0
    case EbvFragDepthLesser:
9751
0
        intermediate.setDepthReplacing();
9752
0
        intermediate.setDepth(EldLess);
9753
0
        qualifier.builtIn = EbvFragDepth;
9754
0
        break;
9755
0
    default:
9756
0
        break;
9757
0
    }
9758
9759
0
    if (! isOutputBuiltIn(qualifier))
9760
0
        qualifier.builtIn = EbvNone;
9761
0
}
9762
9763
// Make the IO decorations etc. be appropriate only for uniform type interfaces.
9764
void HlslParseContext::correctUniform(TQualifier& qualifier)
9765
0
{
9766
0
    if (qualifier.declaredBuiltIn == EbvNone)
9767
0
        qualifier.declaredBuiltIn = qualifier.builtIn;
9768
9769
0
    qualifier.builtIn = EbvNone;
9770
0
    qualifier.clearInterstage();
9771
0
    qualifier.clearInterstageLayout();
9772
0
}
9773
9774
// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
9775
void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
9776
0
{
9777
0
    clearUniform(qualifier);
9778
0
    correctUniform(qualifier);
9779
0
}
9780
9781
9782
// Set texture return type.  Returns success (not all types are valid).
9783
bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc)
9784
35.7k
{
9785
    // Seed the output with an invalid index.  We will set it to a valid one if we can.
9786
35.7k
    sampler.structReturnIndex = TSampler::noReturnStruct;
9787
9788
    // Arrays aren't supported.
9789
35.7k
    if (retType.isArray()) {
9790
0
        error(loc, "Arrays not supported in texture template types", "", "");
9791
0
        return false;
9792
0
    }
9793
9794
    // If return type is a vector, remember the vector size in the sampler, and return.
9795
35.7k
    if (retType.isVector() || retType.isScalar()) {
9796
35.7k
        sampler.vectorSize = retType.getVectorSize();
9797
35.7k
        return true;
9798
35.7k
    }
9799
9800
    // If it wasn't a vector, it must be a struct meeting certain requirements.  The requirements
9801
    // are checked below: just check for struct-ness here.
9802
0
    if (!retType.isStruct()) {
9803
0
        error(loc, "Invalid texture template type", "", "");
9804
0
        return false;
9805
0
    }
9806
9807
    // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
9808
0
    if (sampler.isSubpass()) {
9809
0
        error(loc, "Unimplemented: structure template type in subpass input", "", "");
9810
0
        return false;
9811
0
    }
9812
9813
0
    TTypeList* members = retType.getWritableStruct();
9814
9815
    // Check for too many or not enough structure members.
9816
0
    if (members->size() > 4 || members->size() == 0) {
9817
0
        error(loc, "Invalid member count in texture template structure", "", "");
9818
0
        return false;
9819
0
    }
9820
9821
    // Error checking: We must have <= 4 total components, all of the same basic type.
9822
0
    unsigned totalComponents = 0;
9823
0
    for (unsigned m = 0; m < members->size(); ++m) {
9824
        // Check for bad member types
9825
0
        if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) {
9826
0
            error(loc, "Invalid texture template struct member type", "", "");
9827
0
            return false;
9828
0
        }
9829
9830
0
        const unsigned memberVectorSize = (*members)[m].type->getVectorSize();
9831
0
        totalComponents += memberVectorSize;
9832
9833
        // too many total member components
9834
0
        if (totalComponents > 4) {
9835
0
            error(loc, "Too many components in texture template structure type", "", "");
9836
0
            return false;
9837
0
        }
9838
9839
        // All members must be of a common basic type
9840
0
        if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) {
9841
0
            error(loc, "Texture template structure members must same basic type", "", "");
9842
0
            return false;
9843
0
        }
9844
0
    }
9845
9846
    // If the structure in the return type already exists in the table, we'll use it.  Otherwise, we'll make
9847
    // a new entry.  This is a linear search, but it hardly ever happens, and the list cannot be very large.
9848
0
    for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) {
9849
0
        if (textureReturnStruct[idx] == members) {
9850
0
            sampler.structReturnIndex = idx;
9851
0
            return true;
9852
0
        }
9853
0
    }
9854
9855
    // It wasn't found as an existing entry.  See if we have room for a new one.
9856
0
    if (textureReturnStruct.size() >= TSampler::structReturnSlots) {
9857
0
        error(loc, "Texture template struct return slots exceeded", "", "");
9858
0
        return false;
9859
0
    }
9860
9861
    // Insert it in the vector that tracks struct return types.
9862
0
    sampler.structReturnIndex = unsigned(textureReturnStruct.size());
9863
0
    textureReturnStruct.push_back(members);
9864
9865
    // Success!
9866
0
    return true;
9867
0
}
9868
9869
// Return the sampler return type in retType.
9870
void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const
9871
0
{
9872
0
    if (sampler.hasReturnStruct()) {
9873
0
        assert(textureReturnStruct.size() >= sampler.structReturnIndex);
9874
9875
        // We land here if the texture return is a structure.
9876
0
        TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex];
9877
9878
0
        const TType resultType(blockStruct, "");
9879
0
        retType.shallowCopy(resultType);
9880
0
    } else {
9881
        // We land here if the texture return is a vector or scalar.
9882
0
        const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize());
9883
0
        retType.shallowCopy(resultType);
9884
0
    }
9885
0
}
9886
9887
9888
// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type
9889
TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const
9890
0
{
9891
0
    const auto it = builtInTessLinkageSymbols.find(biType);
9892
0
    if (it == builtInTessLinkageSymbols.end())  // if it wasn't declared by the user, return nullptr
9893
0
        return nullptr;
9894
9895
0
    return intermediate.addSymbol(*it->second->getAsVariable());
9896
0
}
9897
9898
// Find the patch constant function (issues error, returns nullptr if not found)
9899
const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc)
9900
0
{
9901
0
    if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
9902
0
        error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
9903
0
        return nullptr;
9904
0
    }
9905
9906
0
    const TString mangledName = patchConstantFunctionName + "(";
9907
9908
    // create list of PCF candidates
9909
0
    TVector<const TFunction*> candidateList;
9910
0
    bool builtIn;
9911
0
    symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
9912
9913
    // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
9914
    // allow any disambiguation of overloads.
9915
0
    if (candidateList.empty()) {
9916
0
        error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
9917
0
        return nullptr;
9918
0
    }
9919
9920
    // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
9921
    // HLSL picks the last one in shader source order.  Since that isn't yet implemented here, error
9922
    // out if there is more than one candidate.
9923
0
    if (candidateList.size() > 1) {
9924
0
        error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
9925
0
        return nullptr;
9926
0
    }
9927
9928
0
    return candidateList[0];
9929
0
}
9930
9931
// Finalization step: Add patch constant function invocation
9932
void HlslParseContext::addPatchConstantInvocation()
9933
52
{
9934
52
    TSourceLoc loc;
9935
52
    loc.init();
9936
9937
    // If there's no patch constant function, or we're not a HS, do nothing.
9938
52
    if (patchConstantFunctionName.empty() || language != EShLangTessControl)
9939
52
        return;
9940
9941
    // Look for built-in variables in a function's parameter list.
9942
0
    const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
9943
0
        for (int p=0; p<function.getParamCount(); ++p) {
9944
0
            TStorageQualifier storage = function[p].type->getQualifier().storage;
9945
9946
0
            if (storage == EvqConstReadOnly) // treated identically to input
9947
0
                storage = EvqIn;
9948
9949
0
            if (function[p].getDeclaredBuiltIn() != EbvNone)
9950
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage));
9951
0
            else
9952
0
                builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
9953
0
        }
9954
0
    };
9955
9956
    // If we synthesize a built-in interface variable, we must add it to the linkage.
9957
0
    const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
9958
0
        if (name == nullptr) {
9959
0
            error(loc, "unable to locate patch function parameter name", "", "");
9960
0
            return;
9961
0
        } else {
9962
0
            TVariable& variable = *new TVariable(name, type);
9963
0
            if (! symbolTable.insert(variable)) {
9964
0
                error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
9965
0
                return;
9966
0
            }
9967
9968
0
            globalQualifierFix(loc, variable.getWritableType().getQualifier());
9969
9970
0
            if (symbolNode != nullptr)
9971
0
                *symbolNode = intermediate.addSymbol(variable);
9972
9973
0
            trackLinkage(variable);
9974
0
        }
9975
0
    };
9976
9977
0
    const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) {
9978
0
        const TType& type = *patchConstantFunction[param].type;
9979
0
        const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn();
9980
9981
0
        return type.isSizedArray() && biType == EbvOutputPatch;
9982
0
    };
9983
9984
    // We will perform these steps.  Each is in a scoped block for separation: they could
9985
    // become separate functions to make addPatchConstantInvocation shorter.
9986
    //
9987
    // 1. Union the interfaces, and create built-ins for anything present in the PCF and
9988
    //    declared as a built-in variable that isn't present in the entry point's signature.
9989
    //
9990
    // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main,
9991
    //    or the ones we created.  Matching is based on built-in type.  We may use synthesized
9992
    //    variables from (1) above.
9993
    //
9994
    // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
9995
    //
9996
    // 3. Create a return sequence: copy the return value (if any) from the PCF to a
9997
    //    (non-sanitized) output variable.  In case this may involve multiple copies, such as for
9998
    //    an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
9999
    //    indirections into a complex R-value coming from the call to the PCF.
10000
    //
10001
    // 4. Create a barrier.
10002
    //
10003
    // 5/5B. Call the PCF inside an if test for (invocation id == 0).
10004
10005
0
    TFunction* patchConstantFunctionPtr = const_cast<TFunction*>(findPatchConstantFunction(loc));
10006
10007
0
    if (patchConstantFunctionPtr == nullptr)
10008
0
        return;
10009
10010
0
    TFunction& patchConstantFunction = *patchConstantFunctionPtr;
10011
10012
0
    const int pcfParamCount = patchConstantFunction.getParamCount();
10013
0
    TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
10014
0
    TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
10015
10016
0
    int outPatchParam = -1; // -1 means there isn't one.
10017
10018
    // ================ Step 1A: Union Interfaces ================
10019
    // Our patch constant function.
10020
0
    {
10021
0
        std::set<tInterstageIoData> pcfBuiltIns;  // patch constant function built-ins
10022
0
        std::set<tInterstageIoData> epfBuiltIns;  // entry point function built-ins
10023
10024
0
        assert(entryPointFunction);
10025
0
        assert(entryPointFunctionBody);
10026
10027
0
        findBuiltIns(patchConstantFunction, pcfBuiltIns);
10028
0
        findBuiltIns(*entryPointFunction,   epfBuiltIns);
10029
10030
        // Find the set of built-ins in the PCF that are not present in the entry point.
10031
0
        std::set<tInterstageIoData> notInEntryPoint;
10032
10033
0
        notInEntryPoint = pcfBuiltIns;
10034
10035
        // std::set_difference not usable on unordered containers
10036
0
        for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
10037
0
            notInEntryPoint.erase(*bi);
10038
10039
        // Now we'll add those to the entry and to the linkage.
10040
0
        for (int p=0; p<pcfParamCount; ++p) {
10041
0
            const TBuiltInVariable biType   = patchConstantFunction[p].getDeclaredBuiltIn();
10042
0
            TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
10043
10044
            // Track whether there is an output patch param
10045
0
            if (isOutputPatch(patchConstantFunction, p)) {
10046
0
                if (outPatchParam >= 0) {
10047
                    // Presently we only support one per ctrl pt input.
10048
0
                    error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
10049
0
                    return;
10050
0
                }
10051
0
                outPatchParam = p;
10052
0
            }
10053
10054
0
            if (biType != EbvNone) {
10055
0
                TType* paramType = patchConstantFunction[p].type->clone();
10056
10057
0
                if (storage == EvqConstReadOnly) // treated identically to input
10058
0
                    storage = EvqIn;
10059
10060
                // Presently, the only non-built-in we support is InputPatch, which is treated as
10061
                // a pseudo-built-in.
10062
0
                if (biType == EbvInputPatch) {
10063
0
                    builtInTessLinkageSymbols[biType] = inputPatch;
10064
0
                } else if (biType == EbvOutputPatch) {
10065
                    // Nothing...
10066
0
                } else {
10067
                    // Use the original declaration type for the linkage
10068
0
                    paramType->getQualifier().builtIn = biType;
10069
0
                    if (biType == EbvTessLevelInner || biType == EbvTessLevelOuter)
10070
0
                        paramType->getQualifier().patch = true;
10071
10072
0
                    if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
10073
0
                        addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
10074
0
                }
10075
0
            }
10076
0
        }
10077
10078
        // If we didn't find it because the shader made one, add our own.
10079
0
        if (invocationIdSym == nullptr) {
10080
0
            TType invocationIdType(EbtUint, EvqIn, 1);
10081
0
            TString* invocationIdName = NewPoolTString("InvocationId");
10082
0
            invocationIdType.getQualifier().builtIn = EbvInvocationId;
10083
0
            addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
10084
0
        }
10085
10086
0
        assert(invocationIdSym);
10087
0
    }
10088
10089
0
    TIntermTyped* pcfArguments = nullptr;
10090
0
    TVariable* perCtrlPtVar = nullptr;
10091
10092
    // ================ Step 1B: Argument synthesis ================
10093
    // Create pcfArguments for synthesis of patchconstantfunction invocation
10094
0
    {
10095
0
        for (int p=0; p<pcfParamCount; ++p) {
10096
0
            TIntermTyped* inputArg = nullptr;
10097
10098
0
            if (p == outPatchParam) {
10099
0
                if (perCtrlPtVar == nullptr) {
10100
0
                    perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
10101
0
                                                        *patchConstantFunction[outPatchParam].type);
10102
10103
0
                    perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
10104
0
                }
10105
0
                inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
10106
0
            } else {
10107
                // find which built-in it is
10108
0
                const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
10109
10110
0
                if (biType == EbvInputPatch && inputPatch == nullptr) {
10111
0
                    error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", "");
10112
0
                    return;
10113
0
                }
10114
10115
0
                inputArg = findTessLinkageSymbol(biType);
10116
10117
0
                if (inputArg == nullptr) {
10118
0
                    error(loc, "unable to find patch constant function built-in variable", "", "");
10119
0
                    return;
10120
0
                }
10121
0
            }
10122
10123
0
            if (pcfParamCount == 1)
10124
0
                pcfArguments = inputArg;
10125
0
            else
10126
0
                pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
10127
0
        }
10128
0
    }
10129
10130
    // ================ Step 2: Synthesize call to PCF ================
10131
0
    TIntermAggregate* pcfCallSequence = nullptr;
10132
0
    TIntermTyped* pcfCall = nullptr;
10133
10134
0
    {
10135
        // Create a function call to the patchconstantfunction
10136
0
        if (pcfArguments)
10137
0
            addInputArgumentConversions(patchConstantFunction, pcfArguments);
10138
10139
        // Synthetic call.
10140
0
        pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
10141
0
        pcfCall->getAsAggregate()->setUserDefined();
10142
0
        pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
10143
0
        intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
10144
0
                                    patchConstantFunction.getMangledName());
10145
10146
0
        if (pcfCall->getAsAggregate()) {
10147
0
            TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
10148
0
            for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
10149
0
                TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
10150
0
                qualifierList.push_back(qual);
10151
0
            }
10152
0
            pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
10153
0
        }
10154
0
    }
10155
10156
    // ================ Step 2B: Per Control Point synthesis ================
10157
    // If there is per control point data, we must either emulate that with multiple
10158
    // invocations of the entry point to build up an array, or (TODO:) use a yet
10159
    // unavailable extension to look across the SIMD lanes.  This is the former
10160
    // as a placeholder for the latter.
10161
0
    if (outPatchParam >= 0) {
10162
        // We must introduce a local temp variable of the type wanted by the PCF input.
10163
0
        const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
10164
10165
0
        if (entryPointFunction->getType().getBasicType() == EbtVoid) {
10166
0
            error(loc, "entry point must return a value for use with patch constant function", "", "");
10167
0
            return;
10168
0
        }
10169
10170
        // Create calls to wrapped main to fill in the array.  We will substitute fixed values
10171
        // of invocation ID when calling the wrapped main.
10172
10173
        // This is the type of the each member of the per ctrl point array.
10174
0
        const TType derefType(perCtrlPtVar->getType(), 0);
10175
10176
0
        for (int cpt = 0; cpt < arraySize; ++cpt) {
10177
            // TODO: improve.  substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
10178
            // for this function.
10179
0
            const TString origName = entryPointFunction->getName().substr(1);
10180
0
            TFunction callee(&origName, TType(EbtVoid));
10181
0
            TIntermTyped* callingArgs = nullptr;
10182
10183
0
            for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
10184
0
                TParameter& param = (*entryPointFunction)[i];
10185
0
                TType& paramType = *param.type;
10186
10187
0
                if (paramType.getQualifier().isParamOutput()) {
10188
0
                    error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
10189
0
                    return;
10190
0
                }
10191
10192
0
                if (paramType.getQualifier().isParamInput())  {
10193
0
                    TIntermTyped* arg = nullptr;
10194
0
                    if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) {
10195
                        // substitute invocation ID with the array element ID
10196
0
                        arg = intermediate.addConstantUnion(cpt, loc);
10197
0
                    } else {
10198
0
                        TVariable* argVar = makeInternalVariable(*param.name, *param.type);
10199
0
                        argVar->getWritableType().getQualifier().makeTemporary();
10200
0
                        arg = intermediate.addSymbol(*argVar);
10201
0
                    }
10202
10203
0
                    handleFunctionArgument(&callee, callingArgs, arg);
10204
0
                }
10205
0
            }
10206
10207
            // Call and assign to per ctrl point variable
10208
0
            currentCaller = intermediate.getEntryPointMangledName().c_str();
10209
0
            TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
10210
0
            TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
10211
0
            TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
10212
0
            TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
10213
0
            element->setType(derefType);
10214
0
            element->setLoc(loc);
10215
10216
0
            pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
10217
0
                                                         handleAssign(loc, EOpAssign, element, callReturn));
10218
0
        }
10219
0
    }
10220
10221
    // ================ Step 3: Create return Sequence ================
10222
    // Return sequence: copy PCF result to a temporary, then to shader output variable.
10223
0
    if (pcfCall->getBasicType() != EbtVoid) {
10224
0
        const TType* retType = &patchConstantFunction.getType();  // return type from the PCF
10225
0
        TType outType; // output type that goes with the return type.
10226
0
        outType.shallowCopy(*retType);
10227
10228
        // substitute the output type
10229
0
        const auto newLists = ioTypeMap.find(retType->getStruct());
10230
0
        if (newLists != ioTypeMap.end())
10231
0
            outType.setStruct(newLists->second.output);
10232
10233
        // Substitute the top level type's built-in type
10234
0
        if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
10235
0
            outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
10236
10237
0
        outType.getQualifier().patch = true; // make it a per-patch variable
10238
10239
0
        TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
10240
0
        pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
10241
10242
0
        if (pcfOutput->getType().isStruct())
10243
0
            flatten(*pcfOutput, false);
10244
10245
0
        assignToInterface(*pcfOutput);
10246
10247
0
        TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
10248
10249
        // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
10250
        // repeated calls to the PCF:
10251
0
        TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
10252
0
        pcfCallResult->getWritableType().getQualifier().makeTemporary();
10253
10254
0
        TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
10255
0
        TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
10256
0
        TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym,
10257
0
                                                   intermediate.addSymbol(*pcfCallResult, loc));
10258
10259
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
10260
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
10261
0
    } else {
10262
0
        pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
10263
0
    }
10264
10265
    // ================ Step 4: Barrier ================
10266
0
    TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
10267
0
    barrier->setLoc(loc);
10268
0
    barrier->setType(TType(EbtVoid));
10269
0
    epBodySeq.insert(epBodySeq.end(), barrier);
10270
10271
    // ================ Step 5: Test on invocation ID ================
10272
0
    TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
10273
0
    TIntermTyped* cmp =  intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
10274
10275
10276
    // ================ Step 5B: Create if statement on Invocation ID == 0 ================
10277
0
    intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
10278
0
    TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
10279
0
    invocationIdTest->setLoc(loc);
10280
10281
    // add our test sequence before the return.
10282
0
    epBodySeq.insert(epBodySeq.end(), invocationIdTest);
10283
0
}
10284
10285
// Finalization step: remove unused buffer blocks from linkage (we don't know until the
10286
// shader is entirely compiled).
10287
// Preserve order of remaining symbols.
10288
void HlslParseContext::removeUnusedStructBufferCounters()
10289
52
{
10290
52
    const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
10291
52
                                      [this](const TSymbol* sym) {
10292
0
                                          const auto sbcIt = structBufferCounter.find(sym->getName());
10293
0
                                          return sbcIt != structBufferCounter.end() && !sbcIt->second;
10294
0
                                      });
10295
10296
52
    linkageSymbols.erase(endIt, linkageSymbols.end());
10297
52
}
10298
10299
// Finalization step: patch texture shadow modes to match samplers they were combined with
10300
void HlslParseContext::fixTextureShadowModes()
10301
52
{
10302
52
    for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) {
10303
0
        TSampler& sampler = (*symbol)->getWritableType().getSampler();
10304
10305
0
        if (sampler.isTexture()) {
10306
0
            const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId());
10307
0
            if (shadowMode != textureShadowVariant.end()) {
10308
10309
0
                if (shadowMode->second->overloaded())
10310
                    // Texture needs legalization if it's been seen with both shadow and non-shadow modes.
10311
0
                    intermediate.setNeedsLegalization();
10312
10313
0
                sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId());
10314
0
            }
10315
0
        }
10316
0
    }
10317
52
}
10318
10319
// Finalization step: patch append methods to use proper stream output, which isn't known until
10320
// main is parsed, which could happen after the append method is parsed.
10321
void HlslParseContext::finalizeAppendMethods()
10322
52
{
10323
52
    TSourceLoc loc;
10324
52
    loc.init();
10325
10326
    // Nothing to do: bypass test for valid stream output.
10327
52
    if (gsAppends.empty())
10328
52
        return;
10329
10330
0
    if (gsStreamOutput == nullptr) {
10331
0
        error(loc, "unable to find output symbol for Append()", "", "");
10332
0
        return;
10333
0
    }
10334
10335
    // Patch append sequences, now that we know the stream output symbol.
10336
0
    for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) {
10337
0
        append->node->getSequence()[0] =
10338
0
            handleAssign(append->loc, EOpAssign,
10339
0
                         intermediate.addSymbol(*gsStreamOutput, append->loc),
10340
0
                         append->node->getSequence()[0]->getAsTyped());
10341
0
    }
10342
0
}
10343
10344
// post-processing
10345
void HlslParseContext::finish()
10346
52
{
10347
    // Error check: There was a dangling .mips operator.  These are not nested constructs in the grammar, so
10348
    // cannot be detected there.  This is not strictly needed in a non-validating parser; it's just helpful.
10349
52
    if (! mipsOperatorMipArg.empty()) {
10350
0
        error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", "");
10351
0
    }
10352
10353
52
    removeUnusedStructBufferCounters();
10354
52
    addPatchConstantInvocation();
10355
52
    fixTextureShadowModes();
10356
52
    finalizeAppendMethods();
10357
10358
    // Communicate out (esp. for command line) that we formed AST that will make
10359
    // illegal AST SPIR-V and it needs transforms to legalize it.
10360
52
    if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization))
10361
0
        infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize";
10362
10363
52
    TParseContextBase::finish();
10364
52
}
10365
10366
} // end namespace glslang