Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/glslang/SPIRV/SpvPostProcess.cpp
21011 views
1
//
2
// Copyright (C) 2018 Google, Inc.
3
//
4
// All rights reserved.
5
//
6
// Redistribution and use in source and binary forms, with or without
7
// modification, are permitted provided that the following conditions
8
// are met:
9
//
10
// Redistributions of source code must retain the above copyright
11
// notice, this list of conditions and the following disclaimer.
12
//
13
// Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following
15
// disclaimer in the documentation and/or other materials provided
16
// with the distribution.
17
//
18
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19
// contributors may be used to endorse or promote products derived
20
// from this software without specific prior written permission.
21
//
22
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
// POSSIBILITY OF SUCH DAMAGE.
34
35
//
36
// Post-processing for SPIR-V IR, in internal form, not standard binary form.
37
//
38
39
#include <cassert>
40
#include <cstdlib>
41
42
#include <unordered_map>
43
#include <unordered_set>
44
#include <algorithm>
45
46
#include "SPIRV/spvIR.h"
47
#include "SpvBuilder.h"
48
#include "spirv.hpp11"
49
#include "spvUtil.h"
50
51
namespace spv {
52
#include "GLSL.std.450.h"
53
#include "GLSL.ext.KHR.h"
54
#include "GLSL.ext.EXT.h"
55
#include "GLSL.ext.AMD.h"
56
#include "GLSL.ext.NV.h"
57
#include "GLSL.ext.ARM.h"
58
#include "GLSL.ext.QCOM.h"
59
}
60
61
namespace spv {
62
63
// Hook to visit each operand type and result type of an instruction.
64
// Will be called multiple times for one instruction, once for each typed
65
// operand and the result.
66
void Builder::postProcessType(const Instruction& inst, Id typeId)
67
{
68
// Characterize the type being questioned
69
Op basicTypeOp = getMostBasicTypeClass(typeId);
70
int width = 0;
71
if (basicTypeOp == Op::OpTypeFloat || basicTypeOp == Op::OpTypeInt)
72
width = getScalarTypeWidth(typeId);
73
74
// Do opcode-specific checks
75
switch (inst.getOpCode()) {
76
case Op::OpLoad:
77
case Op::OpStore:
78
if (basicTypeOp == Op::OpTypeStruct) {
79
if (containsType(typeId, Op::OpTypeInt, 8))
80
addCapability(Capability::Int8);
81
if (containsType(typeId, Op::OpTypeInt, 16))
82
addCapability(Capability::Int16);
83
if (containsType(typeId, Op::OpTypeFloat, 16))
84
addCapability(Capability::Float16);
85
} else {
86
StorageClass storageClass = getStorageClass(inst.getIdOperand(0));
87
if (width == 8) {
88
switch (storageClass) {
89
case StorageClass::PhysicalStorageBufferEXT:
90
case StorageClass::Uniform:
91
case StorageClass::StorageBuffer:
92
case StorageClass::PushConstant:
93
break;
94
default:
95
addCapability(Capability::Int8);
96
break;
97
}
98
} else if (width == 16) {
99
switch (storageClass) {
100
case StorageClass::PhysicalStorageBufferEXT:
101
case StorageClass::Uniform:
102
case StorageClass::StorageBuffer:
103
case StorageClass::PushConstant:
104
case StorageClass::Input:
105
case StorageClass::Output:
106
break;
107
default:
108
if (basicTypeOp == Op::OpTypeInt)
109
addCapability(Capability::Int16);
110
if (basicTypeOp == Op::OpTypeFloat)
111
addCapability(Capability::Float16);
112
break;
113
}
114
}
115
}
116
break;
117
case Op::OpCopyObject:
118
break;
119
case Op::OpFConvert:
120
case Op::OpSConvert:
121
case Op::OpUConvert:
122
// Look for any 8/16-bit storage capabilities. If there are none, assume that
123
// the convert instruction requires the Float16/Int8/16 capability.
124
if (containsType(typeId, Op::OpTypeFloat, 16) || containsType(typeId, Op::OpTypeInt, 16)) {
125
bool foundStorage = false;
126
for (auto it = capabilities.begin(); it != capabilities.end(); ++it) {
127
spv::Capability cap = *it;
128
if (cap == spv::Capability::StorageInputOutput16 ||
129
cap == spv::Capability::StoragePushConstant16 ||
130
cap == spv::Capability::StorageUniformBufferBlock16 ||
131
cap == spv::Capability::StorageUniform16) {
132
foundStorage = true;
133
break;
134
}
135
}
136
if (!foundStorage) {
137
if (containsType(typeId, Op::OpTypeFloat, 16))
138
addCapability(Capability::Float16);
139
if (containsType(typeId, Op::OpTypeInt, 16))
140
addCapability(Capability::Int16);
141
}
142
}
143
if (containsType(typeId, Op::OpTypeInt, 8)) {
144
bool foundStorage = false;
145
for (auto it = capabilities.begin(); it != capabilities.end(); ++it) {
146
spv::Capability cap = *it;
147
if (cap == spv::Capability::StoragePushConstant8 ||
148
cap == spv::Capability::UniformAndStorageBuffer8BitAccess ||
149
cap == spv::Capability::StorageBuffer8BitAccess) {
150
foundStorage = true;
151
break;
152
}
153
}
154
if (!foundStorage) {
155
addCapability(Capability::Int8);
156
}
157
}
158
break;
159
case Op::OpExtInst:
160
switch (inst.getImmediateOperand(1)) {
161
case GLSLstd450Frexp:
162
case GLSLstd450FrexpStruct:
163
if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, Op::OpTypeInt, 16))
164
addExtension(spv::E_SPV_AMD_gpu_shader_int16);
165
break;
166
case GLSLstd450InterpolateAtCentroid:
167
case GLSLstd450InterpolateAtSample:
168
case GLSLstd450InterpolateAtOffset:
169
if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, Op::OpTypeFloat, 16))
170
addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
171
break;
172
default:
173
break;
174
}
175
break;
176
case Op::OpAccessChain:
177
case Op::OpPtrAccessChain:
178
if (isPointerType(typeId))
179
break;
180
if (basicTypeOp == Op::OpTypeInt) {
181
if (width == 16)
182
addCapability(Capability::Int16);
183
else if (width == 8)
184
addCapability(Capability::Int8);
185
}
186
break;
187
default:
188
if (basicTypeOp == Op::OpTypeInt) {
189
if (width == 16)
190
addCapability(Capability::Int16);
191
else if (width == 8)
192
addCapability(Capability::Int8);
193
else if (width == 64)
194
addCapability(Capability::Int64);
195
} else if (basicTypeOp == Op::OpTypeFloat) {
196
if (width == 16)
197
addCapability(Capability::Float16);
198
else if (width == 64)
199
addCapability(Capability::Float64);
200
}
201
break;
202
}
203
}
204
205
unsigned int Builder::postProcessGetLargestScalarSize(const Instruction& type)
206
{
207
switch (type.getOpCode()) {
208
case Op::OpTypeBool:
209
return 1;
210
case Op::OpTypeInt:
211
case Op::OpTypeFloat:
212
return type.getImmediateOperand(0) / 8;
213
case Op::OpTypePointer:
214
return 8;
215
case Op::OpTypeVector:
216
case Op::OpTypeMatrix:
217
case Op::OpTypeArray:
218
case Op::OpTypeRuntimeArray: {
219
const Instruction* elem_type = module.getInstruction(type.getIdOperand(0));
220
return postProcessGetLargestScalarSize(*elem_type);
221
}
222
case Op::OpTypeStruct: {
223
unsigned int largest = 0;
224
for (int i = 0; i < type.getNumOperands(); ++i) {
225
const Instruction* elem_type = module.getInstruction(type.getIdOperand(i));
226
unsigned int elem_size = postProcessGetLargestScalarSize(*elem_type);
227
largest = std::max(largest, elem_size);
228
}
229
return largest;
230
}
231
default:
232
return 0;
233
}
234
}
235
236
// Called for each instruction that resides in a block.
237
void Builder::postProcess(Instruction& inst)
238
{
239
// Add capabilities based simply on the opcode.
240
switch (inst.getOpCode()) {
241
case Op::OpExtInst:
242
switch (inst.getImmediateOperand(1)) {
243
case GLSLstd450InterpolateAtCentroid:
244
case GLSLstd450InterpolateAtSample:
245
case GLSLstd450InterpolateAtOffset:
246
addCapability(Capability::InterpolationFunction);
247
break;
248
default:
249
break;
250
}
251
break;
252
case Op::OpDPdxFine:
253
case Op::OpDPdyFine:
254
case Op::OpFwidthFine:
255
case Op::OpDPdxCoarse:
256
case Op::OpDPdyCoarse:
257
case Op::OpFwidthCoarse:
258
addCapability(Capability::DerivativeControl);
259
break;
260
261
case Op::OpImageQueryLod:
262
case Op::OpImageQuerySize:
263
case Op::OpImageQuerySizeLod:
264
case Op::OpImageQuerySamples:
265
case Op::OpImageQueryLevels:
266
addCapability(Capability::ImageQuery);
267
break;
268
269
case Op::OpGroupNonUniformPartitionNV:
270
addExtension(E_SPV_NV_shader_subgroup_partitioned);
271
addCapability(Capability::GroupNonUniformPartitionedNV);
272
break;
273
274
case Op::OpLoad:
275
case Op::OpStore:
276
{
277
// For any load/store to a PhysicalStorageBufferEXT, walk the accesschain
278
// index list to compute the misalignment. The pre-existing alignment value
279
// (set via Builder::AccessChain::alignment) only accounts for the base of
280
// the reference type and any scalar component selection in the accesschain,
281
// and this function computes the rest from the SPIR-V Offset decorations.
282
Instruction *accessChain = module.getInstruction(inst.getIdOperand(0));
283
if (accessChain->getOpCode() == Op::OpAccessChain) {
284
const Instruction* base = module.getInstruction(accessChain->getIdOperand(0));
285
// Get the type of the base of the access chain. It must be a pointer type.
286
Id typeId = base->getTypeId();
287
Instruction *type = module.getInstruction(typeId);
288
assert(type->getOpCode() == Op::OpTypePointer);
289
if (type->getImmediateOperand(0) != StorageClass::PhysicalStorageBuffer) {
290
break;
291
}
292
// Get the pointee type.
293
typeId = type->getIdOperand(1);
294
type = module.getInstruction(typeId);
295
// Walk the index list for the access chain. For each index, find any
296
// misalignment that can apply when accessing the member/element via
297
// Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all
298
// together.
299
int alignment = 0;
300
bool first_struct_elem = false;
301
for (int i = 1; i < accessChain->getNumOperands(); ++i) {
302
Instruction *idx = module.getInstruction(accessChain->getIdOperand(i));
303
if (type->getOpCode() == Op::OpTypeStruct) {
304
assert(idx->getOpCode() == Op::OpConstant);
305
unsigned int c = idx->getImmediateOperand(0);
306
307
const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
308
if (decoration.get()->getOpCode() == Op::OpMemberDecorate &&
309
decoration.get()->getIdOperand(0) == typeId &&
310
decoration.get()->getImmediateOperand(1) == c &&
311
(decoration.get()->getImmediateOperand(2) == Decoration::Offset ||
312
decoration.get()->getImmediateOperand(2) == Decoration::MatrixStride)) {
313
unsigned int opernad_value = decoration.get()->getImmediateOperand(3);
314
alignment |= opernad_value;
315
if (opernad_value == 0 &&
316
decoration.get()->getImmediateOperand(2) == Decoration::Offset) {
317
first_struct_elem = true;
318
}
319
}
320
};
321
std::for_each(decorations.begin(), decorations.end(), function);
322
// get the next member type
323
typeId = type->getIdOperand(c);
324
type = module.getInstruction(typeId);
325
} else if (type->getOpCode() == Op::OpTypeArray ||
326
type->getOpCode() == Op::OpTypeRuntimeArray) {
327
const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
328
if (decoration.get()->getOpCode() == Op::OpDecorate &&
329
decoration.get()->getIdOperand(0) == typeId &&
330
decoration.get()->getImmediateOperand(1) == Decoration::ArrayStride) {
331
alignment |= decoration.get()->getImmediateOperand(2);
332
}
333
};
334
std::for_each(decorations.begin(), decorations.end(), function);
335
// Get the element type
336
typeId = type->getIdOperand(0);
337
type = module.getInstruction(typeId);
338
} else {
339
// Once we get to any non-aggregate type, we're done.
340
break;
341
}
342
}
343
assert(inst.getNumOperands() >= 3);
344
const bool is_store = inst.getOpCode() == Op::OpStore;
345
auto const memoryAccess = (MemoryAccessMask)inst.getImmediateOperand(is_store ? 2 : 1);
346
assert(anySet(memoryAccess, MemoryAccessMask::Aligned));
347
static_cast<void>(memoryAccess);
348
349
// Compute the index of the alignment operand.
350
int alignmentIdx = 2;
351
if (is_store)
352
alignmentIdx++;
353
// Merge new and old (mis)alignment
354
alignment |= inst.getImmediateOperand(alignmentIdx);
355
356
if (!is_store) {
357
Instruction* inst_type = module.getInstruction(inst.getTypeId());
358
if (inst_type->getOpCode() == Op::OpTypePointer &&
359
inst_type->getImmediateOperand(0) == StorageClass::PhysicalStorageBuffer) {
360
// This means we are loading a pointer which means need to ensure it is at least 8-byte aligned
361
// See https://github.com/KhronosGroup/glslang/issues/4084
362
// In case the alignment is currently 4, need to ensure it is 8 before grabbing the LSB
363
alignment |= 8;
364
alignment &= 8;
365
}
366
}
367
368
// Pick the LSB
369
alignment = alignment & ~(alignment & (alignment-1));
370
371
// The edge case we find is when copying a struct to another struct, we never find the alignment anywhere,
372
// so in this case, fallback to doing a full size lookup on the type
373
if (alignment == 0 && first_struct_elem) {
374
// Quick get the struct type back
375
const Instruction* pointer_type = module.getInstruction(base->getTypeId());
376
const Instruction* struct_type = module.getInstruction(pointer_type->getIdOperand(1));
377
assert(struct_type->getOpCode() == Op::OpTypeStruct);
378
379
const Instruction* elem_type = module.getInstruction(struct_type->getIdOperand(0));
380
unsigned int largest_scalar = postProcessGetLargestScalarSize(*elem_type);
381
if (largest_scalar != 0) {
382
alignment = largest_scalar;
383
} else {
384
alignment = 16; // fallback if can't determine a godo alignment
385
}
386
}
387
// update the Aligned operand
388
assert(alignment != 0);
389
inst.setImmediateOperand(alignmentIdx, alignment);
390
}
391
break;
392
}
393
394
default:
395
break;
396
}
397
398
// Checks based on type
399
if (inst.getTypeId() != NoType)
400
postProcessType(inst, inst.getTypeId());
401
for (int op = 0; op < inst.getNumOperands(); ++op) {
402
if (inst.isIdOperand(op)) {
403
// In blocks, these are always result ids, but we are relying on
404
// getTypeId() to return NoType for things like OpLabel.
405
if (getTypeId(inst.getIdOperand(op)) != NoType)
406
postProcessType(inst, getTypeId(inst.getIdOperand(op)));
407
}
408
}
409
}
410
411
// comment in header
412
void Builder::postProcessCFG()
413
{
414
// reachableBlocks is the set of blockss reached via control flow, or which are
415
// unreachable continue targert or unreachable merge.
416
std::unordered_set<const Block*> reachableBlocks;
417
std::unordered_map<Block*, Block*> headerForUnreachableContinue;
418
std::unordered_set<Block*> unreachableMerges;
419
std::unordered_set<Id> unreachableDefinitions;
420
// Collect IDs defined in unreachable blocks. For each function, label the
421
// reachable blocks first. Then for each unreachable block, collect the
422
// result IDs of the instructions in it.
423
for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
424
Function* f = *fi;
425
Block* entry = f->getEntryBlock();
426
inReadableOrder(entry,
427
[&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue]
428
(Block* b, ReachReason why, Block* header) {
429
reachableBlocks.insert(b);
430
if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header;
431
if (why == ReachDeadMerge) unreachableMerges.insert(b);
432
});
433
for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
434
Block* b = *bi;
435
if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) {
436
auto ii = b->getInstructions().cbegin();
437
++ii; // Keep potential decorations on the label.
438
for (; ii != b->getInstructions().cend(); ++ii)
439
unreachableDefinitions.insert(ii->get()->getResultId());
440
} else if (reachableBlocks.count(b) == 0) {
441
// The normal case for unreachable code. All definitions are considered dead.
442
for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii)
443
unreachableDefinitions.insert(ii->get()->getResultId());
444
}
445
}
446
}
447
448
// Modify unreachable merge blocks and unreachable continue targets.
449
// Delete their contents.
450
for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) {
451
(*mergeIter)->rewriteAsCanonicalUnreachableMerge();
452
}
453
for (auto continueIter = headerForUnreachableContinue.begin();
454
continueIter != headerForUnreachableContinue.end();
455
++continueIter) {
456
Block* continue_target = continueIter->first;
457
Block* header = continueIter->second;
458
continue_target->rewriteAsCanonicalUnreachableContinue(header);
459
}
460
461
// Remove unneeded decorations, for unreachable instructions
462
for (auto decorationIter = decorations.begin(); decorationIter != decorations.end();) {
463
Id decorationId = (*decorationIter)->getIdOperand(0);
464
if (unreachableDefinitions.count(decorationId) != 0) {
465
decorationIter = decorations.erase(decorationIter);
466
} else {
467
++decorationIter;
468
}
469
}
470
}
471
472
// comment in header
473
void Builder::postProcessFeatures() {
474
// Add per-instruction capabilities, extensions, etc.,
475
476
// Look for any 8/16 bit type in physical storage buffer class, and set the
477
// appropriate capability. This happens in createSpvVariable for other storage
478
// classes, but there isn't always a variable for physical storage buffer.
479
for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) {
480
Instruction* type = groupedTypes[enumCast(Op::OpTypePointer)][t];
481
if (type->getImmediateOperand(0) == (unsigned)StorageClass::PhysicalStorageBufferEXT) {
482
if (containsType(type->getIdOperand(1), Op::OpTypeInt, 8)) {
483
addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5);
484
addCapability(spv::Capability::StorageBuffer8BitAccess);
485
}
486
if (containsType(type->getIdOperand(1), Op::OpTypeInt, 16) ||
487
containsType(type->getIdOperand(1), Op::OpTypeFloat, 16)) {
488
addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3);
489
addCapability(spv::Capability::StorageBuffer16BitAccess);
490
}
491
}
492
}
493
494
// process all block-contained instructions
495
for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
496
Function* f = *fi;
497
for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
498
Block* b = *bi;
499
for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
500
postProcess(*ii->get());
501
502
// For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether
503
// there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the
504
// default.
505
for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) {
506
const Instruction& inst = *vi->get();
507
Id resultId = inst.getResultId();
508
if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) {
509
bool foundDecoration = false;
510
const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
511
if (decoration.get()->getIdOperand(0) == resultId &&
512
decoration.get()->getOpCode() == Op::OpDecorate &&
513
(decoration.get()->getImmediateOperand(1) == spv::Decoration::AliasedPointerEXT ||
514
decoration.get()->getImmediateOperand(1) == spv::Decoration::RestrictPointerEXT)) {
515
foundDecoration = true;
516
}
517
};
518
std::for_each(decorations.begin(), decorations.end(), function);
519
if (!foundDecoration) {
520
addDecoration(resultId, spv::Decoration::AliasedPointerEXT);
521
}
522
}
523
}
524
}
525
}
526
527
// If any Vulkan memory model-specific functionality is used, update the
528
// OpMemoryModel to match.
529
if (capabilities.find(spv::Capability::VulkanMemoryModelKHR) != capabilities.end()) {
530
memoryModel = spv::MemoryModel::VulkanKHR;
531
addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5);
532
}
533
534
// Add Aliased decoration if there's more than one Workgroup Block variable.
535
if (capabilities.find(spv::Capability::WorkgroupMemoryExplicitLayoutKHR) != capabilities.end()) {
536
assert(entryPoints.size() == 1);
537
auto &ep = entryPoints[0];
538
539
std::vector<Id> workgroup_variables;
540
for (int i = 0; i < (int)ep->getNumOperands(); i++) {
541
if (!ep->isIdOperand(i))
542
continue;
543
544
const Id id = ep->getIdOperand(i);
545
const Instruction *instr = module.getInstruction(id);
546
if (instr->getOpCode() != spv::Op::OpVariable)
547
continue;
548
549
if (instr->getImmediateOperand(0) == spv::StorageClass::Workgroup)
550
workgroup_variables.push_back(id);
551
}
552
553
if (workgroup_variables.size() > 1) {
554
for (size_t i = 0; i < workgroup_variables.size(); i++)
555
addDecoration(workgroup_variables[i], spv::Decoration::Aliased);
556
}
557
}
558
}
559
560
// SPIR-V requires that any instruction consuming the result of an OpSampledImage
561
// be in the same block as the OpSampledImage instruction. This pass goes finds
562
// uses of OpSampledImage where that is not the case and duplicates the
563
// OpSampledImage to be immediately before the instruction that consumes it.
564
// The old OpSampledImage is left in place, potentially with no users.
565
void Builder::postProcessSamplers()
566
{
567
// first, find all OpSampledImage instructions and store them in a map.
568
std::map<Id, Instruction*> sampledImageInstrs;
569
for (auto f: module.getFunctions()) {
570
for (auto b: f->getBlocks()) {
571
for (auto &i: b->getInstructions()) {
572
if (i->getOpCode() == spv::Op::OpSampledImage) {
573
sampledImageInstrs[i->getResultId()] = i.get();
574
}
575
}
576
}
577
}
578
// next find all uses of the given ids and rewrite them if needed.
579
for (auto f: module.getFunctions()) {
580
for (auto b: f->getBlocks()) {
581
auto &instrs = b->getInstructions();
582
for (size_t idx = 0; idx < instrs.size(); idx++) {
583
Instruction *i = instrs[idx].get();
584
for (int opnum = 0; opnum < i->getNumOperands(); opnum++) {
585
// Is this operand of the current instruction the result of an OpSampledImage?
586
if (i->isIdOperand(opnum) &&
587
sampledImageInstrs.count(i->getIdOperand(opnum)))
588
{
589
Instruction *opSampImg = sampledImageInstrs[i->getIdOperand(opnum)];
590
if (i->getBlock() != opSampImg->getBlock()) {
591
Instruction *newInstr = new Instruction(getUniqueId(),
592
opSampImg->getTypeId(),
593
spv::Op::OpSampledImage);
594
newInstr->addIdOperand(opSampImg->getIdOperand(0));
595
newInstr->addIdOperand(opSampImg->getIdOperand(1));
596
newInstr->setBlock(b);
597
598
// rewrite the user of the OpSampledImage to use the new instruction.
599
i->setIdOperand(opnum, newInstr->getResultId());
600
// insert the new OpSampledImage right before the current instruction.
601
instrs.insert(instrs.begin() + idx,
602
std::unique_ptr<Instruction>(newInstr));
603
idx++;
604
}
605
}
606
}
607
}
608
}
609
}
610
}
611
612
// comment in header
613
void Builder::postProcess(bool compileOnly)
614
{
615
// postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint
616
if (!compileOnly)
617
postProcessCFG();
618
619
postProcessFeatures();
620
postProcessSamplers();
621
}
622
623
} // end spv namespace
624
625