Coverage Report

Created: 2026-05-30 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/SPIRV/SpvBuilder.h
Line
Count
Source
1
//
2
// Copyright (C) 2014-2015 LunarG, Inc.
3
// Copyright (C) 2015-2020 Google, Inc.
4
// Copyright (C) 2017 ARM Limited.
5
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
6
//
7
// All rights reserved.
8
//
9
// Redistribution and use in source and binary forms, with or without
10
// modification, are permitted provided that the following conditions
11
// are met:
12
//
13
//    Redistributions of source code must retain the above copyright
14
//    notice, this list of conditions and the following disclaimer.
15
//
16
//    Redistributions in binary form must reproduce the above
17
//    copyright notice, this list of conditions and the following
18
//    disclaimer in the documentation and/or other materials provided
19
//    with the distribution.
20
//
21
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
22
//    contributors may be used to endorse or promote products derived
23
//    from this software without specific prior written permission.
24
//
25
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
// POSSIBILITY OF SUCH DAMAGE.
37
38
//
39
// "Builder" is an interface to fully build SPIR-V IR.   Allocate one of
40
// these to build (a thread safe) internal SPIR-V representation (IR),
41
// and then dump it as a binary stream according to the SPIR-V specification.
42
//
43
// A Builder has a 1:1 relationship with a SPIR-V module.
44
//
45
46
#pragma once
47
#ifndef SpvBuilder_H
48
#define SpvBuilder_H
49
50
#include "Logger.h"
51
#define SPV_ENABLE_UTILITY_CODE
52
#include "spirv.hpp11"
53
#include "spvIR.h"
54
#include "spvUtil.h"
55
56
namespace spv {
57
    #include "GLSL.ext.KHR.h"
58
    #include "GLSL.ext.EXT.h"
59
    #include "NonSemanticShaderDebugInfo.h"
60
}
61
62
#include <algorithm>
63
#include <cstdint>
64
#include <map>
65
#include <memory>
66
#include <set>
67
#include <sstream>
68
#include <stack>
69
#include <unordered_map>
70
#include <unordered_set>
71
#include <map>
72
73
namespace spv {
74
75
typedef enum {
76
    Spv_1_0 = (1 << 16),
77
    Spv_1_1 = (1 << 16) | (1 << 8),
78
    Spv_1_2 = (1 << 16) | (2 << 8),
79
    Spv_1_3 = (1 << 16) | (3 << 8),
80
    Spv_1_4 = (1 << 16) | (4 << 8),
81
    Spv_1_5 = (1 << 16) | (5 << 8),
82
    Spv_1_6 = (1 << 16) | (6 << 8),
83
} SpvVersion;
84
85
struct StructMemberDebugInfo {
86
    std::string name {};
87
    int line {0};
88
    int column {0};
89
    // Set if the caller knows a better debug type than what is associated with the functional SPIR-V type.
90
    spv::Id debugTypeOverride {0};
91
};
92
93
class Builder {
94
public:
95
    Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
96
    virtual ~Builder();
97
98
    static const int maxMatrixSize = 4;
99
100
2.03k
    unsigned int getSpvVersion() const { return spvVersion; }
101
102
    void setSource(spv::SourceLanguage lang, int version)
103
445
    {
104
445
        sourceLang = lang;
105
445
        sourceVersion = version;
106
445
    }
107
    spv::Id getStringId(const std::string& str)
108
0
    {
109
0
        auto sItr = stringIds.find(str);
110
0
        if (sItr != stringIds.end())
111
0
            return sItr->second;
112
0
        spv::Id strId = getUniqueId();
113
0
        Instruction* fileString = new Instruction(strId, NoType, Op::OpString);
114
0
        const char* file_c_str = str.c_str();
115
0
        fileString->addStringOperand(file_c_str);
116
0
        strings.push_back(std::unique_ptr<Instruction>(fileString));
117
0
        module.mapInstruction(fileString);
118
0
        stringIds[file_c_str] = strId;
119
0
        return strId;
120
0
    }
121
122
0
    spv::Id getMainFileId() const { return mainFileId; }
123
124
    // Initialize the main source file name
125
    void setDebugMainSourceFile(const std::string& file)
126
0
    {
127
0
        if (trackDebugInfo) {
128
0
            dirtyLineTracker = true;
129
0
            mainFileId = getStringId(file);
130
0
            currentFileId = mainFileId;
131
0
        }
132
0
    }
133
134
    // Set the debug source location tracker in the builder.
135
    // The upcoming instructions in basic blocks will be associated to this location.
136
    void setDebugSourceLocation(int line, const char* filename)
137
84.5k
    {
138
84.5k
        if (trackDebugInfo) {
139
0
            dirtyLineTracker = true;
140
0
            if (line != 0) {
141
                // TODO: This is special handling of some AST nodes having (untracked) line 0.
142
                //       But they should have a valid line number.
143
0
                currentLine = line;
144
0
                if (filename) {
145
0
                    currentFileId = getStringId(filename);
146
0
                }
147
0
            }
148
0
        }
149
84.5k
    }
150
151
0
    void setSourceText(const std::string& text) { sourceText = text; }
152
2.06k
    void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
153
0
    void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
154
    void setEmitSpirvDebugInfo()
155
0
    {
156
0
        trackDebugInfo = true;
157
0
        emitSpirvDebugInfo = true;
158
0
    }
159
    void setEmitNonSemanticShaderDebugInfo(bool emitSourceText)
160
0
    {
161
0
        trackDebugInfo = true;
162
0
        emitNonSemanticShaderDebugInfo = true;
163
0
        importNonSemanticShaderDebugInfoInstructions();
164
165
0
        if (emitSourceText) {
166
0
            emitNonSemanticShaderDebugSource = emitSourceText;
167
0
        }
168
0
    }
169
5.01k
    void addExtension(const char* ext) { extensions.insert(ext); }
170
    void removeExtension(const char* ext)
171
0
    {
172
0
        extensions.erase(ext);
173
0
    }
174
    void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion)
175
216
    {
176
216
        if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion))
177
216
            addExtension(ext);
178
216
    }
179
    void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion)
180
0
    {
181
0
        removeExtension(baseExt);
182
0
        addIncorporatedExtension(promoExt, incorporatedVersion);
183
0
    }
184
    void addInclude(const std::string& name, const std::string& text)
185
0
    {
186
0
        spv::Id incId = getStringId(name);
187
0
        includeFiles[incId] = &text;
188
0
    }
189
    Id import(const char*);
190
    void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
191
445
    {
192
445
        addressModel = addr;
193
445
        memoryModel = mem;
194
445
    }
195
196
47.3k
    void addCapability(spv::Capability cap) { capabilities.insert(cap); }
197
198
    // To get a new <id> for anything needing a new one.
199
85.3k
    Id getUniqueId() { return ++uniqueId; }
200
201
    // To get a set of new <id>s, e.g., for a set of function parameters
202
    Id getUniqueIds(int numIds)
203
198
    {
204
198
        Id id = uniqueId + 1;
205
198
        uniqueId += numIds;
206
198
        return id;
207
198
    }
208
209
    // Maps the given OpType Id to a Non-Semantic DebugType Id.
210
0
    Id getDebugType(Id type) {
211
0
        if (auto it = debugTypeIdLookup.find(type); it != debugTypeIdLookup.end()) {
212
0
            return it->second;
213
0
        }
214
        
215
0
        return NoType;
216
0
    }
217
218
    // Maps the given OpFunction Id to a Non-Semantic DebugFunction Id.
219
0
    Id getDebugFunction(Id func) {
220
0
        if (auto it = debugFuncIdLookup.find(func); it != debugFuncIdLookup.end()) {
221
0
            return it->second;
222
0
        }
223
        
224
0
        return NoResult;
225
0
    }
226
227
    // For creating new types (will return old type if the requested one was already made).
228
    Id makeVoidType();
229
    Id makeBoolType();
230
    Id makePointer(StorageClass, Id pointee);
231
    Id makeForwardPointer(StorageClass);
232
    Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
233
    Id makeUntypedPointer(StorageClass storageClass, bool setBufferPointer = false);
234
    Id makeIntegerType(int width, bool hasSign);   // generic
235
31.2k
    Id makeIntType(int width) { return makeIntegerType(width, true); }
236
21.1k
    Id makeUintType(int width) { return makeIntegerType(width, false); }
237
    Id makeFloatType(int width);
238
    Id makeBFloat16Type();
239
    Id makeFloatE5M2Type();
240
    Id makeFloatE4M3Type();
241
    Id makeStructType(const std::vector<Id>& members, const std::vector<spv::StructMemberDebugInfo>& memberDebugInfo,
242
                      const char* name, bool const compilerGenerated = true);
243
    Id makeStructResultType(Id type0, Id type1);
244
    Id makeVectorType(Id component, int size);
245
    Id makeMatrixType(Id component, int cols, int rows);
246
    Id makeArrayType(Id element, Id sizeId, int stride);  // 0 stride means no stride decoration
247
    Id makeRuntimeArray(Id element);
248
    Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
249
    Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format, const char* debugNames);
250
    Id makeSamplerType(const char* debugName);
251
    Id makeSampledImageType(Id imageType, const char* debugName);
252
    Id makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use);
253
    Id makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols);
254
    Id makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType);
255
    Id makeCooperativeVectorTypeNV(Id componentType, Id components);
256
    Id makeTensorTypeARM(Id elementType, Id rank);
257
    Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands);
258
259
    // SPIR-V NonSemantic Shader DebugInfo Instructions
260
    Id makeDebugInfoNone();
261
    Id makeBoolDebugType(int const size);
262
    Id makeIntegerDebugType(int const width, bool const hasSign);
263
    Id makeFloatDebugType(int const width, Id const fpEncoding = NoType);
264
    Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfoInstructions const sequenceType);
265
    Id makeArrayDebugType(Id const baseType, Id const componentCount);
266
    Id makeVectorDebugType(Id const baseType, int const componentCount);
267
    Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true);
268
    Id makeMemberDebugType(Id const memberType, StructMemberDebugInfo const& debugTypeLoc);
269
    Id makeCompositeDebugType(std::vector<Id> const& memberTypes, std::vector<StructMemberDebugInfo> const& memberDebugInfo,
270
                              char const* const name, NonSemanticShaderDebugInfoDebugCompositeType const tag);
271
    Id makeOpaqueDebugType(char const* const name);
272
    Id makeVectorIdDebugType(Id componentType, Id componentCount);
273
    Id makeCooperativeMatrixDebugTypeKHR(Id componentType, Id scope, Id rows, Id cols, Id use);
274
    Id makePointerDebugType(StorageClass storageClass, Id const baseType);
275
    Id makeForwardPointerDebugType(StorageClass storageClass);
276
    Id makeDebugSource(const Id fileName);
277
    Id makeDebugCompilationUnit();
278
    Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable);
279
    Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0);
280
    Id makeDebugExpression();
281
    Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer);
282
    Id makeDebugValue(Id const debugLocalVariable, Id const value);
283
    Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes);
284
    Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId);
285
    Id makeDebugLexicalBlock(uint32_t line, uint32_t column);
286
    std::string unmangleFunctionName(std::string const& name) const;
287
288
    // Initialize non-semantic debug information for a function, including those of:
289
    // - The function definition
290
    // - The function parameters
291
    void setupFunctionDebugInfo(Function* function, const char* name, const std::vector<Id>& paramTypes,
292
                                const std::vector<char const*>& paramNames);
293
294
    // accelerationStructureNV type
295
    Id makeAccelerationStructureType();
296
    // rayQueryEXT type
297
    Id makeRayQueryType();
298
    // hitObjectNV type
299
    Id makeHitObjectNVType();
300
    // hitObjectEXT type
301
    Id makeHitObjectEXTType();
302
303
    // For querying about types.
304
717k
    Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
305
    Id getDerefTypeId(Id resultId) const;
306
317k
    Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
307
315k
    Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
308
    Op getMostBasicTypeClass(Id typeId) const;
309
1.86k
    unsigned int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
310
    unsigned int getNumTypeConstituents(Id typeId) const;
311
8.21k
    unsigned int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
312
    Id getScalarTypeId(Id typeId) const;
313
    Id getContainedTypeId(Id typeId) const;
314
    Id getContainedTypeId(Id typeId, int) const;
315
158k
    StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
316
    ImageFormat getImageTypeFormat(Id typeId) const
317
437
        { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
318
    Id getResultingAccessChainType() const;
319
513
    Id getIdOperand(Id resultId, int idx) { return module.getInstruction(resultId)->getIdOperand(idx); }
320
0
    Id getCooperativeVectorNumComponents(Id typeId) const { return module.getInstruction(typeId)->getIdOperand(1); }
321
322
33.1k
    bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
323
    bool isUntypedPointer(Id resultId) const
324
80.7k
    {
325
80.7k
        const Id tid = getTypeId(resultId);
326
        // Expect that OpString have no type
327
80.7k
        if (tid == 0)
328
0
            return false;
329
80.7k
        return isUntypedPointerType(tid);
330
80.7k
    }
331
10.7k
    bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }
332
833
    bool isVector(Id resultId)       const { return isVectorType(getTypeId(resultId)); }
333
9.82k
    bool isMatrix(Id resultId)       const { return isMatrixType(getTypeId(resultId)); }
334
9.61k
    bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
335
539
    bool isCooperativeVector(Id resultId)const { return isCooperativeVectorType(getTypeId(resultId)); }
336
203
    bool isAggregate(Id resultId)    const { return isAggregateType(getTypeId(resultId)); }
337
3.85k
    bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
338
0
    bool isTensorView(Id resultId)const { return isTensorViewType(getTypeId(resultId)); }
339
340
    bool isBoolType(Id typeId)
341
36
        { return groupedTypes[enumCast(Op::OpTypeBool)].size() > 0 && typeId == groupedTypes[enumCast(Op::OpTypeBool)].back()->getResultId(); }
342
    bool isIntType(Id typeId)          const
343
2.08k
        { return getTypeClass(typeId) == Op::OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
344
    bool isUintType(Id typeId)         const
345
275
        { return getTypeClass(typeId) == Op::OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
346
1.04k
    bool isFloatType(Id typeId)        const { return getTypeClass(typeId) == Op::OpTypeFloat; }
347
133k
    bool isPointerType(Id typeId)      const { return getTypeClass(typeId) == Op::OpTypePointer; }
348
80.7k
    bool isUntypedPointerType(Id typeId) const { return getTypeClass(typeId) == Op::OpTypeUntypedPointerKHR; }
349
    bool isScalarType(Id typeId)       const
350
14.4k
        { return getTypeClass(typeId) == Op::OpTypeFloat || getTypeClass(typeId) == Op::OpTypeInt ||
351
9.84k
          getTypeClass(typeId) == Op::OpTypeBool; }
352
4.83k
    bool isVectorType(Id typeId)       const { return getTypeClass(typeId) == Op::OpTypeVector; }
353
9.92k
    bool isMatrixType(Id typeId)       const { return getTypeClass(typeId) == Op::OpTypeMatrix; }
354
15.4k
    bool isStructType(Id typeId)       const { return getTypeClass(typeId) == Op::OpTypeStruct; }
355
203
    bool isArrayType(Id typeId)        const { return getTypeClass(typeId) == Op::OpTypeArray; }
356
    bool isCooperativeMatrixType(Id typeId)const
357
9.81k
    {
358
9.81k
        return getTypeClass(typeId) == Op::OpTypeCooperativeMatrixKHR || getTypeClass(typeId) == Op::OpTypeCooperativeMatrixNV;
359
9.81k
    }
360
0
    bool isTensorViewType(Id typeId) const { return getTypeClass(typeId) == Op::OpTypeTensorViewNV; }
361
1.41k
    bool isCooperativeVectorType(Id typeId) const { return getTypeClass(typeId) == Op::OpTypeCooperativeVectorNV; }
362
0
    bool isTensorTypeARM(Id typeId)    const { return getTypeClass(typeId) == Op::OpTypeTensorARM; }
363
    bool isAggregateType(Id typeId)    const
364
203
        { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
365
0
    bool isImageType(Id typeId)        const { return getTypeClass(typeId) == Op::OpTypeImage; }
366
0
    bool isSamplerType(Id typeId)      const { return getTypeClass(typeId) == Op::OpTypeSampler; }
367
4.66k
    bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == Op::OpTypeSampledImage; }
368
    bool containsType(Id typeId, Op typeOp, unsigned int width) const;
369
    bool containsPhysicalStorageBufferOrArray(Id typeId) const;
370
371
    bool isConstantOpCode(Op opcode) const;
372
    bool isSpecConstantOpCode(Op opcode) const;
373
530
    bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
374
284
    bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == Op::OpConstant; }
375
0
    bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
376
    unsigned int getConstantScalar(Id resultId) const
377
18.1k
        { return module.getInstruction(resultId)->getImmediateOperand(0); }
378
158k
    StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
379
380
0
    bool isVariableOpCode(Op opcode) const { return opcode == Op::OpVariable; }
381
0
    bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); }
382
0
    bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClass::Function; }
383
0
    bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); }
384
    // See if a resultId is valid for use as an initializer.
385
0
    bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); }
386
387
    int getScalarTypeWidth(Id typeId) const
388
140k
    {
389
140k
        Id scalarTypeId = getScalarTypeId(typeId);
390
140k
        assert(getTypeClass(scalarTypeId) == Op::OpTypeInt || getTypeClass(scalarTypeId) == Op::OpTypeFloat);
391
140k
        return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
392
140k
    }
393
394
    unsigned int getTypeNumColumns(Id typeId) const
395
81
    {
396
81
        assert(isMatrixType(typeId));
397
81
        return getNumTypeConstituents(typeId);
398
81
    }
399
45
    unsigned int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
400
    unsigned int getTypeNumRows(Id typeId) const
401
117
    {
402
117
        assert(isMatrixType(typeId));
403
117
        return getNumTypeComponents(getContainedTypeId(typeId));
404
117
    }
405
81
    unsigned int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
406
407
    Dim getTypeDimensionality(Id typeId) const
408
87
    {
409
87
        assert(isImageType(typeId));
410
87
        return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
411
87
    }
412
    Id getImageType(Id resultId) const
413
818
    {
414
818
        Id typeId = getTypeId(resultId);
415
818
        assert(isImageType(typeId) || isSampledImageType(typeId));
416
818
        return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
417
818
    }
418
    bool isArrayedImageType(Id typeId) const
419
87
    {
420
87
        assert(isImageType(typeId));
421
87
        return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
422
87
    }
423
424
    // For making new constants (will return old constant if the requested one was already made).
425
    Id makeNullConstant(Id typeId);
426
    Id makeBoolConstant(bool b, bool specConstant = false);
427
    Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
428
    Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
429
    Id makeInt8Constant(int i, bool specConstant = false)
430
751
        { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); }
431
    Id makeUint8Constant(unsigned u, bool specConstant = false)
432
752
        { return makeIntConstant(makeUintType(8),           u, specConstant); }
433
    Id makeInt16Constant(int i, bool specConstant = false)
434
882
        { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); }
435
    Id makeUint16Constant(unsigned u, bool specConstant = false)
436
864
        { return makeIntConstant(makeUintType(16),           u, specConstant); }
437
    Id makeIntConstant(int i, bool specConstant = false)
438
15.0k
        { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); }
439
    Id makeUintConstant(unsigned u, bool specConstant = false)
440
11.0k
        { return makeIntConstant(makeUintType(32),           u, specConstant); }
441
    Id makeUintConstant(Scope u, bool specConstant = false)
442
2.06k
        { return makeUintConstant((unsigned)u, specConstant); }
443
    Id makeUintConstant(StorageClass u, bool specConstant = false)
444
0
        { return makeUintConstant((unsigned)u, specConstant); }
445
    Id makeUintConstant(MemorySemanticsMask u, bool specConstant = false)
446
2.06k
        { return makeUintConstant((unsigned)u, specConstant); }
447
    Id makeUintConstant(SourceLanguage u, bool specConstant = false)
448
0
        { return makeUintConstant((unsigned)u, specConstant); }
449
    Id makeInt64Constant(long long i, bool specConstant = false)
450
808
        { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); }
451
    Id makeUint64Constant(unsigned long long u, bool specConstant = false)
452
1.06k
        { return makeInt64Constant(makeUintType(64),                     u, specConstant); }
453
    Id makeFloatConstant(float f, bool specConstant = false);
454
    Id makeDoubleConstant(double d, bool specConstant = false);
455
    Id makeFloat16Constant(float f16, bool specConstant = false);
456
    Id makeBFloat16Constant(float bf16, bool specConstant = false);
457
    Id makeFloatE5M2Constant(float fe5m2, bool specConstant = false);
458
    Id makeFloatE4M3Constant(float fe4m3, bool specConstant = false);
459
    Id makeFpConstant(Id type, double d, bool specConstant = false);
460
461
    Id importNonSemanticShaderDebugInfoInstructions();
462
    // Ensure the NonSemantic.Shader.DebugInfo import string names at least `version`.
463
    // If the import instruction already exists, its name is patched in place.
464
    // If it has not been created yet, importNonSemanticShaderDebugInfoInstructions()
465
    // will use the updated version when it runs.
466
    void requireNonSemanticShaderDebugInfoVersion(unsigned version);
467
468
    // Turn the array of constants into a proper spv constant of the requested type.
469
    Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
470
471
    // Methods for adding information outside the CFG.
472
    Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
473
    void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
474
    void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals);
475
    void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds);
476
    void addName(Id, const char* name);
477
    void addMemberName(Id, int member, const char* name);
478
    void addDecoration(Id, Decoration, int num = -1);
479
    void addDecoration(Id, Decoration, const char*);
480
    void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
481
    void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
482
    void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType);
483
    void addDecorationId(Id id, Decoration, Id idDecoration);
484
    void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
485
    void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
486
    void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
487
    void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals);
488
    void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings);
489
    void addMemberDecorationIdEXT(Id, unsigned int member, Decoration, const std::vector<unsigned>& operands);
490
491
    // At the end of what block do the next create*() instructions go?
492
    // Also reset current last DebugScope and current source line to unknown
493
9.22k
    void setBuildPoint(Block* bp) {
494
9.22k
        buildPoint = bp;
495
9.22k
        dirtyLineTracker = true;
496
9.22k
        dirtyScopeTracker = true;
497
9.22k
    }
498
2.68k
    Block* getBuildPoint() const { return buildPoint; }
499
500
    // Append an instruction to the end of the current build point.
501
    // Optionally, additional debug info instructions may also be prepended.
502
    void addInstruction(std::unique_ptr<Instruction> inst);
503
504
    // Append an instruction to the end of the current build point without prepending any debug instructions.
505
    // This is useful for insertion of some debug info instructions themselves or some control flow instructions
506
    // that are attached to its predecessor instruction.
507
    void addInstructionNoDebugInfo(std::unique_ptr<Instruction> inst);
508
509
    // Make the entry-point function. The returned pointer is only valid
510
    // for the lifetime of this builder.
511
    Function* makeEntryPoint(const char*);
512
513
    // Make a shader-style function, and create its entry block if entry is non-zero.
514
    // Return the function, pass back the entry.
515
    // The returned pointer is only valid for the lifetime of this builder.
516
    Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
517
                                const std::vector<Id>& paramTypes,
518
                                const std::vector<std::vector<Decoration>>& precisions, Block** entry = nullptr);
519
520
    // Create a return. An 'implicit' return is one not appearing in the source
521
    // code.  In the case of an implicit return, no post-return block is inserted.
522
    void makeReturn(bool implicit, Id retVal = 0);
523
524
    // Initialize state and generate instructions for new lexical scope
525
    void enterLexicalBlock(uint32_t line, uint32_t column);
526
527
    // Set state and generate instructions to exit current lexical scope
528
    void leaveLexicalBlock();
529
530
    // Prepare builder for generation of instructions for a function.
531
    void enterFunction(Function const* function);
532
533
    // Generate all the code needed to finish up a function.
534
    void leaveFunction();
535
536
    // Create block terminator instruction for certain statements like
537
    // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT
538
    void makeStatementTerminator(spv::Op opcode, const char *name);
539
540
    // Create block terminator instruction for statements that have input operands
541
    // such as OpEmitMeshTasksEXT
542
    void makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name);
543
544
    // Create a global/local constant. Because OpConstant is automatically emitted by getting the constant
545
    // ids, this function only handles debug info.
546
    void createConstVariable(Id type, const char* name, Id constant, bool isGlobal);
547
548
    // Create a global or function local or IO variable.
549
    Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr,
550
        Id initializer = NoResult, bool const compilerGenerated = true);
551
552
    // Create an untyped global or function local or IO variable.
553
    Id createUntypedVariable(Decoration precision, StorageClass storageClass, const char* name = nullptr,
554
                             Id dataType = NoResult, Id initializer = NoResult);
555
556
    // Create an intermediate with an undefined value.
557
    Id createUndefined(Id type);
558
559
    // Create load/store instruction with a remapped descriptor heap base.
560
    Instruction* createDescHeapLoadStoreBaseRemap(Id base, Op op);
561
562
    // Store into an Id and return the l-value
563
    void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone,
564
        spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0);
565
566
    // Load from an Id and return it
567
    Id createLoad(Id lValue, spv::Decoration precision,
568
        spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone,
569
        spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0);
570
571
    // Create an OpAccessChain instruction
572
    Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
573
574
    // Create an OpArrayLength instruction
575
    Id createArrayLength(Id base, unsigned int member, unsigned int bits);
576
577
    // Create an OpCooperativeMatrixLengthKHR instruction
578
    Id createCooperativeMatrixLengthKHR(Id type);
579
    // Create an OpCooperativeMatrixLengthNV instruction
580
    Id createCooperativeMatrixLengthNV(Id type);
581
582
    // Create an OpCompositeExtract instruction
583
    Id createCompositeExtract(Id composite, Id typeId, unsigned index);
584
    Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
585
    Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
586
    Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
587
588
    Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
589
    Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
590
591
    void createNoResultOp(Op);
592
    void createNoResultOp(Op, Id operand);
593
    void createNoResultOp(Op, const std::vector<Id>& operands);
594
    void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
595
    void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
596
    void createMemoryBarrier(Scope executionScope, MemorySemanticsMask memorySemantics);
597
    Id createUnaryOp(Op, Id typeId, Id operand);
598
    Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
599
    Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
600
    Id createOp(Op, Id typeId, const std::vector<Id>& operands);
601
    Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
602
    Id createConstData(Op opCode, Id typeId, const std::vector<const char*> operands);
603
    Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
604
    Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
605
606
    // Take an rvalue (source) and a set of channels to extract from it to
607
    // make a new rvalue, which is returned.
608
    Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
609
610
    // Take a copy of an lvalue (target) and a source of components, and set the
611
    // source components into the lvalue where the 'channels' say to put them.
612
    // An updated version of the target is returned.
613
    // (No true lvalue or stores are used.)
614
    Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
615
616
    // If both the id and precision are valid, the id
617
    // gets tagged with the requested precision.
618
    // The passed in id is always the returned id, to simplify use patterns.
619
    Id setPrecision(Id id, Decoration precision)
620
42.9k
    {
621
42.9k
        if (precision != NoPrecision && id != NoResult)
622
9
            addDecoration(id, precision);
623
624
42.9k
        return id;
625
42.9k
    }
626
627
    // Can smear a scalar to a vector for the following forms:
628
    //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
629
    //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
630
    //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
631
    //   - promoteScalar(scalar, scalar)  // do nothing
632
    // Other forms are not allowed.
633
    //
634
    // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
635
    // The type of the created vector is a vector of components of the same type as the scalar.
636
    //
637
    // Note: One of the arguments will change, with the result coming back that way rather than
638
    // through the return value.
639
    void promoteScalar(Decoration precision, Id& left, Id& right);
640
641
    // Make a value by smearing the scalar to fill the type.
642
    // vectorType should be the correct type for making a vector of scalarVal.
643
    // (No conversions are done.)
644
    Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
645
646
    // Create a call to a built-in function.
647
    Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
648
649
    // List of parameters used to create a texture operation
650
    struct TextureParameters {
651
        Id sampler;
652
        Id coords;
653
        Id bias;
654
        Id lod;
655
        Id Dref;
656
        Id offset;
657
        Id offsets;
658
        Id gradX;
659
        Id gradY;
660
        Id sample;
661
        Id component;
662
        Id texelOut;
663
        Id lodClamp;
664
        Id granularity;
665
        Id coarse;
666
        bool nonprivate;
667
        bool volatil;
668
        bool nontemporal;
669
    };
670
671
    // Select the correct texture operation based on all inputs, and emit the correct instruction
672
    Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
673
        bool noImplicit, const TextureParameters&, ImageOperandsMask);
674
675
    // Emit the OpTextureQuery* instruction that was passed in.
676
    // Figure out the right return value and type, and return it.
677
    Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
678
679
    Id createSamplePositionCall(Decoration precision, Id, Id);
680
681
    Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
682
    Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
683
684
    // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
685
    Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
686
687
    // OpCompositeConstruct
688
    Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
689
690
    // vector or scalar constructor
691
    Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
692
693
    // matrix constructor
694
    Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
695
696
    // coopmat conversion
697
    Id createCooperativeMatrixConversion(Id typeId, Id source);
698
    Id createCooperativeMatrixReduce(Op opcode, Id typeId, Id source, unsigned int mask, Id func);
699
    Id createCooperativeMatrixPerElementOp(Id typeId, const std::vector<Id>& operands);
700
701
    // Helper to use for building nested control flow with if-then-else.
702
    class If {
703
    public:
704
        If(Id condition, SelectionControlMask ctrl, Builder& builder);
705
1.23k
        ~If() {}
706
707
        void makeBeginElse();
708
        void makeEndIf();
709
710
    private:
711
        If(const If&);
712
        If& operator=(If&);
713
714
        Builder& builder;
715
        Id condition;
716
        SelectionControlMask control;
717
        Function* function;
718
        Block* headerBlock;
719
        Block* thenBlock;
720
        Block* elseBlock;
721
        Block* mergeBlock;
722
    };
723
724
    // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
725
    // any case/default labels, all separated by one or more case/default labels.  Each possible
726
    // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
727
    // number space.  How to compute the value is given by 'condition', as in switch(condition).
728
    //
729
    // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
730
    //
731
    // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
732
    //
733
    // Returns the right set of basic blocks to start each code segment with, so that the caller's
734
    // recursion stack can hold the memory for it.
735
    //
736
    void makeSwitch(Id condition, SelectionControlMask control, int numSegments, const std::vector<int>& caseValues,
737
                    const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB);
738
739
    // Add a branch to the innermost switch's merge block.
740
    void addSwitchBreak(bool implicit);
741
742
    // Move to the next code segment, passing in the return argument in makeSwitch()
743
    void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
744
745
    // Finish off the innermost switch.
746
    void endSwitch(std::vector<Block*>& segmentBB);
747
748
    struct LoopBlocks {
749
        LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
750
353
            head(head), body(body), merge(merge), continue_target(continue_target) { }
751
        Block &head, &body, &merge, &continue_target;
752
    private:
753
        LoopBlocks();
754
        LoopBlocks& operator=(const LoopBlocks&) = delete;
755
    };
756
757
    // Start a new loop and prepare the builder to generate code for it.  Until
758
    // closeLoop() is called for this loop, createLoopContinue() and
759
    // createLoopExit() will target its corresponding blocks.
760
    LoopBlocks& makeNewLoop();
761
762
    // Create a new block in the function containing the build point.  Memory is
763
    // owned by the function object.
764
    Block& makeNewBlock();
765
766
    // Add a branch to the continue_target of the current (innermost) loop.
767
    void createLoopContinue();
768
769
    // Add an exit (e.g. "break") from the innermost loop that we're currently
770
    // in.
771
    void createLoopExit();
772
773
    // Close the innermost loop that you're in
774
    void closeLoop();
775
776
    //
777
    // Access chain design for an R-Value vs. L-Value:
778
    //
779
    // There is a single access chain the builder is building at
780
    // any particular time.  Such a chain can be used to either to a load or
781
    // a store, when desired.
782
    //
783
    // Expressions can be r-values, l-values, or both, or only r-values:
784
    //    a[b.c].d = ....  // l-value
785
    //    ... = a[b.c].d;  // r-value, that also looks like an l-value
786
    //    ++a[b.c].d;      // r-value and l-value
787
    //    (x + y)[2];      // r-value only, can't possibly be l-value
788
    //
789
    // Computing an r-value means generating code.  Hence,
790
    // r-values should only be computed when they are needed, not speculatively.
791
    //
792
    // Computing an l-value means saving away information for later use in the compiler,
793
    // no code is generated until the l-value is later dereferenced.  It is okay
794
    // to speculatively generate an l-value, just not okay to speculatively dereference it.
795
    //
796
    // The base of the access chain (the left-most variable or expression
797
    // from which everything is based) can be set either as an l-value
798
    // or as an r-value.  Most efficient would be to set an l-value if one
799
    // is available.  If an expression was evaluated, the resulting r-value
800
    // can be set as the chain base.
801
    //
802
    // The users of this single access chain can save and restore if they
803
    // want to nest or manage multiple chains.
804
    //
805
806
    struct AccessChain {
807
        Id base;                       // for l-values, pointer to the base object, for r-values, the base object
808
        std::vector<Id> indexChain;
809
        Id instr;                      // cache the instruction that generates this access chain
810
        std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
811
        Id component;                  // a dynamic component index, can coexist with a swizzle,
812
                                       // done after the swizzle, NoResult if not present
813
        Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied;
814
                                       // NoType unless a swizzle or component is present
815
        bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
816
        unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment.
817
                                       // Only tracks base and (optional) component selection alignment.
818
819
        struct DescHeapInfo {
820
            Id descHeapBaseTy;                  // for descriptor heap, record its base data type.
821
            StorageClass descHeapStorageClass;  // for descriptor heap, record its basic storage class.
822
            uint32_t descHeapBaseArrayStride;   // for descriptor heap, record its explicit array stride.
823
            std::vector<Instruction*> descHeapInstId;
824
                                                // for descriptor heap, record its data type for loading/store results.
825
            uint32_t structRsrcTyOffsetCount;
826
            uint32_t structRsrcTyFirstArrIndex;
827
            Id structRemappedBase;
828
        };
829
        DescHeapInfo descHeapInfo;
830
831
        // Accumulate whether anything in the chain of structures has coherent decorations.
832
        struct CoherentFlags {
833
120k
            CoherentFlags() { clear(); }
834
2.06k
            bool isVolatile() const { return volatil; }
835
56.0k
            bool isNonUniform() const { return nonUniform; }
836
70.5k
            bool anyCoherent() const {
837
70.5k
                return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent ||
838
69.1k
                    subgroupcoherent || shadercallcoherent;
839
70.5k
            }
840
841
            unsigned coherent : 1;
842
            unsigned devicecoherent : 1;
843
            unsigned queuefamilycoherent : 1;
844
            unsigned workgroupcoherent : 1;
845
            unsigned subgroupcoherent : 1;
846
            unsigned shadercallcoherent : 1;
847
            unsigned nonprivate : 1;
848
            unsigned volatil : 1;
849
            unsigned nontemporal : 1;
850
            unsigned isImage : 1;
851
            unsigned nonUniform : 1;
852
853
235k
            void clear() {
854
235k
                coherent = 0;
855
235k
                devicecoherent = 0;
856
235k
                queuefamilycoherent = 0;
857
235k
                workgroupcoherent = 0;
858
235k
                subgroupcoherent = 0;
859
235k
                shadercallcoherent = 0;
860
235k
                nonprivate = 0;
861
235k
                volatil = 0;
862
235k
                nontemporal = 0;
863
235k
                isImage = 0;
864
235k
                nonUniform = 0;
865
235k
            }
866
867
66.8k
            CoherentFlags operator |=(const CoherentFlags &other) {
868
66.8k
                coherent |= other.coherent;
869
66.8k
                devicecoherent |= other.devicecoherent;
870
66.8k
                queuefamilycoherent |= other.queuefamilycoherent;
871
66.8k
                workgroupcoherent |= other.workgroupcoherent;
872
66.8k
                subgroupcoherent |= other.subgroupcoherent;
873
66.8k
                shadercallcoherent |= other.shadercallcoherent;
874
66.8k
                nonprivate |= other.nonprivate;
875
66.8k
                volatil |= other.volatil;
876
66.8k
                nontemporal = other.nontemporal;
877
66.8k
                isImage |= other.isImage;
878
66.8k
                nonUniform |= other.nonUniform;
879
66.8k
                return *this;
880
66.8k
            }
881
        };
882
        CoherentFlags coherentFlags;
883
    };
884
885
    //
886
    // the SPIR-V builder maintains a single active chain that
887
    // the following methods operate on
888
    //
889
890
    // for external save and restore
891
205k
    AccessChain getAccessChain() { return accessChain; }
892
12.5k
    void setAccessChain(AccessChain newChain) { accessChain = newChain; }
893
894
    // clear accessChain
895
    void clearAccessChain();
896
897
    Id createDescHeapAccessChain();
898
    Id createConstantSizeOfEXT(Id typeId);
899
    uint32_t isStructureHeapMember(Id id, std::vector<Id> indexChain, unsigned int idx, spv::BuiltIn* bt = nullptr,
900
                                   uint32_t* firstArrIndex = nullptr);
901
902
    // set new base as an l-value base
903
    void setAccessChainLValue(Id lValue)
904
29.7k
    {
905
29.7k
        assert(isPointer(lValue) || isUntypedPointer(lValue));
906
29.7k
        accessChain.base = lValue;
907
29.7k
    }
908
909
    // set new base value as an r-value
910
    void setAccessChainRValue(Id rValue)
911
35.1k
    {
912
35.1k
        accessChain.isRValue = true;
913
35.1k
        accessChain.base = rValue;
914
35.1k
    }
915
916
    // set access chain info for untyped descriptor heap variable
917
    void setAccessChainDescHeapInfo(StorageClass storageClass = StorageClass::Max, Id baseTy = NoResult,
918
                                    uint32_t explicitArrayStride = NoResult, uint32_t structRsrcTyOffsetCount = 0,
919
                                    spv::Id structRemappedBase = NoResult, uint32_t firstArrIndex = NoResult)
920
0
    {
921
0
        if (accessChain.descHeapInfo.descHeapStorageClass == StorageClass::Max)
922
0
            accessChain.descHeapInfo.descHeapStorageClass = storageClass;
923
0
        if (accessChain.descHeapInfo.descHeapBaseTy == NoResult)
924
0
            accessChain.descHeapInfo.descHeapBaseTy = baseTy;
925
0
        if (accessChain.descHeapInfo.descHeapBaseArrayStride == NoResult)
926
0
            accessChain.descHeapInfo.descHeapBaseArrayStride = explicitArrayStride;
927
0
        if (accessChain.descHeapInfo.structRemappedBase == NoResult)
928
0
            accessChain.descHeapInfo.structRemappedBase = structRemappedBase;
929
0
        if (accessChain.descHeapInfo.structRsrcTyOffsetCount == 0)
930
0
            accessChain.descHeapInfo.structRsrcTyOffsetCount = structRsrcTyOffsetCount;
931
0
        if (accessChain.descHeapInfo.structRsrcTyFirstArrIndex == 0)
932
0
            accessChain.descHeapInfo.structRsrcTyFirstArrIndex = firstArrIndex;
933
0
    }
934
935
    // push offset onto the end of the chain
936
    void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
937
5.99k
    {
938
5.99k
        accessChain.indexChain.push_back(offset);
939
5.99k
        accessChain.coherentFlags |= coherentFlags;
940
5.99k
        accessChain.alignment |= alignment;
941
5.99k
    }
942
943
    // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
944
    void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
945
        AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
946
947
    // push a dynamic component selection onto the access chain, only applicable with a
948
    // non-trivial swizzle or no swizzle
949
    void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags,
950
        unsigned int alignment)
951
0
    {
952
0
        if (accessChain.swizzle.size() != 1) {
953
0
            accessChain.component = component;
954
0
            if (accessChain.preSwizzleBaseType == NoType)
955
0
                accessChain.preSwizzleBaseType = preSwizzleBaseType;
956
0
        }
957
0
        accessChain.coherentFlags |= coherentFlags;
958
0
        accessChain.alignment |= alignment;
959
0
    }
960
961
    // use accessChain and swizzle to store value
962
    void accessChainStore(Id rvalue, Decoration nonUniform,
963
        spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone,
964
        spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0);
965
966
    // use accessChain and swizzle to load an r-value
967
    Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType,
968
        spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone, spv::Scope scope = spv::Scope::Max,
969
            unsigned int alignment = 0);
970
971
    // Return whether or not the access chain can be represented in SPIR-V
972
    // as an l-value.
973
    // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
974
1.17k
    bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; }
975
976
    // get the direct pointer for an l-value
977
    Id accessChainGetLValue();
978
979
    // Get the inferred SPIR-V type of the result of the current access chain,
980
    // based on the type of the base and the chain of dereferences.
981
    Id accessChainGetInferredType();
982
983
    // Add capabilities, extensions, remove unneeded decorations, etc.,
984
    // based on the resulting SPIR-V.
985
    void postProcess(bool compileOnly);
986
987
    // Prune unreachable blocks in the CFG and remove unneeded decorations.
988
    void postProcessCFG();
989
990
    // Add capabilities, extensions based on instructions in the module.
991
    void postProcessFeatures();
992
    // Hook to visit each instruction in a block in a function
993
    void postProcess(Instruction&);
994
    // Hook to visit each non-32-bit sized float/int operation in a block.
995
    void postProcessType(const Instruction&, spv::Id typeId);
996
    // move OpSampledImage instructions to be next to their users.
997
    void postProcessSamplers();
998
999
    void dump(std::vector<unsigned int>&) const;
1000
1001
    // Add a branch to the target block.
1002
    // If set implicit, the branch instruction shouldn't have debug source location.
1003
    void createBranch(bool implicit, Block* block);
1004
    void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
1005
    void createLoopMerge(Block* mergeBlock, Block* continueBlock, LoopControlMask control,
1006
        const std::vector<unsigned int>& operands);
1007
1008
    // Sets to generate opcode for specialization constants.
1009
2.43k
    void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
1010
    // Sets to generate opcode for non-specialization constants (normal mode).
1011
79.6k
    void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
1012
    // Check if the builder is generating code for spec constants.
1013
81.0k
    bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
1014
1015
445
    void setUseReplicatedComposites(bool use) { useReplicatedComposites = use; }
1016
1017
private:
1018
    // Helper to get size of a scalar (in bytes)
1019
    unsigned int postProcessGetLargestScalarSize(const Instruction& type);
1020
1021
protected:
1022
    Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
1023
    Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
1024
    Id findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std::vector<Id>& comps, size_t numMembers);
1025
    Id findStructConstant(Id typeId, const std::vector<Id>& comps);
1026
    Id collapseAccessChain();
1027
    void remapDynamicSwizzle();
1028
    void transferAccessChainSwizzle(bool dynamic);
1029
    void simplifyAccessChainSwizzle();
1030
    void createAndSetNoPredecessorBlock(const char*);
1031
    void createSelectionMerge(Block* mergeBlock, SelectionControlMask control);
1032
    void dumpSourceInstructions(std::vector<unsigned int>&) const;
1033
    void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
1034
    template <class Range> void dumpInstructions(std::vector<unsigned int>& out, const Range& instructions) const;
1035
    void dumpModuleProcesses(std::vector<unsigned int>&) const;
1036
    spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
1037
        const;
1038
    struct DecorationInstructionLessThan {
1039
        bool operator()(const std::unique_ptr<Instruction>& lhs, const std::unique_ptr<Instruction>& rhs) const;
1040
    };
1041
1042
    unsigned int spvVersion;     // the version of SPIR-V to emit in the header
1043
    SourceLanguage sourceLang;
1044
    int sourceVersion;
1045
    spv::Id nonSemanticShaderCompilationUnitId {0};
1046
    spv::Id nonSemanticShaderDebugInfo {0};
1047
    // Pointer to the OpExtInstImport instruction for NonSemantic.Shader.DebugInfo.
1048
    // Kept so requireNonSemanticShaderDebugInfoVersion() can patch the name in place.
1049
    Instruction* nonSemanticShaderDebugInfoImportInst {nullptr};
1050
    // Spec version encoded in the NonSemantic.Shader.DebugInfo import name.
1051
    // Defaults to 100. Promoted to N the first time a version-N opcode is emitted.
1052
    unsigned int nonSemanticShaderDebugInfoVersion{100};
1053
    spv::Id debugInfoNone {0};
1054
    spv::Id debugExpression {0}; // Debug expression with zero operations.
1055
    std::string sourceText;
1056
1057
    // True if an new OpLine/OpDebugLine may need to be inserted. Either:
1058
    // 1. The current debug location changed
1059
    // 2. The current build point changed
1060
    bool dirtyLineTracker;
1061
    int currentLine = 0;
1062
    // OpString id of the current file name. Always 0 if debug info is off.
1063
    spv::Id currentFileId = 0;
1064
    // OpString id of the main file name. Always 0 if debug info is off.
1065
    spv::Id mainFileId = 0;
1066
1067
    // True if an new OpDebugScope may need to be inserted. Either:
1068
    // 1. A new lexical block is pushed
1069
    // 2. The current build point changed
1070
    bool dirtyScopeTracker;
1071
    std::stack<spv::Id> currentDebugScopeId;
1072
1073
    // This flag toggles tracking of debug info while building the SPIR-V.
1074
    bool trackDebugInfo = false;
1075
    // This flag toggles emission of SPIR-V debug instructions, like OpLine and OpSource.
1076
    bool emitSpirvDebugInfo = false;
1077
    // This flag toggles emission of Non-Semantic Debug extension debug instructions.
1078
    bool emitNonSemanticShaderDebugInfo = false;
1079
    bool restoreNonSemanticShaderDebugInfo = false;
1080
    bool emitNonSemanticShaderDebugSource = false;
1081
1082
    std::set<std::string> extensions;
1083
    std::vector<const char*> sourceExtensions;
1084
    std::vector<const char*> moduleProcesses;
1085
    AddressingModel addressModel;
1086
    MemoryModel memoryModel;
1087
    std::set<spv::Capability> capabilities;
1088
    int builderNumber;
1089
    Module module;
1090
    Block* buildPoint;
1091
    Id uniqueId;
1092
    Function* entryPointFunction;
1093
    // This tracks the current function being built, or nullptr if not in a function.
1094
    Function const* currentFunction { nullptr };
1095
    bool generatingOpCodeForSpecConst;
1096
    bool useReplicatedComposites { false };
1097
    AccessChain accessChain;
1098
1099
    // special blocks of instructions for output
1100
    std::vector<std::unique_ptr<Instruction> > strings;
1101
    std::vector<std::unique_ptr<Instruction> > imports;
1102
    std::vector<std::unique_ptr<Instruction> > entryPoints;
1103
    std::vector<std::unique_ptr<Instruction> > executionModes;
1104
    std::vector<std::unique_ptr<Instruction> > names;
1105
    std::set<std::unique_ptr<Instruction>, DecorationInstructionLessThan> decorations;
1106
    std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
1107
    std::vector<std::unique_ptr<Instruction> > externals;
1108
    std::vector<std::unique_ptr<Function> > functions;
1109
1110
    // not output, internally used for quick & dirty canonical (unique) creation
1111
1112
    // Key for scalar constants (handles both 32-bit and 64-bit)
1113
    struct ScalarConstantKey {
1114
        unsigned int typeClass;  // OpTypeInt, OpTypeFloat, OpTypeBool
1115
        unsigned int opcode;     // OpConstant, OpSpecConstant, OpConstantTrue, etc.
1116
        Id typeId;               // The specific type
1117
        unsigned value1;         // First operand (or only operand)
1118
        unsigned value2;         // Second operand (0 for single-operand constants)
1119
1120
32.8k
        bool operator==(const ScalarConstantKey& other) const {
1121
32.8k
            return typeClass == other.typeClass &&
1122
32.8k
                   opcode == other.opcode &&
1123
32.8k
                   typeId == other.typeId &&
1124
32.8k
                   value1 == other.value1 &&
1125
32.8k
                   value2 == other.value2;
1126
32.8k
        }
1127
    };
1128
1129
    struct ScalarConstantKeyHash {
1130
        // 64/32 bit mix function from MurmurHash3
1131
160k
        inline std::size_t hash_mix(std::size_t h) const {
1132
160k
            if constexpr (sizeof(std::size_t) == 8) {
1133
160k
                h ^= h >> 33;
1134
160k
                h *= UINT64_C(0xff51afd7ed558ccd);
1135
160k
                h ^= h >> 33;
1136
160k
                h *= UINT64_C(0xc4ceb9fe1a85ec53);
1137
160k
                h ^= h >> 33;
1138
160k
                return h;
1139
            } else {
1140
                h ^= h >> 16;
1141
                h *= UINT32_C(0x85ebca6b);
1142
                h ^= h >> 13;
1143
                h *= UINT32_C(0xc2b2ae35);
1144
                h ^= h >> 16;
1145
                return h;
1146
            }
1147
160k
        }
1148
1149
        // Hash combine from boost
1150
160k
        inline std::size_t hash_combine(std::size_t seed, std::size_t v) const {
1151
160k
            return hash_mix(seed + 0x9e3779b9 + v);
1152
160k
        }
1153
        
1154
40.0k
        std::size_t operator()(const ScalarConstantKey& k) const {
1155
40.0k
            size_t hash1 = hash_combine(std::hash<unsigned>{}(k.typeClass), std::hash<unsigned>{}(k.opcode));
1156
40.0k
            size_t hash2 = hash_combine(std::hash<Id>{}(k.value1), std::hash<unsigned>{}(k.value2));
1157
40.0k
            size_t hash3 = hash_combine(hash1, hash2);
1158
40.0k
            return hash_combine(hash3, std::hash<unsigned>{}(k.typeId));
1159
40.0k
        }
1160
    };
1161
1162
    // map type opcodes to constant inst.
1163
    std::unordered_map<unsigned int, std::vector<Instruction*>> groupedCompositeConstants;
1164
    // map struct-id to constant instructions
1165
    std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
1166
    // map type opcodes to type instructions
1167
    std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;
1168
    // map type opcodes to debug type instructions
1169
    std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes;
1170
    // list of OpConstantNull instructions
1171
    std::vector<Instruction*> nullConstants;
1172
    // map scalar constants to result IDs
1173
    std::unordered_map<ScalarConstantKey, Id, ScalarConstantKeyHash> groupedScalarConstantResultIDs;
1174
1175
    // Track which types have explicit layouts, to avoid reusing in storage classes without layout.
1176
    // Currently only tracks array types.
1177
    std::unordered_set<unsigned int> explicitlyLaidOut;
1178
1179
    // stack of switches
1180
    std::stack<Block*> switchMerges;
1181
1182
    // Our loop stack.
1183
    std::stack<LoopBlocks> loops;
1184
1185
    // map from strings to their string ids
1186
    std::unordered_map<std::string, spv::Id> stringIds;
1187
1188
    // map from include file name ids to their contents
1189
    std::map<spv::Id, const std::string*> includeFiles;
1190
1191
    // maps from OpTypeXXX id to DebugTypeXXX id
1192
    std::unordered_map<spv::Id, spv::Id> debugTypeIdLookup;
1193
1194
    // maps from OpFunction id to DebugFunction id
1195
    std::unordered_map<spv::Id, spv::Id> debugFuncIdLookup;
1196
1197
    // map from file name string id to DebugSource id
1198
    std::unordered_map<spv::Id, spv::Id> debugSourceId;
1199
1200
    // The stream for outputting warnings and errors.
1201
    SpvBuildLogger* logger;
1202
};  // end Builder class
1203
1204
} // end spv namespace
1205
1206
#endif // SpvBuilder_H