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