Coverage Report

Created: 2026-09-03 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/SPIRV/spvIR.h
Line
Count
Source
1
//
2
// Copyright (C) 2014 LunarG, Inc.
3
// Copyright (C) 2015-2018 Google, Inc.
4
//
5
// All rights reserved.
6
//
7
// Redistribution and use in source and binary forms, with or without
8
// modification, are permitted provided that the following conditions
9
// are met:
10
//
11
//    Redistributions of source code must retain the above copyright
12
//    notice, this list of conditions and the following disclaimer.
13
//
14
//    Redistributions in binary form must reproduce the above
15
//    copyright notice, this list of conditions and the following
16
//    disclaimer in the documentation and/or other materials provided
17
//    with the distribution.
18
//
19
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20
//    contributors may be used to endorse or promote products derived
21
//    from this software without specific prior written permission.
22
//
23
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
// POSSIBILITY OF SUCH DAMAGE.
35
36
// SPIRV-IR
37
//
38
// Simple in-memory representation (IR) of SPIRV.  Just for holding
39
// Each function's CFG of blocks.  Has this hierarchy:
40
//  - Module, which is a list of
41
//    - Function, which is a list of
42
//      - Block, which is a list of
43
//        - Instruction
44
//
45
46
#pragma once
47
#ifndef spvIR_H
48
#define spvIR_H
49
50
#include "spirv.hpp11"
51
52
#include <algorithm>
53
#include <cassert>
54
#include <functional>
55
#include <iostream>
56
#include <memory>
57
#include <vector>
58
#include <set>
59
#include <optional>
60
61
namespace spv {
62
63
class Block;
64
class Function;
65
class Module;
66
67
const Id NoResult = 0;
68
const Id NoType = 0;
69
70
const Decoration NoPrecision = Decoration::Max;
71
72
#ifdef __GNUC__
73
#   define POTENTIALLY_UNUSED __attribute__((unused))
74
#else
75
#   define POTENTIALLY_UNUSED
76
#endif
77
78
POTENTIALLY_UNUSED
79
const MemorySemanticsMask MemorySemanticsAllMemory =
80
                (MemorySemanticsMask)(MemorySemanticsMask::UniformMemory |
81
                                      MemorySemanticsMask::WorkgroupMemory |
82
                                      MemorySemanticsMask::AtomicCounterMemory | 
83
                                      MemorySemanticsMask::ImageMemory);
84
85
struct IdImmediate {
86
    bool isId;      // true if word is an Id, false if word is an immediate
87
    unsigned word;
88
9.64k
    IdImmediate(bool i, unsigned w) : isId(i), word(w) {}
89
0
    IdImmediate(bool i, spv::MemoryAccessMask w) : isId(i), word((unsigned)w) {}
90
0
    IdImmediate(bool i, spv::TensorAddressingOperandsMask w) : isId(i), word((unsigned)w) {}
91
6
    IdImmediate(bool i, spv::ImageOperandsMask w) : isId(i), word((unsigned)w) {}
92
0
    IdImmediate(bool i, spv::CooperativeMatrixOperandsMask w) : isId(i), word((unsigned)w) {}
93
};
94
95
//
96
// SPIR-V IR instruction.
97
//
98
99
class Instruction {
100
public:
101
92.5k
    Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
102
39.3k
    explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
103
131k
    virtual ~Instruction() {}
104
80.2k
    void reserveOperands(size_t count) {
105
80.2k
        operands.reserve(count);
106
80.2k
        idOperand.reserve(count);
107
80.2k
    }
108
165k
    void addIdOperand(Id id) {
109
        // ids can't be 0
110
165k
        assert(id);
111
165k
        operands.push_back(id);
112
165k
        idOperand.push_back(true);
113
165k
    }
114
    // This method is potentially dangerous as it can break assumptions
115
    // about SSA and lack of forward references.
116
0
    void setIdOperand(unsigned idx, Id id) {
117
0
        assert(id);
118
0
        assert(idOperand[idx]);
119
0
        operands[idx] = id;
120
0
    }
121
122
112k
    void addImmediateOperand(unsigned int immediate) {
123
112k
        operands.push_back(immediate);
124
112k
        idOperand.push_back(false);
125
112k
    }
126
127
10.3k
    void addImmediateOperand(spv::StorageClass immediate) {
128
10.3k
        addImmediateOperand((unsigned)immediate);
129
10.3k
    }
130
131
31
    void addImmediateOperand(spv::ExecutionMode immediate) {
132
31
        addImmediateOperand((unsigned)immediate);
133
31
    }
134
135
445
    void addImmediateOperand(spv::ExecutionModel immediate) {
136
445
        addImmediateOperand((unsigned)immediate);
137
445
    }
138
139
9.83k
    void addImmediateOperand(spv::Decoration immediate) {
140
9.83k
        addImmediateOperand((unsigned)immediate);
141
9.83k
    }
142
143
0
    void addImmediateOperand(spv::LinkageType immediate) {
144
0
        addImmediateOperand((unsigned)immediate);
145
0
    }
146
147
72
    void addImmediateOperand(spv::MemoryAccessMask immediate) {
148
72
        addImmediateOperand((unsigned)immediate);
149
72
    }
150
151
1.45k
    void addImmediateOperand(spv::Capability immediate) {
152
1.45k
        addImmediateOperand((unsigned)immediate);
153
1.45k
    }
154
155
445
    void addImmediateOperand(spv::AddressingModel immediate) {
156
445
        addImmediateOperand((unsigned)immediate);
157
445
    }
158
159
445
    void addImmediateOperand(spv::MemoryModel immediate) {
160
445
        addImmediateOperand((unsigned)immediate);
161
445
    }
162
163
0
    void addImmediateOperand(spv::FPEncoding immediate) {
164
0
        addImmediateOperand((unsigned)immediate);
165
0
    }
166
167
445
    void addImmediateOperand(spv::SourceLanguage immediate) {
168
445
        addImmediateOperand((unsigned)immediate);
169
445
    }
170
171
943
    void addImmediateOperand(spv::Dim immediate) {
172
943
        addImmediateOperand((unsigned)immediate);
173
943
    }
174
175
908
    void addImmediateOperand(spv::FunctionControlMask immediate){
176
908
        addImmediateOperand((unsigned)immediate);
177
908
    }
178
179
1.44k
    void addImmediateOperand(spv::SelectionControlMask immediate) {
180
1.44k
        addImmediateOperand((unsigned)immediate);
181
1.44k
    }
182
183
336
    void addImmediateOperand(spv::LoopControlMask immediate) {
184
336
        addImmediateOperand((unsigned)immediate);
185
336
    }
186
187
980
    void setImmediateOperand(unsigned idx, unsigned int immediate) {
188
980
        assert(!idOperand[idx]);
189
980
        operands[idx] = immediate;
190
980
    }
191
192
0
    void clearOperands() {
193
0
        operands.clear();
194
0
        idOperand.clear();
195
0
    }
196
197
    void addStringOperand(const char* str)
198
14.7k
    {
199
14.7k
        unsigned int word = 0;
200
14.7k
        unsigned int shiftAmount = 0;
201
14.7k
        unsigned char c;
202
203
175k
        do {
204
175k
            c = *(str++);
205
175k
            word |= ((unsigned int)c) << shiftAmount;
206
175k
            shiftAmount += 8;
207
175k
            if (shiftAmount == 32) {
208
38.2k
                addImmediateOperand(word);
209
38.2k
                word = 0;
210
38.2k
                shiftAmount = 0;
211
38.2k
            }
212
175k
        } while (c != 0);
213
214
        // deal with partial last word
215
14.7k
        if (shiftAmount > 0) {
216
11.8k
            addImmediateOperand(word);
217
11.8k
        }
218
14.7k
    }
219
320k
    bool isIdOperand(int op) const { return idOperand[op]; }
220
79.2k
    void setBlock(Block* b) { block = b; }
221
4.73k
    Block* getBlock() const { return block; }
222
1.59M
    Op getOpCode() const { return opCode; }
223
    int getNumOperands() const
224
518k
    {
225
518k
        assert(operands.size() == idOperand.size());
226
518k
        return (int)operands.size();
227
518k
    }
228
561k
    Id getResultId() const { return resultId; }
229
824k
    Id getTypeId() const { return typeId; }
230
0
    void setTypeId(Id tId) { typeId = tId; }
231
1.40M
    Id getIdOperand(int op) const {
232
1.40M
        assert(idOperand[op]);
233
1.40M
        return operands[op];
234
1.40M
    }
235
798k
    unsigned int getImmediateOperand(int op) const {
236
798k
        assert(!idOperand[op]);
237
798k
        return operands[op];
238
798k
    }
239
240
    // Write out the binary form.
241
    void dump(std::vector<unsigned int>& out) const
242
129k
    {
243
        // Compute the wordCount
244
129k
        unsigned int wordCount = 1;
245
129k
        if (typeId)
246
66.2k
            ++wordCount;
247
129k
        if (resultId)
248
84.2k
            ++wordCount;
249
129k
        wordCount += (unsigned int)operands.size();
250
251
        // Write out the beginning of the instruction
252
129k
        out.push_back(((wordCount) << WordCountShift) | (unsigned)opCode);
253
129k
        if (typeId)
254
66.2k
            out.push_back(typeId);
255
129k
        if (resultId)
256
84.2k
            out.push_back(resultId);
257
258
        // Write out the operands
259
404k
        for (int op = 0; op < (int)operands.size(); ++op)
260
275k
            out.push_back(operands[op]);
261
129k
    }
262
263
0
    const char *getNameString() const {
264
0
        if (opCode == Op::OpString) {
265
0
            return (const char *)&operands[0];
266
0
        } else {
267
0
            assert(opCode == Op::OpName);
268
0
            return (const char *)&operands[1];
269
0
        }
270
0
    }
271
272
protected:
273
    Instruction(const Instruction&);
274
    Id resultId;
275
    Id typeId;
276
    Op opCode;
277
    std::vector<Id> operands;     // operands, both <id> and immediates (both are unsigned int)
278
    std::vector<bool> idOperand;  // true for operands that are <id>, false for immediates
279
    Block* block;
280
};
281
282
//
283
// SPIR-V IR block.
284
//
285
286
struct DebugSourceLocation {
287
    int line;
288
    int column;
289
    spv::Id fileId;
290
};
291
292
class Block {
293
public:
294
    Block(Id id, Function& parent);
295
    virtual ~Block()
296
6.57k
    {
297
6.57k
    }
298
299
9.46k
    Id getId() { return instructions.front()->getResultId(); }
300
301
13.7k
    Function& getParent() const { return parent; }
302
    // Returns true if the source location is actually updated.
303
    // Note we still need the builder to insert the line marker instruction. This is just a tracker.
304
0
    bool updateDebugSourceLocation(int line, int column, spv::Id fileId) {
305
0
        if (currentSourceLoc && currentSourceLoc->line == line && currentSourceLoc->column == column &&
306
0
            currentSourceLoc->fileId == fileId) {
307
0
            return false;
308
0
        }
309
310
0
        currentSourceLoc = DebugSourceLocation{line, column, fileId};
311
0
        return true;
312
0
    }
313
    // Returns true if the scope is actually updated.
314
    // Note we still need the builder to insert the debug scope instruction. This is just a tracker.
315
0
    bool updateDebugScope(spv::Id scopeId) {
316
0
        assert(scopeId);
317
0
        if (currentDebugScope && *currentDebugScope == scopeId) {
318
0
            return false;
319
0
        }
320
321
0
        currentDebugScope = scopeId;
322
0
        return true;
323
0
    }
324
    void addInstruction(std::unique_ptr<Instruction> inst);
325
7.16k
    void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
326
3.12k
    void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
327
0
    const std::vector<Block*>& getPredecessors() const { return predecessors; }
328
11.3k
    const std::vector<Block*>& getSuccessors() const { return successors; }
329
109k
    std::vector<std::unique_ptr<Instruction> >& getInstructions() {
330
109k
        return instructions;
331
109k
    }
332
16.2k
    const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
333
907
    void setUnreachable() { unreachable = true; }
334
0
    bool isUnreachable() const { return unreachable; }
335
    // Returns the block's merge instruction, if one exists (otherwise null).
336
11.3k
    const Instruction* getMergeInstruction() const {
337
11.3k
        if (instructions.size() < 2) return nullptr;
338
11.3k
        const Instruction* nextToLast = (instructions.cend() - 2)->get();
339
11.3k
        switch (nextToLast->getOpCode()) {
340
2.88k
            case Op::OpSelectionMerge:
341
3.56k
            case Op::OpLoopMerge:
342
3.56k
                return nextToLast;
343
7.77k
            default:
344
7.77k
                return nullptr;
345
11.3k
        }
346
0
        return nullptr;
347
11.3k
    }
348
349
    // Change this block into a canonical dead merge block.  Delete instructions
350
    // as necessary.  A canonical dead merge block has only an OpLabel and an
351
    // OpUnreachable.
352
18
    void rewriteAsCanonicalUnreachableMerge() {
353
18
        assert(localVariables.empty());
354
        // Delete all instructions except for the label.
355
18
        assert(instructions.size() > 0);
356
18
        instructions.resize(1);
357
18
        successors.clear();
358
18
        addInstruction(std::unique_ptr<Instruction>(new Instruction(Op::OpUnreachable)));
359
18
    }
360
    // Change this block into a canonical dead continue target branching to the
361
    // given header ID.  Delete instructions as necessary.  A canonical dead continue
362
    // target has only an OpLabel and an unconditional branch back to the corresponding
363
    // header.
364
0
    void rewriteAsCanonicalUnreachableContinue(Block* header) {
365
0
        assert(localVariables.empty());
366
        // Delete all instructions except for the label.
367
0
        assert(instructions.size() > 0);
368
0
        instructions.resize(1);
369
0
        successors.clear();
370
        // Add OpBranch back to the header.
371
0
        assert(header != nullptr);
372
0
        Instruction* branch = new Instruction(Op::OpBranch);
373
0
        branch->addIdOperand(header->getId());
374
0
        addInstruction(std::unique_ptr<Instruction>(branch));
375
0
        successors.push_back(header);
376
0
    }
377
378
    bool isTerminated() const
379
1.31k
    {
380
1.31k
        switch (instructions.back()->getOpCode()) {
381
0
        case Op::OpBranch:
382
0
        case Op::OpBranchConditional:
383
0
        case Op::OpSwitch:
384
0
        case Op::OpKill:
385
0
        case Op::OpTerminateInvocation:
386
0
        case Op::OpReturn:
387
0
        case Op::OpReturnValue:
388
0
        case Op::OpUnreachable:
389
0
        case Op::OpAbortKHR:
390
0
            return true;
391
1.31k
        default:
392
1.31k
            return false;
393
1.31k
        }
394
1.31k
    }
395
396
    void dump(std::vector<unsigned int>& out) const
397
5.66k
    {
398
5.66k
        instructions[0]->dump(out);
399
8.79k
        for (int i = 0; i < (int)localVariables.size(); ++i)
400
3.12k
            localVariables[i]->dump(out);
401
77.2k
        for (int i = 1; i < (int)instructions.size(); ++i)
402
71.5k
            instructions[i]->dump(out);
403
5.66k
    }
404
405
protected:
406
    Block(const Block&);
407
    Block& operator=(Block&);
408
409
    // To enforce keeping parent and ownership in sync:
410
    friend Function;
411
412
    std::vector<std::unique_ptr<Instruction> > instructions;
413
    std::vector<Block*> predecessors, successors;
414
    std::vector<std::unique_ptr<Instruction> > localVariables;
415
    Function& parent;
416
417
    // Track source location of the last source location marker instruction.
418
    std::optional<DebugSourceLocation> currentSourceLoc;
419
420
    // Track scope of the last debug scope instruction.
421
    std::optional<spv::Id> currentDebugScope;
422
423
    // track whether this block is known to be uncreachable (not necessarily
424
    // true for all unreachable blocks, but should be set at least
425
    // for the extraneous ones introduced by the builder).
426
    bool unreachable;
427
};
428
429
// The different reasons for reaching a block in the inReadableOrder traversal.
430
enum ReachReason {
431
    // Reachable from the entry block via transfers of control, i.e. branches.
432
    ReachViaControlFlow = 0,
433
    // A continue target that is not reachable via control flow.
434
    ReachDeadContinue,
435
    // A merge block that is not reachable via control flow.
436
    ReachDeadMerge
437
};
438
439
// Traverses the control-flow graph rooted at root in an order suited for
440
// readable code generation.  Invokes callback at every node in the traversal
441
// order.  The callback arguments are:
442
// - the block,
443
// - the reason we reached the block,
444
// - if the reason was that block is an unreachable continue or unreachable merge block
445
//   then the last parameter is the corresponding header block.
446
void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block* header)> callback);
447
448
//
449
// SPIR-V IR Function.
450
//
451
452
class Function {
453
public:
454
    Function(Id id, Id resultType, Id functionType, Id firstParam, LinkageType linkage, const std::string& name, Module& parent);
455
    virtual ~Function()
456
908
    {
457
1.31k
        for (int i = 0; i < (int)parameterInstructions.size(); ++i)
458
408
            delete parameterInstructions[i];
459
460
7.48k
        for (int i = 0; i < (int)blocks.size(); ++i)
461
6.57k
            delete blocks[i];
462
908
    }
463
3.73k
    Id getId() const { return functionInstruction.getResultId(); }
464
816
    Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
465
520
    Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
466
467
6.57k
    void addBlock(Block* block) { blocks.push_back(block); }
468
    void removeBlock(Block* block)
469
0
    {
470
0
        auto found = find(blocks.begin(), blocks.end(), block);
471
0
        assert(found != blocks.end());
472
0
        blocks.erase(found);
473
0
        delete block;
474
0
    }
475
476
64.3k
    Module& getParent() const { return parent; }
477
1.37k
    Block* getEntryBlock() const { return blocks.front(); }
478
890
    Block* getLastBlock() const { return blocks.back(); }
479
18.6k
    const std::vector<Block*>& getBlocks() const { return blocks; }
480
    void addLocalVariable(std::unique_ptr<Instruction> inst);
481
2.97k
    Id getReturnType() const { return functionInstruction.getTypeId(); }
482
0
    Id getFuncId() const { return functionInstruction.getResultId(); }
483
0
    Id getFuncTypeId() const { return functionInstruction.getIdOperand(1); }
484
    void setFunctionControl(FunctionControlMask functionControl)
485
908
    {
486
908
        functionInstruction.setImmediateOperand(0, static_cast<unsigned>(functionControl));
487
908
    }
488
    void setReturnPrecision(Decoration precision)
489
908
    {
490
908
        if (precision == Decoration::RelaxedPrecision)
491
0
            reducedPrecisionReturn = true;
492
908
    }
493
    Decoration getReturnPrecision() const
494
312
        { return reducedPrecisionReturn ? Decoration::RelaxedPrecision : NoPrecision; }
495
496
0
    void setDebugLineInfo(Id fileName, int line, int column) {
497
0
        lineInstruction = std::unique_ptr<Instruction>{new Instruction(Op::OpLine)};
498
0
        lineInstruction->reserveOperands(3);
499
0
        lineInstruction->addIdOperand(fileName);
500
0
        lineInstruction->addImmediateOperand(line);
501
0
        lineInstruction->addImmediateOperand(column);
502
0
    }
503
0
    bool hasDebugLineInfo() const { return lineInstruction != nullptr; }
504
505
0
    void setImplicitThis() { implicitThis = true; }
506
1.56k
    bool hasImplicitThis() const { return implicitThis; }
507
508
    void addParamPrecision(unsigned param, Decoration precision)
509
0
    {
510
0
        if (precision == Decoration::RelaxedPrecision)
511
0
            reducedPrecisionParams.insert(param);
512
0
    }
513
    Decoration getParamPrecision(unsigned param) const
514
520
    {
515
520
        return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ?
516
520
            Decoration::RelaxedPrecision : NoPrecision;
517
520
    }
518
519
    void dump(std::vector<unsigned int>& out) const
520
908
    {
521
        // OpLine
522
908
        if (lineInstruction != nullptr) {
523
0
            lineInstruction->dump(out);
524
0
        }
525
526
        // OpFunction
527
908
        functionInstruction.dump(out);
528
529
        // OpFunctionParameter
530
1.31k
        for (int p = 0; p < (int)parameterInstructions.size(); ++p)
531
408
            parameterInstructions[p]->dump(out);
532
533
        // Blocks
534
5.66k
        inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); });
535
908
        Instruction end(0, 0, Op::OpFunctionEnd);
536
908
        end.dump(out);
537
908
    }
538
539
908
    LinkageType getLinkType() const { return linkType; }
540
0
    const char* getExportName() const { return exportName.c_str(); }
541
542
protected:
543
    Function(const Function&);
544
    Function& operator=(Function&);
545
546
    Module& parent;
547
    std::unique_ptr<Instruction> lineInstruction;
548
    Instruction functionInstruction;
549
    std::vector<Instruction*> parameterInstructions;
550
    std::vector<Block*> blocks;
551
    bool implicitThis;  // true if this is a member function expecting to be passed a 'this' as the first argument
552
    bool reducedPrecisionReturn;
553
    std::set<int> reducedPrecisionParams;  // list of parameter indexes that need a relaxed precision arg
554
    LinkageType linkType;
555
    std::string exportName;
556
};
557
558
//
559
// SPIR-V IR Module.
560
//
561
562
class Module {
563
public:
564
445
    Module() {}
565
    virtual ~Module()
566
445
    {
567
        // TODO delete things
568
445
    }
569
570
908
    void addFunction(Function *fun) { functions.push_back(fun); }
571
572
    void mapInstruction(Instruction *instruction)
573
85.4k
    {
574
85.4k
        spv::Id resultId = instruction->getResultId();
575
        // map the instruction's result id
576
85.4k
        if (resultId >= idToInstruction.size())
577
5.57k
            idToInstruction.resize(resultId + 16);
578
85.4k
        idToInstruction[resultId] = instruction;
579
85.4k
    }
580
581
1.35M
    Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
582
4.48k
    const std::vector<Function*>& getFunctions() const { return functions; }
583
674k
    spv::Id getTypeId(Id resultId) const {
584
674k
        return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
585
674k
    }
586
    StorageClass getStorageClass(Id typeId) const
587
167k
    {
588
167k
        assert(idToInstruction[typeId]->getOpCode() == spv::Op::OpTypePointer ||
589
167k
               idToInstruction[typeId]->getOpCode() == spv::Op::OpTypeUntypedPointerKHR);
590
167k
        return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
591
167k
    }
592
593
    void dump(std::vector<unsigned int>& out) const
594
445
    {
595
1.35k
        for (int f = 0; f < (int)functions.size(); ++f)
596
908
            functions[f]->dump(out);
597
445
    }
598
599
protected:
600
    Module(const Module&);
601
    std::vector<Function*> functions;
602
603
    // map from result id to instruction having that result id
604
    std::vector<Instruction*> idToInstruction;
605
606
    // map from a result id to its type id
607
};
608
609
//
610
// Implementation (it's here due to circular type definitions).
611
//
612
613
// Add both
614
// - the OpFunction instruction
615
// - all the OpFunctionParameter instructions
616
__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, LinkageType linkage, const std::string& name, Module& parent)
617
908
    : parent(parent), lineInstruction(nullptr),
618
908
      functionInstruction(id, resultType, Op::OpFunction), implicitThis(false),
619
908
      reducedPrecisionReturn(false),
620
908
      linkType(linkage)
621
908
{
622
    // OpFunction
623
908
    functionInstruction.reserveOperands(2);
624
908
    functionInstruction.addImmediateOperand(FunctionControlMask::MaskNone);
625
908
    functionInstruction.addIdOperand(functionType);
626
908
    parent.mapInstruction(&functionInstruction);
627
908
    parent.addFunction(this);
628
629
    // OpFunctionParameter
630
908
    Instruction* typeInst = parent.getInstruction(functionType);
631
908
    int numParams = typeInst->getNumOperands() - 1;
632
1.31k
    for (int p = 0; p < numParams; ++p) {
633
408
        Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), Op::OpFunctionParameter);
634
408
        parent.mapInstruction(param);
635
408
        parameterInstructions.push_back(param);
636
408
    }
637
638
    // If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration
639
908
    if (linkType != LinkageType::Max) {
640
0
        exportName = name.substr(0, name.find_first_of('('));
641
0
    }
642
908
}
643
644
__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
645
3.12k
{
646
3.12k
    Instruction* raw_instruction = inst.get();
647
3.12k
    blocks[0]->addLocalVariable(std::move(inst));
648
3.12k
    parent.mapInstruction(raw_instruction);
649
3.12k
}
650
651
6.57k
__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
652
6.57k
{
653
6.57k
    instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, Op::OpLabel)));
654
6.57k
    instructions.back()->setBlock(this);
655
6.57k
    parent.getParent().mapInstruction(instructions.back().get());
656
6.57k
}
657
658
__inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
659
72.6k
{
660
72.6k
    Instruction* raw_instruction = inst.get();
661
72.6k
    instructions.push_back(std::move(inst));
662
72.6k
    raw_instruction->setBlock(this);
663
72.6k
    if (raw_instruction->getResultId())
664
53.5k
        parent.getParent().mapInstruction(raw_instruction);
665
72.6k
}
666
667
}  // end spv namespace
668
669
#endif // spvIR_H