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