yum-mirror/glslang

git clone https://git.yummers.dev/yum-mirror/glslang

Saddamfix oob read on empty entryPoints in postProcessFeatures3c14556a

main
28.6 KiB646 linesraw
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
51namespace 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
61namespace 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.
66void 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 = StorageClass::Max;
87            if (module.getInstruction(inst.getIdOperand(0))->getOpCode() != Op::OpUntypedAccessChainKHR) {
88                storageClass = getStorageClass(inst.getIdOperand(0));
89            }
90            if (width == 8) {
91                switch (storageClass) {
92                case StorageClass::PhysicalStorageBufferEXT:
93                case StorageClass::Uniform:
94                case StorageClass::StorageBuffer:
95                case StorageClass::PushConstant:
96                    break;
97                default:
98                    addCapability(Capability::Int8);
99                    break;
100                }
101            } else if (width == 16) {
102                switch (storageClass) {
103                case StorageClass::PhysicalStorageBufferEXT:
104                case StorageClass::Uniform:
105                case StorageClass::StorageBuffer:
106                case StorageClass::PushConstant:
107                case StorageClass::Input:
108                case StorageClass::Output:
109                    break;
110                default:
111                    if (basicTypeOp == Op::OpTypeInt)
112                        addCapability(Capability::Int16);
113                    if (basicTypeOp == Op::OpTypeFloat)
114                        addCapability(Capability::Float16);
115                    break;
116                }
117            }
118        }
119        break;
120    case Op::OpCopyObject:
121        break;
122    case Op::OpFConvert:
123    case Op::OpSConvert:
124    case Op::OpUConvert:
125        // Look for any 8/16-bit storage capabilities. If there are none, assume that
126        // the convert instruction requires the Float16/Int8/16 capability.
127        if (containsType(typeId, Op::OpTypeFloat, 16) || containsType(typeId, Op::OpTypeInt, 16)) {
128            bool foundStorage = false;
129            for (auto it = capabilities.begin(); it != capabilities.end(); ++it) {
130                spv::Capability cap = *it;
131                if (cap == spv::Capability::StorageInputOutput16 ||
132                    cap == spv::Capability::StoragePushConstant16 ||
133                    cap == spv::Capability::StorageUniformBufferBlock16 ||
134                    cap == spv::Capability::StorageUniform16) {
135                    foundStorage = true;
136                    break;
137                }
138            }
139            if (!foundStorage) {
140                if (containsType(typeId, Op::OpTypeFloat, 16))
141                    addCapability(Capability::Float16);
142                if (containsType(typeId, Op::OpTypeInt, 16))
143                    addCapability(Capability::Int16);
144            }
145        }
146        if (containsType(typeId, Op::OpTypeInt, 8)) {
147            bool foundStorage = false;
148            for (auto it = capabilities.begin(); it != capabilities.end(); ++it) {
149                spv::Capability cap = *it;
150                if (cap == spv::Capability::StoragePushConstant8 ||
151                    cap == spv::Capability::UniformAndStorageBuffer8BitAccess ||
152                    cap == spv::Capability::StorageBuffer8BitAccess) {
153                    foundStorage = true;
154                    break;
155                }
156            }
157            if (!foundStorage) {
158                addCapability(Capability::Int8);
159            }
160        }
161        break;
162    case Op::OpExtInst:
163        switch (inst.getImmediateOperand(1)) {
164        case GLSLstd450Frexp:
165        case GLSLstd450FrexpStruct:
166            if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, Op::OpTypeInt, 16))
167                addExtension(spv::E_SPV_AMD_gpu_shader_int16);
168            break;
169        case GLSLstd450InterpolateAtCentroid:
170        case GLSLstd450InterpolateAtSample:
171        case GLSLstd450InterpolateAtOffset:
172            if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, Op::OpTypeFloat, 16))
173                addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
174            break;
175        default:
176            break;
177        }
178        if (basicTypeOp == Op::OpTypeInt) {
179            if (width == 16)
180                addCapability(Capability::Int16);
181            else if (width == 8)
182                addCapability(Capability::Int8);
183        } else if (basicTypeOp == Op::OpTypeFloat) {
184            if (width == 16)
185                addCapability(Capability::Float16);
186        }
187        break;
188    case Op::OpAccessChain:
189    case Op::OpPtrAccessChain:
190        if (isPointerType(typeId))
191            break;
192        if (basicTypeOp == Op::OpTypeInt) {
193            if (width == 16)
194                addCapability(Capability::Int16);
195            else if (width == 8)
196                addCapability(Capability::Int8);
197        }
198        break;
199    default:
200        if (basicTypeOp == Op::OpTypeInt) {
201            if (width == 16)
202                addCapability(Capability::Int16);
203            else if (width == 8)
204                addCapability(Capability::Int8);
205            else if (width == 64)
206                addCapability(Capability::Int64);
207        } else if (basicTypeOp == Op::OpTypeFloat) {
208            if (width == 16)
209                addCapability(Capability::Float16);
210            else if (width == 64)
211                addCapability(Capability::Float64);
212        }
213        break;
214    }
215}
216
217unsigned int Builder::postProcessGetLargestScalarSize(const Instruction& type)
218{
219    switch (type.getOpCode()) {
220    case Op::OpTypeBool:
221        return 1;
222    case Op::OpTypeInt:
223    case Op::OpTypeFloat:
224        return type.getImmediateOperand(0) / 8;
225    case Op::OpTypePointer:
226        return 8;
227    case Op::OpTypeVector:
228    case Op::OpTypeMatrix:
229    case Op::OpTypeArray:
230    case Op::OpTypeRuntimeArray: {
231        const Instruction* elem_type = module.getInstruction(type.getIdOperand(0));
232        return postProcessGetLargestScalarSize(*elem_type);
233    }
234    case Op::OpTypeStruct: {
235        unsigned int largest = 0;
236        for (int i = 0; i < type.getNumOperands(); ++i) {
237            const Instruction* elem_type = module.getInstruction(type.getIdOperand(i));
238            unsigned int elem_size = postProcessGetLargestScalarSize(*elem_type);
239            largest = std::max(largest, elem_size);
240        }
241        return largest;
242    }
243    default:
244        return 0;
245    }
246}
247
248// Called for each instruction that resides in a block.
249void Builder::postProcess(Instruction& inst)
250{
251    // Add capabilities based simply on the opcode.
252    switch (inst.getOpCode()) {
253    case Op::OpExtInst:
254        switch (inst.getImmediateOperand(1)) {
255        case GLSLstd450InterpolateAtCentroid:
256        case GLSLstd450InterpolateAtSample:
257        case GLSLstd450InterpolateAtOffset:
258            addCapability(Capability::InterpolationFunction);
259            break;
260        default:
261            break;
262        }
263        break;
264    case Op::OpDPdxFine:
265    case Op::OpDPdyFine:
266    case Op::OpFwidthFine:
267    case Op::OpDPdxCoarse:
268    case Op::OpDPdyCoarse:
269    case Op::OpFwidthCoarse:
270        addCapability(Capability::DerivativeControl);
271        break;
272
273    case Op::OpImageQueryLod:
274    case Op::OpImageQuerySize:
275    case Op::OpImageQuerySizeLod:
276    case Op::OpImageQuerySamples:
277    case Op::OpImageQueryLevels:
278        addCapability(Capability::ImageQuery);
279        break;
280
281    case Op::OpGroupNonUniformPartitionNV:
282        addExtension(E_SPV_NV_shader_subgroup_partitioned);
283        addCapability(Capability::GroupNonUniformPartitionedNV);
284        break;
285
286    case Op::OpLoad:
287    case Op::OpStore:
288        {
289            // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain
290            // index list to compute the misalignment. The pre-existing alignment value
291            // (set via Builder::AccessChain::alignment) only accounts for the base of
292            // the reference type and any scalar component selection in the accesschain,
293            // and this function computes the rest from the SPIR-V Offset decorations.
294            Instruction *accessChain = module.getInstruction(inst.getIdOperand(0));
295            if (accessChain->getOpCode() == Op::OpAccessChain) {
296                const Instruction* base = module.getInstruction(accessChain->getIdOperand(0));
297                // Get the type of the base of the access chain. It must be a pointer type.
298                Id typeId = base->getTypeId();
299                Instruction *type = module.getInstruction(typeId);
300                assert(type->getOpCode() == Op::OpTypePointer);
301                if (type->getImmediateOperand(0) != StorageClass::PhysicalStorageBuffer) {
302                    break;
303                }
304                // Get the pointee type.
305                typeId = type->getIdOperand(1);
306                type = module.getInstruction(typeId);
307                // Walk the index list for the access chain. For each index, find any
308                // misalignment that can apply when accessing the member/element via
309                // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all
310                // together.
311                int alignment = 0;
312                bool first_struct_elem = false;
313                for (int i = 1; i < accessChain->getNumOperands(); ++i) {
314                    Instruction *idx = module.getInstruction(accessChain->getIdOperand(i));
315                    if (type->getOpCode() == Op::OpTypeStruct) {
316                        assert(idx->getOpCode() == Op::OpConstant);
317                        unsigned int c = idx->getImmediateOperand(0);
318
319                        const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
320                            if (decoration.get()->getOpCode() == Op::OpMemberDecorate &&
321                                decoration.get()->getIdOperand(0) == typeId &&
322                                decoration.get()->getImmediateOperand(1) == c &&
323                                (decoration.get()->getImmediateOperand(2) == Decoration::Offset ||
324                                 decoration.get()->getImmediateOperand(2) == Decoration::MatrixStride)) {
325                                unsigned int opernad_value = decoration.get()->getImmediateOperand(3);
326                                alignment |= opernad_value;
327                                if (opernad_value == 0 &&
328                                    decoration.get()->getImmediateOperand(2) == Decoration::Offset) {
329                                    first_struct_elem = true;
330                                }
331                            }
332                        };
333                        std::for_each(decorations.begin(), decorations.end(), function);
334                        // get the next member type
335                        typeId = type->getIdOperand(c);
336                        type = module.getInstruction(typeId);
337                    } else if (type->getOpCode() == Op::OpTypeArray ||
338                               type->getOpCode() == Op::OpTypeRuntimeArray) {
339                        const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
340                            if (decoration.get()->getOpCode() == Op::OpDecorate &&
341                                decoration.get()->getIdOperand(0) == typeId &&
342                                decoration.get()->getImmediateOperand(1) == Decoration::ArrayStride) {
343                                alignment |= decoration.get()->getImmediateOperand(2);
344                            }
345                        };
346                        std::for_each(decorations.begin(), decorations.end(), function);
347                        // Get the element type
348                        typeId = type->getIdOperand(0);
349                        type = module.getInstruction(typeId);
350                    } else {
351                        // Once we get to any non-aggregate type, we're done.
352                        break;
353                    }
354                }
355                assert(inst.getNumOperands() >= 3);
356                const bool is_store = inst.getOpCode() == Op::OpStore;
357                auto const memoryAccess = (MemoryAccessMask)inst.getImmediateOperand(is_store ? 2 : 1);
358                assert(anySet(memoryAccess, MemoryAccessMask::Aligned));
359                static_cast<void>(memoryAccess);
360
361                // Compute the index of the alignment operand.
362                int alignmentIdx = 2;
363                if (is_store)
364                    alignmentIdx++;
365                // Merge new and old (mis)alignment
366                alignment |= inst.getImmediateOperand(alignmentIdx);
367
368                Instruction* inst_type = nullptr;
369                if (is_store) {
370                    // For a store, we are storing through a pointer, so what this ends up looking like when storing a BDA pointer is we have a "OpTypePointer PhysicalStorageBuffer" pointing at the actual "OpTypePointer PhysicalStorageBuffer" value storing
371                    // https://godbolt.org/z/58ff4P3sG
372                    inst_type = module.getInstruction(accessChain->getTypeId());
373                    if (inst_type->getOpCode() == Op::OpTypePointer &&
374                        inst_type->getImmediateOperand(0) == StorageClass::PhysicalStorageBuffer) {
375                        inst_type = module.getInstruction(inst_type->getIdOperand(1));
376                    }
377                } else {
378                    inst_type = module.getInstruction(inst.getTypeId());
379                }
380
381                if (inst_type->getOpCode() == Op::OpTypePointer &&
382                    inst_type->getImmediateOperand(0) == StorageClass::PhysicalStorageBuffer) {
383                    // This means we are loading a pointer which means need to ensure it is at least 8-byte aligned
384                    // See https://github.com/KhronosGroup/glslang/issues/4084
385                    // In case the alignment is currently 4, need to ensure it is 8 before grabbing the LSB
386                    alignment |= 8;
387                    alignment &= 8;
388                }
389
390                // Pick the LSB
391                alignment = alignment & ~(alignment & (alignment-1));
392
393                // The edge case we find is when copying a struct to another struct, we never find the alignment anywhere,
394                // so in this case, fallback to doing a full size lookup on the type
395                if (alignment == 0 && first_struct_elem) {
396                    // Quick get the struct type back
397                    const Instruction* pointer_type = module.getInstruction(base->getTypeId());
398                    const Instruction* struct_type = module.getInstruction(pointer_type->getIdOperand(1));
399                    assert(struct_type->getOpCode() == Op::OpTypeStruct);
400
401                    const Instruction* elem_type = module.getInstruction(struct_type->getIdOperand(0));
402                    unsigned int largest_scalar = postProcessGetLargestScalarSize(*elem_type);
403                    if (largest_scalar != 0) {
404                        alignment = largest_scalar;
405                    } else {
406                        alignment = 16; // fallback if can't determine a godo alignment
407                    }
408                }
409                // update the Aligned operand
410                assert(alignment != 0);
411                inst.setImmediateOperand(alignmentIdx, alignment);
412            }
413            break;
414        }
415
416    default:
417        break;
418    }
419
420    // Checks based on type
421    if (inst.getTypeId() != NoType)
422        postProcessType(inst, inst.getTypeId());
423    for (int op = 0; op < inst.getNumOperands(); ++op) {
424        if (inst.isIdOperand(op)) {
425            // In blocks, these are always result ids, but we are relying on
426            // getTypeId() to return NoType for things like OpLabel.
427            if (getTypeId(inst.getIdOperand(op)) != NoType)
428                postProcessType(inst, getTypeId(inst.getIdOperand(op)));
429        }
430    }
431}
432
433// comment in header
434void Builder::postProcessCFG()
435{
436    // reachableBlocks is the set of blockss reached via control flow, or which are
437    // unreachable continue targert or unreachable merge.
438    std::unordered_set<const Block*> reachableBlocks;
439    std::unordered_map<Block*, Block*> headerForUnreachableContinue;
440    std::unordered_set<Block*> unreachableMerges;
441    std::unordered_set<Id> unreachableDefinitions;
442    // Collect IDs defined in unreachable blocks. For each function, label the
443    // reachable blocks first. Then for each unreachable block, collect the
444    // result IDs of the instructions in it.
445    for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
446        Function* f = *fi;
447        Block* entry = f->getEntryBlock();
448        inReadableOrder(entry,
449            [&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue]
450            (Block* b, ReachReason why, Block* header) {
451               reachableBlocks.insert(b);
452               if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header;
453               if (why == ReachDeadMerge) unreachableMerges.insert(b);
454            });
455        for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
456            Block* b = *bi;
457            if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) {
458                auto ii = b->getInstructions().cbegin();
459                ++ii; // Keep potential decorations on the label.
460                for (; ii != b->getInstructions().cend(); ++ii)
461                    unreachableDefinitions.insert(ii->get()->getResultId());
462            } else if (reachableBlocks.count(b) == 0) {
463                // The normal case for unreachable code.  All definitions are considered dead.
464                for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii)
465                    unreachableDefinitions.insert(ii->get()->getResultId());
466            }
467        }
468    }
469
470    // Modify unreachable merge blocks and unreachable continue targets.
471    // Delete their contents.
472    for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) {
473        (*mergeIter)->rewriteAsCanonicalUnreachableMerge();
474    }
475    for (auto continueIter = headerForUnreachableContinue.begin();
476         continueIter != headerForUnreachableContinue.end();
477         ++continueIter) {
478        Block* continue_target = continueIter->first;
479        Block* header = continueIter->second;
480        continue_target->rewriteAsCanonicalUnreachableContinue(header);
481    }
482
483    // Remove unneeded decorations, for unreachable instructions
484    for (auto decorationIter = decorations.begin(); decorationIter != decorations.end();) {
485        Id decorationId = (*decorationIter)->getIdOperand(0);
486        if (unreachableDefinitions.count(decorationId) != 0) {
487            decorationIter = decorations.erase(decorationIter);
488        } else {
489            ++decorationIter;
490        }
491    }
492}
493
494// comment in header
495void Builder::postProcessFeatures() {
496    // Add per-instruction capabilities, extensions, etc.,
497
498    // Look for any 8/16 bit type in physical storage buffer class, and set the
499    // appropriate capability. This happens in createSpvVariable for other storage
500    // classes, but there isn't always a variable for physical storage buffer.
501    for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) {
502        Instruction* type = groupedTypes[enumCast(Op::OpTypePointer)][t];
503        if (type->getImmediateOperand(0) == (unsigned)StorageClass::PhysicalStorageBufferEXT) {
504            if (containsType(type->getIdOperand(1), Op::OpTypeInt, 8)) {
505                addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5);
506                addCapability(spv::Capability::StorageBuffer8BitAccess);
507            }
508            if (containsType(type->getIdOperand(1), Op::OpTypeInt, 16) ||
509                containsType(type->getIdOperand(1), Op::OpTypeFloat, 16)) {
510                addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3);
511                addCapability(spv::Capability::StorageBuffer16BitAccess);
512            }
513        }
514    }
515
516    // process all block-contained instructions
517    for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
518        Function* f = *fi;
519        for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
520            Block* b = *bi;
521            for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
522                postProcess(*ii->get());
523
524            // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether
525            // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the
526            // default.
527            for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) {
528                const Instruction& inst = *vi->get();
529                Id resultId = inst.getResultId();
530                if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) {
531                    bool foundDecoration = false;
532                    const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
533                        if (decoration.get()->getIdOperand(0) == resultId &&
534                            decoration.get()->getOpCode() == Op::OpDecorate &&
535                            (decoration.get()->getImmediateOperand(1) == spv::Decoration::AliasedPointerEXT ||
536                             decoration.get()->getImmediateOperand(1) == spv::Decoration::RestrictPointerEXT)) {
537                            foundDecoration = true;
538                        }
539                    };
540                    std::for_each(decorations.begin(), decorations.end(), function);
541                    if (!foundDecoration) {
542                        addDecoration(resultId, spv::Decoration::AliasedPointerEXT);
543                    }
544                }
545            }
546        }
547    }
548
549    // If any Vulkan memory model-specific functionality is used, update the
550    // OpMemoryModel to match.
551    if (capabilities.find(spv::Capability::VulkanMemoryModelKHR) != capabilities.end()) {
552        memoryModel = spv::MemoryModel::VulkanKHR;
553        addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5);
554    }
555
556    // Add Aliased decoration if there's more than one Workgroup Block variable.
557    // Compile-only mode emits no entry point, so there is no interface to scan.
558    if (capabilities.find(spv::Capability::WorkgroupMemoryExplicitLayoutKHR) != capabilities.end() &&
559        !entryPoints.empty()) {
560        auto &ep = entryPoints[0];
561
562        std::vector<Id> workgroup_variables;
563        for (int i = 0; i < (int)ep->getNumOperands(); i++) {
564            if (!ep->isIdOperand(i))
565                continue;
566
567            const Id id = ep->getIdOperand(i);
568            const Instruction *instr = module.getInstruction(id);
569            if (instr->getOpCode() != spv::Op::OpVariable)
570                continue;
571
572            if (instr->getImmediateOperand(0) == spv::StorageClass::Workgroup)
573                workgroup_variables.push_back(id);
574        }
575
576        if (workgroup_variables.size() > 1) {
577            for (size_t i = 0; i < workgroup_variables.size(); i++)
578                addDecoration(workgroup_variables[i], spv::Decoration::Aliased);
579        }
580    }
581}
582
583// SPIR-V requires that any instruction consuming the result of an OpSampledImage
584// be in the same block as the OpSampledImage instruction. This pass goes finds
585// uses of OpSampledImage where that is not the case and duplicates the
586// OpSampledImage to be immediately before the instruction that consumes it.
587// The old OpSampledImage is left in place, potentially with no users.
588void Builder::postProcessSamplers()
589{
590    // first, find all OpSampledImage instructions and store them in a map.
591    std::map<Id, Instruction*> sampledImageInstrs;
592    for (auto f: module.getFunctions()) {
593	for (auto b: f->getBlocks()) {
594	    for (auto &i: b->getInstructions()) {
595        if (i->getOpCode() == spv::Op::OpSampledImage) {
596		    sampledImageInstrs[i->getResultId()] = i.get();
597		}
598	    }
599	}
600    }
601    // next find all uses of the given ids and rewrite them if needed.
602    for (auto f: module.getFunctions()) {
603	for (auto b: f->getBlocks()) {
604            auto &instrs = b->getInstructions();
605            for (size_t idx = 0; idx < instrs.size(); idx++) {
606                Instruction *i = instrs[idx].get();
607                for (int opnum = 0; opnum < i->getNumOperands(); opnum++) {
608                    // Is this operand of the current instruction the result of an OpSampledImage?
609                    if (i->isIdOperand(opnum) &&
610                        sampledImageInstrs.count(i->getIdOperand(opnum)))
611                    {
612                        Instruction *opSampImg = sampledImageInstrs[i->getIdOperand(opnum)];
613                        if (i->getBlock() != opSampImg->getBlock()) {
614                            Instruction *newInstr = new Instruction(getUniqueId(),
615                                                                    opSampImg->getTypeId(),
616                                                                    spv::Op::OpSampledImage);
617                            newInstr->addIdOperand(opSampImg->getIdOperand(0));
618                            newInstr->addIdOperand(opSampImg->getIdOperand(1));
619                            newInstr->setBlock(b);
620
621                            // rewrite the user of the OpSampledImage to use the new instruction.
622                            i->setIdOperand(opnum, newInstr->getResultId());
623                            // insert the new OpSampledImage right before the current instruction.
624                            instrs.insert(instrs.begin() + idx,
625                                    std::unique_ptr<Instruction>(newInstr));
626                            idx++;
627                        }
628                    }
629                }
630            }
631	}
632    }
633}
634
635// comment in header
636void Builder::postProcess(bool compileOnly)
637{
638    // postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint
639    if (!compileOnly)
640        postProcessCFG();
641
642    postProcessFeatures();
643    postProcessSamplers();
644}
645
646} // end spv namespace