Coverage Report

Created: 2026-09-13 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
3
// Copyright (C) 2013-2016 LunarG, Inc.
4
// Copyright (C) 2015-2020 Google, Inc.
5
//
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions
10
// are met:
11
//
12
//    Redistributions of source code must retain the above copyright
13
//    notice, this list of conditions and the following disclaimer.
14
//
15
//    Redistributions in binary form must reproduce the above
16
//    copyright notice, this list of conditions and the following
17
//    disclaimer in the documentation and/or other materials provided
18
//    with the distribution.
19
//
20
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
21
//    contributors may be used to endorse or promote products derived
22
//    from this software without specific prior written permission.
23
//
24
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
// POSSIBILITY OF SUCH DAMAGE.
36
//
37
38
//
39
// Implement the top-level of interface to the compiler/linker,
40
// as defined in ShaderLang.h
41
// This is the platform independent interface between an OGL driver
42
// and the shading language compiler/linker.
43
//
44
#include <cstring>
45
#include <iostream>
46
#include <sstream>
47
#include <memory>
48
#include <mutex>
49
#include "SymbolTable.h"
50
#include "ParseHelper.h"
51
#include "Scan.h"
52
#include "ScanContext.h"
53
54
#ifdef ENABLE_HLSL
55
#include "../HLSL/hlslParseHelper.h"
56
#include "../HLSL/hlslParseables.h"
57
#include "../HLSL/hlslScanContext.h"
58
#endif
59
60
#include "../Include/ShHandle.h"
61
62
#include "preprocessor/PpContext.h"
63
64
#define SH_EXPORTING
65
#include "../Public/ShaderLang.h"
66
#include "reflection.h"
67
#include "iomapper.h"
68
#include "Initialize.h"
69
70
// TODO: this really shouldn't be here, it is only because of the trial addition
71
// of printing pre-processed tokens, which requires knowing the string literal
72
// token to print ", but none of that seems appropriate for this file.
73
#include "preprocessor/PpTokens.h"
74
75
// Build-time generated includes
76
#include "glslang/build_info.h"
77
78
namespace { // anonymous namespace for file-local functions and symbols
79
80
// Total number of successful initializers of glslang: a refcount
81
// Shared global; access should be protected by a global mutex/critical section.
82
int NumberOfClients = 0;
83
84
// global initialization lock
85
#ifndef DISABLE_THREAD_SUPPORT
86
std::mutex init_lock;
87
#endif
88
89
90
using namespace glslang;
91
92
// Create a language specific version of parseables.
93
TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source)
94
17.0k
{
95
17.0k
    switch (source) {
96
16.8k
    case EShSourceGlsl: return new TBuiltIns();              // GLSL builtIns
97
0
#ifdef ENABLE_HLSL
98
158
    case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics
99
0
#endif
100
101
0
    default:
102
0
        infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
103
0
        return nullptr;
104
17.0k
    }
105
17.0k
}
106
107
// Create a language specific version of a parse context.
108
TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate,
109
                                      int version, EProfile profile, EShSource source,
110
                                      EShLanguage language, TInfoSink& infoSink,
111
                                      SpvVersion spvVersion, bool forwardCompatible, EShMessages messages,
112
                                      bool parsingBuiltIns, std::string sourceEntryPointName = "")
113
57.2k
{
114
57.2k
    switch (source) {
115
56.3k
    case EShSourceGlsl: {
116
56.3k
        if (sourceEntryPointName.size() == 0)
117
56.3k
            intermediate.setEntryPointName("main");
118
56.3k
        TString entryPoint = sourceEntryPointName.c_str();
119
56.3k
        return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
120
56.3k
                                 language, infoSink, forwardCompatible, messages, &entryPoint);
121
0
    }
122
0
#ifdef ENABLE_HLSL
123
927
    case EShSourceHlsl:
124
927
        return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
125
927
                                    language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages);
126
0
#endif
127
0
    default:
128
0
        infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
129
0
        return nullptr;
130
57.2k
    }
131
57.2k
}
132
133
// Local mapping functions for making arrays of symbol tables....
134
135
const int VersionCount = 17;  // index range in MapVersionToIndex
136
137
int MapVersionToIndex(int version)
138
24.1k
{
139
24.1k
    int index = 0;
140
141
24.1k
    switch (version) {
142
0
    case 100: index =  0; break;
143
0
    case 110: index =  1; break;
144
0
    case 120: index =  2; break;
145
0
    case 130: index =  3; break;
146
7.06k
    case 140: index =  4; break;
147
1.29k
    case 150: index =  5; break;
148
0
    case 300: index =  6; break;
149
1.24k
    case 330: index =  7; break;
150
1.47k
    case 400: index =  8; break;
151
6
    case 410: index =  9; break;
152
126
    case 420: index = 10; break;
153
2.36k
    case 430: index = 11; break;
154
766
    case 440: index = 12; break;
155
1.47k
    case 310: index = 13; break;
156
6.80k
    case 450: index = 14; break;
157
222
    case 500: index =  0; break; // HLSL
158
328
    case 320: index = 15; break;
159
994
    case 460: index = 16; break;
160
0
    default:  assert(0);  break;
161
24.1k
    }
162
163
24.1k
    assert(index < VersionCount);
164
165
24.1k
    return index;
166
24.1k
}
167
168
const int SpvVersionCount = 4;  // index range in MapSpvVersionToIndex
169
170
int MapSpvVersionToIndex(const SpvVersion& spvVersion)
171
24.1k
{
172
24.1k
    int index = 0;
173
174
24.1k
    if (spvVersion.openGl > 0)
175
0
        index = 1;
176
24.1k
    else if (spvVersion.vulkan > 0) {
177
24.1k
        if (!spvVersion.vulkanRelaxed)
178
24.1k
            index = 2;
179
0
        else
180
0
            index = 3;
181
24.1k
    }
182
183
24.1k
    assert(index < SpvVersionCount);
184
185
24.1k
    return index;
186
24.1k
}
187
188
const int ProfileCount = 4;   // index range in MapProfileToIndex
189
190
int MapProfileToIndex(EProfile profile)
191
24.1k
{
192
24.1k
    int index = 0;
193
194
24.1k
    switch (profile) {
195
7.06k
    case ENoProfile:            index = 0; break;
196
15.2k
    case ECoreProfile:          index = 1; break;
197
78
    case ECompatibilityProfile: index = 2; break;
198
1.80k
    case EEsProfile:            index = 3; break;
199
0
    default:                               break;
200
24.1k
    }
201
202
24.1k
    assert(index < ProfileCount);
203
204
24.1k
    return index;
205
24.1k
}
206
207
const int SourceCount = 2;
208
209
int MapSourceToIndex(EShSource source)
210
24.1k
{
211
24.1k
    int index = 0;
212
213
24.1k
    switch (source) {
214
23.9k
    case EShSourceGlsl: index = 0; break;
215
222
    case EShSourceHlsl: index = 1; break;
216
0
    default:                       break;
217
24.1k
    }
218
219
24.1k
    assert(index < SourceCount);
220
221
24.1k
    return index;
222
24.1k
}
223
224
// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins
225
enum EPrecisionClass {
226
    EPcGeneral,
227
    EPcFragment,
228
    EPcCount
229
};
230
231
// A process-global symbol table per version per profile for built-ins common
232
// to multiple stages (languages), and a process-global symbol table per version
233
// per profile per stage for built-ins unique to each stage.  They will be sparsely
234
// populated, so they will only be generated as needed.
235
//
236
// Each has a different set of built-ins, and we want to preserve that from
237
// compile to compile.
238
//
239
TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {};
240
TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {};
241
242
TPoolAllocator* PerProcessGPA = nullptr;
243
244
//
245
// Parse and add to the given symbol table the content of the given shader string.
246
//
247
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
248
                           EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable)
249
45.2k
{
250
45.2k
    TIntermediate intermediate(language, version, profile);
251
252
45.2k
    intermediate.setSource(source);
253
254
45.2k
    std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source,
255
45.2k
                                                                       language, infoSink, spvVersion, true, EShMsgDefault,
256
45.2k
                                                                       true));
257
258
45.2k
    TShader::ForbidIncluder includer;
259
45.2k
    TPpContext ppContext(*parseContext, "", includer);
260
45.2k
    TScanContext scanContext(*parseContext);
261
45.2k
    parseContext->setScanContext(&scanContext);
262
45.2k
    parseContext->setPpContext(&ppContext);
263
264
    //
265
    // Push the symbol table to give it an initial scope.  This
266
    // push should not have a corresponding pop, so that built-ins
267
    // are preserved, and the test for an empty table fails.
268
    //
269
270
45.2k
    symbolTable.push();
271
272
45.2k
    const char* builtInShaders[2];
273
45.2k
    size_t builtInLengths[2];
274
45.2k
    builtInShaders[0] = builtIns.c_str();
275
45.2k
    builtInLengths[0] = builtIns.size();
276
277
45.2k
    if (builtInLengths[0] == 0)
278
769
        return true;
279
280
44.4k
    TInputScanner input(1, builtInShaders, builtInLengths);
281
44.4k
    if (! parseContext->parseShaderStrings(ppContext, input) != 0) {
282
12
        infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
283
12
        printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str());
284
12
        printf("%s\n", builtInShaders[0]);
285
286
12
        return false;
287
12
    }
288
289
44.4k
    return true;
290
44.4k
}
291
292
int CommonIndex(EProfile profile, EShLanguage language)
293
55.6k
{
294
55.6k
    return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
295
55.6k
}
296
297
//
298
// To initialize per-stage shared tables, with the common table already complete.
299
//
300
bool InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion,
301
                                EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable,
302
                                TSymbolTable** symbolTables)
303
27.8k
{
304
27.8k
    (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
305
27.8k
    if (!InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source,
306
27.8k
                          infoSink, *symbolTables[language]))
307
6
        return false;
308
27.8k
    builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]);
309
27.8k
    if (profile == EEsProfile && version >= 300)
310
1.80k
        (*symbolTables[language]).setNoBuiltInRedeclarations();
311
27.8k
    if (version == 110)
312
0
        (*symbolTables[language]).setSeparateNameSpaces();
313
314
27.8k
    return true;
315
27.8k
}
316
317
//
318
// Initialize the full set of shareable symbol tables;
319
// The common (cross-stage) and those shareable per-stage.
320
//
321
bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable,  TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
322
4.97k
{
323
4.97k
    bool success = true;
324
4.97k
    std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
325
326
4.97k
    if (builtInParseables == nullptr)
327
0
        return false;
328
329
4.97k
    builtInParseables->initialize(version, profile, spvVersion);
330
331
    // do the common tables
332
4.97k
    success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source,
333
4.97k
                          infoSink, *commonTable[EPcGeneral]);
334
4.97k
    if (profile == EEsProfile)
335
285
        success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source,
336
285
                              infoSink, *commonTable[EPcFragment]);
337
338
    // do the per-stage tables
339
340
    // always have vertex and fragment
341
4.97k
    success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source,
342
4.97k
                               infoSink, commonTable, symbolTables);
343
4.97k
    success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source,
344
4.97k
                               infoSink, commonTable, symbolTables);
345
346
    // check for tessellation
347
4.97k
    if ((profile != EEsProfile && version >= 150) ||
348
2.89k
        (profile == EEsProfile && version >= 310)) {
349
2.36k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source,
350
2.36k
                                   infoSink, commonTable, symbolTables);
351
2.36k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source,
352
2.36k
                                   infoSink, commonTable, symbolTables);
353
2.36k
    }
354
355
    // check for geometry
356
4.97k
    if ((profile != EEsProfile && version >= 150) ||
357
2.89k
        (profile == EEsProfile && version >= 310))
358
2.36k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source,
359
2.36k
                                   infoSink, commonTable, symbolTables);
360
361
    // check for compute
362
4.97k
    if ((profile != EEsProfile && version >= 420) ||
363
3.43k
        (profile == EEsProfile && version >= 310))
364
1.82k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source,
365
1.82k
                                   infoSink, commonTable, symbolTables);
366
367
    // check for ray tracing stages
368
4.97k
    if (profile != EEsProfile && version >= 450) {
369
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source,
370
1.11k
            infoSink, commonTable, symbolTables);
371
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source,
372
1.11k
            infoSink, commonTable, symbolTables);
373
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source,
374
1.11k
            infoSink, commonTable, symbolTables);
375
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source,
376
1.11k
            infoSink, commonTable, symbolTables);
377
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source,
378
1.11k
            infoSink, commonTable, symbolTables);
379
1.11k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source,
380
1.11k
            infoSink, commonTable, symbolTables);
381
1.11k
    }
382
383
    // check for mesh
384
4.97k
    if ((profile != EEsProfile && version >= 450) ||
385
3.86k
        (profile == EEsProfile && version >= 320))
386
1.16k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMesh, source,
387
1.16k
                                   infoSink, commonTable, symbolTables);
388
389
    // check for task
390
4.97k
    if ((profile != EEsProfile && version >= 450) ||
391
3.86k
        (profile == EEsProfile && version >= 320))
392
1.16k
        success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTask, source,
393
1.16k
                                   infoSink, commonTable, symbolTables);
394
395
4.97k
    return success;
396
4.97k
}
397
398
bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version,
399
                               EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source)
400
12.0k
{
401
12.0k
    std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
402
403
12.0k
    if (builtInParseables == nullptr)
404
0
        return false;
405
406
12.0k
    builtInParseables->initialize(*resources, version, profile, spvVersion, language);
407
12.0k
    if (!InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable))
408
0
        return false;
409
12.0k
    builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources);
410
411
12.0k
    return true;
412
12.0k
}
413
414
//
415
// To do this on the fly, we want to leave the current state of our thread's
416
// pool allocator intact, so:
417
//  - Switch to a new pool for parsing the built-ins
418
//  - Do the parsing, which builds the symbol table, using the new pool
419
//  - Switch to the process-global pool to save a copy of the resulting symbol table
420
//  - Free up the new pool used to parse the built-ins
421
//  - Switch back to the original thread's pool
422
//
423
// This only gets done the first time any thread needs a particular symbol table
424
// (lazy evaluation).
425
//
426
bool SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
427
12.0k
{
428
12.0k
    TInfoSink infoSink;
429
12.0k
    bool success;
430
431
    // Make sure only one thread tries to do this at a time
432
12.0k
#ifndef DISABLE_THREAD_SUPPORT
433
12.0k
    const std::lock_guard<std::mutex> lock(init_lock);
434
12.0k
#endif
435
436
    // See if it's already been done for this version/profile combination
437
12.0k
    int versionIndex = MapVersionToIndex(version);
438
12.0k
    int spvVersionIndex = MapSpvVersionToIndex(spvVersion);
439
12.0k
    int profileIndex = MapProfileToIndex(profile);
440
12.0k
    int sourceIndex = MapSourceToIndex(source);
441
12.0k
    if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) {
442
7.10k
        return true;
443
7.10k
    }
444
445
    // Switch to a new pool
446
4.97k
    TPoolAllocator& previousAllocator = GetThreadPoolAllocator();
447
4.97k
    TPoolAllocator* builtInPoolAllocator = new TPoolAllocator;
448
4.97k
    SetThreadPoolAllocator(builtInPoolAllocator);
449
450
    // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped.
451
4.97k
    TSymbolTable* commonTable[EPcCount];
452
4.97k
    TSymbolTable* stageTables[EShLangCount];
453
14.9k
    for (int precClass = 0; precClass < EPcCount; ++precClass)
454
9.95k
        commonTable[precClass] = new TSymbolTable;
455
74.6k
    for (int stage = 0; stage < EShLangCount; ++stage)
456
69.6k
        stageTables[stage] = new TSymbolTable;
457
458
    // Generate the local symbol tables using the new pool
459
4.97k
    if (!InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source)) {
460
6
        success = false;
461
6
        goto cleanup;
462
6
    }
463
464
    // Switch to the process-global pool
465
4.97k
    SetThreadPoolAllocator(PerProcessGPA);
466
467
    // Copy the local symbol tables from the new pool to the global tables using the process-global pool
468
14.9k
    for (int precClass = 0; precClass < EPcCount; ++precClass) {
469
9.94k
        if (! commonTable[precClass]->isEmpty()) {
470
5.25k
            CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable;
471
5.25k
            CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]);
472
5.25k
            CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly();
473
5.25k
        }
474
9.94k
    }
475
74.5k
    for (int stage = 0; stage < EShLangCount; ++stage) {
476
69.5k
        if (! stageTables[stage]->isEmpty()) {
477
27.7k
            SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable;
478
27.7k
            SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable
479
27.7k
                              [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]);
480
27.7k
            SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]);
481
27.7k
            SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly();
482
27.7k
        }
483
69.5k
    }
484
4.97k
    success = true;
485
486
4.97k
cleanup:
487
    // Clean up the local tables before deleting the pool they used.
488
14.9k
    for (int precClass = 0; precClass < EPcCount; ++precClass)
489
9.95k
        delete commonTable[precClass];
490
74.6k
    for (int stage = 0; stage < EShLangCount; ++stage)
491
69.6k
        delete stageTables[stage];
492
493
4.97k
    delete builtInPoolAllocator;
494
4.97k
    SetThreadPoolAllocator(&previousAllocator);
495
496
4.97k
    return success;
497
4.97k
}
498
499
// Function to Print all builtins
500
void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable)
501
0
{
502
0
    infoSink.debug << "BuiltinSymbolTable {\n";
503
504
0
    symbolTable.dump(infoSink, true);
505
506
0
    infoSink.debug << "}\n";
507
0
}
508
509
// Return true if the shader was correctly specified for version/profile/stage.
510
bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion,
511
                          EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion)
512
12.0k
{
513
12.0k
    const int FirstProfileVersion = 150;
514
12.0k
    bool correct = true;
515
516
12.0k
    if (source == EShSourceHlsl) {
517
111
        version = 500;          // shader model; currently a characteristic of glslang, not the input
518
111
        profile = ECoreProfile; // allow doubles in prototype parsing
519
111
        return correct;
520
111
    }
521
522
    // Get a version...
523
11.9k
    if (version == 0) {
524
3.19k
        version = defaultVersion;
525
        // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader");
526
3.19k
    }
527
528
    // Get a good profile...
529
11.9k
    if (profile == ENoProfile) {
530
8.54k
        if (version == 300 || version == 310 || version == 320) {
531
18
            correct = false;
532
18
            infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile");
533
18
            profile = EEsProfile;
534
8.52k
        } else if (version == 100)
535
56
            profile = EEsProfile;
536
8.47k
        else if (version >= FirstProfileVersion)
537
4.87k
            profile = ECoreProfile;
538
3.59k
        else
539
3.59k
            profile = ENoProfile;
540
8.54k
    } else {
541
        // a profile was provided...
542
3.42k
        if (version < 150) {
543
0
            correct = false;
544
0
            infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token");
545
0
            if (version == 100)
546
0
                profile = EEsProfile;
547
0
            else
548
0
                profile = ENoProfile;
549
3.42k
        } else if (version == 300 || version == 310 || version == 320) {
550
828
            if (profile != EEsProfile) {
551
0
                correct = false;
552
0
                infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile");
553
0
            }
554
828
            profile = EEsProfile;
555
2.60k
        } else {
556
2.60k
            if (profile == EEsProfile) {
557
3
                correct = false;
558
3
                infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile");
559
3
                if (version >= FirstProfileVersion)
560
3
                    profile = ECoreProfile;
561
0
                else
562
0
                    profile = ENoProfile;
563
3
            }
564
            // else: typical desktop case... e.g., "#version 410 core"
565
2.60k
        }
566
3.42k
    }
567
568
    // Fix version...
569
11.9k
    switch (version) {
570
    // ES versions
571
56
    case 100: break;
572
94
    case 300: break;
573
588
    case 310: break;
574
164
    case 320: break;
575
576
    // desktop versions
577
3.20k
    case 110: break;
578
0
    case 120: break;
579
40
    case 130: break;
580
296
    case 140: break;
581
647
    case 150: break;
582
623
    case 330: break;
583
732
    case 400: break;
584
3
    case 410: break;
585
63
    case 420: break;
586
1.18k
    case 430: break;
587
383
    case 440: break;
588
3.33k
    case 450: break;
589
497
    case 460: break;
590
591
    // unknown version
592
64
    default:
593
64
        correct = false;
594
64
        infoSink.info.message(EPrefixError, "version not supported");
595
64
        if (profile == EEsProfile)
596
0
            version = 310;
597
64
        else {
598
64
            version = 450;
599
64
            profile = ECoreProfile;
600
64
        }
601
64
        break;
602
11.9k
    }
603
604
    // Correct for stage type...
605
11.9k
    switch (stage) {
606
1
    case EShLangGeometry:
607
1
        if ((profile == EEsProfile && version < 310) ||
608
1
            (profile != EEsProfile && version < 150)) {
609
1
            correct = false;
610
1
            infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above");
611
1
            version = (profile == EEsProfile) ? 310 : 150;
612
1
            if (profile == EEsProfile || profile == ENoProfile)
613
1
                profile = ECoreProfile;
614
1
        }
615
1
        break;
616
8
    case EShLangTessControl:
617
10
    case EShLangTessEvaluation:
618
10
        if ((profile == EEsProfile && version < 310) ||
619
10
            (profile != EEsProfile && version < 150)) {
620
4
            correct = false;
621
4
            infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above");
622
4
            version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not
623
4
            if (profile == EEsProfile || profile == ENoProfile)
624
4
                profile = ECoreProfile;
625
4
        }
626
10
        break;
627
0
    case EShLangCompute:
628
0
        if ((profile == EEsProfile && version < 310) ||
629
0
            (profile != EEsProfile && version < 420)) {
630
0
            correct = false;
631
0
            infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above");
632
0
            version = profile == EEsProfile ? 310 : 420;
633
0
        }
634
0
        break;
635
0
    case EShLangRayGen:
636
0
    case EShLangIntersect:
637
0
    case EShLangAnyHit:
638
0
    case EShLangClosestHit:
639
0
    case EShLangMiss:
640
0
    case EShLangCallable:
641
0
        if (profile == EEsProfile || version < 460) {
642
0
            correct = false;
643
0
            infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above");
644
0
            version = 460;
645
0
        }
646
0
        break;
647
6
    case EShLangMesh:
648
6
    case EShLangTask:
649
6
        if ((profile == EEsProfile && version < 320) ||
650
6
            (profile != EEsProfile && version < 450)) {
651
6
            correct = false;
652
6
            infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above");
653
6
            version = profile == EEsProfile ? 320 : 450;
654
6
        }
655
6
        break;
656
11.9k
    default:
657
11.9k
        break;
658
11.9k
    }
659
660
11.9k
    if (profile == EEsProfile && version >= 300 && versionNotFirst) {
661
12
        correct = false;
662
12
        infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines");
663
12
    }
664
665
    // Check for SPIR-V compatibility
666
11.9k
    if (spvVersion.spv != 0) {
667
11.9k
        switch (profile) {
668
902
        case EEsProfile:
669
902
            if (version < 310) {
670
150
                correct = false;
671
150
                infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher");
672
150
                version = 310;
673
150
            }
674
902
            break;
675
39
        case ECompatibilityProfile:
676
39
            infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile");
677
39
            break;
678
11.0k
        default:
679
11.0k
            if (spvVersion.vulkan > 0 && version < 140) {
680
3.23k
                correct = false;
681
3.23k
                infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher");
682
3.23k
                version = 140;
683
3.23k
            }
684
11.0k
            if (spvVersion.openGl >= 100 && version < 330) {
685
0
                correct = false;
686
0
                infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher");
687
0
                version = 330;
688
0
            }
689
11.0k
            break;
690
11.9k
        }
691
11.9k
    }
692
693
11.9k
    return correct;
694
11.9k
}
695
696
// There are multiple paths in for setting environment stuff.
697
// TEnvironment takes precedence, for what it sets, so sort all this out.
698
// Ideally, the internal code could be made to use TEnvironment, but for
699
// now, translate it to the historically used parameters.
700
void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source,
701
                          EShLanguage& stage, SpvVersion& spvVersion)
702
12.0k
{
703
    // Set up environmental defaults, first ignoring 'environment'.
704
12.0k
    if (messages & EShMsgSpvRules)
705
12.0k
        spvVersion.spv = EShTargetSpv_1_0;
706
12.0k
    if (messages & EShMsgVulkanRules) {
707
12.0k
        spvVersion.vulkan = EShTargetVulkan_1_0;
708
12.0k
        spvVersion.vulkanGlsl = 100;
709
12.0k
    } else if (spvVersion.spv != 0)
710
0
        spvVersion.openGl = 100;
711
712
    // Now, override, based on any content set in 'environment'.
713
    // 'environment' must be cleared to ESh*None settings when items
714
    // are not being set.
715
12.0k
    if (environment != nullptr) {
716
        // input language
717
12.0k
        if (environment->input.languageFamily != EShSourceNone) {
718
0
            stage = environment->input.stage;
719
0
            switch (environment->input.dialect) {
720
0
            case EShClientNone:
721
0
                break;
722
0
            case EShClientVulkan:
723
0
                spvVersion.vulkanGlsl = environment->input.dialectVersion;
724
0
                spvVersion.vulkanRelaxed = environment->input.vulkanRulesRelaxed;
725
0
                break;
726
0
            case EShClientOpenGL:
727
0
                spvVersion.openGl = environment->input.dialectVersion;
728
0
                break;
729
0
            case EShClientCount:
730
0
                assert(0);
731
0
                break;
732
0
            }
733
0
            switch (environment->input.languageFamily) {
734
0
            case EShSourceNone:
735
0
                break;
736
0
            case EShSourceGlsl:
737
0
                source = EShSourceGlsl;
738
0
                messages = static_cast<EShMessages>(messages & ~EShMsgReadHlsl);
739
0
                break;
740
0
            case EShSourceHlsl:
741
0
                source = EShSourceHlsl;
742
0
                messages = static_cast<EShMessages>(messages | EShMsgReadHlsl);
743
0
                break;
744
0
            case EShSourceCount:
745
0
                assert(0);
746
0
                break;
747
0
            }
748
0
        }
749
750
        // client
751
12.0k
        switch (environment->client.client) {
752
12.0k
        case EShClientVulkan:
753
12.0k
            spvVersion.vulkan = environment->client.version;
754
12.0k
            break;
755
0
        default:
756
0
            break;
757
12.0k
        }
758
759
        // generated code
760
12.0k
        switch (environment->target.language) {
761
6.30k
        case EshTargetSpv:
762
6.30k
            spvVersion.spv = environment->target.version;
763
6.30k
            break;
764
5.78k
        default:
765
5.78k
            break;
766
12.0k
        }
767
12.0k
    }
768
12.0k
}
769
770
// Most processes are recorded when set in the intermediate representation,
771
// These are the few that are not.
772
void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName)
773
12.0k
{
774
12.0k
    if ((messages & EShMsgRelaxedErrors) != 0)
775
0
        intermediate.addProcess("relaxed-errors");
776
12.0k
    if ((messages & EShMsgSuppressWarnings) != 0)
777
0
        intermediate.addProcess("suppress-warnings");
778
12.0k
    if ((messages & EShMsgKeepUncalled) != 0)
779
0
        intermediate.addProcess("keep-uncalled");
780
12.0k
    if ((messages & EShMsgRelaxSetBindingLimits) != 0)
781
0
        intermediate.setRelaxSetBindingLimits(true);
782
12.0k
    if (sourceEntryPointName.size() > 0) {
783
0
        intermediate.addProcess("source-entrypoint");
784
0
        intermediate.addProcessArgument(sourceEntryPointName);
785
0
    }
786
12.0k
}
787
788
// This is the common setup and cleanup code for PreprocessDeferred and
789
// CompileDeferred.
790
// It takes any callable with a signature of
791
//  bool (TParseContextBase& parseContext, TPpContext& ppContext,
792
//                  TInputScanner& input, bool versionWillBeError,
793
//                  TSymbolTable& , TIntermediate& ,
794
//                  EShOptimizationLevel , EShMessages );
795
// Which returns false if a failure was detected and true otherwise.
796
//
797
template<typename ProcessingContext>
798
bool ProcessDeferred(
799
    TCompiler* compiler,
800
    const char* const shaderStrings[],
801
    const int numStrings,
802
    const int* inputLengths,
803
    const char* const stringNames[],
804
    const char* customPreamble,
805
    const EShOptimizationLevel optLevel,
806
    const TBuiltInResource* resources,
807
    int defaultVersion,  // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
808
    EProfile defaultProfile,
809
    // set version/profile to defaultVersion/defaultProfile regardless of the #version
810
    // directive in the source code
811
    bool forceDefaultVersionAndProfile,
812
    int overrideVersion, // overrides version specified by #version or default version
813
    bool forwardCompatible,     // give errors for use of deprecated features
814
    EShMessages messages,       // warnings/errors/AST; things to print out
815
    TIntermediate& intermediate, // returned tree, etc.
816
    ProcessingContext& processingContext,
817
    bool requireNonempty,
818
    TShader::Includer& includer,
819
    const std::string sourceEntryPointName = "",
820
    const TEnvironment* environment = nullptr,  // optional way of fully setting all versions, overriding the above
821
    bool compileOnly = false)
822
12.0k
{
823
    // This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
824
12.0k
    GetThreadPoolAllocator().push();
825
826
12.0k
    if (numStrings == 0)
827
0
        return true;
828
829
    // Move to length-based strings, rather than null-terminated strings.
830
    // Also, add strings to include the preamble and to ensure the shader is not null,
831
    // which lets the grammar accept what was a null (post preprocessing) shader.
832
    //
833
    // Shader will look like
834
    //   string 0:                system preamble
835
    //   string 1:                custom preamble
836
    //   string 2...numStrings+1: user's shader
837
    //   string numStrings+2:     "int;"
838
12.0k
    const int numPre = 2;
839
12.0k
    const int numPost = requireNonempty? 1 : 0;
840
12.0k
    const int numTotal = numPre + numStrings + numPost;
841
12.0k
    std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
842
12.0k
    std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
843
12.0k
    std::unique_ptr<const char*[]> names(new const char*[numTotal]);
844
24.1k
    for (int s = 0; s < numStrings; ++s) {
845
12.0k
        strings[s + numPre] = shaderStrings[s];
846
12.0k
        if (inputLengths == nullptr || inputLengths[s] < 0)
847
0
            lengths[s + numPre] = strlen(shaderStrings[s]);
848
12.0k
        else
849
12.0k
            lengths[s + numPre] = inputLengths[s];
850
12.0k
    }
851
12.0k
    if (stringNames != nullptr) {
852
24.1k
        for (int s = 0; s < numStrings; ++s)
853
12.0k
            names[s + numPre] = stringNames[s];
854
12.0k
    } else {
855
0
        for (int s = 0; s < numStrings; ++s)
856
0
            names[s + numPre] = nullptr;
857
0
    }
858
859
    // Get all the stages, languages, clients, and other environment
860
    // stuff sorted out.
861
12.0k
    EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
862
12.0k
    SpvVersion spvVersion;
863
12.0k
    EShLanguage stage = compiler->getLanguage();
864
12.0k
    TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion);
865
12.0k
#ifdef ENABLE_HLSL
866
12.0k
    EShSource source = sourceGuess;
867
12.0k
    if (environment != nullptr && environment->target.hlslFunctionality1)
868
2.03k
        intermediate.setHlslFunctionality1();
869
#else
870
    const EShSource source = EShSourceGlsl;
871
#endif
872
    // First, without using the preprocessor or parser, find the #version, so we know what
873
    // symbol tables, processing rules, etc. to set up.  This does not need the extra strings
874
    // outlined above, just the user shader, after the system and user preambles.
875
12.0k
    glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
876
12.0k
    int version = 0;
877
12.0k
    EProfile profile = ENoProfile;
878
12.0k
    bool versionNotFirstToken = false;
879
12.0k
    bool versionNotFirst = (source == EShSourceHlsl)
880
12.0k
                                ? true
881
12.0k
                                : userInput.scanVersion(version, profile, versionNotFirstToken);
882
12.0k
    bool versionNotFound = version == 0;
883
12.0k
    if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
884
0
        if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
885
0
            (version != defaultVersion || profile != defaultProfile)) {
886
0
            compiler->infoSink.info << "Warning, (version, profile) forced to be ("
887
0
                                    << defaultVersion << ", " << ProfileName(defaultProfile)
888
0
                                    << "), while in source code it is ("
889
0
                                    << version << ", " << ProfileName(profile) << ")\n";
890
0
        }
891
892
0
        if (versionNotFound) {
893
0
            versionNotFirstToken = false;
894
0
            versionNotFirst = false;
895
0
            versionNotFound = false;
896
0
        }
897
0
        version = defaultVersion;
898
0
        profile = defaultProfile;
899
0
    }
900
12.0k
    if (source == EShSourceGlsl && overrideVersion != 0) {
901
0
        version = overrideVersion;
902
0
    }
903
904
12.0k
    bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
905
12.0k
                                            versionNotFirst, defaultVersion, source, version, profile, spvVersion);
906
12.0k
    bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
907
12.0k
    bool warnVersionNotFirst = false;
908
12.0k
    if (! versionWillBeError && versionNotFirstToken) {
909
94
        if (messages & EShMsgRelaxedErrors)
910
0
            warnVersionNotFirst = true;
911
94
        else
912
94
            versionWillBeError = true;
913
94
    }
914
915
12.0k
    intermediate.setSource(source);
916
12.0k
    intermediate.setVersion(version);
917
12.0k
    intermediate.setProfile(profile);
918
12.0k
    intermediate.setSpv(spvVersion);
919
12.0k
    RecordProcesses(intermediate, messages, sourceEntryPointName);
920
12.0k
    if (spvVersion.vulkan > 0)
921
12.0k
        intermediate.setOriginUpperLeft();
922
12.0k
#ifdef ENABLE_HLSL
923
12.0k
    if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
924
2.05k
        intermediate.setHlslOffsets();
925
12.0k
#endif
926
12.0k
    if (messages & EShMsgDebugInfo) {
927
0
        intermediate.setSourceFile(names[numPre]);
928
0
        for (int s = 0; s < numStrings; ++s) {
929
            // The string may not be null-terminated, so make sure we provide
930
            // the length along with the string.
931
0
            intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
932
0
        }
933
0
    }
934
12.0k
    if (!SetupBuiltinSymbolTable(version, profile, spvVersion, source)) {
935
6
        return false;
936
6
    }
937
938
12.0k
    TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
939
12.0k
                                                  [MapSpvVersionToIndex(spvVersion)]
940
12.0k
                                                  [MapProfileToIndex(profile)]
941
12.0k
                                                  [MapSourceToIndex(source)]
942
12.0k
                                                  [stage];
943
944
    // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
945
12.0k
    std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
946
12.0k
    if (cachedTable)
947
12.0k
        symbolTable->adoptLevels(*cachedTable);
948
949
12.0k
    if (intermediate.getUniqueId() != 0)
950
0
        symbolTable->overwriteUniqueId(intermediate.getUniqueId());
951
952
    // Add built-in symbols that are potentially context dependent;
953
    // they get popped again further down.
954
12.0k
    if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
955
12.0k
                                    stage, source)) {
956
0
        return false;
957
0
    }
958
959
12.0k
    if (messages & EShMsgBuiltinSymbolTable)
960
0
        DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable);
961
962
    //
963
    // Now we can process the full shader under proper symbols and rules.
964
    //
965
966
12.0k
    std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
967
12.0k
                                                    stage, compiler->infoSink,
968
12.0k
                                                    spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
969
12.0k
    parseContext->compileOnly = compileOnly;
970
12.0k
    TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
971
972
    // only GLSL (bison triggered, really) needs an externally set scan context
973
12.0k
    glslang::TScanContext scanContext(*parseContext);
974
12.0k
    if (source == EShSourceGlsl)
975
11.9k
        parseContext->setScanContext(&scanContext);
976
977
12.0k
    parseContext->setPpContext(&ppContext);
978
12.0k
    parseContext->setLimits(*resources);
979
12.0k
    if (! goodVersion)
980
3.46k
        parseContext->addError();
981
12.0k
    if (warnVersionNotFirst) {
982
0
        TSourceLoc loc;
983
0
        loc.init();
984
0
        parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
985
0
    }
986
987
12.0k
    parseContext->initializeExtensionBehavior();
988
989
    // Fill in the strings as outlined above.
990
12.0k
    std::string preamble;
991
12.0k
    parseContext->getPreamble(preamble);
992
12.0k
    strings[0] = preamble.c_str();
993
12.0k
    lengths[0] = strlen(strings[0]);
994
12.0k
    names[0] = nullptr;
995
12.0k
    strings[1] = customPreamble;
996
12.0k
    lengths[1] = strlen(strings[1]);
997
12.0k
    names[1] = nullptr;
998
12.0k
    assert(2 == numPre);
999
12.0k
    if (requireNonempty) {
1000
6.29k
        const int postIndex = numStrings + numPre;
1001
6.29k
        strings[postIndex] = "\n int;";
1002
6.29k
        lengths[postIndex] = strlen(strings[numStrings + numPre]);
1003
6.29k
        names[postIndex] = nullptr;
1004
6.29k
    }
1005
12.0k
    TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost);
1006
1007
    // Push a new symbol allocation scope that will get used for the shader's globals.
1008
12.0k
    symbolTable->push();
1009
1010
12.0k
    bool success = processingContext(*parseContext, ppContext, fullInput,
1011
12.0k
                                     versionWillBeError, *symbolTable,
1012
12.0k
                                     intermediate, optLevel, messages);
1013
12.0k
    intermediate.setUniqueId(symbolTable->getMaxSymbolId());
1014
12.0k
    return success;
1015
12.0k
}
ShaderLang.cpp:bool (anonymous namespace)::ProcessDeferred<(anonymous namespace)::DoFullParse>(TCompiler*, char const* const*, int, int const*, char const* const*, char const*, EShOptimizationLevel, TBuiltInResource const*, int, EProfile, bool, int, bool, EShMessages, glslang::TIntermediate&, (anonymous namespace)::DoFullParse&, bool, glslang::TShader::Includer&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, glslang::TEnvironment const*, bool)
Line
Count
Source
822
6.30k
{
823
    // This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
824
6.30k
    GetThreadPoolAllocator().push();
825
826
6.30k
    if (numStrings == 0)
827
0
        return true;
828
829
    // Move to length-based strings, rather than null-terminated strings.
830
    // Also, add strings to include the preamble and to ensure the shader is not null,
831
    // which lets the grammar accept what was a null (post preprocessing) shader.
832
    //
833
    // Shader will look like
834
    //   string 0:                system preamble
835
    //   string 1:                custom preamble
836
    //   string 2...numStrings+1: user's shader
837
    //   string numStrings+2:     "int;"
838
6.30k
    const int numPre = 2;
839
6.30k
    const int numPost = requireNonempty? 1 : 0;
840
6.30k
    const int numTotal = numPre + numStrings + numPost;
841
6.30k
    std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
842
6.30k
    std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
843
6.30k
    std::unique_ptr<const char*[]> names(new const char*[numTotal]);
844
12.6k
    for (int s = 0; s < numStrings; ++s) {
845
6.30k
        strings[s + numPre] = shaderStrings[s];
846
6.30k
        if (inputLengths == nullptr || inputLengths[s] < 0)
847
0
            lengths[s + numPre] = strlen(shaderStrings[s]);
848
6.30k
        else
849
6.30k
            lengths[s + numPre] = inputLengths[s];
850
6.30k
    }
851
6.30k
    if (stringNames != nullptr) {
852
12.6k
        for (int s = 0; s < numStrings; ++s)
853
6.30k
            names[s + numPre] = stringNames[s];
854
6.30k
    } else {
855
0
        for (int s = 0; s < numStrings; ++s)
856
0
            names[s + numPre] = nullptr;
857
0
    }
858
859
    // Get all the stages, languages, clients, and other environment
860
    // stuff sorted out.
861
6.30k
    EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
862
6.30k
    SpvVersion spvVersion;
863
6.30k
    EShLanguage stage = compiler->getLanguage();
864
6.30k
    TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion);
865
6.30k
#ifdef ENABLE_HLSL
866
6.30k
    EShSource source = sourceGuess;
867
6.30k
    if (environment != nullptr && environment->target.hlslFunctionality1)
868
920
        intermediate.setHlslFunctionality1();
869
#else
870
    const EShSource source = EShSourceGlsl;
871
#endif
872
    // First, without using the preprocessor or parser, find the #version, so we know what
873
    // symbol tables, processing rules, etc. to set up.  This does not need the extra strings
874
    // outlined above, just the user shader, after the system and user preambles.
875
6.30k
    glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
876
6.30k
    int version = 0;
877
6.30k
    EProfile profile = ENoProfile;
878
6.30k
    bool versionNotFirstToken = false;
879
6.30k
    bool versionNotFirst = (source == EShSourceHlsl)
880
6.30k
                                ? true
881
6.30k
                                : userInput.scanVersion(version, profile, versionNotFirstToken);
882
6.30k
    bool versionNotFound = version == 0;
883
6.30k
    if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
884
0
        if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
885
0
            (version != defaultVersion || profile != defaultProfile)) {
886
0
            compiler->infoSink.info << "Warning, (version, profile) forced to be ("
887
0
                                    << defaultVersion << ", " << ProfileName(defaultProfile)
888
0
                                    << "), while in source code it is ("
889
0
                                    << version << ", " << ProfileName(profile) << ")\n";
890
0
        }
891
892
0
        if (versionNotFound) {
893
0
            versionNotFirstToken = false;
894
0
            versionNotFirst = false;
895
0
            versionNotFound = false;
896
0
        }
897
0
        version = defaultVersion;
898
0
        profile = defaultProfile;
899
0
    }
900
6.30k
    if (source == EShSourceGlsl && overrideVersion != 0) {
901
0
        version = overrideVersion;
902
0
    }
903
904
6.30k
    bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
905
6.30k
                                            versionNotFirst, defaultVersion, source, version, profile, spvVersion);
906
6.30k
    bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
907
6.30k
    bool warnVersionNotFirst = false;
908
6.30k
    if (! versionWillBeError && versionNotFirstToken) {
909
28
        if (messages & EShMsgRelaxedErrors)
910
0
            warnVersionNotFirst = true;
911
28
        else
912
28
            versionWillBeError = true;
913
28
    }
914
915
6.30k
    intermediate.setSource(source);
916
6.30k
    intermediate.setVersion(version);
917
6.30k
    intermediate.setProfile(profile);
918
6.30k
    intermediate.setSpv(spvVersion);
919
6.30k
    RecordProcesses(intermediate, messages, sourceEntryPointName);
920
6.30k
    if (spvVersion.vulkan > 0)
921
6.30k
        intermediate.setOriginUpperLeft();
922
6.30k
#ifdef ENABLE_HLSL
923
6.30k
    if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
924
930
        intermediate.setHlslOffsets();
925
6.30k
#endif
926
6.30k
    if (messages & EShMsgDebugInfo) {
927
0
        intermediate.setSourceFile(names[numPre]);
928
0
        for (int s = 0; s < numStrings; ++s) {
929
            // The string may not be null-terminated, so make sure we provide
930
            // the length along with the string.
931
0
            intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
932
0
        }
933
0
    }
934
6.30k
    if (!SetupBuiltinSymbolTable(version, profile, spvVersion, source)) {
935
6
        return false;
936
6
    }
937
938
6.29k
    TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
939
6.29k
                                                  [MapSpvVersionToIndex(spvVersion)]
940
6.29k
                                                  [MapProfileToIndex(profile)]
941
6.29k
                                                  [MapSourceToIndex(source)]
942
6.29k
                                                  [stage];
943
944
    // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
945
6.29k
    std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
946
6.29k
    if (cachedTable)
947
6.29k
        symbolTable->adoptLevels(*cachedTable);
948
949
6.29k
    if (intermediate.getUniqueId() != 0)
950
0
        symbolTable->overwriteUniqueId(intermediate.getUniqueId());
951
952
    // Add built-in symbols that are potentially context dependent;
953
    // they get popped again further down.
954
6.29k
    if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
955
6.29k
                                    stage, source)) {
956
0
        return false;
957
0
    }
958
959
6.29k
    if (messages & EShMsgBuiltinSymbolTable)
960
0
        DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable);
961
962
    //
963
    // Now we can process the full shader under proper symbols and rules.
964
    //
965
966
6.29k
    std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
967
6.29k
                                                    stage, compiler->infoSink,
968
6.29k
                                                    spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
969
6.29k
    parseContext->compileOnly = compileOnly;
970
6.29k
    TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
971
972
    // only GLSL (bison triggered, really) needs an externally set scan context
973
6.29k
    glslang::TScanContext scanContext(*parseContext);
974
6.29k
    if (source == EShSourceGlsl)
975
6.24k
        parseContext->setScanContext(&scanContext);
976
977
6.29k
    parseContext->setPpContext(&ppContext);
978
6.29k
    parseContext->setLimits(*resources);
979
6.29k
    if (! goodVersion)
980
430
        parseContext->addError();
981
6.29k
    if (warnVersionNotFirst) {
982
0
        TSourceLoc loc;
983
0
        loc.init();
984
0
        parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
985
0
    }
986
987
6.29k
    parseContext->initializeExtensionBehavior();
988
989
    // Fill in the strings as outlined above.
990
6.29k
    std::string preamble;
991
6.29k
    parseContext->getPreamble(preamble);
992
6.29k
    strings[0] = preamble.c_str();
993
6.29k
    lengths[0] = strlen(strings[0]);
994
6.29k
    names[0] = nullptr;
995
6.29k
    strings[1] = customPreamble;
996
6.29k
    lengths[1] = strlen(strings[1]);
997
6.29k
    names[1] = nullptr;
998
6.29k
    assert(2 == numPre);
999
6.29k
    if (requireNonempty) {
1000
6.29k
        const int postIndex = numStrings + numPre;
1001
6.29k
        strings[postIndex] = "\n int;";
1002
6.29k
        lengths[postIndex] = strlen(strings[numStrings + numPre]);
1003
6.29k
        names[postIndex] = nullptr;
1004
6.29k
    }
1005
6.29k
    TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost);
1006
1007
    // Push a new symbol allocation scope that will get used for the shader's globals.
1008
6.29k
    symbolTable->push();
1009
1010
6.29k
    bool success = processingContext(*parseContext, ppContext, fullInput,
1011
6.29k
                                     versionWillBeError, *symbolTable,
1012
6.29k
                                     intermediate, optLevel, messages);
1013
6.29k
    intermediate.setUniqueId(symbolTable->getMaxSymbolId());
1014
6.29k
    return success;
1015
6.29k
}
ShaderLang.cpp:bool (anonymous namespace)::ProcessDeferred<(anonymous namespace)::DoPreprocessing>(TCompiler*, char const* const*, int, int const*, char const* const*, char const*, EShOptimizationLevel, TBuiltInResource const*, int, EProfile, bool, int, bool, EShMessages, glslang::TIntermediate&, (anonymous namespace)::DoPreprocessing&, bool, glslang::TShader::Includer&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, glslang::TEnvironment const*, bool)
Line
Count
Source
822
5.78k
{
823
    // This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
824
5.78k
    GetThreadPoolAllocator().push();
825
826
5.78k
    if (numStrings == 0)
827
0
        return true;
828
829
    // Move to length-based strings, rather than null-terminated strings.
830
    // Also, add strings to include the preamble and to ensure the shader is not null,
831
    // which lets the grammar accept what was a null (post preprocessing) shader.
832
    //
833
    // Shader will look like
834
    //   string 0:                system preamble
835
    //   string 1:                custom preamble
836
    //   string 2...numStrings+1: user's shader
837
    //   string numStrings+2:     "int;"
838
5.78k
    const int numPre = 2;
839
5.78k
    const int numPost = requireNonempty? 1 : 0;
840
5.78k
    const int numTotal = numPre + numStrings + numPost;
841
5.78k
    std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
842
5.78k
    std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
843
5.78k
    std::unique_ptr<const char*[]> names(new const char*[numTotal]);
844
11.5k
    for (int s = 0; s < numStrings; ++s) {
845
5.78k
        strings[s + numPre] = shaderStrings[s];
846
5.78k
        if (inputLengths == nullptr || inputLengths[s] < 0)
847
0
            lengths[s + numPre] = strlen(shaderStrings[s]);
848
5.78k
        else
849
5.78k
            lengths[s + numPre] = inputLengths[s];
850
5.78k
    }
851
5.78k
    if (stringNames != nullptr) {
852
11.5k
        for (int s = 0; s < numStrings; ++s)
853
5.78k
            names[s + numPre] = stringNames[s];
854
5.78k
    } else {
855
0
        for (int s = 0; s < numStrings; ++s)
856
0
            names[s + numPre] = nullptr;
857
0
    }
858
859
    // Get all the stages, languages, clients, and other environment
860
    // stuff sorted out.
861
5.78k
    EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
862
5.78k
    SpvVersion spvVersion;
863
5.78k
    EShLanguage stage = compiler->getLanguage();
864
5.78k
    TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion);
865
5.78k
#ifdef ENABLE_HLSL
866
5.78k
    EShSource source = sourceGuess;
867
5.78k
    if (environment != nullptr && environment->target.hlslFunctionality1)
868
1.11k
        intermediate.setHlslFunctionality1();
869
#else
870
    const EShSource source = EShSourceGlsl;
871
#endif
872
    // First, without using the preprocessor or parser, find the #version, so we know what
873
    // symbol tables, processing rules, etc. to set up.  This does not need the extra strings
874
    // outlined above, just the user shader, after the system and user preambles.
875
5.78k
    glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
876
5.78k
    int version = 0;
877
5.78k
    EProfile profile = ENoProfile;
878
5.78k
    bool versionNotFirstToken = false;
879
5.78k
    bool versionNotFirst = (source == EShSourceHlsl)
880
5.78k
                                ? true
881
5.78k
                                : userInput.scanVersion(version, profile, versionNotFirstToken);
882
5.78k
    bool versionNotFound = version == 0;
883
5.78k
    if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
884
0
        if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
885
0
            (version != defaultVersion || profile != defaultProfile)) {
886
0
            compiler->infoSink.info << "Warning, (version, profile) forced to be ("
887
0
                                    << defaultVersion << ", " << ProfileName(defaultProfile)
888
0
                                    << "), while in source code it is ("
889
0
                                    << version << ", " << ProfileName(profile) << ")\n";
890
0
        }
891
892
0
        if (versionNotFound) {
893
0
            versionNotFirstToken = false;
894
0
            versionNotFirst = false;
895
0
            versionNotFound = false;
896
0
        }
897
0
        version = defaultVersion;
898
0
        profile = defaultProfile;
899
0
    }
900
5.78k
    if (source == EShSourceGlsl && overrideVersion != 0) {
901
0
        version = overrideVersion;
902
0
    }
903
904
5.78k
    bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
905
5.78k
                                            versionNotFirst, defaultVersion, source, version, profile, spvVersion);
906
5.78k
    bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
907
5.78k
    bool warnVersionNotFirst = false;
908
5.78k
    if (! versionWillBeError && versionNotFirstToken) {
909
66
        if (messages & EShMsgRelaxedErrors)
910
0
            warnVersionNotFirst = true;
911
66
        else
912
66
            versionWillBeError = true;
913
66
    }
914
915
5.78k
    intermediate.setSource(source);
916
5.78k
    intermediate.setVersion(version);
917
5.78k
    intermediate.setProfile(profile);
918
5.78k
    intermediate.setSpv(spvVersion);
919
5.78k
    RecordProcesses(intermediate, messages, sourceEntryPointName);
920
5.78k
    if (spvVersion.vulkan > 0)
921
5.78k
        intermediate.setOriginUpperLeft();
922
5.78k
#ifdef ENABLE_HLSL
923
5.78k
    if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
924
1.12k
        intermediate.setHlslOffsets();
925
5.78k
#endif
926
5.78k
    if (messages & EShMsgDebugInfo) {
927
0
        intermediate.setSourceFile(names[numPre]);
928
0
        for (int s = 0; s < numStrings; ++s) {
929
            // The string may not be null-terminated, so make sure we provide
930
            // the length along with the string.
931
0
            intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
932
0
        }
933
0
    }
934
5.78k
    if (!SetupBuiltinSymbolTable(version, profile, spvVersion, source)) {
935
0
        return false;
936
0
    }
937
938
5.78k
    TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
939
5.78k
                                                  [MapSpvVersionToIndex(spvVersion)]
940
5.78k
                                                  [MapProfileToIndex(profile)]
941
5.78k
                                                  [MapSourceToIndex(source)]
942
5.78k
                                                  [stage];
943
944
    // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
945
5.78k
    std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
946
5.78k
    if (cachedTable)
947
5.78k
        symbolTable->adoptLevels(*cachedTable);
948
949
5.78k
    if (intermediate.getUniqueId() != 0)
950
0
        symbolTable->overwriteUniqueId(intermediate.getUniqueId());
951
952
    // Add built-in symbols that are potentially context dependent;
953
    // they get popped again further down.
954
5.78k
    if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
955
5.78k
                                    stage, source)) {
956
0
        return false;
957
0
    }
958
959
5.78k
    if (messages & EShMsgBuiltinSymbolTable)
960
0
        DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable);
961
962
    //
963
    // Now we can process the full shader under proper symbols and rules.
964
    //
965
966
5.78k
    std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
967
5.78k
                                                    stage, compiler->infoSink,
968
5.78k
                                                    spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
969
5.78k
    parseContext->compileOnly = compileOnly;
970
5.78k
    TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
971
972
    // only GLSL (bison triggered, really) needs an externally set scan context
973
5.78k
    glslang::TScanContext scanContext(*parseContext);
974
5.78k
    if (source == EShSourceGlsl)
975
5.71k
        parseContext->setScanContext(&scanContext);
976
977
5.78k
    parseContext->setPpContext(&ppContext);
978
5.78k
    parseContext->setLimits(*resources);
979
5.78k
    if (! goodVersion)
980
3.03k
        parseContext->addError();
981
5.78k
    if (warnVersionNotFirst) {
982
0
        TSourceLoc loc;
983
0
        loc.init();
984
0
        parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
985
0
    }
986
987
5.78k
    parseContext->initializeExtensionBehavior();
988
989
    // Fill in the strings as outlined above.
990
5.78k
    std::string preamble;
991
5.78k
    parseContext->getPreamble(preamble);
992
5.78k
    strings[0] = preamble.c_str();
993
5.78k
    lengths[0] = strlen(strings[0]);
994
5.78k
    names[0] = nullptr;
995
5.78k
    strings[1] = customPreamble;
996
5.78k
    lengths[1] = strlen(strings[1]);
997
5.78k
    names[1] = nullptr;
998
5.78k
    assert(2 == numPre);
999
5.78k
    if (requireNonempty) {
1000
0
        const int postIndex = numStrings + numPre;
1001
0
        strings[postIndex] = "\n int;";
1002
0
        lengths[postIndex] = strlen(strings[numStrings + numPre]);
1003
0
        names[postIndex] = nullptr;
1004
0
    }
1005
5.78k
    TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost);
1006
1007
    // Push a new symbol allocation scope that will get used for the shader's globals.
1008
5.78k
    symbolTable->push();
1009
1010
5.78k
    bool success = processingContext(*parseContext, ppContext, fullInput,
1011
5.78k
                                     versionWillBeError, *symbolTable,
1012
5.78k
                                     intermediate, optLevel, messages);
1013
5.78k
    intermediate.setUniqueId(symbolTable->getMaxSymbolId());
1014
5.78k
    return success;
1015
5.78k
}
1016
1017
// Responsible for keeping track of the most recent source string and line in
1018
// the preprocessor and outputting newlines appropriately if the source string
1019
// or line changes.
1020
class SourceLineSynchronizer {
1021
public:
1022
    SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
1023
                           std::string* output)
1024
5.78k
      : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
1025
//    SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
1026
//    SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete;
1027
1028
    // Sets the internally tracked source string index to that of the most
1029
    // recently read token. If we switched to a new source string, returns
1030
    // true and inserts a newline. Otherwise, returns false and outputs nothing.
1031
207M
    bool syncToMostRecentString() {
1032
207M
        if (getLastSourceIndex() != lastSource) {
1033
            // After switching to a new source string, we need to reset lastLine
1034
            // because line number resets every time a new source string is
1035
            // used. We also need to output a newline to separate the output
1036
            // from the previous source string (if there is one).
1037
11.5k
            if (lastSource != -1 || lastLine != 0)
1038
5.72k
                *output += '\n';
1039
11.5k
            lastSource = getLastSourceIndex();
1040
11.5k
            lastLine = -1;
1041
11.5k
            return true;
1042
11.5k
        }
1043
207M
        return false;
1044
207M
    }
1045
1046
    // Calls syncToMostRecentString() and then sets the internally tracked line
1047
    // number to tokenLine. If we switched to a new line, returns true and inserts
1048
    // newlines appropriately. Otherwise, returns false and outputs nothing.
1049
103M
    bool syncToLine(int tokenLine) {
1050
103M
        syncToMostRecentString();
1051
103M
        const bool newLineStarted = lastLine < tokenLine;
1052
101G
        for (; lastLine < tokenLine; ++lastLine) {
1053
100G
            if (lastLine > 0) *output += '\n';
1054
100G
        }
1055
103M
        return newLineStarted;
1056
103M
    }
1057
1058
    // Sets the internally tracked line number to newLineNum.
1059
500
    void setLineNum(int newLineNum) { lastLine = newLineNum; }
1060
1061
private:
1062
    SourceLineSynchronizer& operator=(const SourceLineSynchronizer&);
1063
1064
    // A function for getting the index of the last valid source string we've
1065
    // read tokens from.
1066
    const std::function<int()> getLastSourceIndex;
1067
    // output string for newlines.
1068
    std::string* output;
1069
    // lastSource is the source string index (starting from 0) of the last token
1070
    // processed. It is tracked in order for newlines to be inserted when a new
1071
    // source string starts. -1 means we haven't started processing any source
1072
    // string.
1073
    int lastSource;
1074
    // lastLine is the line number (starting from 1) of the last token processed.
1075
    // It is tracked in order for newlines to be inserted when a token appears
1076
    // on a new line. 0 means we haven't started processing any line in the
1077
    // current source string.
1078
    int lastLine;
1079
};
1080
1081
// Re-escape characters that would otherwise be emitted literally
1082
// so the preprocessed output remains valid GLSL source.
1083
56.6k
static void appendEscapedString(std::string& output, const char* string) {
1084
1085
248k
    for (const char* p = string; *p != '\0'; ++p) {
1086
191k
        switch (*p) {
1087
8
        case '"': output += "\\\""; break;
1088
12
        case '\\': output += "\\\\"; break;
1089
41
        case '\a': output += "\\a"; break;
1090
45
        case '\b': output += "\\b"; break;
1091
2.24k
        case '\f': output += "\\f"; break;
1092
10
        case '\n': output += "\\n"; break;
1093
0
        case '\r': output += "\\r"; break;
1094
1.48k
        case '\t': output += "\\t"; break;
1095
25
        case '\v': output += "\\v"; break;
1096
187k
        default:
1097
187k
            output += *p;
1098
187k
            break;
1099
191k
        }
1100
191k
    }
1101
56.6k
}
1102
1103
// DoPreprocessing is a valid ProcessingContext template argument,
1104
// which only performs the preprocessing step of compilation.
1105
// It places the result in the "string" argument to its constructor.
1106
//
1107
// This is not an officially supported or fully working path.
1108
struct DoPreprocessing {
1109
5.78k
    explicit DoPreprocessing(std::string* string): outputString(string) {}
1110
    bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
1111
                    TInputScanner& input, bool versionWillBeError,
1112
                    TSymbolTable&, TIntermediate&,
1113
                    EShOptimizationLevel, EShMessages)
1114
5.78k
    {
1115
        // This is a list of tokens that do not require a space before or after.
1116
5.78k
        static const std::string noNeededSpaceBeforeTokens = ";)[].,";
1117
5.78k
        static const std::string noNeededSpaceAfterTokens = ".([";
1118
5.78k
        glslang::TPpToken ppToken;
1119
1120
5.78k
        parseContext.setScanner(&input);
1121
5.78k
        ppContext.setInput(input, versionWillBeError);
1122
1123
5.78k
        std::string outputBuffer;
1124
5.78k
        SourceLineSynchronizer lineSync(
1125
5.78k
            std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer);
1126
1127
5.78k
        parseContext.setExtensionCallback([&lineSync, &outputBuffer](
1128
15.5k
            int line, const char* extension, const char* behavior) {
1129
15.5k
                lineSync.syncToLine(line);
1130
15.5k
                outputBuffer += "#extension ";
1131
15.5k
                outputBuffer += extension;
1132
15.5k
                outputBuffer += " : ";
1133
15.5k
                outputBuffer += behavior;
1134
15.5k
        });
1135
1136
5.78k
        parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext](
1137
5.78k
            int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
1138
            // SourceNum is the number of the source-string that is being parsed.
1139
500
            lineSync.syncToLine(curLineNum);
1140
500
            outputBuffer += "#line ";
1141
500
            outputBuffer += std::to_string(newLineNum);
1142
500
            if (hasSource) {
1143
429
                outputBuffer += ' ';
1144
429
                if (sourceName != nullptr) {
1145
61
                    outputBuffer += '\"';
1146
61
                    outputBuffer += sourceName;
1147
61
                    outputBuffer += '\"';
1148
368
                } else {
1149
368
                    outputBuffer += std::to_string(sourceNum);
1150
368
                }
1151
429
            }
1152
500
            if (parseContext.lineDirectiveShouldSetNextLine()) {
1153
                // newLineNum is the new line number for the line following the #line
1154
                // directive. So the new line number for the current line is
1155
126
                newLineNum -= 1;
1156
126
            }
1157
500
            outputBuffer += '\n';
1158
            // And we are at the next line of the #line directive now.
1159
500
            lineSync.setLineNum(newLineNum + 1);
1160
500
        });
1161
1162
5.78k
        parseContext.setVersionCallback(
1163
5.78k
            [&lineSync, &outputBuffer](int line, int version, const char* str) {
1164
3.27k
                lineSync.syncToLine(line);
1165
3.27k
                outputBuffer += "#version ";
1166
3.27k
                outputBuffer += std::to_string(version);
1167
3.27k
                if (str) {
1168
1.47k
                    outputBuffer += ' ';
1169
1.47k
                    outputBuffer += str;
1170
1.47k
                }
1171
3.27k
            });
1172
1173
5.78k
        parseContext.setPragmaCallback([&lineSync, &outputBuffer](
1174
5.78k
            int line, const glslang::TVector<glslang::TString>& ops) {
1175
724
                lineSync.syncToLine(line);
1176
724
                outputBuffer += "#pragma ";
1177
65.1k
                for(size_t i = 0; i < ops.size(); ++i) {
1178
64.3k
                    outputBuffer += ops[i].c_str();
1179
64.3k
                }
1180
724
        });
1181
1182
5.78k
        parseContext.setErrorCallback([&lineSync, &outputBuffer](
1183
5.78k
            int line, const char* errorMessage) {
1184
270
                lineSync.syncToLine(line);
1185
270
                outputBuffer += "#error ";
1186
270
                outputBuffer += errorMessage;
1187
270
        });
1188
1189
5.78k
        int lastToken = EndOfInput; // lastToken records the last token processed.
1190
5.78k
        std::string lastTokenName;
1191
103M
        do {
1192
103M
            int token = ppContext.tokenize(ppToken);
1193
103M
            if (token == EndOfInput)
1194
5.78k
                break;
1195
1196
103M
            bool isNewString = lineSync.syncToMostRecentString();
1197
103M
            bool isNewLine = lineSync.syncToLine(ppToken.loc.line);
1198
1199
103M
            if (isNewLine) {
1200
                // Don't emit whitespace onto empty lines.
1201
                // Copy any whitespace characters at the start of a line
1202
                // from the input to the output.
1203
6.37M
                if (ppToken.loc.column > 0)
1204
6.37M
                    outputBuffer += std::string(ppToken.loc.column - 1, ' ');
1205
6.37M
            }
1206
1207
            // Output a space in between tokens, but not at the start of a line,
1208
            // and also not around special tokens. This helps with readability
1209
            // and consistency.
1210
103M
            if (!isNewString && !isNewLine && lastToken != EndOfInput) {
1211
                // left parenthesis need a leading space, except it is in a function-call-like context.
1212
                // examples: `for (xxx)`, `a * (b + c)`, `vec(2.0)`, `foo(x, y, z)`
1213
97.3M
                if (token == '(') {
1214
569k
                    if (lastToken != PpAtomIdentifier ||
1215
441k
                        lastTokenName == "if" ||
1216
426k
                        lastTokenName == "for" ||
1217
424k
                        lastTokenName == "while" ||
1218
421k
                        lastTokenName == "switch")
1219
148k
                        outputBuffer += ' ';
1220
96.7M
                } else if ((noNeededSpaceBeforeTokens.find((char)token) == std::string::npos) &&
1221
91.1M
                    (noNeededSpaceAfterTokens.find((char)lastToken) == std::string::npos)) {
1222
89.6M
                    outputBuffer += ' ';
1223
89.6M
                }
1224
97.3M
            }
1225
103M
            if (token == PpAtomIdentifier)
1226
10.4M
                lastTokenName = ppToken.name;
1227
103M
            lastToken = token;
1228
103M
            if (token == PpAtomConstString) {
1229
56.6k
                outputBuffer += "\"";
1230
56.6k
                appendEscapedString(outputBuffer, ppToken.name);
1231
56.6k
                outputBuffer += "\"";
1232
103M
            } else {
1233
103M
                outputBuffer += ppToken.name;
1234
103M
            }
1235
103M
        } while (true);
1236
5.78k
        outputBuffer += '\n';
1237
5.78k
        *outputString = std::move(outputBuffer);
1238
1239
5.78k
        bool success = true;
1240
5.78k
        if (parseContext.getNumErrors() > 0) {
1241
3.48k
            success = false;
1242
3.48k
            parseContext.infoSink.info.prefix(EPrefixError);
1243
3.48k
            parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors.  No code generated.\n\n";
1244
3.48k
        }
1245
5.78k
        return success;
1246
5.78k
    }
1247
    std::string* outputString;
1248
};
1249
1250
// DoFullParse is a valid ProcessingConext template argument for fully
1251
// parsing the shader.  It populates the "intermediate" with the AST.
1252
struct DoFullParse{
1253
  bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
1254
                  TInputScanner& fullInput, bool versionWillBeError,
1255
                  TSymbolTable&, TIntermediate& intermediate,
1256
                  EShOptimizationLevel optLevel, EShMessages messages)
1257
6.29k
    {
1258
6.29k
        bool success = true;
1259
        // Parse the full shader.
1260
6.29k
        if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError))
1261
5.67k
            success = false;
1262
1263
6.29k
        if (success && intermediate.getTreeRoot()) {
1264
620
            if (optLevel == EShOptNoGeneration)
1265
0
                parseContext.infoSink.info.message(EPrefixNone, "No errors.  No code generation or linking was requested.");
1266
620
            else
1267
620
                success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage());
1268
5.67k
        } else if (! success) {
1269
5.67k
            parseContext.infoSink.info.prefix(EPrefixError);
1270
5.67k
            parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors.  No code generated.\n\n";
1271
5.67k
        }
1272
1273
6.29k
        if (messages & EShMsgAST)
1274
0
            intermediate.output(parseContext.infoSink, true);
1275
1276
6.29k
        return success;
1277
6.29k
    }
1278
};
1279
1280
// Take a single compilation unit, and run the preprocessor on it.
1281
// Return: True if there were no issues found in preprocessing,
1282
//         False if during preprocessing any unknown version, pragmas or
1283
//         extensions were found.
1284
//
1285
// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
1286
// is not an officially supported or fully working path.
1287
bool PreprocessDeferred(
1288
    TCompiler* compiler,
1289
    const char* const shaderStrings[],
1290
    const int numStrings,
1291
    const int* inputLengths,
1292
    const char* const stringNames[],
1293
    const char* preamble,
1294
    const EShOptimizationLevel optLevel,
1295
    const TBuiltInResource* resources,
1296
    int defaultVersion,         // use 100 for ES environment, 110 for desktop
1297
    EProfile defaultProfile,
1298
    bool forceDefaultVersionAndProfile,
1299
    int overrideVersion,        // use 0 if not overriding GLSL version
1300
    bool forwardCompatible,     // give errors for use of deprecated features
1301
    EShMessages messages,       // warnings/errors/AST; things to print out
1302
    TShader::Includer& includer,
1303
    TIntermediate& intermediate, // returned tree, etc.
1304
    std::string* outputString,
1305
    TEnvironment* environment = nullptr)
1306
5.78k
{
1307
5.78k
    DoPreprocessing parser(outputString);
1308
5.78k
    return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
1309
5.78k
                           preamble, optLevel, resources, defaultVersion,
1310
5.78k
                           defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1311
5.78k
                           forwardCompatible, messages, intermediate, parser,
1312
5.78k
                           false, includer, "", environment);
1313
5.78k
}
1314
1315
//
1316
// do a partial compile on the given strings for a single compilation unit
1317
// for a potential deferred link into a single stage (and deferred full compile of that
1318
// stage through machine-dependent compilation).
1319
//
1320
// all preprocessing, parsing, semantic checks, etc. for a single compilation unit
1321
// are done here.
1322
//
1323
// return:  the tree and other information is filled into the intermediate argument,
1324
//          and true is returned by the function for success.
1325
//
1326
bool CompileDeferred(
1327
    TCompiler* compiler,
1328
    const char* const shaderStrings[],
1329
    const int numStrings,
1330
    const int* inputLengths,
1331
    const char* const stringNames[],
1332
    const char* preamble,
1333
    const EShOptimizationLevel optLevel,
1334
    const TBuiltInResource* resources,
1335
    int defaultVersion,         // use 100 for ES environment, 110 for desktop
1336
    EProfile defaultProfile,
1337
    bool forceDefaultVersionAndProfile,
1338
    int overrideVersion,        // use 0 if not overriding GLSL version
1339
    bool forwardCompatible,     // give errors for use of deprecated features
1340
    EShMessages messages,       // warnings/errors/AST; things to print out
1341
    TIntermediate& intermediate,// returned tree, etc.
1342
    TShader::Includer& includer,
1343
    const std::string sourceEntryPointName = "",
1344
    TEnvironment* environment = nullptr,
1345
    bool compileOnly = false)
1346
6.30k
{
1347
6.30k
    DoFullParse parser;
1348
6.30k
    return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
1349
6.30k
                           preamble, optLevel, resources, defaultVersion,
1350
6.30k
                           defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1351
6.30k
                           forwardCompatible, messages, intermediate, parser,
1352
6.30k
                           true, includer, sourceEntryPointName, environment, compileOnly);
1353
6.30k
}
1354
1355
} // end anonymous namespace for local functions
1356
1357
//
1358
// ShInitialize() should be called exactly once per process, not per thread.
1359
//
1360
int ShInitialize()
1361
4.95k
{
1362
4.95k
#ifndef DISABLE_THREAD_SUPPORT
1363
4.95k
    const std::lock_guard<std::mutex> lock(init_lock);
1364
4.95k
#endif
1365
4.95k
    ++NumberOfClients;
1366
1367
4.95k
    if (PerProcessGPA == nullptr)
1368
4.95k
        PerProcessGPA = new TPoolAllocator();
1369
1370
4.95k
    return 1;
1371
4.95k
}
1372
1373
//
1374
// Driver calls these to create and destroy compiler/linker
1375
// objects.
1376
//
1377
1378
ShHandle ShConstructCompiler(const EShLanguage language, int /*debugOptions unused*/)
1379
0
{
1380
0
    TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, 0));
1381
1382
0
    return reinterpret_cast<void*>(base);
1383
0
}
1384
1385
ShHandle ShConstructLinker(const EShExecutable executable, int /*debugOptions unused*/)
1386
0
{
1387
0
    TShHandleBase* base = static_cast<TShHandleBase*>(ConstructLinker(executable, 0));
1388
1389
0
    return reinterpret_cast<void*>(base);
1390
0
}
1391
1392
ShHandle ShConstructUniformMap()
1393
0
{
1394
0
    TShHandleBase* base = static_cast<TShHandleBase*>(ConstructUniformMap());
1395
1396
0
    return reinterpret_cast<void*>(base);
1397
0
}
1398
1399
void ShDestruct(ShHandle handle)
1400
0
{
1401
0
    if (handle == nullptr)
1402
0
        return;
1403
1404
0
    TShHandleBase* base = static_cast<TShHandleBase*>(handle);
1405
1406
0
    if (base->getAsCompiler())
1407
0
        DeleteCompiler(base->getAsCompiler());
1408
0
    else if (base->getAsLinker())
1409
0
        DeleteLinker(base->getAsLinker());
1410
0
    else if (base->getAsUniformMap())
1411
0
        DeleteUniformMap(base->getAsUniformMap());
1412
0
}
1413
1414
//
1415
// Cleanup symbol tables
1416
//
1417
int ShFinalize()
1418
4.95k
{
1419
4.95k
#ifndef DISABLE_THREAD_SUPPORT
1420
4.95k
    const std::lock_guard<std::mutex> lock(init_lock);
1421
4.95k
#endif
1422
4.95k
    --NumberOfClients;
1423
4.95k
    assert(NumberOfClients >= 0);
1424
4.95k
    if (NumberOfClients > 0)
1425
0
        return 1;
1426
1427
89.1k
    for (int version = 0; version < VersionCount; ++version) {
1428
421k
        for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
1429
1.68M
            for (int p = 0; p < ProfileCount; ++p) {
1430
4.04M
                for (int source = 0; source < SourceCount; ++source) {
1431
40.4M
                    for (int stage = 0; stage < EShLangCount; ++stage) {
1432
37.7M
                        delete SharedSymbolTables[version][spvVersion][p][source][stage];
1433
37.7M
                        SharedSymbolTables[version][spvVersion][p][source][stage] = nullptr;
1434
37.7M
                    }
1435
2.69M
                }
1436
1.34M
            }
1437
336k
        }
1438
84.2k
    }
1439
1440
89.1k
    for (int version = 0; version < VersionCount; ++version) {
1441
421k
        for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
1442
1.68M
            for (int p = 0; p < ProfileCount; ++p) {
1443
4.04M
                for (int source = 0; source < SourceCount; ++source) {
1444
8.08M
                    for (int pc = 0; pc < EPcCount; ++pc) {
1445
5.38M
                        delete CommonSymbolTable[version][spvVersion][p][source][pc];
1446
5.38M
                        CommonSymbolTable[version][spvVersion][p][source][pc] = nullptr;
1447
5.38M
                    }
1448
2.69M
                }
1449
1.34M
            }
1450
336k
        }
1451
84.2k
    }
1452
1453
4.95k
    if (PerProcessGPA != nullptr) {
1454
4.95k
        delete PerProcessGPA;
1455
4.95k
        PerProcessGPA = nullptr;
1456
4.95k
    }
1457
1458
4.95k
    return 1;
1459
4.95k
}
1460
1461
//
1462
// Do a full compile on the given strings for a single compilation unit
1463
// forming a complete stage.  The result of the machine dependent compilation
1464
// is left in the provided compile object.
1465
//
1466
// Return:  The return value is really boolean, indicating
1467
// success (1) or failure (0).
1468
//
1469
int ShCompile(
1470
    const ShHandle handle,
1471
    const char* const shaderStrings[],
1472
    const int numStrings,
1473
    const int* inputLengths,
1474
    const EShOptimizationLevel optLevel,
1475
    const TBuiltInResource* resources,
1476
    int /*debugOptions*/,
1477
    int defaultVersion,        // use 100 for ES environment, 110 for desktop
1478
    bool forwardCompatible,    // give errors for use of deprecated features
1479
    EShMessages messages,       // warnings/errors/AST; things to print out,
1480
    const char *shaderFileName // the filename
1481
    )
1482
0
{
1483
    // Map the generic handle to the C++ object
1484
0
    if (handle == nullptr)
1485
0
        return 0;
1486
1487
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1488
0
    TCompiler* compiler = base->getAsCompiler();
1489
0
    if (compiler == nullptr)
1490
0
        return 0;
1491
1492
0
    SetThreadPoolAllocator(compiler->getPool());
1493
1494
0
    compiler->infoSink.info.erase();
1495
0
    compiler->infoSink.debug.erase();
1496
0
    compiler->infoSink.info.setShaderFileName(shaderFileName);
1497
0
    compiler->infoSink.debug.setShaderFileName(shaderFileName);
1498
1499
1500
0
    TIntermediate intermediate(compiler->getLanguage());
1501
0
    TShader::ForbidIncluder includer;
1502
0
    bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
1503
0
                                   "", optLevel, resources, defaultVersion, ENoProfile, false, 0,
1504
0
                                   forwardCompatible, messages, intermediate, includer);
1505
1506
    //
1507
    // Call the machine dependent compiler
1508
    //
1509
0
    if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration)
1510
0
        success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile());
1511
1512
0
    intermediate.removeTree();
1513
1514
    // Throw away all the temporary memory used by the compilation process.
1515
    // The push was done in the CompileDeferred() call above.
1516
0
    GetThreadPoolAllocator().pop();
1517
1518
0
    return success ? 1 : 0;
1519
0
}
1520
1521
//
1522
// Link the given compile objects.
1523
//
1524
// Return:  The return value of is really boolean, indicating
1525
// success or failure.
1526
//
1527
int ShLinkExt(
1528
    const ShHandle linkHandle,
1529
    const ShHandle compHandles[],
1530
    const int numHandles)
1531
0
{
1532
0
    if (linkHandle == nullptr || numHandles == 0)
1533
0
        return 0;
1534
1535
0
    THandleList cObjects;
1536
1537
0
    for (int i = 0; i < numHandles; ++i) {
1538
0
        if (compHandles[i] == nullptr)
1539
0
            return 0;
1540
0
        TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
1541
0
        if (base->getAsLinker()) {
1542
0
            cObjects.push_back(base->getAsLinker());
1543
0
        }
1544
0
        if (base->getAsCompiler())
1545
0
            cObjects.push_back(base->getAsCompiler());
1546
1547
0
        if (cObjects[i] == nullptr)
1548
0
            return 0;
1549
0
    }
1550
1551
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
1552
0
    TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1553
1554
0
    if (linker == nullptr)
1555
0
        return 0;
1556
1557
0
    SetThreadPoolAllocator(linker->getPool());
1558
0
    linker->infoSink.info.erase();
1559
1560
0
    for (int i = 0; i < numHandles; ++i) {
1561
0
        if (cObjects[i]->getAsCompiler()) {
1562
0
            if (! cObjects[i]->getAsCompiler()->linkable()) {
1563
0
                linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
1564
0
                return 0;
1565
0
            }
1566
0
        }
1567
0
    }
1568
1569
0
    bool ret = linker->link(cObjects);
1570
1571
0
    return ret ? 1 : 0;
1572
0
}
1573
1574
//
1575
// ShSetEncrpytionMethod is a place-holder for specifying
1576
// how source code is encrypted.
1577
//
1578
void ShSetEncryptionMethod(ShHandle handle)
1579
0
{
1580
0
    if (handle == nullptr)
1581
0
        return;
1582
0
}
1583
1584
//
1585
// Return any compiler/linker/uniformmap log of messages for the application.
1586
//
1587
const char* ShGetInfoLog(const ShHandle handle)
1588
0
{
1589
0
    if (handle == nullptr)
1590
0
        return nullptr;
1591
1592
0
    TShHandleBase* base = static_cast<TShHandleBase*>(handle);
1593
0
    TInfoSink* infoSink;
1594
1595
0
    if (base->getAsCompiler())
1596
0
        infoSink = &(base->getAsCompiler()->getInfoSink());
1597
0
    else if (base->getAsLinker())
1598
0
        infoSink = &(base->getAsLinker()->getInfoSink());
1599
0
    else
1600
0
        return nullptr;
1601
1602
0
    infoSink->info << infoSink->debug.c_str();
1603
0
    return infoSink->info.c_str();
1604
0
}
1605
1606
//
1607
// Return the resulting binary code from the link process.  Structure
1608
// is machine dependent.
1609
//
1610
const void* ShGetExecutable(const ShHandle handle)
1611
0
{
1612
0
    if (handle == nullptr)
1613
0
        return nullptr;
1614
1615
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1616
1617
0
    TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1618
0
    if (linker == nullptr)
1619
0
        return nullptr;
1620
1621
0
    return linker->getObjectCode();
1622
0
}
1623
1624
//
1625
// Let the linker know where the application said it's attributes are bound.
1626
// The linker does not use these values, they are remapped by the ICD or
1627
// hardware.  It just needs them to know what's aliased.
1628
//
1629
// Return:  The return value of is really boolean, indicating
1630
// success or failure.
1631
//
1632
int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
1633
0
{
1634
0
    if (handle == nullptr)
1635
0
        return 0;
1636
1637
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1638
0
    TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1639
1640
0
    if (linker == nullptr)
1641
0
        return 0;
1642
1643
0
    linker->setAppAttributeBindings(table);
1644
1645
0
    return 1;
1646
0
}
1647
1648
//
1649
// Let the linker know where the predefined attributes have to live.
1650
//
1651
int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
1652
0
{
1653
0
    if (handle == nullptr)
1654
0
        return 0;
1655
1656
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1657
0
    TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1658
1659
0
    if (linker == nullptr)
1660
0
        return 0;
1661
1662
0
    linker->setFixedAttributeBindings(table);
1663
0
    return 1;
1664
0
}
1665
1666
//
1667
// Some attribute locations are off-limits to the linker...
1668
//
1669
int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
1670
0
{
1671
0
    if (handle == nullptr)
1672
0
        return 0;
1673
1674
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1675
0
    TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1676
0
    if (linker == nullptr)
1677
0
        return 0;
1678
1679
0
    linker->setExcludedAttributes(attributes, count);
1680
1681
0
    return 1;
1682
0
}
1683
1684
//
1685
// Return the index for OpenGL to use for knowing where a uniform lives.
1686
//
1687
// Return:  The return value of is really boolean, indicating
1688
// success or failure.
1689
//
1690
int ShGetUniformLocation(const ShHandle handle, const char* name)
1691
0
{
1692
0
    if (handle == nullptr)
1693
0
        return -1;
1694
1695
0
    TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1696
0
    TUniformMap* uniformMap= base->getAsUniformMap();
1697
0
    if (uniformMap == nullptr)
1698
0
        return -1;
1699
1700
0
    return uniformMap->getLocation(name);
1701
0
}
1702
1703
////////////////////////////////////////////////////////////////////////////////////////////
1704
//
1705
// Deferred-Lowering C++ Interface
1706
// -----------------------------------
1707
//
1708
// Below is a new alternate C++ interface that might potentially replace the above
1709
// opaque handle-based interface.
1710
//
1711
// See more detailed comment in ShaderLang.h
1712
//
1713
1714
namespace glslang {
1715
1716
Version GetVersion()
1717
0
{
1718
0
    Version version;
1719
0
    version.major = GLSLANG_VERSION_MAJOR;
1720
0
    version.minor = GLSLANG_VERSION_MINOR;
1721
0
    version.patch = GLSLANG_VERSION_PATCH;
1722
0
    version.flavor = GLSLANG_VERSION_FLAVOR;
1723
0
    return version;
1724
0
}
1725
1726
#define QUOTE(s) #s
1727
#define STR(n) QUOTE(n)
1728
1729
const char* GetEsslVersionString()
1730
0
{
1731
0
    return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
1732
0
        GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
1733
0
}
1734
1735
const char* GetGlslVersionString()
1736
0
{
1737
0
    return "4.60 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
1738
0
        GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
1739
0
}
1740
1741
int GetKhronosToolId()
1742
618
{
1743
618
    return 8;
1744
618
}
1745
1746
bool InitializeProcess()
1747
4.95k
{
1748
4.95k
    return ShInitialize() != 0;
1749
4.95k
}
1750
1751
void FinalizeProcess()
1752
4.95k
{
1753
4.95k
    ShFinalize();
1754
4.95k
}
1755
1756
class TDeferredCompiler : public TCompiler {
1757
public:
1758
12.0k
    TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { }
1759
0
    virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; }
1760
};
1761
1762
0
TIoMapper* GetGlslIoMapper() {
1763
0
    return static_cast<TIoMapper*>(new TGlslIoMapper());
1764
0
}
1765
1766
TShader::TShader(EShLanguage s)
1767
12.0k
    : stage(s), lengths(nullptr), stringNames(nullptr), preamble(""), overrideVersion(0)
1768
12.0k
{
1769
12.0k
    pool = new TPoolAllocator;
1770
12.0k
    infoSink = new TInfoSink;
1771
12.0k
    compiler = new TDeferredCompiler(stage, *infoSink);
1772
12.0k
    intermediate = new TIntermediate(s);
1773
1774
    // clear environment (avoid constructors in them for use in a C interface)
1775
12.0k
    environment.input.languageFamily = EShSourceNone;
1776
12.0k
    environment.input.dialect = EShClientNone;
1777
12.0k
    environment.input.vulkanRulesRelaxed = false;
1778
12.0k
    environment.client.client = EShClientNone;
1779
12.0k
    environment.target.language = EShTargetNone;
1780
12.0k
    environment.target.hlslFunctionality1 = false;
1781
12.0k
}
1782
1783
TShader::~TShader()
1784
12.0k
{
1785
12.0k
    delete infoSink;
1786
12.0k
    delete compiler;
1787
12.0k
    delete intermediate;
1788
12.0k
    delete pool;
1789
12.0k
}
1790
1791
void TShader::setStrings(const char* const* s, int n)
1792
0
{
1793
0
    strings = s;
1794
0
    numStrings = n;
1795
0
    lengths = nullptr;
1796
0
}
1797
1798
void TShader::setStringsWithLengths(const char* const* s, const int* l, int n)
1799
0
{
1800
0
    strings = s;
1801
0
    numStrings = n;
1802
0
    lengths = l;
1803
0
}
1804
1805
void TShader::setStringsWithLengthsAndNames(
1806
    const char* const* s, const int* l, const char* const* names, int n)
1807
12.0k
{
1808
12.0k
    strings = s;
1809
12.0k
    numStrings = n;
1810
12.0k
    lengths = l;
1811
12.0k
    stringNames = names;
1812
12.0k
}
1813
1814
void TShader::setEntryPoint(const char* entryPoint)
1815
6.30k
{
1816
6.30k
    intermediate->setEntryPointName(entryPoint);
1817
6.30k
}
1818
1819
void TShader::setSourceEntryPoint(const char* name)
1820
0
{
1821
0
    sourceEntryPointName = name;
1822
0
}
1823
1824
// Log initial settings and transforms.
1825
// See comment for class TProcesses.
1826
void TShader::addProcesses(const std::vector<std::string>& p)
1827
0
{
1828
0
    intermediate->addProcesses(p);
1829
0
}
1830
1831
void  TShader::setUniqueId(unsigned long long id)
1832
0
{
1833
0
    intermediate->setUniqueId(id);
1834
0
}
1835
1836
void TShader::setOverrideVersion(int version)
1837
0
{
1838
0
    overrideVersion = version;
1839
0
}
1840
1841
0
void TShader::setDebugInfo(bool debugInfo)              { intermediate->setDebugInfo(debugInfo); }
1842
12.0k
void TShader::setInvertY(bool invert)                   { intermediate->setInvertY(invert); }
1843
0
void TShader::setDxPositionW(bool invert)               { intermediate->setDxPositionW(invert); }
1844
0
void TShader::setEnhancedMsgs()                         { intermediate->setEnhancedMsgs(); }
1845
12.0k
void TShader::setNanMinMaxClamp(bool useNonNan)         { intermediate->setNanMinMaxClamp(useNonNan); }
1846
0
void TShader::setDiscardIsTerminate(bool discardIsTerminate) { intermediate->setDiscardIsTerminate(discardIsTerminate); }
1847
1848
// Set binding base for given resource type
1849
37.8k
void TShader::setShiftBinding(TResourceType res, unsigned int base) {
1850
37.8k
    intermediate->setShiftBinding(res, base);
1851
37.8k
}
1852
1853
// Set binding base for given resource type for a given binding set.
1854
0
void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) {
1855
0
    intermediate->setShiftBindingForSet(res, base, set);
1856
0
}
1857
1858
// Set binding base for sampler types
1859
6.30k
void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); }
1860
// Set binding base for texture types (SRV)
1861
6.30k
void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); }
1862
// Set binding base for image types
1863
6.30k
void TShader::setShiftImageBinding(unsigned int base)   { setShiftBinding(EResImage, base); }
1864
// Set binding base for uniform buffer objects (CBV)
1865
6.30k
void TShader::setShiftUboBinding(unsigned int base)     { setShiftBinding(EResUbo, base); }
1866
// Synonym for setShiftUboBinding, to match HLSL language.
1867
0
void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
1868
// Set binding base for UAV (unordered access view)
1869
6.30k
void TShader::setShiftUavBinding(unsigned int base)     { setShiftBinding(EResUav, base); }
1870
// Set binding base for SSBOs
1871
6.30k
void TShader::setShiftSsboBinding(unsigned int base)    { setShiftBinding(EResSsbo, base); }
1872
// Enables binding automapping using TIoMapper
1873
6.30k
void TShader::setAutoMapBindings(bool map)              { intermediate->setAutoMapBindings(map); }
1874
// Enables position.Y output negation in vertex shader
1875
1876
// Fragile: currently within one stage: simple auto-assignment of location
1877
6.30k
void TShader::setAutoMapLocations(bool map)             { intermediate->setAutoMapLocations(map); }
1878
void TShader::addUniformLocationOverride(const char* name, int loc)
1879
0
{
1880
0
    intermediate->addUniformLocationOverride(name, loc);
1881
0
}
1882
void TShader::setUniformLocationBase(int base)
1883
0
{
1884
0
    intermediate->setUniformLocationBase(base);
1885
0
}
1886
0
void TShader::setBindingsPerResourceType() { intermediate->setBindingsPerResourceType(); }
1887
0
void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); }
1888
6.30k
void TShader::setResourceSetBinding(const std::vector<std::string>& base)   { intermediate->setResourceSetBinding(base); }
1889
0
void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
1890
1891
0
void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); }
1892
1893
0
void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); }
1894
0
void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); }
1895
0
void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); }
1896
1897
0
void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); }
1898
0
void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); }
1899
1900
0
void TShader::addSourceText(const char* text, size_t len) { intermediate->addSourceText(text, len); }
1901
0
void TShader::setSourceFile(const char* file) { intermediate->setSourceFile(file); }
1902
1903
#ifdef ENABLE_HLSL
1904
// See comment above TDefaultHlslIoMapper in iomapper.cpp:
1905
6.30k
void TShader::setHlslIoMapping(bool hlslIoMap)          { intermediate->setHlslIoMapping(hlslIoMap); }
1906
0
void TShader::setFlattenUniformArrays(bool flatten)     { intermediate->setFlattenUniformArrays(flatten); }
1907
#endif
1908
1909
//
1910
// Turn the shader strings into a parse tree in the TIntermediate.
1911
//
1912
// Returns true for success.
1913
//
1914
bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
1915
                    bool forwardCompatible, EShMessages messages, Includer& includer)
1916
6.30k
{
1917
6.30k
    SetThreadPoolAllocator(pool);
1918
1919
6.30k
    if (! preamble)
1920
0
        preamble = "";
1921
1922
6.30k
    return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
1923
6.30k
                           preamble, EShOptNone, builtInResources, defaultVersion,
1924
6.30k
                           defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1925
6.30k
                           forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
1926
6.30k
                           &environment, compileOnly);
1927
6.30k
}
1928
1929
// Fill in a string with the result of preprocessing ShaderStrings
1930
// Returns true if all extensions, pragmas and version strings were valid.
1931
//
1932
// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
1933
// is not an officially supported or fully working path.
1934
bool TShader::preprocess(const TBuiltInResource* builtInResources,
1935
                         int defaultVersion, EProfile defaultProfile,
1936
                         bool forceDefaultVersionAndProfile,
1937
                         bool forwardCompatible, EShMessages message,
1938
                         std::string* output_string,
1939
                         Includer& includer)
1940
5.78k
{
1941
5.78k
    SetThreadPoolAllocator(pool);
1942
1943
5.78k
    if (! preamble)
1944
0
        preamble = "";
1945
1946
5.78k
    return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble,
1947
5.78k
                              EShOptNone, builtInResources, defaultVersion,
1948
5.78k
                              defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1949
5.78k
                              forwardCompatible, message, includer, *intermediate, output_string,
1950
5.78k
                              &environment);
1951
5.78k
}
1952
1953
const char* TShader::getInfoLog()
1954
12.0k
{
1955
12.0k
    return infoSink->info.c_str();
1956
12.0k
}
1957
1958
const char* TShader::getInfoDebugLog()
1959
0
{
1960
0
    return infoSink->debug.c_str();
1961
0
}
1962
1963
620
TProgram::TProgram() : reflection(nullptr), linked(false)
1964
620
{
1965
620
    pool = new TPoolAllocator;
1966
620
    infoSink = new TInfoSink;
1967
9.30k
    for (int s = 0; s < EShLangCount; ++s) {
1968
8.68k
        intermediate[s] = nullptr;
1969
8.68k
        newedIntermediate[s] = false;
1970
8.68k
    }
1971
620
}
1972
1973
TProgram::~TProgram()
1974
620
{
1975
620
    delete infoSink;
1976
620
    delete reflection;
1977
1978
9.30k
    for (int s = 0; s < EShLangCount; ++s)
1979
8.68k
        if (newedIntermediate[s])
1980
0
            delete intermediate[s];
1981
1982
620
    delete pool;
1983
620
}
1984
1985
//
1986
// Merge the compilation units within each stage into a single TIntermediate.
1987
// All starting compilation units need to be the result of calling TShader::parse().
1988
//
1989
// Return true for success.
1990
//
1991
bool TProgram::link(EShMessages messages)
1992
620
{
1993
620
    if (linked)
1994
0
        return false;
1995
620
    linked = true;
1996
1997
620
    bool error = false;
1998
1999
620
    SetThreadPoolAllocator(pool);
2000
2001
9.30k
    for (int s = 0; s < EShLangCount; ++s) {
2002
8.68k
        if (! linkStage((EShLanguage)s, messages))
2003
2
            error = true;
2004
8.68k
    }
2005
2006
620
    if (!error) {
2007
618
        if (! crossStageCheck(messages))
2008
0
            error = true;
2009
618
    }
2010
2011
620
    if (messages & EShMsgAST) {
2012
0
        for (int s = 0; s < EShLangCount; ++s) {
2013
0
            if (intermediate[s] == nullptr)
2014
0
                continue;
2015
0
            intermediate[s]->output(*infoSink, true);
2016
0
        }
2017
0
    }
2018
2019
620
    return ! error;
2020
620
}
2021
2022
//
2023
// Merge the compilation units within the given stage into a single TIntermediate.
2024
//
2025
// Return true for success.
2026
//
2027
bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
2028
8.68k
{
2029
8.68k
    if (stages[stage].size() == 0)
2030
8.06k
        return true;
2031
2032
620
    int numEsShaders = 0, numNonEsShaders = 0;
2033
1.24k
    for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) {
2034
620
        if ((*it)->intermediate->getProfile() == EEsProfile) {
2035
1
            numEsShaders++;
2036
619
        } else {
2037
619
            numNonEsShaders++;
2038
619
        }
2039
620
    }
2040
2041
620
    if (numEsShaders > 0 && numNonEsShaders > 0) {
2042
0
        infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders");
2043
0
        return false;
2044
620
    } else if (numEsShaders > 1) {
2045
0
        infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program");
2046
0
        return false;
2047
0
    }
2048
2049
    //
2050
    // Be efficient for the common single compilation unit per stage case,
2051
    // reusing it's TIntermediate instead of merging into a new one.
2052
    //
2053
620
    TIntermediate *firstIntermediate = stages[stage].front()->intermediate;
2054
620
    if (stages[stage].size() == 1)
2055
620
        intermediate[stage] = firstIntermediate;
2056
0
    else {
2057
0
        intermediate[stage] = new TIntermediate(stage,
2058
0
                                                firstIntermediate->getVersion(),
2059
0
                                                firstIntermediate->getProfile());
2060
0
        intermediate[stage]->setLimits(firstIntermediate->getLimits());
2061
0
        if (firstIntermediate->getEnhancedMsgs())
2062
0
            intermediate[stage]->setEnhancedMsgs();
2063
2064
        // The new TIntermediate must use the same origin as the original TIntermediates.
2065
        // Otherwise linking will fail due to different coordinate systems.
2066
0
        if (firstIntermediate->getOriginUpperLeft()) {
2067
0
            intermediate[stage]->setOriginUpperLeft();
2068
0
        }
2069
0
        intermediate[stage]->setSpv(firstIntermediate->getSpv());
2070
2071
0
        newedIntermediate[stage] = true;
2072
0
    }
2073
2074
620
    if (messages & EShMsgAST)
2075
0
        infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
2076
2077
620
    if (stages[stage].size() > 1) {
2078
0
        std::list<TShader*>::const_iterator it;
2079
0
        for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
2080
0
            intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
2081
0
    }
2082
620
    intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0);
2083
2084
620
    return intermediate[stage]->getNumErrors() == 0;
2085
620
}
2086
2087
//
2088
// Check that there are no errors in linker objects accross stages
2089
//
2090
// Return true if no errors.
2091
//
2092
618
bool TProgram::crossStageCheck(EShMessages messages) {
2093
2094
    // make temporary intermediates to hold the linkage symbols for each linking interface
2095
    // while we do the checks
2096
    // Independent interfaces are:
2097
    //                  all uniform variables and blocks
2098
    //                  all buffer blocks
2099
    //                  all in/out on a stage boundary
2100
2101
618
    TVector<TIntermediate*> activeStages;
2102
9.27k
    for (int s = 0; s < EShLangCount; ++s) {
2103
8.65k
        if (intermediate[s])
2104
618
            activeStages.push_back(intermediate[s]);
2105
8.65k
    }
2106
2107
618
    class TFinalLinkTraverser : public TIntermTraverser {
2108
618
    public:
2109
618
        TFinalLinkTraverser() { }
2110
618
        virtual ~TFinalLinkTraverser() { }
2111
2112
618
        virtual void visitSymbol(TIntermSymbol* symbol)
2113
58.2k
        {
2114
            // Implicitly size arrays.
2115
            // If an unsized array is left as unsized, it effectively
2116
            // becomes run-time sized.
2117
58.2k
            symbol->getWritableType().adoptImplicitArraySizes(false);
2118
58.2k
        }
2119
618
    } finalLinkTraverser;
2120
2121
    // no extra linking if there is only one stage
2122
618
    if (! (activeStages.size() > 1)) {
2123
618
        if (activeStages.size() == 1 && activeStages[0]->getTreeRoot()) {
2124
618
            activeStages[0]->getTreeRoot()->traverse(&finalLinkTraverser);
2125
618
        }
2126
618
        return true;
2127
618
    }
2128
2129
    // setup temporary tree to hold unfirom objects from different stages
2130
0
    TIntermediate* firstIntermediate = activeStages.front();
2131
0
    TIntermediate uniforms(EShLangCount,
2132
0
                           firstIntermediate->getVersion(),
2133
0
                           firstIntermediate->getProfile());
2134
0
    uniforms.setSpv(firstIntermediate->getSpv());
2135
2136
0
    TIntermAggregate uniformObjects(EOpLinkerObjects);
2137
0
    TIntermAggregate root(EOpSequence);
2138
0
    root.getSequence().push_back(&uniformObjects);
2139
0
    uniforms.setTreeRoot(&root);
2140
2141
0
    bool error = false;
2142
2143
    // merge uniforms from all stages into a single intermediate
2144
0
    for (unsigned int i = 0; i < activeStages.size(); ++i) {
2145
0
        uniforms.mergeUniformObjects(*infoSink, *activeStages[i]);
2146
0
    }
2147
0
    error |= uniforms.getNumErrors() != 0;
2148
2149
    // update implicit array sizes across shader stages
2150
0
    for (unsigned int i = 0; i < activeStages.size(); ++i) {
2151
0
        activeStages[i]->mergeImplicitArraySizes(*infoSink, uniforms);
2152
0
        activeStages[i]->getTreeRoot()->traverse(&finalLinkTraverser);
2153
0
    }
2154
2155
    // copy final definition of global block back into each stage
2156
0
    for (unsigned int i = 0; i < activeStages.size(); ++i) {
2157
        // We only want to merge into already existing global uniform blocks.
2158
        // A stage that doesn't already know about the global doesn't care about it's content.
2159
        // Otherwise we end up pointing to the same object between different stages
2160
        // and that will break binding/set remappings
2161
0
        bool mergeExistingOnly = true;
2162
0
        activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms, mergeExistingOnly);
2163
0
    }
2164
2165
    // compare cross stage symbols for each stage boundary
2166
0
    for (unsigned int i = 1; i < activeStages.size(); ++i) {
2167
0
        activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i], messages);
2168
0
        error |= (activeStages[i - 1]->getNumErrors() != 0 || activeStages[i]->getNumErrors() != 0);
2169
0
    }
2170
2171
    // if requested, optimize cross stage IO
2172
0
    if (messages & EShMsgLinkTimeOptimization) {
2173
0
        for (unsigned int i = 1; i < activeStages.size(); ++i) {
2174
0
            activeStages[i - 1]->optimizeStageIO(*infoSink, *activeStages[i]);
2175
0
        }
2176
0
    }
2177
2178
0
    return !error;
2179
618
}
2180
2181
const char* TProgram::getInfoLog()
2182
620
{
2183
620
    return infoSink->info.c_str();
2184
620
}
2185
2186
const char* TProgram::getInfoDebugLog()
2187
0
{
2188
0
    return infoSink->debug.c_str();
2189
0
}
2190
2191
//
2192
// Reflection implementation.
2193
//
2194
2195
0
unsigned int TObjectReflection::layoutLocation() const { return type->getQualifier().layoutLocation; }
2196
2197
bool TProgram::buildReflection(int opts)
2198
0
{
2199
0
    if (! linked || reflection != nullptr)
2200
0
        return false;
2201
2202
0
    SetThreadPoolAllocator(pool);
2203
2204
0
    int firstStage = EShLangVertex, lastStage = EShLangFragment;
2205
2206
0
    if (opts & EShReflectionIntermediateIO) {
2207
        // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the
2208
        // boundaries for which stages generate pipeline inputs/outputs
2209
0
        firstStage = EShLangCount;
2210
0
        lastStage = 0;
2211
0
        for (int s = 0; s < EShLangCount; ++s) {
2212
0
            if (intermediate[s]) {
2213
0
                firstStage = std::min(firstStage, s);
2214
0
                lastStage = std::max(lastStage, s);
2215
0
            }
2216
0
        }
2217
0
    }
2218
2219
0
    reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage);
2220
2221
0
    for (int s = 0; s < EShLangCount; ++s) {
2222
0
        if (intermediate[s]) {
2223
0
            if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
2224
0
                return false;
2225
0
        }
2226
0
    }
2227
2228
0
    return true;
2229
0
}
2230
2231
0
unsigned TProgram::getLocalSize(int dim) const                        { return reflection->getLocalSize(dim); }
2232
0
unsigned TProgram::getTileShadingRateQCOM(int dim) const              { return reflection->getTileShadingRateQCOM(dim); }
2233
0
int TProgram::getReflectionIndex(const char* name) const              { return reflection->getIndex(name); }
2234
int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const
2235
0
                                                                      { return reflection->getPipeIOIndex(name, inOrOut); }
2236
2237
0
int TProgram::getNumUniformVariables() const                          { return reflection->getNumUniforms(); }
2238
0
const TObjectReflection& TProgram::getUniform(int index) const        { return reflection->getUniform(index); }
2239
0
int TProgram::getNumUniformBlocks() const                             { return reflection->getNumUniformBlocks(); }
2240
0
const TObjectReflection& TProgram::getUniformBlock(int index) const   { return reflection->getUniformBlock(index); }
2241
0
int TProgram::getNumPipeInputs() const                                { return reflection->getNumPipeInputs(); }
2242
0
const TObjectReflection& TProgram::getPipeInput(int index) const      { return reflection->getPipeInput(index); }
2243
0
int TProgram::getNumPipeOutputs() const                               { return reflection->getNumPipeOutputs(); }
2244
0
const TObjectReflection& TProgram::getPipeOutput(int index) const     { return reflection->getPipeOutput(index); }
2245
0
int TProgram::getNumBufferVariables() const                           { return reflection->getNumBufferVariables(); }
2246
0
const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); }
2247
0
int TProgram::getNumBufferBlocks() const                              { return reflection->getNumStorageBuffers(); }
2248
0
const TObjectReflection& TProgram::getBufferBlock(int index) const    { return reflection->getStorageBufferBlock(index); }
2249
0
int TProgram::getNumAtomicCounters() const                            { return reflection->getNumAtomicCounters(); }
2250
0
const TObjectReflection& TProgram::getAtomicCounter(int index) const  { return reflection->getAtomicCounter(index); }
2251
0
void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); }
2252
2253
0
TIoMapResolver* TProgram::getGlslIoResolver(EShLanguage stage) {
2254
0
    auto *intermediate = getIntermediate(stage);
2255
0
    if (!intermediate)
2256
0
        return NULL;
2257
0
    return static_cast<TIoMapResolver*>(new TDefaultGlslIoResolver(*intermediate));
2258
0
}
2259
//
2260
// I/O mapping implementation.
2261
//
2262
bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper)
2263
618
{
2264
618
    if (! linked)
2265
0
        return false;
2266
2267
618
    SetThreadPoolAllocator(pool);
2268
2269
618
    TIoMapper* ioMapper = nullptr;
2270
618
    TIoMapper defaultIOMapper;
2271
618
    if (pIoMapper == nullptr)
2272
618
        ioMapper = &defaultIOMapper;
2273
0
    else
2274
0
        ioMapper = pIoMapper;
2275
9.27k
    for (int s = 0; s < EShLangCount; ++s) {
2276
8.65k
        if (intermediate[s]) {
2277
618
            if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver))
2278
0
                return false;
2279
618
        }
2280
8.65k
    }
2281
2282
618
    return ioMapper->doMap(pResolver, *infoSink);
2283
618
}
2284
2285
} // end namespace glslang