Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/glslang/SPIRV/SpvBuilder.cpp
9906 views
1
//
2
// Copyright (C) 2014-2015 LunarG, Inc.
3
// Copyright (C) 2015-2018 Google, Inc.
4
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
5
//
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions
10
// are met:
11
//
12
// Redistributions of source code must retain the above copyright
13
// notice, this list of conditions and the following disclaimer.
14
//
15
// Redistributions in binary form must reproduce the above
16
// copyright notice, this list of conditions and the following
17
// disclaimer in the documentation and/or other materials provided
18
// with the distribution.
19
//
20
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
21
// contributors may be used to endorse or promote products derived
22
// from this software without specific prior written permission.
23
//
24
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
// POSSIBILITY OF SUCH DAMAGE.
36
37
//
38
// Helper for making SPIR-V IR. Generally, this is documented in the header
39
// SpvBuilder.h.
40
//
41
42
#include <cassert>
43
#include <cstdlib>
44
45
#include <unordered_set>
46
#include <algorithm>
47
48
#include "SpvBuilder.h"
49
#include "hex_float.h"
50
51
#ifndef _WIN32
52
#include <cstdio>
53
#endif
54
55
namespace spv {
56
57
Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
58
spvVersion(spvVersion),
59
sourceLang(SourceLanguageUnknown),
60
sourceVersion(0),
61
addressModel(AddressingModelLogical),
62
memoryModel(MemoryModelGLSL450),
63
builderNumber(magicNumber),
64
buildPoint(nullptr),
65
uniqueId(0),
66
entryPointFunction(nullptr),
67
generatingOpCodeForSpecConst(false),
68
logger(buildLogger)
69
{
70
clearAccessChain();
71
}
72
73
Builder::~Builder()
74
{
75
}
76
77
Id Builder::import(const char* name)
78
{
79
Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
80
import->addStringOperand(name);
81
module.mapInstruction(import);
82
83
imports.push_back(std::unique_ptr<Instruction>(import));
84
return import->getResultId();
85
}
86
87
// For creating new groupedTypes (will return old type if the requested one was already made).
88
Id Builder::makeVoidType()
89
{
90
Instruction* type;
91
if (groupedTypes[OpTypeVoid].size() == 0) {
92
Id typeId = getUniqueId();
93
type = new Instruction(typeId, NoType, OpTypeVoid);
94
groupedTypes[OpTypeVoid].push_back(type);
95
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
96
module.mapInstruction(type);
97
// Core OpTypeVoid used for debug void type
98
if (emitNonSemanticShaderDebugInfo)
99
debugId[typeId] = typeId;
100
} else
101
type = groupedTypes[OpTypeVoid].back();
102
103
return type->getResultId();
104
}
105
106
Id Builder::makeBoolType()
107
{
108
Instruction* type;
109
if (groupedTypes[OpTypeBool].size() == 0) {
110
type = new Instruction(getUniqueId(), NoType, OpTypeBool);
111
groupedTypes[OpTypeBool].push_back(type);
112
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
113
module.mapInstruction(type);
114
115
if (emitNonSemanticShaderDebugInfo) {
116
auto const debugResultId = makeBoolDebugType(32);
117
debugId[type->getResultId()] = debugResultId;
118
}
119
120
} else
121
type = groupedTypes[OpTypeBool].back();
122
123
124
return type->getResultId();
125
}
126
127
Id Builder::makeSamplerType()
128
{
129
Instruction* type;
130
if (groupedTypes[OpTypeSampler].size() == 0) {
131
type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
132
groupedTypes[OpTypeSampler].push_back(type);
133
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
134
module.mapInstruction(type);
135
} else
136
type = groupedTypes[OpTypeSampler].back();
137
138
if (emitNonSemanticShaderDebugInfo)
139
{
140
auto const debugResultId = makeCompositeDebugType({}, "type.sampler", NonSemanticShaderDebugInfo100Structure, true);
141
debugId[type->getResultId()] = debugResultId;
142
}
143
144
return type->getResultId();
145
}
146
147
Id Builder::makePointer(StorageClass storageClass, Id pointee)
148
{
149
// try to find it
150
Instruction* type;
151
for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
152
type = groupedTypes[OpTypePointer][t];
153
if (type->getImmediateOperand(0) == (unsigned)storageClass &&
154
type->getIdOperand(1) == pointee)
155
return type->getResultId();
156
}
157
158
// not found, make it
159
type = new Instruction(getUniqueId(), NoType, OpTypePointer);
160
type->reserveOperands(2);
161
type->addImmediateOperand(storageClass);
162
type->addIdOperand(pointee);
163
groupedTypes[OpTypePointer].push_back(type);
164
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
165
module.mapInstruction(type);
166
167
if (emitNonSemanticShaderDebugInfo) {
168
const Id debugResultId = makePointerDebugType(storageClass, pointee);
169
debugId[type->getResultId()] = debugResultId;
170
}
171
172
return type->getResultId();
173
}
174
175
Id Builder::makeForwardPointer(StorageClass storageClass)
176
{
177
// Caching/uniquifying doesn't work here, because we don't know the
178
// pointee type and there can be multiple forward pointers of the same
179
// storage type. Somebody higher up in the stack must keep track.
180
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);
181
type->addImmediateOperand(storageClass);
182
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
183
module.mapInstruction(type);
184
185
return type->getResultId();
186
}
187
188
Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
189
{
190
// try to find it
191
Instruction* type;
192
for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
193
type = groupedTypes[OpTypePointer][t];
194
if (type->getImmediateOperand(0) == (unsigned)storageClass &&
195
type->getIdOperand(1) == pointee)
196
return type->getResultId();
197
}
198
199
type = new Instruction(forwardPointerType, NoType, OpTypePointer);
200
type->reserveOperands(2);
201
type->addImmediateOperand(storageClass);
202
type->addIdOperand(pointee);
203
groupedTypes[OpTypePointer].push_back(type);
204
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
205
module.mapInstruction(type);
206
207
return type->getResultId();
208
}
209
210
Id Builder::makeIntegerType(int width, bool hasSign)
211
{
212
// try to find it
213
Instruction* type;
214
for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
215
type = groupedTypes[OpTypeInt][t];
216
if (type->getImmediateOperand(0) == (unsigned)width &&
217
type->getImmediateOperand(1) == (hasSign ? 1u : 0u))
218
return type->getResultId();
219
}
220
221
// not found, make it
222
type = new Instruction(getUniqueId(), NoType, OpTypeInt);
223
type->reserveOperands(2);
224
type->addImmediateOperand(width);
225
type->addImmediateOperand(hasSign ? 1 : 0);
226
groupedTypes[OpTypeInt].push_back(type);
227
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
228
module.mapInstruction(type);
229
230
// deal with capabilities
231
switch (width) {
232
case 8:
233
case 16:
234
// these are currently handled by storage-type declarations and post processing
235
break;
236
case 64:
237
addCapability(CapabilityInt64);
238
break;
239
default:
240
break;
241
}
242
243
if (emitNonSemanticShaderDebugInfo)
244
{
245
auto const debugResultId = makeIntegerDebugType(width, hasSign);
246
debugId[type->getResultId()] = debugResultId;
247
}
248
249
return type->getResultId();
250
}
251
252
Id Builder::makeFloatType(int width)
253
{
254
// try to find it
255
Instruction* type;
256
for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
257
type = groupedTypes[OpTypeFloat][t];
258
if (type->getImmediateOperand(0) == (unsigned)width)
259
return type->getResultId();
260
}
261
262
// not found, make it
263
type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
264
type->addImmediateOperand(width);
265
groupedTypes[OpTypeFloat].push_back(type);
266
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
267
module.mapInstruction(type);
268
269
// deal with capabilities
270
switch (width) {
271
case 16:
272
// currently handled by storage-type declarations and post processing
273
break;
274
case 64:
275
addCapability(CapabilityFloat64);
276
break;
277
default:
278
break;
279
}
280
281
if (emitNonSemanticShaderDebugInfo)
282
{
283
auto const debugResultId = makeFloatDebugType(width);
284
debugId[type->getResultId()] = debugResultId;
285
}
286
287
return type->getResultId();
288
}
289
290
// Make a struct without checking for duplication.
291
// See makeStructResultType() for non-decorated structs
292
// needed as the result of some instructions, which does
293
// check for duplicates.
294
Id Builder::makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated)
295
{
296
// Don't look for previous one, because in the general case,
297
// structs can be duplicated except for decorations.
298
299
// not found, make it
300
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
301
for (int op = 0; op < (int)members.size(); ++op)
302
type->addIdOperand(members[op]);
303
groupedTypes[OpTypeStruct].push_back(type);
304
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
305
module.mapInstruction(type);
306
addName(type->getResultId(), name);
307
308
if (emitNonSemanticShaderDebugInfo && !compilerGenerated)
309
{
310
auto const debugResultId = makeCompositeDebugType(members, name, NonSemanticShaderDebugInfo100Structure);
311
debugId[type->getResultId()] = debugResultId;
312
}
313
314
return type->getResultId();
315
}
316
317
// Make a struct for the simple results of several instructions,
318
// checking for duplication.
319
Id Builder::makeStructResultType(Id type0, Id type1)
320
{
321
// try to find it
322
Instruction* type;
323
for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
324
type = groupedTypes[OpTypeStruct][t];
325
if (type->getNumOperands() != 2)
326
continue;
327
if (type->getIdOperand(0) != type0 ||
328
type->getIdOperand(1) != type1)
329
continue;
330
return type->getResultId();
331
}
332
333
// not found, make it
334
std::vector<spv::Id> members;
335
members.push_back(type0);
336
members.push_back(type1);
337
338
return makeStructType(members, "ResType");
339
}
340
341
Id Builder::makeVectorType(Id component, int size)
342
{
343
// try to find it
344
Instruction* type;
345
for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
346
type = groupedTypes[OpTypeVector][t];
347
if (type->getIdOperand(0) == component &&
348
type->getImmediateOperand(1) == (unsigned)size)
349
return type->getResultId();
350
}
351
352
// not found, make it
353
type = new Instruction(getUniqueId(), NoType, OpTypeVector);
354
type->reserveOperands(2);
355
type->addIdOperand(component);
356
type->addImmediateOperand(size);
357
groupedTypes[OpTypeVector].push_back(type);
358
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
359
module.mapInstruction(type);
360
361
if (emitNonSemanticShaderDebugInfo)
362
{
363
auto const debugResultId = makeVectorDebugType(component, size);
364
debugId[type->getResultId()] = debugResultId;
365
}
366
367
return type->getResultId();
368
}
369
370
Id Builder::makeMatrixType(Id component, int cols, int rows)
371
{
372
assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
373
374
Id column = makeVectorType(component, rows);
375
376
// try to find it
377
Instruction* type;
378
for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
379
type = groupedTypes[OpTypeMatrix][t];
380
if (type->getIdOperand(0) == column &&
381
type->getImmediateOperand(1) == (unsigned)cols)
382
return type->getResultId();
383
}
384
385
// not found, make it
386
type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
387
type->reserveOperands(2);
388
type->addIdOperand(column);
389
type->addImmediateOperand(cols);
390
groupedTypes[OpTypeMatrix].push_back(type);
391
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
392
module.mapInstruction(type);
393
394
if (emitNonSemanticShaderDebugInfo)
395
{
396
auto const debugResultId = makeMatrixDebugType(column, cols);
397
debugId[type->getResultId()] = debugResultId;
398
}
399
400
return type->getResultId();
401
}
402
403
Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use)
404
{
405
// try to find it
406
Instruction* type;
407
for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixKHR].size(); ++t) {
408
type = groupedTypes[OpTypeCooperativeMatrixKHR][t];
409
if (type->getIdOperand(0) == component &&
410
type->getIdOperand(1) == scope &&
411
type->getIdOperand(2) == rows &&
412
type->getIdOperand(3) == cols &&
413
type->getIdOperand(4) == use)
414
return type->getResultId();
415
}
416
417
// not found, make it
418
type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixKHR);
419
type->reserveOperands(5);
420
type->addIdOperand(component);
421
type->addIdOperand(scope);
422
type->addIdOperand(rows);
423
type->addIdOperand(cols);
424
type->addIdOperand(use);
425
groupedTypes[OpTypeCooperativeMatrixKHR].push_back(type);
426
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
427
module.mapInstruction(type);
428
429
return type->getResultId();
430
}
431
432
Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols)
433
{
434
// try to find it
435
Instruction* type;
436
for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {
437
type = groupedTypes[OpTypeCooperativeMatrixNV][t];
438
if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows &&
439
type->getIdOperand(3) == cols)
440
return type->getResultId();
441
}
442
443
// not found, make it
444
type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);
445
type->reserveOperands(4);
446
type->addIdOperand(component);
447
type->addIdOperand(scope);
448
type->addIdOperand(rows);
449
type->addIdOperand(cols);
450
groupedTypes[OpTypeCooperativeMatrixNV].push_back(type);
451
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
452
module.mapInstruction(type);
453
454
return type->getResultId();
455
}
456
457
Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType)
458
{
459
Instruction* instr = module.getInstruction(otherType);
460
if (instr->getOpCode() == OpTypeCooperativeMatrixNV) {
461
return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3));
462
} else {
463
assert(instr->getOpCode() == OpTypeCooperativeMatrixKHR);
464
return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4));
465
}
466
}
467
468
Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)
469
{
470
// try to find it
471
Instruction* type;
472
for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {
473
type = groupedTypes[opcode][t];
474
if (static_cast<size_t>(type->getNumOperands()) != operands.size())
475
continue; // Number mismatch, find next
476
477
bool match = true;
478
for (int op = 0; match && op < (int)operands.size(); ++op) {
479
match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;
480
}
481
if (match)
482
return type->getResultId();
483
}
484
485
// not found, make it
486
type = new Instruction(getUniqueId(), NoType, opcode);
487
type->reserveOperands(operands.size());
488
for (size_t op = 0; op < operands.size(); ++op) {
489
if (operands[op].isId)
490
type->addIdOperand(operands[op].word);
491
else
492
type->addImmediateOperand(operands[op].word);
493
}
494
groupedTypes[opcode].push_back(type);
495
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
496
module.mapInstruction(type);
497
498
return type->getResultId();
499
}
500
501
// TODO: performance: track arrays per stride
502
// If a stride is supplied (non-zero) make an array.
503
// If no stride (0), reuse previous array types.
504
// 'size' is an Id of a constant or specialization constant of the array size
505
Id Builder::makeArrayType(Id element, Id sizeId, int stride)
506
{
507
Instruction* type;
508
if (stride == 0) {
509
// try to find existing type
510
for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
511
type = groupedTypes[OpTypeArray][t];
512
if (type->getIdOperand(0) == element &&
513
type->getIdOperand(1) == sizeId)
514
return type->getResultId();
515
}
516
}
517
518
// not found, make it
519
type = new Instruction(getUniqueId(), NoType, OpTypeArray);
520
type->reserveOperands(2);
521
type->addIdOperand(element);
522
type->addIdOperand(sizeId);
523
groupedTypes[OpTypeArray].push_back(type);
524
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
525
module.mapInstruction(type);
526
527
if (emitNonSemanticShaderDebugInfo)
528
{
529
auto const debugResultId = makeArrayDebugType(element, sizeId);
530
debugId[type->getResultId()] = debugResultId;
531
}
532
533
return type->getResultId();
534
}
535
536
Id Builder::makeRuntimeArray(Id element)
537
{
538
Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
539
type->addIdOperand(element);
540
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
541
module.mapInstruction(type);
542
543
if (emitNonSemanticShaderDebugInfo)
544
{
545
auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0));
546
debugId[type->getResultId()] = debugResultId;
547
}
548
549
return type->getResultId();
550
}
551
552
Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
553
{
554
// try to find it
555
Instruction* type;
556
for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
557
type = groupedTypes[OpTypeFunction][t];
558
if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
559
continue;
560
bool mismatch = false;
561
for (int p = 0; p < (int)paramTypes.size(); ++p) {
562
if (paramTypes[p] != type->getIdOperand(p + 1)) {
563
mismatch = true;
564
break;
565
}
566
}
567
if (! mismatch)
568
{
569
// If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void)
570
// function type is created for the wrapper function. However, nonsemantic shader debug information is disabled
571
// while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create
572
// the associated debug function type if it hasn't been created yet.
573
if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) {
574
assert(sourceLang == spv::SourceLanguageHLSL);
575
assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0);
576
577
Id debugTypeId = makeDebugFunctionType(returnType, {});
578
debugId[type->getResultId()] = debugTypeId;
579
}
580
return type->getResultId();
581
}
582
}
583
584
// not found, make it
585
Id typeId = getUniqueId();
586
type = new Instruction(typeId, NoType, OpTypeFunction);
587
type->reserveOperands(paramTypes.size() + 1);
588
type->addIdOperand(returnType);
589
for (int p = 0; p < (int)paramTypes.size(); ++p)
590
type->addIdOperand(paramTypes[p]);
591
groupedTypes[OpTypeFunction].push_back(type);
592
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
593
module.mapInstruction(type);
594
595
// make debug type and map it
596
if (emitNonSemanticShaderDebugInfo) {
597
Id debugTypeId = makeDebugFunctionType(returnType, paramTypes);
598
debugId[typeId] = debugTypeId;
599
}
600
601
return type->getResultId();
602
}
603
604
Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes)
605
{
606
assert(debugId[returnType] != 0);
607
608
Id typeId = getUniqueId();
609
auto type = new Instruction(typeId, makeVoidType(), OpExtInst);
610
type->reserveOperands(paramTypes.size() + 4);
611
type->addIdOperand(nonSemanticShaderDebugInfo);
612
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction);
613
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));
614
type->addIdOperand(debugId[returnType]);
615
for (auto const paramType : paramTypes) {
616
if (isPointerType(paramType) || isArrayType(paramType)) {
617
type->addIdOperand(debugId[getContainedTypeId(paramType)]);
618
}
619
else {
620
type->addIdOperand(debugId[paramType]);
621
}
622
}
623
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
624
module.mapInstruction(type);
625
return typeId;
626
}
627
628
Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,
629
ImageFormat format)
630
{
631
assert(sampled == 1 || sampled == 2);
632
633
// try to find it
634
Instruction* type;
635
for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
636
type = groupedTypes[OpTypeImage][t];
637
if (type->getIdOperand(0) == sampledType &&
638
type->getImmediateOperand(1) == (unsigned int)dim &&
639
type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
640
type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
641
type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
642
type->getImmediateOperand(5) == sampled &&
643
type->getImmediateOperand(6) == (unsigned int)format)
644
return type->getResultId();
645
}
646
647
// not found, make it
648
type = new Instruction(getUniqueId(), NoType, OpTypeImage);
649
type->reserveOperands(7);
650
type->addIdOperand(sampledType);
651
type->addImmediateOperand( dim);
652
type->addImmediateOperand( depth ? 1 : 0);
653
type->addImmediateOperand(arrayed ? 1 : 0);
654
type->addImmediateOperand( ms ? 1 : 0);
655
type->addImmediateOperand(sampled);
656
type->addImmediateOperand((unsigned int)format);
657
658
groupedTypes[OpTypeImage].push_back(type);
659
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
660
module.mapInstruction(type);
661
662
// deal with capabilities
663
switch (dim) {
664
case DimBuffer:
665
if (sampled == 1)
666
addCapability(CapabilitySampledBuffer);
667
else
668
addCapability(CapabilityImageBuffer);
669
break;
670
case Dim1D:
671
if (sampled == 1)
672
addCapability(CapabilitySampled1D);
673
else
674
addCapability(CapabilityImage1D);
675
break;
676
case DimCube:
677
if (arrayed) {
678
if (sampled == 1)
679
addCapability(CapabilitySampledCubeArray);
680
else
681
addCapability(CapabilityImageCubeArray);
682
}
683
break;
684
case DimRect:
685
if (sampled == 1)
686
addCapability(CapabilitySampledRect);
687
else
688
addCapability(CapabilityImageRect);
689
break;
690
case DimSubpassData:
691
addCapability(CapabilityInputAttachment);
692
break;
693
default:
694
break;
695
}
696
697
if (ms) {
698
if (sampled == 2) {
699
// Images used with subpass data are not storage
700
// images, so don't require the capability for them.
701
if (dim != Dim::DimSubpassData)
702
addCapability(CapabilityStorageImageMultisample);
703
if (arrayed)
704
addCapability(CapabilityImageMSArray);
705
}
706
}
707
708
if (emitNonSemanticShaderDebugInfo)
709
{
710
auto TypeName = [&dim]() -> char const* {
711
switch (dim) {
712
case Dim1D: return "type.1d.image";
713
case Dim2D: return "type.2d.image";
714
case Dim3D: return "type.3d.image";
715
case DimCube: return "type.cube.image";
716
default: return "type.image";
717
}
718
};
719
720
auto const debugResultId = makeCompositeDebugType({}, TypeName(), NonSemanticShaderDebugInfo100Class, true);
721
debugId[type->getResultId()] = debugResultId;
722
}
723
724
return type->getResultId();
725
}
726
727
Id Builder::makeSampledImageType(Id imageType)
728
{
729
// try to find it
730
Instruction* type;
731
for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
732
type = groupedTypes[OpTypeSampledImage][t];
733
if (type->getIdOperand(0) == imageType)
734
return type->getResultId();
735
}
736
737
// not found, make it
738
type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
739
type->addIdOperand(imageType);
740
741
groupedTypes[OpTypeSampledImage].push_back(type);
742
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
743
module.mapInstruction(type);
744
745
if (emitNonSemanticShaderDebugInfo)
746
{
747
auto const debugResultId = makeCompositeDebugType({}, "type.sampled.image", NonSemanticShaderDebugInfo100Class, true);
748
debugId[type->getResultId()] = debugResultId;
749
}
750
751
return type->getResultId();
752
}
753
754
Id Builder::makeDebugInfoNone()
755
{
756
if (debugInfoNone != 0)
757
return debugInfoNone;
758
759
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
760
inst->reserveOperands(2);
761
inst->addIdOperand(nonSemanticShaderDebugInfo);
762
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone);
763
764
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
765
module.mapInstruction(inst);
766
767
debugInfoNone = inst->getResultId();
768
769
return debugInfoNone;
770
}
771
772
Id Builder::makeBoolDebugType(int const size)
773
{
774
// try to find it
775
Instruction* type;
776
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
777
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
778
if (type->getIdOperand(0) == getStringId("bool") &&
779
type->getIdOperand(1) == static_cast<unsigned int>(size) &&
780
type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean)
781
return type->getResultId();
782
}
783
784
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
785
type->reserveOperands(6);
786
type->addIdOperand(nonSemanticShaderDebugInfo);
787
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
788
789
type->addIdOperand(getStringId("bool")); // name id
790
type->addIdOperand(makeUintConstant(size)); // size id
791
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id
792
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
793
794
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
795
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
796
module.mapInstruction(type);
797
798
return type->getResultId();
799
}
800
801
Id Builder::makeIntegerDebugType(int const width, bool const hasSign)
802
{
803
const char* typeName = nullptr;
804
switch (width) {
805
case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break;
806
case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break;
807
case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break;
808
default: typeName = hasSign ? "int" : "uint";
809
}
810
auto nameId = getStringId(typeName);
811
// try to find it
812
Instruction* type;
813
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
814
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
815
if (type->getIdOperand(0) == nameId &&
816
type->getIdOperand(1) == static_cast<unsigned int>(width) &&
817
type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned))
818
return type->getResultId();
819
}
820
821
// not found, make it
822
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
823
type->reserveOperands(6);
824
type->addIdOperand(nonSemanticShaderDebugInfo);
825
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
826
type->addIdOperand(nameId); // name id
827
type->addIdOperand(makeUintConstant(width)); // size id
828
if(hasSign == true) {
829
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id
830
} else {
831
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id
832
}
833
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
834
835
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
836
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
837
module.mapInstruction(type);
838
839
return type->getResultId();
840
}
841
842
Id Builder::makeFloatDebugType(int const width)
843
{
844
const char* typeName = nullptr;
845
switch (width) {
846
case 16: typeName = "float16_t"; break;
847
case 64: typeName = "double"; break;
848
default: typeName = "float"; break;
849
}
850
auto nameId = getStringId(typeName);
851
// try to find it
852
Instruction* type;
853
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) {
854
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t];
855
if (type->getIdOperand(0) == nameId &&
856
type->getIdOperand(1) == static_cast<unsigned int>(width) &&
857
type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float)
858
return type->getResultId();
859
}
860
861
// not found, make it
862
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
863
type->reserveOperands(6);
864
type->addIdOperand(nonSemanticShaderDebugInfo);
865
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic);
866
type->addIdOperand(nameId); // name id
867
type->addIdOperand(makeUintConstant(width)); // size id
868
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id
869
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id
870
871
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type);
872
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
873
module.mapInstruction(type);
874
875
return type->getResultId();
876
}
877
878
Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType)
879
{
880
assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray ||
881
sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector);
882
883
// try to find it
884
Instruction* type;
885
for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) {
886
type = groupedDebugTypes[sequenceType][t];
887
if (type->getIdOperand(0) == baseType &&
888
type->getIdOperand(1) == makeUintConstant(componentCount))
889
return type->getResultId();
890
}
891
892
// not found, make it
893
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
894
type->reserveOperands(4);
895
type->addIdOperand(nonSemanticShaderDebugInfo);
896
type->addImmediateOperand(sequenceType);
897
type->addIdOperand(debugId[baseType]); // base type
898
type->addIdOperand(componentCount); // component count
899
900
groupedDebugTypes[sequenceType].push_back(type);
901
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
902
module.mapInstruction(type);
903
904
return type->getResultId();
905
}
906
907
Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount)
908
{
909
return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray);
910
}
911
912
Id Builder::makeVectorDebugType(Id const baseType, int const componentCount)
913
{
914
return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector);
915
}
916
917
Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor)
918
{
919
// try to find it
920
Instruction* type;
921
for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) {
922
type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t];
923
if (type->getIdOperand(0) == vectorType &&
924
type->getIdOperand(1) == makeUintConstant(vectorCount))
925
return type->getResultId();
926
}
927
928
// not found, make it
929
type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
930
type->reserveOperands(5);
931
type->addIdOperand(nonSemanticShaderDebugInfo);
932
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix);
933
type->addIdOperand(debugId[vectorType]); // vector type id
934
type->addIdOperand(makeUintConstant(vectorCount)); // component count id
935
type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id
936
937
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type);
938
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
939
module.mapInstruction(type);
940
941
return type->getResultId();
942
}
943
944
Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc)
945
{
946
assert(debugId[memberType] != 0);
947
948
Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
949
type->reserveOperands(10);
950
type->addIdOperand(nonSemanticShaderDebugInfo);
951
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember);
952
type->addIdOperand(getStringId(debugTypeLoc.name)); // name id
953
type->addIdOperand(debugId[memberType]); // type id
954
type->addIdOperand(makeDebugSource(currentFileId)); // source id
955
type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero
956
type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id
957
type->addIdOperand(makeUintConstant(0)); // TODO: offset id
958
type->addIdOperand(makeUintConstant(0)); // TODO: size id
959
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id
960
961
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type);
962
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
963
module.mapInstruction(type);
964
965
return type->getResultId();
966
}
967
968
// Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be
969
// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names.
970
Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name,
971
NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType)
972
{
973
// Create the debug member types.
974
std::vector<Id> memberDebugTypes;
975
for(auto const memberType : memberTypes) {
976
assert(debugTypeLocs.find(memberType) != debugTypeLocs.end());
977
978
// There _should_ be debug types for all the member types but currently buffer references
979
// do not have member debug info generated.
980
if (debugId[memberType])
981
memberDebugTypes.emplace_back(makeMemberDebugType(memberType, debugTypeLocs[memberType]));
982
983
// TODO: Need to rethink this method of passing location information.
984
// debugTypeLocs.erase(memberType);
985
}
986
987
// Create The structure debug type.
988
Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
989
type->reserveOperands(memberDebugTypes.size() + 11);
990
type->addIdOperand(nonSemanticShaderDebugInfo);
991
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite);
992
type->addIdOperand(getStringId(name)); // name id
993
type->addIdOperand(makeUintConstant(tag)); // tag id
994
type->addIdOperand(makeDebugSource(currentFileId)); // source id
995
type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?
996
type->addIdOperand(makeUintConstant(0)); // TODO: column id
997
type->addIdOperand(makeDebugCompilationUnit()); // scope id
998
if(isOpaqueType == true) {
999
// Prepend '@' to opaque types.
1000
type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id
1001
type->addIdOperand(makeDebugInfoNone()); // size id
1002
} else {
1003
type->addIdOperand(getStringId(name)); // linkage name id
1004
type->addIdOperand(makeUintConstant(0)); // TODO: size id
1005
}
1006
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id
1007
assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty()));
1008
for(auto const memberDebugType : memberDebugTypes) {
1009
type->addIdOperand(memberDebugType);
1010
}
1011
1012
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type);
1013
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
1014
module.mapInstruction(type);
1015
1016
return type->getResultId();
1017
}
1018
1019
Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType)
1020
{
1021
const Id debugBaseType = debugId[baseType];
1022
if (!debugBaseType) {
1023
return makeDebugInfoNone();
1024
}
1025
const Id scID = makeUintConstant(storageClass);
1026
for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer]) {
1027
if (otherType->getIdOperand(2) == debugBaseType &&
1028
otherType->getIdOperand(3) == scID) {
1029
return otherType->getResultId();
1030
}
1031
}
1032
1033
Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1034
type->reserveOperands(5);
1035
type->addIdOperand(nonSemanticShaderDebugInfo);
1036
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer);
1037
type->addIdOperand(debugBaseType);
1038
type->addIdOperand(scID);
1039
type->addIdOperand(makeUintConstant(0));
1040
1041
groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type);
1042
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
1043
module.mapInstruction(type);
1044
1045
return type->getResultId();
1046
}
1047
1048
Id Builder::makeDebugSource(const Id fileName) {
1049
if (debugSourceId.find(fileName) != debugSourceId.end())
1050
return debugSourceId[fileName];
1051
spv::Id resultId = getUniqueId();
1052
Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);
1053
sourceInst->reserveOperands(3);
1054
sourceInst->addIdOperand(nonSemanticShaderDebugInfo);
1055
sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource);
1056
sourceInst->addIdOperand(fileName);
1057
if (emitNonSemanticShaderDebugSource) {
1058
spv::Id sourceId = 0;
1059
if (fileName == mainFileId) {
1060
sourceId = getStringId(sourceText);
1061
} else {
1062
auto incItr = includeFiles.find(fileName);
1063
if (incItr != includeFiles.end()) {
1064
sourceId = getStringId(*incItr->second);
1065
}
1066
}
1067
1068
// We omit the optional source text item if not available in glslang
1069
if (sourceId != 0) {
1070
sourceInst->addIdOperand(sourceId);
1071
}
1072
}
1073
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));
1074
module.mapInstruction(sourceInst);
1075
debugSourceId[fileName] = resultId;
1076
return resultId;
1077
}
1078
1079
Id Builder::makeDebugCompilationUnit() {
1080
if (nonSemanticShaderCompilationUnitId != 0)
1081
return nonSemanticShaderCompilationUnitId;
1082
spv::Id resultId = getUniqueId();
1083
Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst);
1084
sourceInst->reserveOperands(6);
1085
sourceInst->addIdOperand(nonSemanticShaderDebugInfo);
1086
sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit);
1087
sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number
1088
sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number
1089
sourceInst->addIdOperand(makeDebugSource(mainFileId));
1090
sourceInst->addIdOperand(makeUintConstant(sourceLang));
1091
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst));
1092
module.mapInstruction(sourceInst);
1093
nonSemanticShaderCompilationUnitId = resultId;
1094
1095
// We can reasonably assume that makeDebugCompilationUnit will be called before any of
1096
// debug-scope stack. Function scopes and lexical scopes will occur afterward.
1097
assert(currentDebugScopeId.empty());
1098
currentDebugScopeId.push(nonSemanticShaderCompilationUnitId);
1099
1100
return resultId;
1101
}
1102
1103
Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable)
1104
{
1105
assert(type != 0);
1106
1107
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1108
inst->reserveOperands(11);
1109
inst->addIdOperand(nonSemanticShaderDebugInfo);
1110
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable);
1111
inst->addIdOperand(getStringId(name)); // name id
1112
inst->addIdOperand(type); // type id
1113
inst->addIdOperand(makeDebugSource(currentFileId)); // source id
1114
inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero?
1115
inst->addIdOperand(makeUintConstant(0)); // TODO: column id
1116
inst->addIdOperand(makeDebugCompilationUnit()); // scope id
1117
inst->addIdOperand(getStringId(name)); // linkage name id
1118
inst->addIdOperand(variable); // variable id
1119
inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id
1120
1121
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1122
module.mapInstruction(inst);
1123
1124
return inst->getResultId();
1125
}
1126
1127
Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber)
1128
{
1129
assert(name != nullptr);
1130
assert(!currentDebugScopeId.empty());
1131
1132
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1133
inst->reserveOperands(9);
1134
inst->addIdOperand(nonSemanticShaderDebugInfo);
1135
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable);
1136
inst->addIdOperand(getStringId(name)); // name id
1137
inst->addIdOperand(type); // type id
1138
inst->addIdOperand(makeDebugSource(currentFileId)); // source id
1139
inst->addIdOperand(makeUintConstant(currentLine)); // line id
1140
inst->addIdOperand(makeUintConstant(0)); // TODO: column id
1141
inst->addIdOperand(currentDebugScopeId.top()); // scope id
1142
inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id
1143
if(argNumber != 0) {
1144
inst->addIdOperand(makeUintConstant(argNumber));
1145
}
1146
1147
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1148
module.mapInstruction(inst);
1149
1150
return inst->getResultId();
1151
}
1152
1153
Id Builder::makeDebugExpression()
1154
{
1155
if (debugExpression != 0)
1156
return debugExpression;
1157
1158
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1159
inst->reserveOperands(2);
1160
inst->addIdOperand(nonSemanticShaderDebugInfo);
1161
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression);
1162
1163
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1164
module.mapInstruction(inst);
1165
1166
debugExpression = inst->getResultId();
1167
1168
return debugExpression;
1169
}
1170
1171
Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer)
1172
{
1173
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1174
inst->reserveOperands(5);
1175
inst->addIdOperand(nonSemanticShaderDebugInfo);
1176
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare);
1177
inst->addIdOperand(debugLocalVariable); // debug local variable id
1178
inst->addIdOperand(pointer); // pointer to local variable id
1179
inst->addIdOperand(makeDebugExpression()); // expression id
1180
addInstruction(std::unique_ptr<Instruction>(inst));
1181
1182
return inst->getResultId();
1183
}
1184
1185
Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value)
1186
{
1187
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
1188
inst->reserveOperands(5);
1189
inst->addIdOperand(nonSemanticShaderDebugInfo);
1190
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugValue);
1191
inst->addIdOperand(debugLocalVariable); // debug local variable id
1192
inst->addIdOperand(value); // value of local variable id
1193
inst->addIdOperand(makeDebugExpression()); // expression id
1194
addInstruction(std::unique_ptr<Instruction>(inst));
1195
1196
return inst->getResultId();
1197
}
1198
1199
Id Builder::makeAccelerationStructureType()
1200
{
1201
Instruction *type;
1202
if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {
1203
type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);
1204
groupedTypes[OpTypeAccelerationStructureKHR].push_back(type);
1205
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
1206
module.mapInstruction(type);
1207
if (emitNonSemanticShaderDebugInfo) {
1208
spv::Id debugType = makeCompositeDebugType({}, "accelerationStructure", NonSemanticShaderDebugInfo100Structure, true);
1209
debugId[type->getResultId()] = debugType;
1210
}
1211
} else {
1212
type = groupedTypes[OpTypeAccelerationStructureKHR].back();
1213
}
1214
1215
return type->getResultId();
1216
}
1217
1218
Id Builder::makeRayQueryType()
1219
{
1220
Instruction *type;
1221
if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {
1222
type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);
1223
groupedTypes[OpTypeRayQueryKHR].push_back(type);
1224
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
1225
module.mapInstruction(type);
1226
if (emitNonSemanticShaderDebugInfo) {
1227
spv::Id debugType = makeCompositeDebugType({}, "rayQuery", NonSemanticShaderDebugInfo100Structure, true);
1228
debugId[type->getResultId()] = debugType;
1229
}
1230
} else {
1231
type = groupedTypes[OpTypeRayQueryKHR].back();
1232
}
1233
1234
return type->getResultId();
1235
}
1236
1237
Id Builder::makeHitObjectNVType()
1238
{
1239
Instruction *type;
1240
if (groupedTypes[OpTypeHitObjectNV].size() == 0) {
1241
type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV);
1242
groupedTypes[OpTypeHitObjectNV].push_back(type);
1243
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
1244
module.mapInstruction(type);
1245
} else {
1246
type = groupedTypes[OpTypeHitObjectNV].back();
1247
}
1248
1249
return type->getResultId();
1250
}
1251
1252
Id Builder::getDerefTypeId(Id resultId) const
1253
{
1254
Id typeId = getTypeId(resultId);
1255
assert(isPointerType(typeId));
1256
1257
return module.getInstruction(typeId)->getIdOperand(1);
1258
}
1259
1260
Op Builder::getMostBasicTypeClass(Id typeId) const
1261
{
1262
Instruction* instr = module.getInstruction(typeId);
1263
1264
Op typeClass = instr->getOpCode();
1265
switch (typeClass)
1266
{
1267
case OpTypeVector:
1268
case OpTypeMatrix:
1269
case OpTypeArray:
1270
case OpTypeRuntimeArray:
1271
return getMostBasicTypeClass(instr->getIdOperand(0));
1272
case OpTypePointer:
1273
return getMostBasicTypeClass(instr->getIdOperand(1));
1274
default:
1275
return typeClass;
1276
}
1277
}
1278
1279
int Builder::getNumTypeConstituents(Id typeId) const
1280
{
1281
Instruction* instr = module.getInstruction(typeId);
1282
1283
switch (instr->getOpCode())
1284
{
1285
case OpTypeBool:
1286
case OpTypeInt:
1287
case OpTypeFloat:
1288
case OpTypePointer:
1289
return 1;
1290
case OpTypeVector:
1291
case OpTypeMatrix:
1292
return instr->getImmediateOperand(1);
1293
case OpTypeArray:
1294
{
1295
Id lengthId = instr->getIdOperand(1);
1296
return module.getInstruction(lengthId)->getImmediateOperand(0);
1297
}
1298
case OpTypeStruct:
1299
return instr->getNumOperands();
1300
case OpTypeCooperativeMatrixKHR:
1301
case OpTypeCooperativeMatrixNV:
1302
// has only one constituent when used with OpCompositeConstruct.
1303
return 1;
1304
default:
1305
assert(0);
1306
return 1;
1307
}
1308
}
1309
1310
// Return the lowest-level type of scalar that an homogeneous composite is made out of.
1311
// Typically, this is just to find out if something is made out of ints or floats.
1312
// However, it includes returning a structure, if say, it is an array of structure.
1313
Id Builder::getScalarTypeId(Id typeId) const
1314
{
1315
Instruction* instr = module.getInstruction(typeId);
1316
1317
Op typeClass = instr->getOpCode();
1318
switch (typeClass)
1319
{
1320
case OpTypeVoid:
1321
case OpTypeBool:
1322
case OpTypeInt:
1323
case OpTypeFloat:
1324
case OpTypeStruct:
1325
return instr->getResultId();
1326
case OpTypeVector:
1327
case OpTypeMatrix:
1328
case OpTypeArray:
1329
case OpTypeRuntimeArray:
1330
case OpTypePointer:
1331
return getScalarTypeId(getContainedTypeId(typeId));
1332
default:
1333
assert(0);
1334
return NoResult;
1335
}
1336
}
1337
1338
// Return the type of 'member' of a composite.
1339
Id Builder::getContainedTypeId(Id typeId, int member) const
1340
{
1341
Instruction* instr = module.getInstruction(typeId);
1342
1343
Op typeClass = instr->getOpCode();
1344
switch (typeClass)
1345
{
1346
case OpTypeVector:
1347
case OpTypeMatrix:
1348
case OpTypeArray:
1349
case OpTypeRuntimeArray:
1350
case OpTypeCooperativeMatrixKHR:
1351
case OpTypeCooperativeMatrixNV:
1352
return instr->getIdOperand(0);
1353
case OpTypePointer:
1354
return instr->getIdOperand(1);
1355
case OpTypeStruct:
1356
return instr->getIdOperand(member);
1357
default:
1358
assert(0);
1359
return NoResult;
1360
}
1361
}
1362
1363
// Figure out the final resulting type of the access chain.
1364
Id Builder::getResultingAccessChainType() const
1365
{
1366
assert(accessChain.base != NoResult);
1367
Id typeId = getTypeId(accessChain.base);
1368
1369
assert(isPointerType(typeId));
1370
typeId = getContainedTypeId(typeId);
1371
1372
for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
1373
if (isStructType(typeId)) {
1374
assert(isConstantScalar(accessChain.indexChain[i]));
1375
typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));
1376
} else
1377
typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);
1378
}
1379
1380
return typeId;
1381
}
1382
1383
// Return the immediately contained type of a given composite type.
1384
Id Builder::getContainedTypeId(Id typeId) const
1385
{
1386
return getContainedTypeId(typeId, 0);
1387
}
1388
1389
// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
1390
// of width 'width'. The 'width' is only consumed for int and float types.
1391
// Returns false otherwise.
1392
bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
1393
{
1394
const Instruction& instr = *module.getInstruction(typeId);
1395
1396
Op typeClass = instr.getOpCode();
1397
switch (typeClass)
1398
{
1399
case OpTypeInt:
1400
case OpTypeFloat:
1401
return typeClass == typeOp && instr.getImmediateOperand(0) == width;
1402
case OpTypeStruct:
1403
for (int m = 0; m < instr.getNumOperands(); ++m) {
1404
if (containsType(instr.getIdOperand(m), typeOp, width))
1405
return true;
1406
}
1407
return false;
1408
case OpTypePointer:
1409
return false;
1410
case OpTypeVector:
1411
case OpTypeMatrix:
1412
case OpTypeArray:
1413
case OpTypeRuntimeArray:
1414
return containsType(getContainedTypeId(typeId), typeOp, width);
1415
default:
1416
return typeClass == typeOp;
1417
}
1418
}
1419
1420
// return true if the type is a pointer to PhysicalStorageBufferEXT or an
1421
// contains such a pointer. These require restrict/aliased decorations.
1422
bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
1423
{
1424
const Instruction& instr = *module.getInstruction(typeId);
1425
1426
Op typeClass = instr.getOpCode();
1427
switch (typeClass)
1428
{
1429
case OpTypePointer:
1430
return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
1431
case OpTypeArray:
1432
return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));
1433
case OpTypeStruct:
1434
for (int m = 0; m < instr.getNumOperands(); ++m) {
1435
if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m)))
1436
return true;
1437
}
1438
return false;
1439
default:
1440
return false;
1441
}
1442
}
1443
1444
// See if a scalar constant of this type has already been created, so it
1445
// can be reused rather than duplicated. (Required by the specification).
1446
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
1447
{
1448
Instruction* constant;
1449
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
1450
constant = groupedConstants[typeClass][i];
1451
if (constant->getOpCode() == opcode &&
1452
constant->getTypeId() == typeId &&
1453
constant->getImmediateOperand(0) == value)
1454
return constant->getResultId();
1455
}
1456
1457
return 0;
1458
}
1459
1460
// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
1461
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
1462
{
1463
Instruction* constant;
1464
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
1465
constant = groupedConstants[typeClass][i];
1466
if (constant->getOpCode() == opcode &&
1467
constant->getTypeId() == typeId &&
1468
constant->getImmediateOperand(0) == v1 &&
1469
constant->getImmediateOperand(1) == v2)
1470
return constant->getResultId();
1471
}
1472
1473
return 0;
1474
}
1475
1476
// Return true if consuming 'opcode' means consuming a constant.
1477
// "constant" here means after final transform to executable code,
1478
// the value consumed will be a constant, so includes specialization.
1479
bool Builder::isConstantOpCode(Op opcode) const
1480
{
1481
switch (opcode) {
1482
case OpUndef:
1483
case OpConstantTrue:
1484
case OpConstantFalse:
1485
case OpConstant:
1486
case OpConstantComposite:
1487
case OpConstantSampler:
1488
case OpConstantNull:
1489
case OpSpecConstantTrue:
1490
case OpSpecConstantFalse:
1491
case OpSpecConstant:
1492
case OpSpecConstantComposite:
1493
case OpSpecConstantOp:
1494
return true;
1495
default:
1496
return false;
1497
}
1498
}
1499
1500
// Return true if consuming 'opcode' means consuming a specialization constant.
1501
bool Builder::isSpecConstantOpCode(Op opcode) const
1502
{
1503
switch (opcode) {
1504
case OpSpecConstantTrue:
1505
case OpSpecConstantFalse:
1506
case OpSpecConstant:
1507
case OpSpecConstantComposite:
1508
case OpSpecConstantOp:
1509
return true;
1510
default:
1511
return false;
1512
}
1513
}
1514
1515
Id Builder::makeNullConstant(Id typeId)
1516
{
1517
Instruction* constant;
1518
1519
// See if we already made it.
1520
Id existing = NoResult;
1521
for (int i = 0; i < (int)nullConstants.size(); ++i) {
1522
constant = nullConstants[i];
1523
if (constant->getTypeId() == typeId)
1524
existing = constant->getResultId();
1525
}
1526
1527
if (existing != NoResult)
1528
return existing;
1529
1530
// Make it
1531
Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);
1532
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1533
nullConstants.push_back(c);
1534
module.mapInstruction(c);
1535
1536
return c->getResultId();
1537
}
1538
1539
Id Builder::makeBoolConstant(bool b, bool specConstant)
1540
{
1541
Id typeId = makeBoolType();
1542
Instruction* constant;
1543
Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
1544
1545
// See if we already made it. Applies only to regular constants, because specialization constants
1546
// must remain distinct for the purpose of applying a SpecId decoration.
1547
if (! specConstant) {
1548
Id existing = 0;
1549
for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
1550
constant = groupedConstants[OpTypeBool][i];
1551
if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
1552
existing = constant->getResultId();
1553
}
1554
1555
if (existing)
1556
return existing;
1557
}
1558
1559
// Make it
1560
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1561
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1562
groupedConstants[OpTypeBool].push_back(c);
1563
module.mapInstruction(c);
1564
1565
return c->getResultId();
1566
}
1567
1568
Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
1569
{
1570
Op opcode = specConstant ? OpSpecConstant : OpConstant;
1571
1572
// See if we already made it. Applies only to regular constants, because specialization constants
1573
// must remain distinct for the purpose of applying a SpecId decoration.
1574
if (! specConstant) {
1575
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
1576
if (existing)
1577
return existing;
1578
}
1579
1580
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1581
c->addImmediateOperand(value);
1582
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1583
groupedConstants[OpTypeInt].push_back(c);
1584
module.mapInstruction(c);
1585
1586
return c->getResultId();
1587
}
1588
1589
Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
1590
{
1591
Op opcode = specConstant ? OpSpecConstant : OpConstant;
1592
1593
unsigned op1 = value & 0xFFFFFFFF;
1594
unsigned op2 = value >> 32;
1595
1596
// See if we already made it. Applies only to regular constants, because specialization constants
1597
// must remain distinct for the purpose of applying a SpecId decoration.
1598
if (! specConstant) {
1599
Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
1600
if (existing)
1601
return existing;
1602
}
1603
1604
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1605
c->reserveOperands(2);
1606
c->addImmediateOperand(op1);
1607
c->addImmediateOperand(op2);
1608
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1609
groupedConstants[OpTypeInt].push_back(c);
1610
module.mapInstruction(c);
1611
1612
return c->getResultId();
1613
}
1614
1615
Id Builder::makeFloatConstant(float f, bool specConstant)
1616
{
1617
Op opcode = specConstant ? OpSpecConstant : OpConstant;
1618
Id typeId = makeFloatType(32);
1619
union { float fl; unsigned int ui; } u;
1620
u.fl = f;
1621
unsigned value = u.ui;
1622
1623
// See if we already made it. Applies only to regular constants, because specialization constants
1624
// must remain distinct for the purpose of applying a SpecId decoration.
1625
if (! specConstant) {
1626
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
1627
if (existing)
1628
return existing;
1629
}
1630
1631
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1632
c->addImmediateOperand(value);
1633
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1634
groupedConstants[OpTypeFloat].push_back(c);
1635
module.mapInstruction(c);
1636
1637
return c->getResultId();
1638
}
1639
1640
Id Builder::makeDoubleConstant(double d, bool specConstant)
1641
{
1642
Op opcode = specConstant ? OpSpecConstant : OpConstant;
1643
Id typeId = makeFloatType(64);
1644
union { double db; unsigned long long ull; } u;
1645
u.db = d;
1646
unsigned long long value = u.ull;
1647
unsigned op1 = value & 0xFFFFFFFF;
1648
unsigned op2 = value >> 32;
1649
1650
// See if we already made it. Applies only to regular constants, because specialization constants
1651
// must remain distinct for the purpose of applying a SpecId decoration.
1652
if (! specConstant) {
1653
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
1654
if (existing)
1655
return existing;
1656
}
1657
1658
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1659
c->reserveOperands(2);
1660
c->addImmediateOperand(op1);
1661
c->addImmediateOperand(op2);
1662
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1663
groupedConstants[OpTypeFloat].push_back(c);
1664
module.mapInstruction(c);
1665
1666
return c->getResultId();
1667
}
1668
1669
Id Builder::makeFloat16Constant(float f16, bool specConstant)
1670
{
1671
Op opcode = specConstant ? OpSpecConstant : OpConstant;
1672
Id typeId = makeFloatType(16);
1673
1674
spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
1675
spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
1676
fVal.castTo(f16Val, spvutils::kRoundToZero);
1677
1678
unsigned value = f16Val.value().getAsFloat().get_value();
1679
1680
// See if we already made it. Applies only to regular constants, because specialization constants
1681
// must remain distinct for the purpose of applying a SpecId decoration.
1682
if (!specConstant) {
1683
Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
1684
if (existing)
1685
return existing;
1686
}
1687
1688
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1689
c->addImmediateOperand(value);
1690
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1691
groupedConstants[OpTypeFloat].push_back(c);
1692
module.mapInstruction(c);
1693
1694
return c->getResultId();
1695
}
1696
1697
Id Builder::makeFpConstant(Id type, double d, bool specConstant)
1698
{
1699
const int width = getScalarTypeWidth(type);
1700
1701
assert(isFloatType(type));
1702
1703
switch (width) {
1704
case 16:
1705
return makeFloat16Constant((float)d, specConstant);
1706
case 32:
1707
return makeFloatConstant((float)d, specConstant);
1708
case 64:
1709
return makeDoubleConstant(d, specConstant);
1710
default:
1711
break;
1712
}
1713
1714
assert(false);
1715
return NoResult;
1716
}
1717
1718
Id Builder::importNonSemanticShaderDebugInfoInstructions()
1719
{
1720
assert(emitNonSemanticShaderDebugInfo == true);
1721
1722
if(nonSemanticShaderDebugInfo == 0)
1723
{
1724
this->addExtension(spv::E_SPV_KHR_non_semantic_info);
1725
nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100");
1726
}
1727
1728
return nonSemanticShaderDebugInfo;
1729
}
1730
1731
Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
1732
{
1733
Instruction* constant = nullptr;
1734
bool found = false;
1735
for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
1736
constant = groupedConstants[typeClass][i];
1737
1738
if (constant->getTypeId() != typeId)
1739
continue;
1740
1741
// same contents?
1742
bool mismatch = false;
1743
for (int op = 0; op < constant->getNumOperands(); ++op) {
1744
if (constant->getIdOperand(op) != comps[op]) {
1745
mismatch = true;
1746
break;
1747
}
1748
}
1749
if (! mismatch) {
1750
found = true;
1751
break;
1752
}
1753
}
1754
1755
return found ? constant->getResultId() : NoResult;
1756
}
1757
1758
Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
1759
{
1760
Instruction* constant = nullptr;
1761
bool found = false;
1762
for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
1763
constant = groupedStructConstants[typeId][i];
1764
1765
// same contents?
1766
bool mismatch = false;
1767
for (int op = 0; op < constant->getNumOperands(); ++op) {
1768
if (constant->getIdOperand(op) != comps[op]) {
1769
mismatch = true;
1770
break;
1771
}
1772
}
1773
if (! mismatch) {
1774
found = true;
1775
break;
1776
}
1777
}
1778
1779
return found ? constant->getResultId() : NoResult;
1780
}
1781
1782
// Comments in header
1783
Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
1784
{
1785
Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
1786
assert(typeId);
1787
Op typeClass = getTypeClass(typeId);
1788
1789
switch (typeClass) {
1790
case OpTypeVector:
1791
case OpTypeArray:
1792
case OpTypeMatrix:
1793
case OpTypeCooperativeMatrixKHR:
1794
case OpTypeCooperativeMatrixNV:
1795
if (! specConstant) {
1796
Id existing = findCompositeConstant(typeClass, typeId, members);
1797
if (existing)
1798
return existing;
1799
}
1800
break;
1801
case OpTypeStruct:
1802
if (! specConstant) {
1803
Id existing = findStructConstant(typeId, members);
1804
if (existing)
1805
return existing;
1806
}
1807
break;
1808
default:
1809
assert(0);
1810
return makeFloatConstant(0.0);
1811
}
1812
1813
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1814
c->reserveOperands(members.size());
1815
for (int op = 0; op < (int)members.size(); ++op)
1816
c->addIdOperand(members[op]);
1817
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1818
if (typeClass == OpTypeStruct)
1819
groupedStructConstants[typeId].push_back(c);
1820
else
1821
groupedConstants[typeClass].push_back(c);
1822
module.mapInstruction(c);
1823
1824
return c->getResultId();
1825
}
1826
1827
Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
1828
{
1829
Instruction* entryPoint = new Instruction(OpEntryPoint);
1830
entryPoint->reserveOperands(3);
1831
entryPoint->addImmediateOperand(model);
1832
entryPoint->addIdOperand(function->getId());
1833
entryPoint->addStringOperand(name);
1834
1835
entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
1836
1837
return entryPoint;
1838
}
1839
1840
// Currently relying on the fact that all 'value' of interest are small non-negative values.
1841
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
1842
{
1843
// entryPoint can be null if we are in compile-only mode
1844
if (!entryPoint)
1845
return;
1846
1847
Instruction* instr = new Instruction(OpExecutionMode);
1848
instr->reserveOperands(3);
1849
instr->addIdOperand(entryPoint->getId());
1850
instr->addImmediateOperand(mode);
1851
if (value1 >= 0)
1852
instr->addImmediateOperand(value1);
1853
if (value2 >= 0)
1854
instr->addImmediateOperand(value2);
1855
if (value3 >= 0)
1856
instr->addImmediateOperand(value3);
1857
1858
executionModes.push_back(std::unique_ptr<Instruction>(instr));
1859
}
1860
1861
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
1862
{
1863
// entryPoint can be null if we are in compile-only mode
1864
if (!entryPoint)
1865
return;
1866
1867
Instruction* instr = new Instruction(OpExecutionMode);
1868
instr->reserveOperands(literals.size() + 2);
1869
instr->addIdOperand(entryPoint->getId());
1870
instr->addImmediateOperand(mode);
1871
for (auto literal : literals)
1872
instr->addImmediateOperand(literal);
1873
1874
executionModes.push_back(std::unique_ptr<Instruction>(instr));
1875
}
1876
1877
void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
1878
{
1879
// entryPoint can be null if we are in compile-only mode
1880
if (!entryPoint)
1881
return;
1882
1883
Instruction* instr = new Instruction(OpExecutionModeId);
1884
instr->reserveOperands(operandIds.size() + 2);
1885
instr->addIdOperand(entryPoint->getId());
1886
instr->addImmediateOperand(mode);
1887
for (auto operandId : operandIds)
1888
instr->addIdOperand(operandId);
1889
1890
executionModes.push_back(std::unique_ptr<Instruction>(instr));
1891
}
1892
1893
void Builder::addName(Id id, const char* string)
1894
{
1895
Instruction* name = new Instruction(OpName);
1896
name->reserveOperands(2);
1897
name->addIdOperand(id);
1898
name->addStringOperand(string);
1899
1900
names.push_back(std::unique_ptr<Instruction>(name));
1901
}
1902
1903
void Builder::addMemberName(Id id, int memberNumber, const char* string)
1904
{
1905
Instruction* name = new Instruction(OpMemberName);
1906
name->reserveOperands(3);
1907
name->addIdOperand(id);
1908
name->addImmediateOperand(memberNumber);
1909
name->addStringOperand(string);
1910
1911
names.push_back(std::unique_ptr<Instruction>(name));
1912
}
1913
1914
void Builder::addDecoration(Id id, Decoration decoration, int num)
1915
{
1916
if (decoration == spv::DecorationMax)
1917
return;
1918
1919
Instruction* dec = new Instruction(OpDecorate);
1920
dec->reserveOperands(2);
1921
dec->addIdOperand(id);
1922
dec->addImmediateOperand(decoration);
1923
if (num >= 0)
1924
dec->addImmediateOperand(num);
1925
1926
decorations.push_back(std::unique_ptr<Instruction>(dec));
1927
}
1928
1929
void Builder::addDecoration(Id id, Decoration decoration, const char* s)
1930
{
1931
if (decoration == spv::DecorationMax)
1932
return;
1933
1934
Instruction* dec = new Instruction(OpDecorateString);
1935
dec->reserveOperands(3);
1936
dec->addIdOperand(id);
1937
dec->addImmediateOperand(decoration);
1938
dec->addStringOperand(s);
1939
1940
decorations.push_back(std::unique_ptr<Instruction>(dec));
1941
}
1942
1943
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)
1944
{
1945
if (decoration == spv::DecorationMax)
1946
return;
1947
1948
Instruction* dec = new Instruction(OpDecorate);
1949
dec->reserveOperands(literals.size() + 2);
1950
dec->addIdOperand(id);
1951
dec->addImmediateOperand(decoration);
1952
for (auto literal : literals)
1953
dec->addImmediateOperand(literal);
1954
1955
decorations.push_back(std::unique_ptr<Instruction>(dec));
1956
}
1957
1958
void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)
1959
{
1960
if (decoration == spv::DecorationMax)
1961
return;
1962
1963
Instruction* dec = new Instruction(OpDecorateString);
1964
dec->reserveOperands(strings.size() + 2);
1965
dec->addIdOperand(id);
1966
dec->addImmediateOperand(decoration);
1967
for (auto string : strings)
1968
dec->addStringOperand(string);
1969
1970
decorations.push_back(std::unique_ptr<Instruction>(dec));
1971
}
1972
1973
void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) {
1974
Instruction* dec = new Instruction(OpDecorate);
1975
dec->reserveOperands(4);
1976
dec->addIdOperand(id);
1977
dec->addImmediateOperand(spv::DecorationLinkageAttributes);
1978
dec->addStringOperand(name);
1979
dec->addImmediateOperand(linkType);
1980
1981
decorations.push_back(std::unique_ptr<Instruction>(dec));
1982
}
1983
1984
void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
1985
{
1986
if (decoration == spv::DecorationMax)
1987
return;
1988
1989
Instruction* dec = new Instruction(OpDecorateId);
1990
dec->reserveOperands(3);
1991
dec->addIdOperand(id);
1992
dec->addImmediateOperand(decoration);
1993
dec->addIdOperand(idDecoration);
1994
1995
decorations.push_back(std::unique_ptr<Instruction>(dec));
1996
}
1997
1998
void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)
1999
{
2000
if(decoration == spv::DecorationMax)
2001
return;
2002
2003
Instruction* dec = new Instruction(OpDecorateId);
2004
dec->reserveOperands(operandIds.size() + 2);
2005
dec->addIdOperand(id);
2006
dec->addImmediateOperand(decoration);
2007
2008
for (auto operandId : operandIds)
2009
dec->addIdOperand(operandId);
2010
2011
decorations.push_back(std::unique_ptr<Instruction>(dec));
2012
}
2013
2014
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
2015
{
2016
if (decoration == spv::DecorationMax)
2017
return;
2018
2019
Instruction* dec = new Instruction(OpMemberDecorate);
2020
dec->reserveOperands(3);
2021
dec->addIdOperand(id);
2022
dec->addImmediateOperand(member);
2023
dec->addImmediateOperand(decoration);
2024
if (num >= 0)
2025
dec->addImmediateOperand(num);
2026
2027
decorations.push_back(std::unique_ptr<Instruction>(dec));
2028
}
2029
2030
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
2031
{
2032
if (decoration == spv::DecorationMax)
2033
return;
2034
2035
Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
2036
dec->reserveOperands(4);
2037
dec->addIdOperand(id);
2038
dec->addImmediateOperand(member);
2039
dec->addImmediateOperand(decoration);
2040
dec->addStringOperand(s);
2041
2042
decorations.push_back(std::unique_ptr<Instruction>(dec));
2043
}
2044
2045
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)
2046
{
2047
if (decoration == spv::DecorationMax)
2048
return;
2049
2050
Instruction* dec = new Instruction(OpMemberDecorate);
2051
dec->reserveOperands(literals.size() + 3);
2052
dec->addIdOperand(id);
2053
dec->addImmediateOperand(member);
2054
dec->addImmediateOperand(decoration);
2055
for (auto literal : literals)
2056
dec->addImmediateOperand(literal);
2057
2058
decorations.push_back(std::unique_ptr<Instruction>(dec));
2059
}
2060
2061
void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)
2062
{
2063
if (decoration == spv::DecorationMax)
2064
return;
2065
2066
Instruction* dec = new Instruction(OpMemberDecorateString);
2067
dec->reserveOperands(strings.size() + 3);
2068
dec->addIdOperand(id);
2069
dec->addImmediateOperand(member);
2070
dec->addImmediateOperand(decoration);
2071
for (auto string : strings)
2072
dec->addStringOperand(string);
2073
2074
decorations.push_back(std::unique_ptr<Instruction>(dec));
2075
}
2076
2077
void Builder::addInstruction(std::unique_ptr<Instruction> inst) {
2078
// Optionally insert OpDebugScope
2079
if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) {
2080
if (buildPoint->updateDebugScope(currentDebugScopeId.top())) {
2081
auto scopeInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), OpExtInst);
2082
scopeInst->reserveOperands(3);
2083
scopeInst->addIdOperand(nonSemanticShaderDebugInfo);
2084
scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope);
2085
scopeInst->addIdOperand(currentDebugScopeId.top());
2086
buildPoint->addInstruction(std::move(scopeInst));
2087
}
2088
2089
dirtyScopeTracker = false;
2090
}
2091
2092
// Insert OpLine/OpDebugLine if the debug source location has changed
2093
if (trackDebugInfo && dirtyLineTracker) {
2094
if (buildPoint->updateDebugSourceLocation(currentLine, 0, currentFileId)) {
2095
if (emitSpirvDebugInfo) {
2096
auto lineInst = std::make_unique<Instruction>(OpLine);
2097
lineInst->reserveOperands(3);
2098
lineInst->addIdOperand(currentFileId);
2099
lineInst->addImmediateOperand(currentLine);
2100
lineInst->addImmediateOperand(0);
2101
buildPoint->addInstruction(std::move(lineInst));
2102
}
2103
if (emitNonSemanticShaderDebugInfo) {
2104
auto lineInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), OpExtInst);
2105
lineInst->reserveOperands(7);
2106
lineInst->addIdOperand(nonSemanticShaderDebugInfo);
2107
lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine);
2108
lineInst->addIdOperand(makeDebugSource(currentFileId));
2109
lineInst->addIdOperand(makeUintConstant(currentLine));
2110
lineInst->addIdOperand(makeUintConstant(currentLine));
2111
lineInst->addIdOperand(makeUintConstant(0));
2112
lineInst->addIdOperand(makeUintConstant(0));
2113
buildPoint->addInstruction(std::move(lineInst));
2114
}
2115
}
2116
2117
dirtyLineTracker = false;
2118
}
2119
2120
buildPoint->addInstruction(std::move(inst));
2121
}
2122
2123
// Comments in header
2124
Function* Builder::makeEntryPoint(const char* entryPoint)
2125
{
2126
assert(! entryPointFunction);
2127
2128
auto const returnType = makeVoidType();
2129
2130
restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;
2131
if(sourceLang == spv::SourceLanguageHLSL) {
2132
emitNonSemanticShaderDebugInfo = false;
2133
}
2134
2135
Block* entry = nullptr;
2136
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, {}, {}, &entry);
2137
2138
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
2139
2140
return entryPointFunction;
2141
}
2142
2143
// Comments in header
2144
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
2145
const std::vector<Id>& paramTypes,
2146
const std::vector<std::vector<Decoration>>& decorations, Block** entry)
2147
{
2148
// Make the function and initial instructions in it
2149
Id typeId = makeFunctionType(returnType, paramTypes);
2150
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
2151
Id funcId = getUniqueId();
2152
Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module);
2153
2154
// Set up the precisions
2155
setPrecision(function->getId(), precision);
2156
function->setReturnPrecision(precision);
2157
for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
2158
for (int d = 0; d < (int)decorations[p].size(); ++d) {
2159
addDecoration(firstParamId + p, decorations[p][d]);
2160
function->addParamPrecision(p, decorations[p][d]);
2161
}
2162
}
2163
2164
// reset last debug scope
2165
if (emitNonSemanticShaderDebugInfo) {
2166
dirtyScopeTracker = true;
2167
}
2168
2169
// CFG
2170
assert(entry != nullptr);
2171
*entry = new Block(getUniqueId(), *function);
2172
function->addBlock(*entry);
2173
setBuildPoint(*entry);
2174
2175
if (name)
2176
addName(function->getId(), name);
2177
2178
functions.push_back(std::unique_ptr<Function>(function));
2179
2180
return function;
2181
}
2182
2183
void Builder::setupDebugFunctionEntry(Function* function, const char* name, int line, const std::vector<Id>& paramTypes,
2184
const std::vector<char const*>& paramNames)
2185
{
2186
2187
if (!emitNonSemanticShaderDebugInfo)
2188
return;
2189
2190
currentLine = line;
2191
Id nameId = getStringId(unmangleFunctionName(name));
2192
Id funcTypeId = function->getFuncTypeId();
2193
assert(debugId[funcTypeId] != 0);
2194
Id funcId = function->getId();
2195
2196
assert(funcId != 0);
2197
2198
// Make the debug function instruction
2199
Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId);
2200
debugId[funcId] = debugFuncId;
2201
currentDebugScopeId.push(debugFuncId);
2202
2203
// DebugScope and DebugLine for parameter DebugDeclares
2204
assert(paramTypes.size() == paramNames.size());
2205
if ((int)paramTypes.size() > 0) {
2206
Id firstParamId = function->getParamId(0);
2207
2208
for (size_t p = 0; p < paramTypes.size(); ++p) {
2209
bool passByRef = false;
2210
Id paramTypeId = paramTypes[p];
2211
2212
// For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type.
2213
if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) {
2214
passByRef = true;
2215
paramTypeId = getContainedTypeId(paramTypeId);
2216
}
2217
2218
auto const& paramName = paramNames[p];
2219
auto const debugLocalVariableId = createDebugLocalVariable(debugId[paramTypeId], paramName, p + 1);
2220
auto const paramId = static_cast<Id>(firstParamId + p);
2221
debugId[paramId] = debugLocalVariableId;
2222
2223
if (passByRef) {
2224
makeDebugDeclare(debugLocalVariableId, paramId);
2225
} else {
2226
makeDebugValue(debugLocalVariableId, paramId);
2227
}
2228
}
2229
}
2230
2231
// Clear debug scope stack
2232
if (emitNonSemanticShaderDebugInfo)
2233
currentDebugScopeId.pop();
2234
}
2235
2236
Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId)
2237
{
2238
assert(function != nullptr);
2239
assert(nameId != 0);
2240
assert(funcTypeId != 0);
2241
assert(debugId[funcTypeId] != 0);
2242
2243
Id funcId = getUniqueId();
2244
auto type = new Instruction(funcId, makeVoidType(), OpExtInst);
2245
type->reserveOperands(11);
2246
type->addIdOperand(nonSemanticShaderDebugInfo);
2247
type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction);
2248
type->addIdOperand(nameId);
2249
type->addIdOperand(debugId[funcTypeId]);
2250
type->addIdOperand(makeDebugSource(currentFileId)); // TODO: This points to file of definition instead of declaration
2251
type->addIdOperand(makeUintConstant(currentLine)); // TODO: This points to line of definition instead of declaration
2252
type->addIdOperand(makeUintConstant(0)); // column
2253
type->addIdOperand(makeDebugCompilationUnit()); // scope
2254
type->addIdOperand(nameId); // linkage name
2255
type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic));
2256
type->addIdOperand(makeUintConstant(currentLine));
2257
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
2258
module.mapInstruction(type);
2259
return funcId;
2260
}
2261
2262
Id Builder::makeDebugLexicalBlock(uint32_t line) {
2263
assert(!currentDebugScopeId.empty());
2264
2265
Id lexId = getUniqueId();
2266
auto lex = new Instruction(lexId, makeVoidType(), OpExtInst);
2267
lex->reserveOperands(6);
2268
lex->addIdOperand(nonSemanticShaderDebugInfo);
2269
lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock);
2270
lex->addIdOperand(makeDebugSource(currentFileId));
2271
lex->addIdOperand(makeUintConstant(line));
2272
lex->addIdOperand(makeUintConstant(0)); // column
2273
lex->addIdOperand(currentDebugScopeId.top()); // scope
2274
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex));
2275
module.mapInstruction(lex);
2276
return lexId;
2277
}
2278
2279
std::string Builder::unmangleFunctionName(std::string const& name) const
2280
{
2281
assert(name.length() > 0);
2282
2283
if(name.rfind('(') != std::string::npos) {
2284
return name.substr(0, name.rfind('('));
2285
} else {
2286
return name;
2287
}
2288
}
2289
2290
// Comments in header
2291
void Builder::makeReturn(bool implicit, Id retVal)
2292
{
2293
if (retVal) {
2294
Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
2295
inst->addIdOperand(retVal);
2296
addInstruction(std::unique_ptr<Instruction>(inst));
2297
} else
2298
addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
2299
2300
if (! implicit)
2301
createAndSetNoPredecessorBlock("post-return");
2302
}
2303
2304
// Comments in header
2305
void Builder::enterLexicalBlock(uint32_t line)
2306
{
2307
// Generate new lexical scope debug instruction
2308
Id lexId = makeDebugLexicalBlock(line);
2309
currentDebugScopeId.push(lexId);
2310
dirtyScopeTracker = true;
2311
}
2312
2313
// Comments in header
2314
void Builder::leaveLexicalBlock()
2315
{
2316
// Pop current scope from stack and clear current scope
2317
currentDebugScopeId.pop();
2318
dirtyScopeTracker = true;
2319
}
2320
2321
// Comments in header
2322
void Builder::enterFunction(Function const* function)
2323
{
2324
// Save and disable debugInfo for HLSL entry point function. It is a wrapper
2325
// function with no user code in it.
2326
restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;
2327
if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) {
2328
emitNonSemanticShaderDebugInfo = false;
2329
}
2330
2331
if (emitNonSemanticShaderDebugInfo) {
2332
// Initialize scope state
2333
Id funcId = function->getFuncId();
2334
currentDebugScopeId.push(debugId[funcId]);
2335
// Create DebugFunctionDefinition
2336
spv::Id resultId = getUniqueId();
2337
Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst);
2338
defInst->reserveOperands(4);
2339
defInst->addIdOperand(nonSemanticShaderDebugInfo);
2340
defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition);
2341
defInst->addIdOperand(debugId[funcId]);
2342
defInst->addIdOperand(funcId);
2343
addInstruction(std::unique_ptr<Instruction>(defInst));
2344
}
2345
2346
if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) {
2347
Id funcId = function->getFuncId();
2348
addCapability(CapabilityLinkage);
2349
addLinkageDecoration(funcId, function->getExportName(), linkType);
2350
}
2351
}
2352
2353
// Comments in header
2354
void Builder::leaveFunction()
2355
{
2356
Block* block = buildPoint;
2357
Function& function = buildPoint->getParent();
2358
assert(block);
2359
2360
// If our function did not contain a return, add a return void now.
2361
if (! block->isTerminated()) {
2362
if (function.getReturnType() == makeVoidType())
2363
makeReturn(true);
2364
else {
2365
makeReturn(true, createUndefined(function.getReturnType()));
2366
}
2367
}
2368
2369
// Clear function scope from debug scope stack
2370
if (emitNonSemanticShaderDebugInfo)
2371
currentDebugScopeId.pop();
2372
2373
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
2374
}
2375
2376
// Comments in header
2377
void Builder::makeStatementTerminator(spv::Op opcode, const char *name)
2378
{
2379
addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));
2380
createAndSetNoPredecessorBlock(name);
2381
}
2382
2383
// Comments in header
2384
void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name)
2385
{
2386
// It's assumed that the terminator instruction is always of void return type
2387
// However in future if there is a need for non void return type, new helper
2388
// methods can be created.
2389
createNoResultOp(opcode, operands);
2390
createAndSetNoPredecessorBlock(name);
2391
}
2392
2393
// Comments in header
2394
Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer,
2395
bool const compilerGenerated)
2396
{
2397
Id pointerType = makePointer(storageClass, type);
2398
Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
2399
inst->addImmediateOperand(storageClass);
2400
if (initializer != NoResult)
2401
inst->addIdOperand(initializer);
2402
2403
switch (storageClass) {
2404
case StorageClassFunction:
2405
// Validation rules require the declaration in the entry block
2406
buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
2407
2408
if (emitNonSemanticShaderDebugInfo && !compilerGenerated)
2409
{
2410
auto const debugLocalVariableId = createDebugLocalVariable(debugId[type], name);
2411
debugId[inst->getResultId()] = debugLocalVariableId;
2412
2413
makeDebugDeclare(debugLocalVariableId, inst->getResultId());
2414
}
2415
2416
break;
2417
2418
default:
2419
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
2420
module.mapInstruction(inst);
2421
2422
if (emitNonSemanticShaderDebugInfo)
2423
{
2424
auto const debugResultId = createDebugGlobalVariable(debugId[type], name, inst->getResultId());
2425
debugId[inst->getResultId()] = debugResultId;
2426
}
2427
break;
2428
}
2429
2430
if (name)
2431
addName(inst->getResultId(), name);
2432
setPrecision(inst->getResultId(), precision);
2433
2434
return inst->getResultId();
2435
}
2436
2437
// Comments in header
2438
Id Builder::createUndefined(Id type)
2439
{
2440
Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
2441
addInstruction(std::unique_ptr<Instruction>(inst));
2442
return inst->getResultId();
2443
}
2444
2445
// av/vis/nonprivate are unnecessary and illegal for some storage classes.
2446
spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
2447
const
2448
{
2449
switch (sc) {
2450
case spv::StorageClassUniform:
2451
case spv::StorageClassWorkgroup:
2452
case spv::StorageClassStorageBuffer:
2453
case spv::StorageClassPhysicalStorageBufferEXT:
2454
break;
2455
default:
2456
memoryAccess = spv::MemoryAccessMask(memoryAccess &
2457
~(spv::MemoryAccessMakePointerAvailableKHRMask |
2458
spv::MemoryAccessMakePointerVisibleKHRMask |
2459
spv::MemoryAccessNonPrivatePointerKHRMask));
2460
break;
2461
}
2462
return memoryAccess;
2463
}
2464
2465
// Comments in header
2466
void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,
2467
unsigned int alignment)
2468
{
2469
Instruction* store = new Instruction(OpStore);
2470
store->reserveOperands(2);
2471
store->addIdOperand(lValue);
2472
store->addIdOperand(rValue);
2473
2474
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
2475
2476
if (memoryAccess != MemoryAccessMaskNone) {
2477
store->addImmediateOperand(memoryAccess);
2478
if (memoryAccess & spv::MemoryAccessAlignedMask) {
2479
store->addImmediateOperand(alignment);
2480
}
2481
if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
2482
store->addIdOperand(makeUintConstant(scope));
2483
}
2484
}
2485
2486
addInstruction(std::unique_ptr<Instruction>(store));
2487
}
2488
2489
// Comments in header
2490
Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,
2491
spv::Scope scope, unsigned int alignment)
2492
{
2493
Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
2494
load->addIdOperand(lValue);
2495
2496
memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
2497
2498
if (memoryAccess != MemoryAccessMaskNone) {
2499
load->addImmediateOperand(memoryAccess);
2500
if (memoryAccess & spv::MemoryAccessAlignedMask) {
2501
load->addImmediateOperand(alignment);
2502
}
2503
if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
2504
load->addIdOperand(makeUintConstant(scope));
2505
}
2506
}
2507
2508
addInstruction(std::unique_ptr<Instruction>(load));
2509
setPrecision(load->getResultId(), precision);
2510
2511
return load->getResultId();
2512
}
2513
2514
// Comments in header
2515
Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
2516
{
2517
// Figure out the final resulting type.
2518
Id typeId = getResultingAccessChainType();
2519
typeId = makePointer(storageClass, typeId);
2520
2521
// Make the instruction
2522
Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
2523
chain->reserveOperands(offsets.size() + 1);
2524
chain->addIdOperand(base);
2525
for (int i = 0; i < (int)offsets.size(); ++i)
2526
chain->addIdOperand(offsets[i]);
2527
addInstruction(std::unique_ptr<Instruction>(chain));
2528
2529
return chain->getResultId();
2530
}
2531
2532
Id Builder::createArrayLength(Id base, unsigned int member)
2533
{
2534
spv::Id intType = makeUintType(32);
2535
Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
2536
length->reserveOperands(2);
2537
length->addIdOperand(base);
2538
length->addImmediateOperand(member);
2539
addInstruction(std::unique_ptr<Instruction>(length));
2540
2541
return length->getResultId();
2542
}
2543
2544
Id Builder::createCooperativeMatrixLengthKHR(Id type)
2545
{
2546
spv::Id intType = makeUintType(32);
2547
2548
// Generate code for spec constants if in spec constant operation
2549
// generation mode.
2550
if (generatingOpCodeForSpecConst) {
2551
return createSpecConstantOp(OpCooperativeMatrixLengthKHR, intType, std::vector<Id>(1, type), std::vector<Id>());
2552
}
2553
2554
Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthKHR);
2555
length->addIdOperand(type);
2556
addInstruction(std::unique_ptr<Instruction>(length));
2557
2558
return length->getResultId();
2559
}
2560
2561
Id Builder::createCooperativeMatrixLengthNV(Id type)
2562
{
2563
spv::Id intType = makeUintType(32);
2564
2565
// Generate code for spec constants if in spec constant operation
2566
// generation mode.
2567
if (generatingOpCodeForSpecConst) {
2568
return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());
2569
}
2570
2571
Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
2572
length->addIdOperand(type);
2573
addInstruction(std::unique_ptr<Instruction>(length));
2574
2575
return length->getResultId();
2576
}
2577
2578
Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
2579
{
2580
// Generate code for spec constants if in spec constant operation
2581
// generation mode.
2582
if (generatingOpCodeForSpecConst) {
2583
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite),
2584
std::vector<Id>(1, index));
2585
}
2586
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
2587
extract->reserveOperands(2);
2588
extract->addIdOperand(composite);
2589
extract->addImmediateOperand(index);
2590
addInstruction(std::unique_ptr<Instruction>(extract));
2591
2592
return extract->getResultId();
2593
}
2594
2595
Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
2596
{
2597
// Generate code for spec constants if in spec constant operation
2598
// generation mode.
2599
if (generatingOpCodeForSpecConst) {
2600
return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
2601
}
2602
Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
2603
extract->reserveOperands(indexes.size() + 1);
2604
extract->addIdOperand(composite);
2605
for (int i = 0; i < (int)indexes.size(); ++i)
2606
extract->addImmediateOperand(indexes[i]);
2607
addInstruction(std::unique_ptr<Instruction>(extract));
2608
2609
return extract->getResultId();
2610
}
2611
2612
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
2613
{
2614
Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
2615
insert->reserveOperands(3);
2616
insert->addIdOperand(object);
2617
insert->addIdOperand(composite);
2618
insert->addImmediateOperand(index);
2619
addInstruction(std::unique_ptr<Instruction>(insert));
2620
2621
return insert->getResultId();
2622
}
2623
2624
Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
2625
{
2626
Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
2627
insert->reserveOperands(indexes.size() + 2);
2628
insert->addIdOperand(object);
2629
insert->addIdOperand(composite);
2630
for (int i = 0; i < (int)indexes.size(); ++i)
2631
insert->addImmediateOperand(indexes[i]);
2632
addInstruction(std::unique_ptr<Instruction>(insert));
2633
2634
return insert->getResultId();
2635
}
2636
2637
Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
2638
{
2639
Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
2640
extract->reserveOperands(2);
2641
extract->addIdOperand(vector);
2642
extract->addIdOperand(componentIndex);
2643
addInstruction(std::unique_ptr<Instruction>(extract));
2644
2645
return extract->getResultId();
2646
}
2647
2648
Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
2649
{
2650
Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
2651
insert->reserveOperands(3);
2652
insert->addIdOperand(vector);
2653
insert->addIdOperand(component);
2654
insert->addIdOperand(componentIndex);
2655
addInstruction(std::unique_ptr<Instruction>(insert));
2656
2657
return insert->getResultId();
2658
}
2659
2660
// An opcode that has no operands, no result id, and no type
2661
void Builder::createNoResultOp(Op opCode)
2662
{
2663
Instruction* op = new Instruction(opCode);
2664
addInstruction(std::unique_ptr<Instruction>(op));
2665
}
2666
2667
// An opcode that has one id operand, no result id, and no type
2668
void Builder::createNoResultOp(Op opCode, Id operand)
2669
{
2670
Instruction* op = new Instruction(opCode);
2671
op->addIdOperand(operand);
2672
addInstruction(std::unique_ptr<Instruction>(op));
2673
}
2674
2675
// An opcode that has one or more operands, no result id, and no type
2676
void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
2677
{
2678
Instruction* op = new Instruction(opCode);
2679
op->reserveOperands(operands.size());
2680
for (auto id : operands) {
2681
op->addIdOperand(id);
2682
}
2683
addInstruction(std::unique_ptr<Instruction>(op));
2684
}
2685
2686
// An opcode that has multiple operands, no result id, and no type
2687
void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
2688
{
2689
Instruction* op = new Instruction(opCode);
2690
op->reserveOperands(operands.size());
2691
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
2692
if (it->isId)
2693
op->addIdOperand(it->word);
2694
else
2695
op->addImmediateOperand(it->word);
2696
}
2697
addInstruction(std::unique_ptr<Instruction>(op));
2698
}
2699
2700
void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
2701
{
2702
Instruction* op = new Instruction(OpControlBarrier);
2703
op->reserveOperands(3);
2704
op->addIdOperand(makeUintConstant(execution));
2705
op->addIdOperand(makeUintConstant(memory));
2706
op->addIdOperand(makeUintConstant(semantics));
2707
addInstruction(std::unique_ptr<Instruction>(op));
2708
}
2709
2710
void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
2711
{
2712
Instruction* op = new Instruction(OpMemoryBarrier);
2713
op->reserveOperands(2);
2714
op->addIdOperand(makeUintConstant(executionScope));
2715
op->addIdOperand(makeUintConstant(memorySemantics));
2716
addInstruction(std::unique_ptr<Instruction>(op));
2717
}
2718
2719
// An opcode that has one operands, a result id, and a type
2720
Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
2721
{
2722
// Generate code for spec constants if in spec constant operation
2723
// generation mode.
2724
if (generatingOpCodeForSpecConst) {
2725
return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
2726
}
2727
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
2728
op->addIdOperand(operand);
2729
addInstruction(std::unique_ptr<Instruction>(op));
2730
2731
return op->getResultId();
2732
}
2733
2734
Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
2735
{
2736
// Generate code for spec constants if in spec constant operation
2737
// generation mode.
2738
if (generatingOpCodeForSpecConst) {
2739
std::vector<Id> operands(2);
2740
operands[0] = left; operands[1] = right;
2741
return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
2742
}
2743
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
2744
op->reserveOperands(2);
2745
op->addIdOperand(left);
2746
op->addIdOperand(right);
2747
addInstruction(std::unique_ptr<Instruction>(op));
2748
2749
return op->getResultId();
2750
}
2751
2752
Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
2753
{
2754
// Generate code for spec constants if in spec constant operation
2755
// generation mode.
2756
if (generatingOpCodeForSpecConst) {
2757
std::vector<Id> operands(3);
2758
operands[0] = op1;
2759
operands[1] = op2;
2760
operands[2] = op3;
2761
return createSpecConstantOp(
2762
opCode, typeId, operands, std::vector<Id>());
2763
}
2764
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
2765
op->reserveOperands(3);
2766
op->addIdOperand(op1);
2767
op->addIdOperand(op2);
2768
op->addIdOperand(op3);
2769
addInstruction(std::unique_ptr<Instruction>(op));
2770
2771
return op->getResultId();
2772
}
2773
2774
Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
2775
{
2776
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
2777
op->reserveOperands(operands.size());
2778
for (auto id : operands)
2779
op->addIdOperand(id);
2780
addInstruction(std::unique_ptr<Instruction>(op));
2781
2782
return op->getResultId();
2783
}
2784
2785
Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
2786
{
2787
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
2788
op->reserveOperands(operands.size());
2789
for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
2790
if (it->isId)
2791
op->addIdOperand(it->word);
2792
else
2793
op->addImmediateOperand(it->word);
2794
}
2795
addInstruction(std::unique_ptr<Instruction>(op));
2796
2797
return op->getResultId();
2798
}
2799
2800
Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,
2801
const std::vector<unsigned>& literals)
2802
{
2803
Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
2804
op->reserveOperands(operands.size() + literals.size() + 1);
2805
op->addImmediateOperand((unsigned) opCode);
2806
for (auto it = operands.cbegin(); it != operands.cend(); ++it)
2807
op->addIdOperand(*it);
2808
for (auto it = literals.cbegin(); it != literals.cend(); ++it)
2809
op->addImmediateOperand(*it);
2810
module.mapInstruction(op);
2811
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
2812
2813
// OpSpecConstantOp's using 8 or 16 bit types require the associated capability
2814
if (containsType(typeId, OpTypeInt, 8))
2815
addCapability(CapabilityInt8);
2816
if (containsType(typeId, OpTypeInt, 16))
2817
addCapability(CapabilityInt16);
2818
if (containsType(typeId, OpTypeFloat, 16))
2819
addCapability(CapabilityFloat16);
2820
2821
return op->getResultId();
2822
}
2823
2824
Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
2825
{
2826
Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
2827
op->reserveOperands(args.size() + 1);
2828
op->addIdOperand(function->getId());
2829
for (int a = 0; a < (int)args.size(); ++a)
2830
op->addIdOperand(args[a]);
2831
addInstruction(std::unique_ptr<Instruction>(op));
2832
2833
return op->getResultId();
2834
}
2835
2836
// Comments in header
2837
Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
2838
{
2839
if (channels.size() == 1)
2840
return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
2841
2842
if (generatingOpCodeForSpecConst) {
2843
std::vector<Id> operands(2);
2844
operands[0] = operands[1] = source;
2845
return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
2846
}
2847
Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
2848
assert(isVector(source));
2849
swizzle->reserveOperands(channels.size() + 2);
2850
swizzle->addIdOperand(source);
2851
swizzle->addIdOperand(source);
2852
for (int i = 0; i < (int)channels.size(); ++i)
2853
swizzle->addImmediateOperand(channels[i]);
2854
addInstruction(std::unique_ptr<Instruction>(swizzle));
2855
2856
return setPrecision(swizzle->getResultId(), precision);
2857
}
2858
2859
// Comments in header
2860
Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
2861
{
2862
if (channels.size() == 1 && getNumComponents(source) == 1)
2863
return createCompositeInsert(source, target, typeId, channels.front());
2864
2865
Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
2866
2867
assert(isVector(target));
2868
swizzle->reserveOperands(2);
2869
swizzle->addIdOperand(target);
2870
2871
assert(getNumComponents(source) == (int)channels.size());
2872
assert(isVector(source));
2873
swizzle->addIdOperand(source);
2874
2875
// Set up an identity shuffle from the base value to the result value
2876
unsigned int components[4];
2877
int numTargetComponents = getNumComponents(target);
2878
for (int i = 0; i < numTargetComponents; ++i)
2879
components[i] = i;
2880
2881
// Punch in the l-value swizzle
2882
for (int i = 0; i < (int)channels.size(); ++i)
2883
components[channels[i]] = numTargetComponents + i;
2884
2885
// finish the instruction with these components selectors
2886
swizzle->reserveOperands(numTargetComponents);
2887
for (int i = 0; i < numTargetComponents; ++i)
2888
swizzle->addImmediateOperand(components[i]);
2889
addInstruction(std::unique_ptr<Instruction>(swizzle));
2890
2891
return swizzle->getResultId();
2892
}
2893
2894
// Comments in header
2895
void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
2896
{
2897
int direction = getNumComponents(right) - getNumComponents(left);
2898
2899
if (direction > 0)
2900
left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
2901
else if (direction < 0)
2902
right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
2903
2904
return;
2905
}
2906
2907
// Comments in header
2908
Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
2909
{
2910
assert(getNumComponents(scalar) == 1);
2911
assert(getTypeId(scalar) == getScalarTypeId(vectorType));
2912
2913
int numComponents = getNumTypeComponents(vectorType);
2914
if (numComponents == 1)
2915
return scalar;
2916
2917
Instruction* smear = nullptr;
2918
if (generatingOpCodeForSpecConst) {
2919
auto members = std::vector<spv::Id>(numComponents, scalar);
2920
// Sometime even in spec-constant-op mode, the temporary vector created by
2921
// promoting a scalar might not be a spec constant. This should depend on
2922
// the scalar.
2923
// e.g.:
2924
// const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
2925
// In such cases, the temporary vector created from a_front_end_const_scalar
2926
// is not a spec constant vector, even though the binary operation node is marked
2927
// as 'specConstant' and we are in spec-constant-op mode.
2928
auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
2929
smear = module.getInstruction(result_id);
2930
} else {
2931
smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
2932
smear->reserveOperands(numComponents);
2933
for (int c = 0; c < numComponents; ++c)
2934
smear->addIdOperand(scalar);
2935
addInstruction(std::unique_ptr<Instruction>(smear));
2936
}
2937
2938
return setPrecision(smear->getResultId(), precision);
2939
}
2940
2941
// Comments in header
2942
Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
2943
{
2944
Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
2945
inst->reserveOperands(args.size() + 2);
2946
inst->addIdOperand(builtins);
2947
inst->addImmediateOperand(entryPoint);
2948
for (int arg = 0; arg < (int)args.size(); ++arg)
2949
inst->addIdOperand(args[arg]);
2950
2951
addInstruction(std::unique_ptr<Instruction>(inst));
2952
2953
return inst->getResultId();
2954
}
2955
2956
// Accept all parameters needed to create a texture instruction.
2957
// Create the correct instruction based on the inputs, and make the call.
2958
Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
2959
bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)
2960
{
2961
std::vector<Id> texArgs;
2962
2963
//
2964
// Set up the fixed arguments
2965
//
2966
bool explicitLod = false;
2967
texArgs.push_back(parameters.sampler);
2968
texArgs.push_back(parameters.coords);
2969
if (parameters.Dref != NoResult)
2970
texArgs.push_back(parameters.Dref);
2971
if (parameters.component != NoResult)
2972
texArgs.push_back(parameters.component);
2973
2974
if (parameters.granularity != NoResult)
2975
texArgs.push_back(parameters.granularity);
2976
if (parameters.coarse != NoResult)
2977
texArgs.push_back(parameters.coarse);
2978
2979
//
2980
// Set up the optional arguments
2981
//
2982
size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any.
2983
ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
2984
if (parameters.bias) {
2985
mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
2986
texArgs.push_back(parameters.bias);
2987
}
2988
if (parameters.lod) {
2989
mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
2990
texArgs.push_back(parameters.lod);
2991
explicitLod = true;
2992
} else if (parameters.gradX) {
2993
mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
2994
texArgs.push_back(parameters.gradX);
2995
texArgs.push_back(parameters.gradY);
2996
explicitLod = true;
2997
} else if (noImplicitLod && ! fetch && ! gather) {
2998
// have to explicitly use lod of 0 if not allowed to have them be implicit, and
2999
// we would otherwise be about to issue an implicit instruction
3000
mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
3001
texArgs.push_back(makeFloatConstant(0.0));
3002
explicitLod = true;
3003
}
3004
if (parameters.offset) {
3005
if (isConstant(parameters.offset))
3006
mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
3007
else {
3008
addCapability(CapabilityImageGatherExtended);
3009
mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
3010
}
3011
texArgs.push_back(parameters.offset);
3012
}
3013
if (parameters.offsets) {
3014
addCapability(CapabilityImageGatherExtended);
3015
mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
3016
texArgs.push_back(parameters.offsets);
3017
}
3018
if (parameters.sample) {
3019
mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
3020
texArgs.push_back(parameters.sample);
3021
}
3022
if (parameters.lodClamp) {
3023
// capability if this bit is used
3024
addCapability(CapabilityMinLod);
3025
3026
mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
3027
texArgs.push_back(parameters.lodClamp);
3028
}
3029
if (parameters.nonprivate) {
3030
mask = mask | ImageOperandsNonPrivateTexelKHRMask;
3031
}
3032
if (parameters.volatil) {
3033
mask = mask | ImageOperandsVolatileTexelKHRMask;
3034
}
3035
mask = mask | signExtensionMask;
3036
// insert the operand for the mask, if any bits were set.
3037
if (mask != ImageOperandsMaskNone)
3038
texArgs.insert(texArgs.begin() + optArgNum, mask);
3039
3040
//
3041
// Set up the instruction
3042
//
3043
Op opCode = OpNop; // All paths below need to set this
3044
if (fetch) {
3045
if (sparse)
3046
opCode = OpImageSparseFetch;
3047
else
3048
opCode = OpImageFetch;
3049
} else if (parameters.granularity && parameters.coarse) {
3050
opCode = OpImageSampleFootprintNV;
3051
} else if (gather) {
3052
if (parameters.Dref)
3053
if (sparse)
3054
opCode = OpImageSparseDrefGather;
3055
else
3056
opCode = OpImageDrefGather;
3057
else
3058
if (sparse)
3059
opCode = OpImageSparseGather;
3060
else
3061
opCode = OpImageGather;
3062
} else if (explicitLod) {
3063
if (parameters.Dref) {
3064
if (proj)
3065
if (sparse)
3066
opCode = OpImageSparseSampleProjDrefExplicitLod;
3067
else
3068
opCode = OpImageSampleProjDrefExplicitLod;
3069
else
3070
if (sparse)
3071
opCode = OpImageSparseSampleDrefExplicitLod;
3072
else
3073
opCode = OpImageSampleDrefExplicitLod;
3074
} else {
3075
if (proj)
3076
if (sparse)
3077
opCode = OpImageSparseSampleProjExplicitLod;
3078
else
3079
opCode = OpImageSampleProjExplicitLod;
3080
else
3081
if (sparse)
3082
opCode = OpImageSparseSampleExplicitLod;
3083
else
3084
opCode = OpImageSampleExplicitLod;
3085
}
3086
} else {
3087
if (parameters.Dref) {
3088
if (proj)
3089
if (sparse)
3090
opCode = OpImageSparseSampleProjDrefImplicitLod;
3091
else
3092
opCode = OpImageSampleProjDrefImplicitLod;
3093
else
3094
if (sparse)
3095
opCode = OpImageSparseSampleDrefImplicitLod;
3096
else
3097
opCode = OpImageSampleDrefImplicitLod;
3098
} else {
3099
if (proj)
3100
if (sparse)
3101
opCode = OpImageSparseSampleProjImplicitLod;
3102
else
3103
opCode = OpImageSampleProjImplicitLod;
3104
else
3105
if (sparse)
3106
opCode = OpImageSparseSampleImplicitLod;
3107
else
3108
opCode = OpImageSampleImplicitLod;
3109
}
3110
}
3111
3112
// See if the result type is expecting a smeared result.
3113
// This happens when a legacy shadow*() call is made, which
3114
// gets a vec4 back instead of a float.
3115
Id smearedType = resultType;
3116
if (! isScalarType(resultType)) {
3117
switch (opCode) {
3118
case OpImageSampleDrefImplicitLod:
3119
case OpImageSampleDrefExplicitLod:
3120
case OpImageSampleProjDrefImplicitLod:
3121
case OpImageSampleProjDrefExplicitLod:
3122
resultType = getScalarTypeId(resultType);
3123
break;
3124
default:
3125
break;
3126
}
3127
}
3128
3129
Id typeId0 = 0;
3130
Id typeId1 = 0;
3131
3132
if (sparse) {
3133
typeId0 = resultType;
3134
typeId1 = getDerefTypeId(parameters.texelOut);
3135
resultType = makeStructResultType(typeId0, typeId1);
3136
}
3137
3138
// Build the SPIR-V instruction
3139
Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
3140
textureInst->reserveOperands(optArgNum + (texArgs.size() - (optArgNum + 1)));
3141
for (size_t op = 0; op < optArgNum; ++op)
3142
textureInst->addIdOperand(texArgs[op]);
3143
if (optArgNum < texArgs.size())
3144
textureInst->addImmediateOperand(texArgs[optArgNum]);
3145
for (size_t op = optArgNum + 1; op < texArgs.size(); ++op)
3146
textureInst->addIdOperand(texArgs[op]);
3147
setPrecision(textureInst->getResultId(), precision);
3148
addInstruction(std::unique_ptr<Instruction>(textureInst));
3149
3150
Id resultId = textureInst->getResultId();
3151
3152
if (sparse) {
3153
// set capability
3154
addCapability(CapabilitySparseResidency);
3155
3156
// Decode the return type that was a special structure
3157
createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
3158
resultId = createCompositeExtract(resultId, typeId0, 0);
3159
setPrecision(resultId, precision);
3160
} else {
3161
// When a smear is needed, do it, as per what was computed
3162
// above when resultType was changed to a scalar type.
3163
if (resultType != smearedType)
3164
resultId = smearScalar(precision, resultId, smearedType);
3165
}
3166
3167
return resultId;
3168
}
3169
3170
// Comments in header
3171
Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
3172
{
3173
// Figure out the result type
3174
Id resultType = 0;
3175
switch (opCode) {
3176
case OpImageQuerySize:
3177
case OpImageQuerySizeLod:
3178
{
3179
int numComponents = 0;
3180
switch (getTypeDimensionality(getImageType(parameters.sampler))) {
3181
case Dim1D:
3182
case DimBuffer:
3183
numComponents = 1;
3184
break;
3185
case Dim2D:
3186
case DimCube:
3187
case DimRect:
3188
case DimSubpassData:
3189
numComponents = 2;
3190
break;
3191
case Dim3D:
3192
numComponents = 3;
3193
break;
3194
3195
default:
3196
assert(0);
3197
break;
3198
}
3199
if (isArrayedImageType(getImageType(parameters.sampler)))
3200
++numComponents;
3201
3202
Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
3203
if (numComponents == 1)
3204
resultType = intType;
3205
else
3206
resultType = makeVectorType(intType, numComponents);
3207
3208
break;
3209
}
3210
case OpImageQueryLod:
3211
resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
3212
break;
3213
case OpImageQueryLevels:
3214
case OpImageQuerySamples:
3215
resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
3216
break;
3217
default:
3218
assert(0);
3219
break;
3220
}
3221
3222
Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
3223
query->addIdOperand(parameters.sampler);
3224
if (parameters.coords)
3225
query->addIdOperand(parameters.coords);
3226
if (parameters.lod)
3227
query->addIdOperand(parameters.lod);
3228
addInstruction(std::unique_ptr<Instruction>(query));
3229
addCapability(CapabilityImageQuery);
3230
3231
return query->getResultId();
3232
}
3233
3234
// External comments in header.
3235
// Operates recursively to visit the composite's hierarchy.
3236
Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
3237
{
3238
Id boolType = makeBoolType();
3239
Id valueType = getTypeId(value1);
3240
3241
Id resultId = NoResult;
3242
3243
int numConstituents = getNumTypeConstituents(valueType);
3244
3245
// Scalars and Vectors
3246
3247
if (isScalarType(valueType) || isVectorType(valueType)) {
3248
assert(valueType == getTypeId(value2));
3249
// These just need a single comparison, just have
3250
// to figure out what it is.
3251
Op op;
3252
switch (getMostBasicTypeClass(valueType)) {
3253
case OpTypeFloat:
3254
op = equal ? OpFOrdEqual : OpFUnordNotEqual;
3255
break;
3256
case OpTypeInt:
3257
default:
3258
op = equal ? OpIEqual : OpINotEqual;
3259
break;
3260
case OpTypeBool:
3261
op = equal ? OpLogicalEqual : OpLogicalNotEqual;
3262
precision = NoPrecision;
3263
break;
3264
}
3265
3266
if (isScalarType(valueType)) {
3267
// scalar
3268
resultId = createBinOp(op, boolType, value1, value2);
3269
} else {
3270
// vector
3271
resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
3272
setPrecision(resultId, precision);
3273
// reduce vector compares...
3274
resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
3275
}
3276
3277
return setPrecision(resultId, precision);
3278
}
3279
3280
// Only structs, arrays, and matrices should be left.
3281
// They share in common the reduction operation across their constituents.
3282
assert(isAggregateType(valueType) || isMatrixType(valueType));
3283
3284
// Compare each pair of constituents
3285
for (int constituent = 0; constituent < numConstituents; ++constituent) {
3286
std::vector<unsigned> indexes(1, constituent);
3287
Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
3288
Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
3289
Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
3290
Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
3291
3292
Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
3293
3294
if (constituent == 0)
3295
resultId = subResultId;
3296
else
3297
resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId),
3298
precision);
3299
}
3300
3301
return resultId;
3302
}
3303
3304
// OpCompositeConstruct
3305
Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
3306
{
3307
assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 &&
3308
getNumTypeConstituents(typeId) == (int)constituents.size()));
3309
3310
if (generatingOpCodeForSpecConst) {
3311
// Sometime, even in spec-constant-op mode, the constant composite to be
3312
// constructed may not be a specialization constant.
3313
// e.g.:
3314
// const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
3315
// The first column vector should be a spec constant one, as a_spec_const is a spec constant.
3316
// The second column vector should NOT be spec constant, as it does not contain any spec constants.
3317
// To handle such cases, we check the constituents of the constant vector to determine whether this
3318
// vector should be created as a spec constant.
3319
return makeCompositeConstant(typeId, constituents,
3320
std::any_of(constituents.begin(), constituents.end(),
3321
[&](spv::Id id) { return isSpecConstant(id); }));
3322
}
3323
3324
Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
3325
op->reserveOperands(constituents.size());
3326
for (int c = 0; c < (int)constituents.size(); ++c)
3327
op->addIdOperand(constituents[c]);
3328
addInstruction(std::unique_ptr<Instruction>(op));
3329
3330
return op->getResultId();
3331
}
3332
3333
// Vector or scalar constructor
3334
Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
3335
{
3336
Id result = NoResult;
3337
unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
3338
unsigned int targetComponent = 0;
3339
3340
// Special case: when calling a vector constructor with a single scalar
3341
// argument, smear the scalar
3342
if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
3343
return smearScalar(precision, sources[0], resultTypeId);
3344
3345
// accumulate the arguments for OpCompositeConstruct
3346
std::vector<Id> constituents;
3347
Id scalarTypeId = getScalarTypeId(resultTypeId);
3348
3349
// lambda to store the result of visiting an argument component
3350
const auto latchResult = [&](Id comp) {
3351
if (numTargetComponents > 1)
3352
constituents.push_back(comp);
3353
else
3354
result = comp;
3355
++targetComponent;
3356
};
3357
3358
// lambda to visit a vector argument's components
3359
const auto accumulateVectorConstituents = [&](Id sourceArg) {
3360
unsigned int sourceSize = getNumComponents(sourceArg);
3361
unsigned int sourcesToUse = sourceSize;
3362
if (sourcesToUse + targetComponent > numTargetComponents)
3363
sourcesToUse = numTargetComponents - targetComponent;
3364
3365
for (unsigned int s = 0; s < sourcesToUse; ++s) {
3366
std::vector<unsigned> swiz;
3367
swiz.push_back(s);
3368
latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
3369
}
3370
};
3371
3372
// lambda to visit a matrix argument's components
3373
const auto accumulateMatrixConstituents = [&](Id sourceArg) {
3374
unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
3375
unsigned int sourcesToUse = sourceSize;
3376
if (sourcesToUse + targetComponent > numTargetComponents)
3377
sourcesToUse = numTargetComponents - targetComponent;
3378
3379
int col = 0;
3380
int row = 0;
3381
for (unsigned int s = 0; s < sourcesToUse; ++s) {
3382
if (row >= getNumRows(sourceArg)) {
3383
row = 0;
3384
col++;
3385
}
3386
std::vector<Id> indexes;
3387
indexes.push_back(col);
3388
indexes.push_back(row);
3389
latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
3390
row++;
3391
}
3392
};
3393
3394
// Go through the source arguments, each one could have either
3395
// a single or multiple components to contribute.
3396
for (unsigned int i = 0; i < sources.size(); ++i) {
3397
3398
if (isScalar(sources[i]) || isPointer(sources[i]))
3399
latchResult(sources[i]);
3400
else if (isVector(sources[i]))
3401
accumulateVectorConstituents(sources[i]);
3402
else if (isMatrix(sources[i]))
3403
accumulateMatrixConstituents(sources[i]);
3404
else
3405
assert(0);
3406
3407
if (targetComponent >= numTargetComponents)
3408
break;
3409
}
3410
3411
// If the result is a vector, make it from the gathered constituents.
3412
if (constituents.size() > 0) {
3413
result = createCompositeConstruct(resultTypeId, constituents);
3414
return setPrecision(result, precision);
3415
} else {
3416
// Precision was set when generating this component.
3417
return result;
3418
}
3419
}
3420
3421
// Comments in header
3422
Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
3423
{
3424
Id componentTypeId = getScalarTypeId(resultTypeId);
3425
int numCols = getTypeNumColumns(resultTypeId);
3426
int numRows = getTypeNumRows(resultTypeId);
3427
3428
Instruction* instr = module.getInstruction(componentTypeId);
3429
const unsigned bitCount = instr->getImmediateOperand(0);
3430
3431
// Optimize matrix constructed from a bigger matrix
3432
if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
3433
// To truncate the matrix to a smaller number of rows/columns, we need to:
3434
// 1. For each column, extract the column and truncate it to the required size using shuffle
3435
// 2. Assemble the resulting matrix from all columns
3436
Id matrix = sources[0];
3437
Id columnTypeId = getContainedTypeId(resultTypeId);
3438
Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
3439
3440
std::vector<unsigned> channels;
3441
for (int row = 0; row < numRows; ++row)
3442
channels.push_back(row);
3443
3444
std::vector<Id> matrixColumns;
3445
for (int col = 0; col < numCols; ++col) {
3446
std::vector<unsigned> indexes;
3447
indexes.push_back(col);
3448
Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
3449
setPrecision(colv, precision);
3450
3451
if (numRows != getNumRows(matrix)) {
3452
matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
3453
} else {
3454
matrixColumns.push_back(colv);
3455
}
3456
}
3457
3458
return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
3459
}
3460
3461
// Otherwise, will use a two step process
3462
// 1. make a compile-time 2D array of values
3463
// 2. construct a matrix from that array
3464
3465
// Step 1.
3466
3467
// initialize the array to the identity matrix
3468
Id ids[maxMatrixSize][maxMatrixSize];
3469
Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
3470
Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
3471
for (int col = 0; col < 4; ++col) {
3472
for (int row = 0; row < 4; ++row) {
3473
if (col == row)
3474
ids[col][row] = one;
3475
else
3476
ids[col][row] = zero;
3477
}
3478
}
3479
3480
// modify components as dictated by the arguments
3481
if (sources.size() == 1 && isScalar(sources[0])) {
3482
// a single scalar; resets the diagonals
3483
for (int col = 0; col < 4; ++col)
3484
ids[col][col] = sources[0];
3485
} else if (isMatrix(sources[0])) {
3486
// constructing from another matrix; copy over the parts that exist in both the argument and constructee
3487
Id matrix = sources[0];
3488
int minCols = std::min(numCols, getNumColumns(matrix));
3489
int minRows = std::min(numRows, getNumRows(matrix));
3490
for (int col = 0; col < minCols; ++col) {
3491
std::vector<unsigned> indexes;
3492
indexes.push_back(col);
3493
for (int row = 0; row < minRows; ++row) {
3494
indexes.push_back(row);
3495
ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
3496
indexes.pop_back();
3497
setPrecision(ids[col][row], precision);
3498
}
3499
}
3500
} else {
3501
// fill in the matrix in column-major order with whatever argument components are available
3502
int row = 0;
3503
int col = 0;
3504
3505
for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) {
3506
Id argComp = sources[arg];
3507
for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
3508
if (getNumComponents(sources[arg]) > 1) {
3509
argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
3510
setPrecision(argComp, precision);
3511
}
3512
ids[col][row++] = argComp;
3513
if (row == numRows) {
3514
row = 0;
3515
col++;
3516
}
3517
if (col == numCols) {
3518
// If more components are provided than fit the matrix, discard the rest.
3519
break;
3520
}
3521
}
3522
}
3523
}
3524
3525
// Step 2: Construct a matrix from that array.
3526
// First make the column vectors, then make the matrix.
3527
3528
// make the column vectors
3529
Id columnTypeId = getContainedTypeId(resultTypeId);
3530
std::vector<Id> matrixColumns;
3531
for (int col = 0; col < numCols; ++col) {
3532
std::vector<Id> vectorComponents;
3533
for (int row = 0; row < numRows; ++row)
3534
vectorComponents.push_back(ids[col][row]);
3535
Id column = createCompositeConstruct(columnTypeId, vectorComponents);
3536
setPrecision(column, precision);
3537
matrixColumns.push_back(column);
3538
}
3539
3540
// make the matrix
3541
return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
3542
}
3543
3544
// Comments in header
3545
Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
3546
builder(gb),
3547
condition(cond),
3548
control(ctrl),
3549
elseBlock(nullptr)
3550
{
3551
function = &builder.getBuildPoint()->getParent();
3552
3553
// make the blocks, but only put the then-block into the function,
3554
// the else-block and merge-block will be added later, in order, after
3555
// earlier code is emitted
3556
thenBlock = new Block(builder.getUniqueId(), *function);
3557
mergeBlock = new Block(builder.getUniqueId(), *function);
3558
3559
// Save the current block, so that we can add in the flow control split when
3560
// makeEndIf is called.
3561
headerBlock = builder.getBuildPoint();
3562
3563
function->addBlock(thenBlock);
3564
builder.setBuildPoint(thenBlock);
3565
}
3566
3567
// Comments in header
3568
void Builder::If::makeBeginElse()
3569
{
3570
// Close out the "then" by having it jump to the mergeBlock
3571
builder.createBranch(mergeBlock);
3572
3573
// Make the first else block and add it to the function
3574
elseBlock = new Block(builder.getUniqueId(), *function);
3575
function->addBlock(elseBlock);
3576
3577
// Start building the else block
3578
builder.setBuildPoint(elseBlock);
3579
}
3580
3581
// Comments in header
3582
void Builder::If::makeEndIf()
3583
{
3584
// jump to the merge block
3585
builder.createBranch(mergeBlock);
3586
3587
// Go back to the headerBlock and make the flow control split
3588
builder.setBuildPoint(headerBlock);
3589
builder.createSelectionMerge(mergeBlock, control);
3590
if (elseBlock)
3591
builder.createConditionalBranch(condition, thenBlock, elseBlock);
3592
else
3593
builder.createConditionalBranch(condition, thenBlock, mergeBlock);
3594
3595
// add the merge block to the function
3596
function->addBlock(mergeBlock);
3597
builder.setBuildPoint(mergeBlock);
3598
}
3599
3600
// Comments in header
3601
void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
3602
const std::vector<int>& valueIndexToSegment, int defaultSegment,
3603
std::vector<Block*>& segmentBlocks)
3604
{
3605
Function& function = buildPoint->getParent();
3606
3607
// make all the blocks
3608
for (int s = 0; s < numSegments; ++s)
3609
segmentBlocks.push_back(new Block(getUniqueId(), function));
3610
3611
Block* mergeBlock = new Block(getUniqueId(), function);
3612
3613
// make and insert the switch's selection-merge instruction
3614
createSelectionMerge(mergeBlock, control);
3615
3616
// make the switch instruction
3617
Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
3618
switchInst->reserveOperands((caseValues.size() * 2) + 2);
3619
switchInst->addIdOperand(selector);
3620
auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
3621
switchInst->addIdOperand(defaultOrMerge->getId());
3622
defaultOrMerge->addPredecessor(buildPoint);
3623
for (int i = 0; i < (int)caseValues.size(); ++i) {
3624
switchInst->addImmediateOperand(caseValues[i]);
3625
switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
3626
segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
3627
}
3628
addInstruction(std::unique_ptr<Instruction>(switchInst));
3629
3630
// push the merge block
3631
switchMerges.push(mergeBlock);
3632
}
3633
3634
// Comments in header
3635
void Builder::addSwitchBreak()
3636
{
3637
// branch to the top of the merge block stack
3638
createBranch(switchMerges.top());
3639
createAndSetNoPredecessorBlock("post-switch-break");
3640
}
3641
3642
// Comments in header
3643
void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
3644
{
3645
int lastSegment = nextSegment - 1;
3646
if (lastSegment >= 0) {
3647
// Close out previous segment by jumping, if necessary, to next segment
3648
if (! buildPoint->isTerminated())
3649
createBranch(segmentBlock[nextSegment]);
3650
}
3651
Block* block = segmentBlock[nextSegment];
3652
block->getParent().addBlock(block);
3653
setBuildPoint(block);
3654
}
3655
3656
// Comments in header
3657
void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
3658
{
3659
// Close out previous segment by jumping, if necessary, to next segment
3660
if (! buildPoint->isTerminated())
3661
addSwitchBreak();
3662
3663
switchMerges.top()->getParent().addBlock(switchMerges.top());
3664
setBuildPoint(switchMerges.top());
3665
3666
switchMerges.pop();
3667
}
3668
3669
Block& Builder::makeNewBlock()
3670
{
3671
Function& function = buildPoint->getParent();
3672
auto block = new Block(getUniqueId(), function);
3673
function.addBlock(block);
3674
return *block;
3675
}
3676
3677
Builder::LoopBlocks& Builder::makeNewLoop()
3678
{
3679
// This verbosity is needed to simultaneously get the same behavior
3680
// everywhere (id's in the same order), have a syntax that works
3681
// across lots of versions of C++, have no warnings from pedantic
3682
// compilation modes, and leave the rest of the code alone.
3683
Block& head = makeNewBlock();
3684
Block& body = makeNewBlock();
3685
Block& merge = makeNewBlock();
3686
Block& continue_target = makeNewBlock();
3687
LoopBlocks blocks(head, body, merge, continue_target);
3688
loops.push(blocks);
3689
return loops.top();
3690
}
3691
3692
void Builder::createLoopContinue()
3693
{
3694
createBranch(&loops.top().continue_target);
3695
// Set up a block for dead code.
3696
createAndSetNoPredecessorBlock("post-loop-continue");
3697
}
3698
3699
void Builder::createLoopExit()
3700
{
3701
createBranch(&loops.top().merge);
3702
// Set up a block for dead code.
3703
createAndSetNoPredecessorBlock("post-loop-break");
3704
}
3705
3706
void Builder::closeLoop()
3707
{
3708
loops.pop();
3709
}
3710
3711
void Builder::clearAccessChain()
3712
{
3713
accessChain.base = NoResult;
3714
accessChain.indexChain.clear();
3715
accessChain.instr = NoResult;
3716
accessChain.swizzle.clear();
3717
accessChain.component = NoResult;
3718
accessChain.preSwizzleBaseType = NoType;
3719
accessChain.isRValue = false;
3720
accessChain.coherentFlags.clear();
3721
accessChain.alignment = 0;
3722
}
3723
3724
// Comments in header
3725
void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
3726
AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
3727
{
3728
accessChain.coherentFlags |= coherentFlags;
3729
accessChain.alignment |= alignment;
3730
3731
// swizzles can be stacked in GLSL, but simplified to a single
3732
// one here; the base type doesn't change
3733
if (accessChain.preSwizzleBaseType == NoType)
3734
accessChain.preSwizzleBaseType = preSwizzleBaseType;
3735
3736
// if needed, propagate the swizzle for the current access chain
3737
if (accessChain.swizzle.size() > 0) {
3738
std::vector<unsigned> oldSwizzle = accessChain.swizzle;
3739
accessChain.swizzle.resize(0);
3740
for (unsigned int i = 0; i < swizzle.size(); ++i) {
3741
assert(swizzle[i] < oldSwizzle.size());
3742
accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
3743
}
3744
} else
3745
accessChain.swizzle = swizzle;
3746
3747
// determine if we need to track this swizzle anymore
3748
simplifyAccessChainSwizzle();
3749
}
3750
3751
// Comments in header
3752
void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
3753
{
3754
assert(accessChain.isRValue == false);
3755
3756
transferAccessChainSwizzle(true);
3757
3758
// If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.
3759
if (accessChain.swizzle.size() > 0 &&
3760
getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&
3761
accessChain.component == NoResult) {
3762
for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
3763
accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));
3764
accessChain.instr = NoResult;
3765
3766
Id base = collapseAccessChain();
3767
addDecoration(base, nonUniform);
3768
3769
accessChain.indexChain.pop_back();
3770
accessChain.instr = NoResult;
3771
3772
// dynamic component should be gone
3773
assert(accessChain.component == NoResult);
3774
3775
Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);
3776
3777
// take LSB of alignment
3778
alignment = alignment & ~(alignment & (alignment-1));
3779
if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
3780
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
3781
}
3782
3783
createStore(source, base, memoryAccess, scope, alignment);
3784
}
3785
}
3786
else {
3787
Id base = collapseAccessChain();
3788
addDecoration(base, nonUniform);
3789
3790
Id source = rvalue;
3791
3792
// dynamic component should be gone
3793
assert(accessChain.component == NoResult);
3794
3795
// If swizzle still exists, it may be out-of-order, we must load the target vector,
3796
// extract and insert elements to perform writeMask and/or swizzle.
3797
if (accessChain.swizzle.size() > 0) {
3798
Id tempBaseId = createLoad(base, spv::NoPrecision);
3799
source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
3800
}
3801
3802
// take LSB of alignment
3803
alignment = alignment & ~(alignment & (alignment-1));
3804
if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
3805
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
3806
}
3807
3808
createStore(source, base, memoryAccess, scope, alignment);
3809
}
3810
}
3811
3812
// Comments in header
3813
Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,
3814
Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,
3815
spv::Scope scope, unsigned int alignment)
3816
{
3817
Id id;
3818
3819
if (accessChain.isRValue) {
3820
// transfer access chain, but try to stay in registers
3821
transferAccessChainSwizzle(false);
3822
if (accessChain.indexChain.size() > 0) {
3823
Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
3824
3825
// if all the accesses are constants, we can use OpCompositeExtract
3826
std::vector<unsigned> indexes;
3827
bool constant = true;
3828
for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
3829
if (isConstantScalar(accessChain.indexChain[i]))
3830
indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
3831
else {
3832
constant = false;
3833
break;
3834
}
3835
}
3836
3837
if (constant) {
3838
id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
3839
setPrecision(id, precision);
3840
} else {
3841
Id lValue = NoResult;
3842
if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) {
3843
// make a new function variable for this r-value, using an initializer,
3844
// and mark it as NonWritable so that downstream it can be detected as a lookup
3845
// table
3846
lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
3847
"indexable", accessChain.base);
3848
addDecoration(lValue, DecorationNonWritable);
3849
} else {
3850
lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
3851
"indexable");
3852
// store into it
3853
createStore(accessChain.base, lValue);
3854
}
3855
// move base to the new variable
3856
accessChain.base = lValue;
3857
accessChain.isRValue = false;
3858
3859
// load through the access chain
3860
id = createLoad(collapseAccessChain(), precision);
3861
}
3862
} else
3863
id = accessChain.base; // no precision, it was set when this was defined
3864
} else {
3865
transferAccessChainSwizzle(true);
3866
3867
// take LSB of alignment
3868
alignment = alignment & ~(alignment & (alignment-1));
3869
if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) {
3870
memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
3871
}
3872
3873
// load through the access chain
3874
id = collapseAccessChain();
3875
// Apply nonuniform both to the access chain and the loaded value.
3876
// Buffer accesses need the access chain decorated, and this is where
3877
// loaded image types get decorated. TODO: This should maybe move to
3878
// createImageTextureFunctionCall.
3879
addDecoration(id, l_nonUniform);
3880
id = createLoad(id, precision, memoryAccess, scope, alignment);
3881
addDecoration(id, r_nonUniform);
3882
}
3883
3884
// Done, unless there are swizzles to do
3885
if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
3886
return id;
3887
3888
// Do remaining swizzling
3889
3890
// Do the basic swizzle
3891
if (accessChain.swizzle.size() > 0) {
3892
Id swizzledType = getScalarTypeId(getTypeId(id));
3893
if (accessChain.swizzle.size() > 1)
3894
swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
3895
id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
3896
}
3897
3898
// Do the dynamic component
3899
if (accessChain.component != NoResult)
3900
id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
3901
3902
addDecoration(id, r_nonUniform);
3903
return id;
3904
}
3905
3906
Id Builder::accessChainGetLValue()
3907
{
3908
assert(accessChain.isRValue == false);
3909
3910
transferAccessChainSwizzle(true);
3911
Id lvalue = collapseAccessChain();
3912
3913
// If swizzle exists, it is out-of-order or not full, we must load the target vector,
3914
// extract and insert elements to perform writeMask and/or swizzle. This does not
3915
// go with getting a direct l-value pointer.
3916
assert(accessChain.swizzle.size() == 0);
3917
assert(accessChain.component == NoResult);
3918
3919
return lvalue;
3920
}
3921
3922
// comment in header
3923
Id Builder::accessChainGetInferredType()
3924
{
3925
// anything to operate on?
3926
if (accessChain.base == NoResult)
3927
return NoType;
3928
Id type = getTypeId(accessChain.base);
3929
3930
// do initial dereference
3931
if (! accessChain.isRValue)
3932
type = getContainedTypeId(type);
3933
3934
// dereference each index
3935
for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
3936
if (isStructType(type))
3937
type = getContainedTypeId(type, getConstantScalar(*it));
3938
else
3939
type = getContainedTypeId(type);
3940
}
3941
3942
// dereference swizzle
3943
if (accessChain.swizzle.size() == 1)
3944
type = getContainedTypeId(type);
3945
else if (accessChain.swizzle.size() > 1)
3946
type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
3947
3948
// dereference component selection
3949
if (accessChain.component)
3950
type = getContainedTypeId(type);
3951
3952
return type;
3953
}
3954
3955
void Builder::dump(std::vector<unsigned int>& out) const
3956
{
3957
// Header, before first instructions:
3958
out.push_back(MagicNumber);
3959
out.push_back(spvVersion);
3960
out.push_back(builderNumber);
3961
out.push_back(uniqueId + 1);
3962
out.push_back(0);
3963
3964
// Capabilities
3965
for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
3966
Instruction capInst(0, 0, OpCapability);
3967
capInst.addImmediateOperand(*it);
3968
capInst.dump(out);
3969
}
3970
3971
for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
3972
Instruction extInst(0, 0, OpExtension);
3973
extInst.addStringOperand(it->c_str());
3974
extInst.dump(out);
3975
}
3976
3977
dumpInstructions(out, imports);
3978
Instruction memInst(0, 0, OpMemoryModel);
3979
memInst.addImmediateOperand(addressModel);
3980
memInst.addImmediateOperand(memoryModel);
3981
memInst.dump(out);
3982
3983
// Instructions saved up while building:
3984
dumpInstructions(out, entryPoints);
3985
dumpInstructions(out, executionModes);
3986
3987
// Debug instructions
3988
dumpInstructions(out, strings);
3989
dumpSourceInstructions(out);
3990
for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
3991
Instruction sourceExtInst(0, 0, OpSourceExtension);
3992
sourceExtInst.addStringOperand(sourceExtensions[e]);
3993
sourceExtInst.dump(out);
3994
}
3995
dumpInstructions(out, names);
3996
dumpModuleProcesses(out);
3997
3998
// Annotation instructions
3999
dumpInstructions(out, decorations);
4000
4001
dumpInstructions(out, constantsTypesGlobals);
4002
dumpInstructions(out, externals);
4003
4004
// The functions
4005
module.dump(out);
4006
}
4007
4008
//
4009
// Protected methods.
4010
//
4011
4012
// Turn the described access chain in 'accessChain' into an instruction(s)
4013
// computing its address. This *cannot* include complex swizzles, which must
4014
// be handled after this is called.
4015
//
4016
// Can generate code.
4017
Id Builder::collapseAccessChain()
4018
{
4019
assert(accessChain.isRValue == false);
4020
4021
// did we already emit an access chain for this?
4022
if (accessChain.instr != NoResult)
4023
return accessChain.instr;
4024
4025
// If we have a dynamic component, we can still transfer
4026
// that into a final operand to the access chain. We need to remap the
4027
// dynamic component through the swizzle to get a new dynamic component to
4028
// update.
4029
//
4030
// This was not done in transferAccessChainSwizzle() because it might
4031
// generate code.
4032
remapDynamicSwizzle();
4033
if (accessChain.component != NoResult) {
4034
// transfer the dynamic component to the access chain
4035
accessChain.indexChain.push_back(accessChain.component);
4036
accessChain.component = NoResult;
4037
}
4038
4039
// note that non-trivial swizzling is left pending
4040
4041
// do we have an access chain?
4042
if (accessChain.indexChain.size() == 0)
4043
return accessChain.base;
4044
4045
// emit the access chain
4046
StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
4047
accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
4048
4049
return accessChain.instr;
4050
}
4051
4052
// For a dynamic component selection of a swizzle.
4053
//
4054
// Turn the swizzle and dynamic component into just a dynamic component.
4055
//
4056
// Generates code.
4057
void Builder::remapDynamicSwizzle()
4058
{
4059
// do we have a swizzle to remap a dynamic component through?
4060
if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
4061
// build a vector of the swizzle for the component to map into
4062
std::vector<Id> components;
4063
for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
4064
components.push_back(makeUintConstant(accessChain.swizzle[c]));
4065
Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
4066
Id map = makeCompositeConstant(mapType, components);
4067
4068
// use it
4069
accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
4070
accessChain.swizzle.clear();
4071
}
4072
}
4073
4074
// clear out swizzle if it is redundant, that is reselecting the same components
4075
// that would be present without the swizzle.
4076
void Builder::simplifyAccessChainSwizzle()
4077
{
4078
// If the swizzle has fewer components than the vector, it is subsetting, and must stay
4079
// to preserve that fact.
4080
if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
4081
return;
4082
4083
// if components are out of order, it is a swizzle
4084
for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
4085
if (i != accessChain.swizzle[i])
4086
return;
4087
}
4088
4089
// otherwise, there is no need to track this swizzle
4090
accessChain.swizzle.clear();
4091
if (accessChain.component == NoResult)
4092
accessChain.preSwizzleBaseType = NoType;
4093
}
4094
4095
// To the extent any swizzling can become part of the chain
4096
// of accesses instead of a post operation, make it so.
4097
// If 'dynamic' is true, include transferring the dynamic component,
4098
// otherwise, leave it pending.
4099
//
4100
// Does not generate code. just updates the access chain.
4101
void Builder::transferAccessChainSwizzle(bool dynamic)
4102
{
4103
// non existent?
4104
if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
4105
return;
4106
4107
// too complex?
4108
// (this requires either a swizzle, or generating code for a dynamic component)
4109
if (accessChain.swizzle.size() > 1)
4110
return;
4111
4112
// single component, either in the swizzle and/or dynamic component
4113
if (accessChain.swizzle.size() == 1) {
4114
assert(accessChain.component == NoResult);
4115
// handle static component selection
4116
accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
4117
accessChain.swizzle.clear();
4118
accessChain.preSwizzleBaseType = NoType;
4119
} else if (dynamic && accessChain.component != NoResult) {
4120
assert(accessChain.swizzle.size() == 0);
4121
// handle dynamic component
4122
accessChain.indexChain.push_back(accessChain.component);
4123
accessChain.preSwizzleBaseType = NoType;
4124
accessChain.component = NoResult;
4125
}
4126
}
4127
4128
// Utility method for creating a new block and setting the insert point to
4129
// be in it. This is useful for flow-control operations that need a "dummy"
4130
// block proceeding them (e.g. instructions after a discard, etc).
4131
void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
4132
{
4133
Block* block = new Block(getUniqueId(), buildPoint->getParent());
4134
block->setUnreachable();
4135
buildPoint->getParent().addBlock(block);
4136
setBuildPoint(block);
4137
4138
// if (name)
4139
// addName(block->getId(), name);
4140
}
4141
4142
// Comments in header
4143
void Builder::createBranch(Block* block)
4144
{
4145
Instruction* branch = new Instruction(OpBranch);
4146
branch->addIdOperand(block->getId());
4147
addInstruction(std::unique_ptr<Instruction>(branch));
4148
block->addPredecessor(buildPoint);
4149
}
4150
4151
void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
4152
{
4153
Instruction* merge = new Instruction(OpSelectionMerge);
4154
merge->reserveOperands(2);
4155
merge->addIdOperand(mergeBlock->getId());
4156
merge->addImmediateOperand(control);
4157
addInstruction(std::unique_ptr<Instruction>(merge));
4158
}
4159
4160
void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
4161
const std::vector<unsigned int>& operands)
4162
{
4163
Instruction* merge = new Instruction(OpLoopMerge);
4164
merge->reserveOperands(operands.size() + 3);
4165
merge->addIdOperand(mergeBlock->getId());
4166
merge->addIdOperand(continueBlock->getId());
4167
merge->addImmediateOperand(control);
4168
for (int op = 0; op < (int)operands.size(); ++op)
4169
merge->addImmediateOperand(operands[op]);
4170
addInstruction(std::unique_ptr<Instruction>(merge));
4171
}
4172
4173
void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
4174
{
4175
Instruction* branch = new Instruction(OpBranchConditional);
4176
branch->reserveOperands(3);
4177
branch->addIdOperand(condition);
4178
branch->addIdOperand(thenBlock->getId());
4179
branch->addIdOperand(elseBlock->getId());
4180
addInstruction(std::unique_ptr<Instruction>(branch));
4181
thenBlock->addPredecessor(buildPoint);
4182
elseBlock->addPredecessor(buildPoint);
4183
}
4184
4185
// OpSource
4186
// [OpSourceContinued]
4187
// ...
4188
void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
4189
std::vector<unsigned int>& out) const
4190
{
4191
const int maxWordCount = 0xFFFF;
4192
const int opSourceWordCount = 4;
4193
const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
4194
4195
if (sourceLang != SourceLanguageUnknown) {
4196
// OpSource Language Version File Source
4197
Instruction sourceInst(NoResult, NoType, OpSource);
4198
sourceInst.reserveOperands(3);
4199
sourceInst.addImmediateOperand(sourceLang);
4200
sourceInst.addImmediateOperand(sourceVersion);
4201
// File operand
4202
if (fileId != NoResult) {
4203
sourceInst.addIdOperand(fileId);
4204
// Source operand
4205
if (text.size() > 0) {
4206
int nextByte = 0;
4207
std::string subString;
4208
while ((int)text.size() - nextByte > 0) {
4209
subString = text.substr(nextByte, nonNullBytesPerInstruction);
4210
if (nextByte == 0) {
4211
// OpSource
4212
sourceInst.addStringOperand(subString.c_str());
4213
sourceInst.dump(out);
4214
} else {
4215
// OpSourcContinued
4216
Instruction sourceContinuedInst(OpSourceContinued);
4217
sourceContinuedInst.addStringOperand(subString.c_str());
4218
sourceContinuedInst.dump(out);
4219
}
4220
nextByte += nonNullBytesPerInstruction;
4221
}
4222
} else
4223
sourceInst.dump(out);
4224
} else
4225
sourceInst.dump(out);
4226
}
4227
}
4228
4229
// Dump an OpSource[Continued] sequence for the source and every include file
4230
void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
4231
{
4232
if (emitNonSemanticShaderDebugInfo) return;
4233
dumpSourceInstructions(mainFileId, sourceText, out);
4234
for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
4235
dumpSourceInstructions(iItr->first, *iItr->second, out);
4236
}
4237
4238
void Builder::dumpInstructions(std::vector<unsigned int>& out,
4239
const std::vector<std::unique_ptr<Instruction> >& instructions) const
4240
{
4241
for (int i = 0; i < (int)instructions.size(); ++i) {
4242
instructions[i]->dump(out);
4243
}
4244
}
4245
4246
void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
4247
{
4248
for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
4249
Instruction moduleProcessed(OpModuleProcessed);
4250
moduleProcessed.addStringOperand(moduleProcesses[i]);
4251
moduleProcessed.dump(out);
4252
}
4253
}
4254
4255
} // end spv namespace
4256
4257