aboutsummaryrefslogtreecommitdiff
path: root/glslang/HLSL/hlslParseHelper.h
blob: 2d7165cfcd7117b447574cd45bc9eda9a53226a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
//
// Copyright (C) 2016-2018 Google, Inc.
// Copyright (C) 2016 LunarG, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//    Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//    Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#ifndef HLSL_PARSE_INCLUDED_
#define HLSL_PARSE_INCLUDED_

#include "../MachineIndependent/parseVersions.h"
#include "../MachineIndependent/ParseHelper.h"
#include "../MachineIndependent/attribute.h"

#include <array>

namespace glslang {

class TFunctionDeclarator;

class HlslParseContext : public TParseContextBase {
public:
    HlslParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins,
                     int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&,
                     const TString sourceEntryPointName,
                     bool forwardCompatible = false, EShMessages messages = EShMsgDefault);
    virtual ~HlslParseContext();
    void initializeExtensionBehavior() override;

    void setLimits(const TBuiltInResource&) override;
    bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override;
    virtual const char* getGlobalUniformBlockName() const override { return "$Global"; }
    virtual void setUniformBlockDefaults(TType& block) const override
    {
        block.getQualifier().layoutPacking = globalUniformDefaults.layoutPacking;
        block.getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
    }

    void reservedPpErrorCheck(const TSourceLoc&, const char* /*name*/, const char* /*op*/) override { }
    bool lineContinuationCheck(const TSourceLoc&, bool /*endOfComment*/) override { return true; }
    bool lineDirectiveShouldSetNextLine() const override { return true; }
    bool builtInName(const TString&);

    void handlePragma(const TSourceLoc&, const TVector<TString>&) override;
    TIntermTyped* handleVariable(const TSourceLoc&, const TString* string);
    TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
    TIntermTyped* handleBracketOperator(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);

    TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right);
    TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
    TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
    bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field);
    void assignToInterface(TVariable& variable);
    void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
    TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree);
    TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&);
    void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&);
    void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false);
    void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node);
    void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs);
    void remapNonEntryPointIO(TFunction& function);
    TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*);
    void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg);
    TIntermTyped* handleAssign(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
    TIntermTyped* handleAssignToMatrixSwizzle(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
    TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermTyped*);
    TIntermAggregate* assignClipCullDistance(const TSourceLoc&, TOperator, int semanticId, TIntermTyped* left, TIntermTyped* right);
    TIntermTyped* assignPosition(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
    void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
    void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
    void decomposeStructBufferMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
    void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
    void pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments);
    void addInputArgumentConversions(const TFunction&, TIntermTyped*&);
    void expandArguments(const TSourceLoc&, const TFunction&, TIntermTyped*&);
    TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermOperator&);
    void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&);
    TFunction* makeConstructorCall(const TSourceLoc&, const TType&);
    void handleSemantic(TSourceLoc, TQualifier&, TBuiltInVariable, const TString& upperCase);
    void handlePackOffset(const TSourceLoc&, TQualifier&, const glslang::TString& location,
                          const glslang::TString* component);
    void handleRegister(const TSourceLoc&, TQualifier&, const glslang::TString* profile, const glslang::TString& desc,
                        int subComponent, const glslang::TString*);
    TIntermTyped* convertConditionalExpression(const TSourceLoc&, TIntermTyped*, bool mustBeScalar = true);
    TIntermAggregate* handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler);

    bool parseMatrixSwizzleSelector(const TSourceLoc&, const TString&, int cols, int rows, TSwizzleSelectors<TMatrixSelector>&);
    int getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>&);
    void assignError(const TSourceLoc&, const char* op, TString left, TString right);
    void unaryOpError(const TSourceLoc&, const char* op, TString operand);
    void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right);
    void variableCheck(TIntermTyped*& nodePtr);
    void constantValueCheck(TIntermTyped* node, const char* token);
    void integerCheck(const TIntermTyped* node, const char* token);
    void globalCheck(const TSourceLoc&, const char* token);
    bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&);
    void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&);
    void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&);
    void structArrayCheck(const TSourceLoc&, const TType& structure);
    bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType);
    void globalQualifierFix(const TSourceLoc&, TQualifier&);
    bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType);
    void mergeQualifiers(TQualifier& dst, const TQualifier& src);
    int computeSamplerTypeIndex(TSampler&);
    TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&);
    void paramFix(TType& type);
    void specializationCheck(const TSourceLoc&, const TType&, const char* op);

    void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&);
    void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&, const TIntermTyped*);
    void setSpecConstantId(const TSourceLoc&, TQualifier&, int value);
    void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly);
    void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&);

    const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args);
    void addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args);
    void declareTypedef(const TSourceLoc&, const TString& identifier, const TType&);
    void declareStruct(const TSourceLoc&, TString& structName, TType&);
    TSymbol* lookupUserType(const TString&, TType&);
    TIntermNode* declareVariable(const TSourceLoc&, const TString& identifier, TType&, TIntermTyped* initializer = 0);
    void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit);
    TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
    TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
    TIntermTyped* convertArray(TIntermTyped*, const TType&);
    TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
    TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
    void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0);
    void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name);
    void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
    void fixXfbOffsets(TQualifier&, TTypeList&);
    void fixBlockUniformOffsets(const TQualifier&, TTypeList&);
    void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier);
    void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
    void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
    void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
    TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&);

    void nestLooping()       { ++loopNestingLevel; }
    void unnestLooping()     { --loopNestingLevel; }
    void nestAnnotations()   { ++annotationNestingLevel; }
    void unnestAnnotations() { --annotationNestingLevel; }
    int getAnnotationNestingLevel() { return annotationNestingLevel; }
    void pushScope()         { symbolTable.push(); }
    void popScope()          { symbolTable.pop(0); }

    void pushThisScope(const TType&, const TVector<TFunctionDeclarator>&);
    void popThisScope()      { symbolTable.pop(0); }

    void pushImplicitThis(TVariable* thisParameter) { implicitThisStack.push_back(thisParameter); }
    void popImplicitThis() { implicitThisStack.pop_back(); }
    TVariable* getImplicitThis(int thisDepth) const { return implicitThisStack[implicitThisStack.size() - thisDepth]; }

    void pushNamespace(const TString& name);
    void popNamespace();
    void getFullNamespaceName(TString*&) const;
    void addScopeMangler(TString&);

    void beginParameterParsing(TFunction& function)
    {
        parsingEntrypointParameters = isEntrypointName(function.getName());
    }

    void pushSwitchSequence(TIntermSequence* sequence) { switchSequenceStack.push_back(sequence); }
    void popSwitchSequence() { switchSequenceStack.pop_back(); }

    virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName,
        TTypeList* typeList = nullptr) override;

    // Apply L-value conversions.  E.g, turning a write to a RWTexture into an ImageStore.
    TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node);
    bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override;

    TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&);

    bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
    bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);

    // Determine selection control from attributes
    void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes);
    void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes);

    // Determine loop control from attributes
    void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes);

    // Share struct buffer deep types
    void shareStructBufferType(TType&);

    // Set texture return type of the given sampler.  Returns success (not all types are valid).
    bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc);

    // Obtain the sampler return type of the given sampler in retType.
    void getTextureReturnType(const TSampler& sampler, TType& retType) const;

    TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const;

protected:
    struct TFlattenData {
        TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),
                         nextLocation(TQualifier::layoutLocationEnd) { }
        TFlattenData(int nb, int nl) : nextBinding(nb), nextLocation(nl) { }

        TVector<TVariable*> members;     // individual flattened variables
        TVector<int> offsets;            // offset to next tree level
        unsigned int nextBinding;        // next binding to use.
        unsigned int nextLocation;       // next location to use
    };

    void fixConstInit(const TSourceLoc&, const TString& identifier, TType& type, TIntermTyped*& initializer);
    void inheritGlobalDefaults(TQualifier& dst) const;
    TVariable* makeInternalVariable(const char* name, const TType&) const;
    TVariable* makeInternalVariable(const TString& name, const TType& type) const {
        return makeInternalVariable(name.c_str(), type);
    }
    TIntermSymbol* makeInternalVariableNode(const TSourceLoc&, const char* name, const TType&) const;
    TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&, bool track);
    void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&, bool track);
    TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
    TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer, TIntermTyped* scalarInit);
    bool isScalarConstructor(const TIntermNode*);
    TOperator mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage);
    bool isEntrypointName(const TString& name) { return name.compare(intermediate.getEntryPointName().c_str()) == 0; }

    // Return true if this node requires L-value conversion (e.g, to an imageStore).
    bool shouldConvertLValue(const TIntermNode*) const;

    // Array and struct flattening
    TIntermTyped* flattenAccess(TIntermTyped* base, int member);
    TIntermTyped* flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1);
    int findSubtreeOffset(const TIntermNode&) const;
    int findSubtreeOffset(const TType&, int subset, const TVector<int>& offsets) const;
    bool shouldFlatten(const TType&, TStorageQualifier, bool topLevel) const;
    bool wasFlattened(const TIntermTyped* node) const;
    bool wasFlattened(long long id) const { return flattenMap.find(id) != flattenMap.end(); }
    int  addFlattenedMember(const TVariable&, const TType&, TFlattenData&, const TString& name, bool linkage,
                            const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);

    // Structure splitting (splits interstage built-in types into its own struct)
    void split(const TVariable&);
    void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&);
    const TType& split(const TType& type, const TString& name, const TQualifier&);
    bool wasSplit(const TIntermTyped* node) const;
    bool wasSplit(long long id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); }
    TVariable* getSplitNonIoVar(long long id) const;
    void addPatchConstantInvocation();
    void fixTextureShadowModes();
    void finalizeAppendMethods();
    TIntermTyped* makeIntegerIndex(TIntermTyped*);

    void fixBuiltInIoType(TType&);

    void flatten(const TVariable& variable, bool linkage, bool arrayed = false);
    int flatten(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
                const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
    int flattenStruct(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
                      const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
    int flattenArray(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
                     const TQualifier& outerQualifier);

    bool hasUniform(const TQualifier& qualifier) const;
    void clearUniform(TQualifier& qualifier);
    bool isInputBuiltIn(const TQualifier& qualifier) const;
    bool hasInput(const TQualifier& qualifier) const;
    void correctOutput(TQualifier& qualifier);
    bool isOutputBuiltIn(const TQualifier& qualifier) const;
    bool hasOutput(const TQualifier& qualifier) const;
    void correctInput(TQualifier& qualifier);
    void correctUniform(TQualifier& qualifier);
    void clearUniformInputOutput(TQualifier& qualifier);

    // Test method names
    bool isStructBufferMethod(const TString& name) const;
    void counterBufferType(const TSourceLoc& loc, TType& type);

    // Return standard sample position array
    TIntermConstantUnion* getSamplePosArray(int count);

    TType* getStructBufferContentType(const TType& type) const;
    bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; }
    TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const;
    TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer);
    TString getStructBuffCounterName(const TString&) const;
    void addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*&);
    void addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter&, TIntermAggregate*&);

    // Return true if this type is a reference.  This is not currently a type method in case that's
    // a language specific answer.
    bool isReference(const TType& type) const { return isStructBufferType(type); }

    // Return true if this a buffer type that has an associated counter buffer.
    bool hasStructBuffCounter(const TType&) const;

    // Finalization step: remove unused buffer blocks from linkage (we don't know until the
    // shader is entirely compiled)
    void removeUnusedStructBufferCounters();

    static bool isClipOrCullDistance(TBuiltInVariable);
    static bool isClipOrCullDistance(const TQualifier& qual) { return isClipOrCullDistance(qual.builtIn); }
    static bool isClipOrCullDistance(const TType& type) { return isClipOrCullDistance(type.getQualifier()); }

    // Find the patch constant function (issues error, returns nullptr if not found)
    const TFunction* findPatchConstantFunction(const TSourceLoc& loc);

    // Pass through to base class after remembering built-in mappings.
    using TParseContextBase::trackLinkage;
    void trackLinkage(TSymbol& variable) override;

    void finish() override; // post-processing

    // Linkage symbol helpers
    TIntermSymbol* findTessLinkageSymbol(TBuiltInVariable biType) const;

    // Current state of parsing
    int annotationNestingLevel;  // 0 if outside all annotations

    HlslParseContext(HlslParseContext&);
    HlslParseContext& operator=(HlslParseContext&);

    static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2)); // see computeSamplerTypeIndex()
    TQualifier globalBufferDefaults;
    TQualifier globalUniformDefaults;
    TQualifier globalInputDefaults;
    TQualifier globalOutputDefaults;
    TString currentCaller;        // name of last function body entered (not valid when at global scope)
    TIdSetType inductiveLoopIds;
    TVector<TIntermTyped*> needsIndexLimitationChecking;

    //
    // Geometry shader input arrays:
    //  - array sizing is based on input primitive and/or explicit size
    //
    // Tessellation control output arrays:
    //  - array sizing is based on output layout(vertices=...) and/or explicit size
    //
    // Both:
    //  - array sizing is retroactive
    //  - built-in block redeclarations interact with this
    //
    // Design:
    //  - use a per-context "resize-list", a list of symbols whose array sizes
    //    can be fixed
    //
    //  - the resize-list starts empty at beginning of user-shader compilation, it does
    //    not have built-ins in it
    //
    //  - on built-in array use: copyUp() symbol and add it to the resize-list
    //
    //  - on user array declaration: add it to the resize-list
    //
    //  - on block redeclaration: copyUp() symbol and add it to the resize-list
    //     * note, that appropriately gives an error if redeclaring a block that
    //       was already used and hence already copied-up
    //
    //  - on seeing a layout declaration that sizes the array, fix everything in the
    //    resize-list, giving errors for mismatch
    //
    //  - on seeing an array size declaration, give errors on mismatch between it and previous
    //    array-sizing declarations
    //
    TVector<TSymbol*> ioArraySymbolResizeList;

    TMap<long long, TFlattenData> flattenMap;

    // IO-type map. Maps a pure symbol-table form of a structure-member list into
    // each of the (up to) three kinds of IO, as each as different allowed decorations,
    // but HLSL allows mixing all in the same structure.
    struct tIoKinds {
        TTypeList* input;
        TTypeList* output;
        TTypeList* uniform;
    };
    TMap<const TTypeList*, tIoKinds> ioTypeMap;

    // Structure splitting data:
    TMap<long long, TVariable*> splitNonIoVars;  // variables with the built-in interstage IO removed, indexed by unique ID.

    // Structuredbuffer shared types.  Typically there are only a few.
    TVector<TType*> structBufferTypes;

    // This tracks texture sample user structure return types.  Only a limited number are supported, as
    // may fit in TSampler::structReturnIndex.
    TVector<TTypeList*> textureReturnStruct;

    TMap<TString, bool> structBufferCounter;  // true if counter buffer is in use

    // The built-in interstage IO map considers e.g, EvqPosition on input and output separately, so that we
    // can build the linkage correctly if position appears on both sides.  Otherwise, multiple positions
    // are considered identical.
    struct tInterstageIoData {
        tInterstageIoData(TBuiltInVariable bi, TStorageQualifier q) :
            builtIn(bi), storage(q) { }

        TBuiltInVariable  builtIn;
        TStorageQualifier storage;

        // ordering for maps
        bool operator<(const tInterstageIoData d) const {
            return (builtIn != d.builtIn) ? (builtIn < d.builtIn) : (storage < d.storage);
        }
    };

    TMap<tInterstageIoData, TVariable*> splitBuiltIns; // split built-ins, indexed by built-in type.
    TVariable* inputPatch; // input patch is special for PCF: it's the only non-builtin PCF input,
                           // and is handled as a pseudo-builtin.

    unsigned int nextInLocation;
    unsigned int nextOutLocation;

    TFunction* entryPointFunction;
    TIntermNode* entryPointFunctionBody;

    TString patchConstantFunctionName; // hull shader patch constant function name, from function level attribute.
    TMap<TBuiltInVariable, TSymbol*> builtInTessLinkageSymbols; // used for tessellation, finding declared built-ins

    TVector<TString> currentTypePrefix;      // current scoping prefix for nested structures
    TVector<TVariable*> implicitThisStack;   // currently active 'this' variables for nested structures

    TVariable* gsStreamOutput;               // geometry shader stream outputs, for emit (Append method)

    TVariable* clipDistanceOutput;           // synthesized clip distance out variable (shader might have >1)
    TVariable* cullDistanceOutput;           // synthesized cull distance out variable (shader might have >1)
    TVariable* clipDistanceInput;            // synthesized clip distance in variable (shader might have >1)
    TVariable* cullDistanceInput;            // synthesized cull distance in variable (shader might have >1)

    static const int maxClipCullRegs = 2;
    std::array<int, maxClipCullRegs> clipSemanticNSizeIn;  // vector, indexed by clip semantic ID
    std::array<int, maxClipCullRegs> cullSemanticNSizeIn;  // vector, indexed by cull semantic ID
    std::array<int, maxClipCullRegs> clipSemanticNSizeOut; // vector, indexed by clip semantic ID
    std::array<int, maxClipCullRegs> cullSemanticNSizeOut; // vector, indexed by cull semantic ID

    // This tracks the first (mip level) argument to the .mips[][] operator.  Since this can be nested as
    // in tx.mips[tx.mips[0][1].x][2], we need a stack.  We also track the TSourceLoc for error reporting
    // purposes.
    struct tMipsOperatorData {
        tMipsOperatorData(TSourceLoc l, TIntermTyped* m) : loc(l), mipLevel(m) { }
        TSourceLoc loc;
        TIntermTyped* mipLevel;
    };

    TVector<tMipsOperatorData> mipsOperatorMipArg;

    // The geometry output stream is not copied out from the entry point as a typical output variable
    // is.  It's written via EmitVertex (hlsl=Append), which may happen in arbitrary control flow.
    // For this we need the real output symbol.  Since it may not be known at the time and Append()
    // method is parsed, the sequence will be patched during finalization.
    struct tGsAppendData {
        TIntermAggregate* node;
        TSourceLoc loc;
    };

    TVector<tGsAppendData> gsAppends;

    // A texture object may be used with shadow and non-shadow samplers, but both may not be
    // alive post-DCE in the same shader.  We do not know at compilation time which are alive: that's
    // only known post-DCE.  If a texture is used both ways, we create two textures, and
    // leave the elimiation of one to the optimizer.  This maps the shader variant to
    // the shadow variant.
    //
    // This can be removed if and when the texture shadow code in
    // HlslParseContext::handleSamplerTextureCombine is removed.
    struct tShadowTextureSymbols {
        tShadowTextureSymbols() { symId.fill(-1); }

        void set(bool shadow, long long id) { symId[int(shadow)] = id; }
        long long get(bool shadow) const { return symId[int(shadow)]; }

        // True if this texture has been seen with both shadow and non-shadow modes
        bool overloaded() const { return symId[0] != -1 && symId[1] != -1; }
        bool isShadowId(long long id) const { return symId[1] == id; }

    private:
        std::array<long long, 2> symId;
    };

    TMap<long long, tShadowTextureSymbols*> textureShadowVariant;
    bool parsingEntrypointParameters;
};

// This is the prefix we use for built-in methods to avoid namespace collisions with
// global scope user functions.
// TODO: this would be better as a nonparseable character, but that would
// require changing the scanner.
#define BUILTIN_PREFIX "__BI_"

} // end namespace glslang

#endif // HLSL_PARSE_INCLUDED_