Coverage Report

Created: 2026-07-16 07:02

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.9k
    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
132k
    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.5k
    void addImmediateOperand(spv::StorageClass immediate) {
128
10.5k
        addImmediateOperand((unsigned)immediate);
129
10.5k
    }
130
131
28
    void addImmediateOperand(spv::ExecutionMode immediate) {
132
28
        addImmediateOperand((unsigned)immediate);
133
28
    }
134
135
445
    void addImmediateOperand(spv::ExecutionModel immediate) {
136
445
        addImmediateOperand((unsigned)immediate);
137
445
    }
138
139
9.40k
    void addImmediateOperand(spv::Decoration immediate) {
140
9.40k
        addImmediateOperand((unsigned)immediate);
141
9.40k
    }
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.44k
    void addImmediateOperand(spv::Capability immediate) {
152
1.44k
        addImmediateOperand((unsigned)immediate);
153
1.44k
    }
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
937
    void addImmediateOperand(spv::Dim immediate) {
172
937
        addImmediateOperand((unsigned)immediate);
173
937
    }
174
175
917
    void addImmediateOperand(spv::FunctionControlMask immediate){
176
917
        addImmediateOperand((unsigned)immediate);
177
917
    }
178
179
1.46k
    void addImmediateOperand(spv::SelectionControlMask immediate) {
180
1.46k
        addImmediateOperand((unsigned)immediate);
181
1.46k
    }
182
183
348
    void addImmediateOperand(spv::LoopControlMask immediate) {
184
348
        addImmediateOperand((unsigned)immediate);
185
348
    }
186
187
72
    void setImmediateOperand(unsigned idx, unsigned int immediate) {
188
72
        assert(!idOperand[idx]);
189
72
        operands[idx] = immediate;
190
72
    }
191
192
0
    void clearOperands() {
193
0
        operands.clear();
194
0
        idOperand.clear();
195
0
    }
196
197
    void addStringOperand(const char* str)
198
14.9k
    {
199
14.9k
        unsigned int word = 0;
200
14.9k
        unsigned int shiftAmount = 0;
201
14.9k
        unsigned char c;
202
203
176k
        do {
204
176k
            c = *(str++);
205
176k
            word |= ((unsigned int)c) << shiftAmount;
206
176k
            shiftAmount += 8;
207
176k
            if (shiftAmount == 32) {
208
38.3k
                addImmediateOperand(word);
209
38.3k
                word = 0;
210
38.3k
                shiftAmount = 0;
211
38.3k
            }
212
176k
        } while (c != 0);
213
214
        // deal with partial last word
215
14.9k
        if (shiftAmount > 0) {
216
11.9k
            addImmediateOperand(word);
217
11.9k
        }
218
14.9k
    }
219
321k
    bool isIdOperand(int op) const { return idOperand[op]; }
220
79.5k
    void setBlock(Block* b) { block = b; }
221
4.82k
    Block* getBlock() const { return block; }
222
1.59M
    Op getOpCode() const { return opCode; }
223
    int getNumOperands() const
224
520k
    {
225
520k
        assert(operands.size() == idOperand.size());
226
520k
        return (int)operands.size();
227
520k
    }
228
562k
    Id getResultId() const { return resultId; }
229
852k
    Id getTypeId() const { return typeId; }
230
0
    void setTypeId(Id tId) { typeId = tId; }
231
1.35M
    Id getIdOperand(int op) const {
232
1.35M
        assert(idOperand[op]);
233
1.35M
        return operands[op];
234
1.35M
    }
235
799k
    unsigned int getImmediateOperand(int op) const {
236
799k
        assert(!idOperand[op]);
237
799k
        return operands[op];
238
799k
    }
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.3k
            ++wordCount;
247
129k
        if (resultId)
248
84.4k
            ++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.3k
            out.push_back(typeId);
255
129k
        if (resultId)
256
84.4k
            out.push_back(resultId);
257
258
        // Write out the operands
259
405k
        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.72k
    {
297
6.72k
    }
298
299
9.69k
    Id getId() { return instructions.front()->getResultId(); }
300
301
14.1k
    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.33k
    void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
326
3.26k
    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.5k
    const std::vector<Block*>& getSuccessors() const { return successors; }
329
110k
    std::vector<std::unique_ptr<Instruction> >& getInstructions() {
330
110k
        return instructions;
331
110k
    }
332
16.7k
    const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
333
937
    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.5k
    const Instruction* getMergeInstruction() const {
337
11.5k
        if (instructions.size() < 2) return nullptr;
338
11.5k
        const Instruction* nextToLast = (instructions.cend() - 2)->get();
339
11.5k
        switch (nextToLast->getOpCode()) {
340
2.93k
            case Op::OpSelectionMerge:
341
3.62k
            case Op::OpLoopMerge:
342
3.62k
                return nextToLast;
343
7.95k
            default:
344
7.95k
                return nullptr;
345
11.5k
        }
346
0
        return nullptr;
347
11.5k
    }
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.33k
    {
380
1.33k
        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.33k
        default:
392
1.33k
            return false;
393
1.33k
        }
394
1.33k
    }
395
396
    void dump(std::vector<unsigned int>& out) const
397
5.79k
    {
398
5.79k
        instructions[0]->dump(out);
399
9.05k
        for (int i = 0; i < (int)localVariables.size(); ++i)
400
3.26k
            localVariables[i]->dump(out);
401
77.5k
        for (int i = 1; i < (int)instructions.size(); ++i)
402
71.7k
            instructions[i]->dump(out);
403
5.79k
    }
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
917
    {
457
1.34k
        for (int i = 0; i < (int)parameterInstructions.size(); ++i)
458
429
            delete parameterInstructions[i];
459
460
7.64k
        for (int i = 0; i < (int)blocks.size(); ++i)
461
6.72k
            delete blocks[i];
462
917
    }
463
3.76k
    Id getId() const { return functionInstruction.getResultId(); }
464
858
    Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
465
547
    Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
466
467
6.72k
    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.5k
    Module& getParent() const { return parent; }
477
1.38k
    Block* getEntryBlock() const { return blocks.front(); }
478
890
    Block* getLastBlock() const { return blocks.back(); }
479
18.9k
    const std::vector<Block*>& getBlocks() const { return blocks; }
480
    void addLocalVariable(std::unique_ptr<Instruction> inst);
481
3.01k
    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 setReturnPrecision(Decoration precision)
485
917
    {
486
917
        if (precision == Decoration::RelaxedPrecision)
487
0
            reducedPrecisionReturn = true;
488
917
    }
489
    Decoration getReturnPrecision() const
490
321
        { return reducedPrecisionReturn ? Decoration::RelaxedPrecision : NoPrecision; }
491
492
0
    void setDebugLineInfo(Id fileName, int line, int column) {
493
0
        lineInstruction = std::unique_ptr<Instruction>{new Instruction(Op::OpLine)};
494
0
        lineInstruction->reserveOperands(3);
495
0
        lineInstruction->addIdOperand(fileName);
496
0
        lineInstruction->addImmediateOperand(line);
497
0
        lineInstruction->addImmediateOperand(column);
498
0
    }
499
0
    bool hasDebugLineInfo() const { return lineInstruction != nullptr; }
500
501
0
    void setImplicitThis() { implicitThis = true; }
502
1.64k
    bool hasImplicitThis() const { return implicitThis; }
503
504
    void addParamPrecision(unsigned param, Decoration precision)
505
0
    {
506
0
        if (precision == Decoration::RelaxedPrecision)
507
0
            reducedPrecisionParams.insert(param);
508
0
    }
509
    Decoration getParamPrecision(unsigned param) const
510
547
    {
511
547
        return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ?
512
547
            Decoration::RelaxedPrecision : NoPrecision;
513
547
    }
514
515
    void dump(std::vector<unsigned int>& out) const
516
917
    {
517
        // OpLine
518
917
        if (lineInstruction != nullptr) {
519
0
            lineInstruction->dump(out);
520
0
        }
521
522
        // OpFunction
523
917
        functionInstruction.dump(out);
524
525
        // OpFunctionParameter
526
1.34k
        for (int p = 0; p < (int)parameterInstructions.size(); ++p)
527
429
            parameterInstructions[p]->dump(out);
528
529
        // Blocks
530
5.79k
        inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); });
531
917
        Instruction end(0, 0, Op::OpFunctionEnd);
532
917
        end.dump(out);
533
917
    }
534
535
917
    LinkageType getLinkType() const { return linkType; }
536
0
    const char* getExportName() const { return exportName.c_str(); }
537
538
protected:
539
    Function(const Function&);
540
    Function& operator=(Function&);
541
542
    Module& parent;
543
    std::unique_ptr<Instruction> lineInstruction;
544
    Instruction functionInstruction;
545
    std::vector<Instruction*> parameterInstructions;
546
    std::vector<Block*> blocks;
547
    bool implicitThis;  // true if this is a member function expecting to be passed a 'this' as the first argument
548
    bool reducedPrecisionReturn;
549
    std::set<int> reducedPrecisionParams;  // list of parameter indexes that need a relaxed precision arg
550
    LinkageType linkType;
551
    std::string exportName;
552
};
553
554
//
555
// SPIR-V IR Module.
556
//
557
558
class Module {
559
public:
560
445
    Module() {}
561
    virtual ~Module()
562
445
    {
563
        // TODO delete things
564
445
    }
565
566
917
    void addFunction(Function *fun) { functions.push_back(fun); }
567
568
    void mapInstruction(Instruction *instruction)
569
85.7k
    {
570
85.7k
        spv::Id resultId = instruction->getResultId();
571
        // map the instruction's result id
572
85.7k
        if (resultId >= idToInstruction.size())
573
5.58k
            idToInstruction.resize(resultId + 16);
574
85.7k
        idToInstruction[resultId] = instruction;
575
85.7k
    }
576
577
1.34M
    Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
578
4.50k
    const std::vector<Function*>& getFunctions() const { return functions; }
579
673k
    spv::Id getTypeId(Id resultId) const {
580
673k
        return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
581
673k
    }
582
    StorageClass getStorageClass(Id typeId) const
583
166k
    {
584
166k
        assert(idToInstruction[typeId]->getOpCode() == spv::Op::OpTypePointer ||
585
166k
               idToInstruction[typeId]->getOpCode() == spv::Op::OpTypeUntypedPointerKHR);
586
166k
        return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
587
166k
    }
588
589
    void dump(std::vector<unsigned int>& out) const
590
445
    {
591
1.36k
        for (int f = 0; f < (int)functions.size(); ++f)
592
917
            functions[f]->dump(out);
593
445
    }
594
595
protected:
596
    Module(const Module&);
597
    std::vector<Function*> functions;
598
599
    // map from result id to instruction having that result id
600
    std::vector<Instruction*> idToInstruction;
601
602
    // map from a result id to its type id
603
};
604
605
//
606
// Implementation (it's here due to circular type definitions).
607
//
608
609
// Add both
610
// - the OpFunction instruction
611
// - all the OpFunctionParameter instructions
612
__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, LinkageType linkage, const std::string& name, Module& parent)
613
917
    : parent(parent), lineInstruction(nullptr),
614
917
      functionInstruction(id, resultType, Op::OpFunction), implicitThis(false),
615
917
      reducedPrecisionReturn(false),
616
917
      linkType(linkage)
617
917
{
618
    // OpFunction
619
917
    functionInstruction.reserveOperands(2);
620
917
    functionInstruction.addImmediateOperand(FunctionControlMask::MaskNone);
621
917
    functionInstruction.addIdOperand(functionType);
622
917
    parent.mapInstruction(&functionInstruction);
623
917
    parent.addFunction(this);
624
625
    // OpFunctionParameter
626
917
    Instruction* typeInst = parent.getInstruction(functionType);
627
917
    int numParams = typeInst->getNumOperands() - 1;
628
1.34k
    for (int p = 0; p < numParams; ++p) {
629
429
        Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), Op::OpFunctionParameter);
630
429
        parent.mapInstruction(param);
631
429
        parameterInstructions.push_back(param);
632
429
    }
633
634
    // If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration
635
917
    if (linkType != LinkageType::Max) {
636
0
        exportName = name.substr(0, name.find_first_of('('));
637
0
    }
638
917
}
639
640
__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
641
3.26k
{
642
3.26k
    Instruction* raw_instruction = inst.get();
643
3.26k
    blocks[0]->addLocalVariable(std::move(inst));
644
3.26k
    parent.mapInstruction(raw_instruction);
645
3.26k
}
646
647
6.72k
__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
648
6.72k
{
649
6.72k
    instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, Op::OpLabel)));
650
6.72k
    instructions.back()->setBlock(this);
651
6.72k
    parent.getParent().mapInstruction(instructions.back().get());
652
6.72k
}
653
654
__inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
655
72.8k
{
656
72.8k
    Instruction* raw_instruction = inst.get();
657
72.8k
    instructions.push_back(std::move(inst));
658
72.8k
    raw_instruction->setBlock(this);
659
72.8k
    if (raw_instruction->getResultId())
660
53.4k
        parent.getParent().mapInstruction(raw_instruction);
661
72.8k
}
662
663
}  // end spv namespace
664
665
#endif // spvIR_H