yum-mirror/glslang

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

Spencer Frickeremove trailing whitespace6309d623

main
22.4 KiB669 linesraw
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
61namespace spv {
62
63class Block;
64class Function;
65class Module;
66
67const Id NoResult = 0;
68const Id NoType = 0;
69
70const Decoration NoPrecision = Decoration::Max;
71
72#ifdef __GNUC__
73#   define POTENTIALLY_UNUSED __attribute__((unused))
74#else
75#   define POTENTIALLY_UNUSED
76#endif
77
78POTENTIALLY_UNUSED
79const MemorySemanticsMask MemorySemanticsAllMemory =
80                (MemorySemanticsMask)(MemorySemanticsMask::UniformMemory |
81                                      MemorySemanticsMask::WorkgroupMemory |
82                                      MemorySemanticsMask::AtomicCounterMemory |
83                                      MemorySemanticsMask::ImageMemory);
84
85struct IdImmediate {
86    bool isId;      // true if word is an Id, false if word is an immediate
87    unsigned word;
88    IdImmediate(bool i, unsigned w) : isId(i), word(w) {}
89    IdImmediate(bool i, spv::MemoryAccessMask w) : isId(i), word((unsigned)w) {}
90    IdImmediate(bool i, spv::TensorAddressingOperandsMask w) : isId(i), word((unsigned)w) {}
91    IdImmediate(bool i, spv::ImageOperandsMask w) : isId(i), word((unsigned)w) {}
92    IdImmediate(bool i, spv::CooperativeMatrixOperandsMask w) : isId(i), word((unsigned)w) {}
93};
94
95//
96// SPIR-V IR instruction.
97//
98
99class Instruction {
100public:
101    Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
102    explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
103    virtual ~Instruction() {}
104    void reserveOperands(size_t count) {
105        operands.reserve(count);
106        idOperand.reserve(count);
107    }
108    void addIdOperand(Id id) {
109        // ids can't be 0
110        assert(id);
111        operands.push_back(id);
112        idOperand.push_back(true);
113    }
114    // This method is potentially dangerous as it can break assumptions
115    // about SSA and lack of forward references.
116    void setIdOperand(unsigned idx, Id id) {
117        assert(id);
118        assert(idOperand[idx]);
119        operands[idx] = id;
120    }
121
122    void addImmediateOperand(unsigned int immediate) {
123        operands.push_back(immediate);
124        idOperand.push_back(false);
125    }
126
127    void addImmediateOperand(spv::StorageClass immediate) {
128        addImmediateOperand((unsigned)immediate);
129    }
130
131    void addImmediateOperand(spv::ExecutionMode immediate) {
132        addImmediateOperand((unsigned)immediate);
133    }
134
135    void addImmediateOperand(spv::ExecutionModel immediate) {
136        addImmediateOperand((unsigned)immediate);
137    }
138
139    void addImmediateOperand(spv::Decoration immediate) {
140        addImmediateOperand((unsigned)immediate);
141    }
142
143    void addImmediateOperand(spv::LinkageType immediate) {
144        addImmediateOperand((unsigned)immediate);
145    }
146
147    void addImmediateOperand(spv::MemoryAccessMask immediate) {
148        addImmediateOperand((unsigned)immediate);
149    }
150
151    void addImmediateOperand(spv::Capability immediate) {
152        addImmediateOperand((unsigned)immediate);
153    }
154
155    void addImmediateOperand(spv::AddressingModel immediate) {
156        addImmediateOperand((unsigned)immediate);
157    }
158
159    void addImmediateOperand(spv::MemoryModel immediate) {
160        addImmediateOperand((unsigned)immediate);
161    }
162
163    void addImmediateOperand(spv::FPEncoding immediate) {
164        addImmediateOperand((unsigned)immediate);
165    }
166
167    void addImmediateOperand(spv::SourceLanguage immediate) {
168        addImmediateOperand((unsigned)immediate);
169    }
170
171    void addImmediateOperand(spv::Dim immediate) {
172        addImmediateOperand((unsigned)immediate);
173    }
174
175    void addImmediateOperand(spv::FunctionControlMask immediate){
176        addImmediateOperand((unsigned)immediate);
177    }
178
179    void addImmediateOperand(spv::SelectionControlMask immediate) {
180        addImmediateOperand((unsigned)immediate);
181    }
182
183    void addImmediateOperand(spv::LoopControlMask immediate) {
184        addImmediateOperand((unsigned)immediate);
185    }
186
187    void setImmediateOperand(unsigned idx, unsigned int immediate) {
188        assert(!idOperand[idx]);
189        operands[idx] = immediate;
190    }
191
192    void clearOperands() {
193        operands.clear();
194        idOperand.clear();
195    }
196
197    void addStringOperand(const char* str)
198    {
199        unsigned int word = 0;
200        unsigned int shiftAmount = 0;
201        unsigned char c;
202
203        do {
204            c = *(str++);
205            word |= ((unsigned int)c) << shiftAmount;
206            shiftAmount += 8;
207            if (shiftAmount == 32) {
208                addImmediateOperand(word);
209                word = 0;
210                shiftAmount = 0;
211            }
212        } while (c != 0);
213
214        // deal with partial last word
215        if (shiftAmount > 0) {
216            addImmediateOperand(word);
217        }
218    }
219    bool isIdOperand(int op) const { return idOperand[op]; }
220    void setBlock(Block* b) { block = b; }
221    Block* getBlock() const { return block; }
222    Op getOpCode() const { return opCode; }
223    int getNumOperands() const
224    {
225        assert(operands.size() == idOperand.size());
226        return (int)operands.size();
227    }
228    Id getResultId() const { return resultId; }
229    Id getTypeId() const { return typeId; }
230    void setTypeId(Id tId) { typeId = tId; }
231    Id getIdOperand(int op) const {
232        assert(idOperand[op]);
233        return operands[op];
234    }
235    unsigned int getImmediateOperand(int op) const {
236        assert(!idOperand[op]);
237        return operands[op];
238    }
239
240    // Write out the binary form.
241    void dump(std::vector<unsigned int>& out) const
242    {
243        // Compute the wordCount
244        unsigned int wordCount = 1;
245        if (typeId)
246            ++wordCount;
247        if (resultId)
248            ++wordCount;
249        wordCount += (unsigned int)operands.size();
250
251        // Write out the beginning of the instruction
252        out.push_back(((wordCount) << WordCountShift) | (unsigned)opCode);
253        if (typeId)
254            out.push_back(typeId);
255        if (resultId)
256            out.push_back(resultId);
257
258        // Write out the operands
259        for (int op = 0; op < (int)operands.size(); ++op)
260            out.push_back(operands[op]);
261    }
262
263    const char *getNameString() const {
264        if (opCode == Op::OpString) {
265            return (const char *)&operands[0];
266        } else {
267            assert(opCode == Op::OpName);
268            return (const char *)&operands[1];
269        }
270    }
271
272protected:
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
286struct DebugSourceLocation {
287    int line;
288    int column;
289    spv::Id fileId;
290};
291
292class Block {
293public:
294    Block(Id id, Function& parent);
295    virtual ~Block()
296    {
297    }
298
299    Id getId() { return instructions.front()->getResultId(); }
300
301    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    bool updateDebugSourceLocation(int line, int column, spv::Id fileId) {
305        if (currentSourceLoc && currentSourceLoc->line == line && currentSourceLoc->column == column &&
306            currentSourceLoc->fileId == fileId) {
307            return false;
308        }
309
310        currentSourceLoc = DebugSourceLocation{line, column, fileId};
311        return true;
312    }
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    bool updateDebugScope(spv::Id scopeId) {
316        assert(scopeId);
317        if (currentDebugScope && *currentDebugScope == scopeId) {
318            return false;
319        }
320
321        currentDebugScope = scopeId;
322        return true;
323    }
324    void addInstruction(std::unique_ptr<Instruction> inst);
325    void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
326    void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
327    const std::vector<Block*>& getPredecessors() const { return predecessors; }
328    const std::vector<Block*>& getSuccessors() const { return successors; }
329    std::vector<std::unique_ptr<Instruction> >& getInstructions() {
330        return instructions;
331    }
332    const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
333    void setUnreachable() { unreachable = true; }
334    bool isUnreachable() const { return unreachable; }
335    // Returns the block's merge instruction, if one exists (otherwise null).
336    const Instruction* getMergeInstruction() const {
337        if (instructions.size() < 2) return nullptr;
338        const Instruction* nextToLast = (instructions.cend() - 2)->get();
339        switch (nextToLast->getOpCode()) {
340            case Op::OpSelectionMerge:
341            case Op::OpLoopMerge:
342                return nextToLast;
343            default:
344                return nullptr;
345        }
346        return nullptr;
347    }
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    void rewriteAsCanonicalUnreachableMerge() {
353        assert(localVariables.empty());
354        // Delete all instructions except for the label.
355        assert(instructions.size() > 0);
356        instructions.resize(1);
357        successors.clear();
358        addInstruction(std::unique_ptr<Instruction>(new Instruction(Op::OpUnreachable)));
359    }
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    void rewriteAsCanonicalUnreachableContinue(Block* header) {
365        assert(localVariables.empty());
366        // Delete all instructions except for the label.
367        assert(instructions.size() > 0);
368        instructions.resize(1);
369        successors.clear();
370        // Add OpBranch back to the header.
371        assert(header != nullptr);
372        Instruction* branch = new Instruction(Op::OpBranch);
373        branch->addIdOperand(header->getId());
374        addInstruction(std::unique_ptr<Instruction>(branch));
375        successors.push_back(header);
376    }
377
378    bool isTerminated() const
379    {
380        switch (instructions.back()->getOpCode()) {
381        case Op::OpBranch:
382        case Op::OpBranchConditional:
383        case Op::OpSwitch:
384        case Op::OpKill:
385        case Op::OpTerminateInvocation:
386        case Op::OpReturn:
387        case Op::OpReturnValue:
388        case Op::OpUnreachable:
389        case Op::OpAbortKHR:
390            return true;
391        default:
392            return false;
393        }
394    }
395
396    void dump(std::vector<unsigned int>& out) const
397    {
398        instructions[0]->dump(out);
399        for (int i = 0; i < (int)localVariables.size(); ++i)
400            localVariables[i]->dump(out);
401        for (int i = 1; i < (int)instructions.size(); ++i)
402            instructions[i]->dump(out);
403    }
404
405protected:
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.
430enum 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.
446void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block* header)> callback);
447
448//
449// SPIR-V IR Function.
450//
451
452class Function {
453public:
454    Function(Id id, Id resultType, Id functionType, Id firstParam, LinkageType linkage, const std::string& name, Module& parent);
455    virtual ~Function()
456    {
457        for (int i = 0; i < (int)parameterInstructions.size(); ++i)
458            delete parameterInstructions[i];
459
460        for (int i = 0; i < (int)blocks.size(); ++i)
461            delete blocks[i];
462    }
463    Id getId() const { return functionInstruction.getResultId(); }
464    Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
465    Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
466
467    void addBlock(Block* block) { blocks.push_back(block); }
468    void removeBlock(Block* block)
469    {
470        auto found = find(blocks.begin(), blocks.end(), block);
471        assert(found != blocks.end());
472        blocks.erase(found);
473        delete block;
474    }
475
476    Module& getParent() const { return parent; }
477    Block* getEntryBlock() const { return blocks.front(); }
478    Block* getLastBlock() const { return blocks.back(); }
479    const std::vector<Block*>& getBlocks() const { return blocks; }
480    void addLocalVariable(std::unique_ptr<Instruction> inst);
481    Id getReturnType() const { return functionInstruction.getTypeId(); }
482    Id getFuncId() const { return functionInstruction.getResultId(); }
483    Id getFuncTypeId() const { return functionInstruction.getIdOperand(1); }
484    void setFunctionControl(FunctionControlMask functionControl)
485    {
486        functionInstruction.setImmediateOperand(0, static_cast<unsigned>(functionControl));
487    }
488    void setReturnPrecision(Decoration precision)
489    {
490        if (precision == Decoration::RelaxedPrecision)
491            reducedPrecisionReturn = true;
492    }
493    Decoration getReturnPrecision() const
494        { return reducedPrecisionReturn ? Decoration::RelaxedPrecision : NoPrecision; }
495
496    void setDebugLineInfo(Id fileName, int line, int column) {
497        lineInstruction = std::unique_ptr<Instruction>{new Instruction(Op::OpLine)};
498        lineInstruction->reserveOperands(3);
499        lineInstruction->addIdOperand(fileName);
500        lineInstruction->addImmediateOperand(line);
501        lineInstruction->addImmediateOperand(column);
502    }
503    bool hasDebugLineInfo() const { return lineInstruction != nullptr; }
504
505    void setImplicitThis() { implicitThis = true; }
506    bool hasImplicitThis() const { return implicitThis; }
507
508    void addParamPrecision(unsigned param, Decoration precision)
509    {
510        if (precision == Decoration::RelaxedPrecision)
511            reducedPrecisionParams.insert(param);
512    }
513    Decoration getParamPrecision(unsigned param) const
514    {
515        return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ?
516            Decoration::RelaxedPrecision : NoPrecision;
517    }
518
519    void dump(std::vector<unsigned int>& out) const
520    {
521        // OpLine
522        if (lineInstruction != nullptr) {
523            lineInstruction->dump(out);
524        }
525
526        // OpFunction
527        functionInstruction.dump(out);
528
529        // OpFunctionParameter
530        for (int p = 0; p < (int)parameterInstructions.size(); ++p)
531            parameterInstructions[p]->dump(out);
532
533        // Blocks
534        inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); });
535        Instruction end(0, 0, Op::OpFunctionEnd);
536        end.dump(out);
537    }
538
539    LinkageType getLinkType() const { return linkType; }
540    const char* getExportName() const { return exportName.c_str(); }
541
542protected:
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
562class Module {
563public:
564    Module() {}
565    virtual ~Module()
566    {
567        // TODO delete things
568    }
569
570    void addFunction(Function *fun) { functions.push_back(fun); }
571
572    void mapInstruction(Instruction *instruction)
573    {
574        spv::Id resultId = instruction->getResultId();
575        // map the instruction's result id
576        if (resultId >= idToInstruction.size())
577            idToInstruction.resize(resultId + 16);
578        idToInstruction[resultId] = instruction;
579    }
580
581    Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
582    const std::vector<Function*>& getFunctions() const { return functions; }
583    spv::Id getTypeId(Id resultId) const {
584        return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
585    }
586    StorageClass getStorageClass(Id typeId) const
587    {
588        assert(idToInstruction[typeId]->getOpCode() == spv::Op::OpTypePointer ||
589               idToInstruction[typeId]->getOpCode() == spv::Op::OpTypeUntypedPointerKHR);
590        return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
591    }
592
593    void dump(std::vector<unsigned int>& out) const
594    {
595        for (int f = 0; f < (int)functions.size(); ++f)
596            functions[f]->dump(out);
597    }
598
599protected:
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    : parent(parent), lineInstruction(nullptr),
618      functionInstruction(id, resultType, Op::OpFunction), implicitThis(false),
619      reducedPrecisionReturn(false),
620      linkType(linkage)
621{
622    // OpFunction
623    functionInstruction.reserveOperands(2);
624    functionInstruction.addImmediateOperand(FunctionControlMask::MaskNone);
625    functionInstruction.addIdOperand(functionType);
626    parent.mapInstruction(&functionInstruction);
627    parent.addFunction(this);
628
629    // OpFunctionParameter
630    Instruction* typeInst = parent.getInstruction(functionType);
631    int numParams = typeInst->getNumOperands() - 1;
632    for (int p = 0; p < numParams; ++p) {
633        Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), Op::OpFunctionParameter);
634        parent.mapInstruction(param);
635        parameterInstructions.push_back(param);
636    }
637
638    // If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration
639    if (linkType != LinkageType::Max) {
640        exportName = name.substr(0, name.find_first_of('('));
641    }
642}
643
644__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
645{
646    Instruction* raw_instruction = inst.get();
647    blocks[0]->addLocalVariable(std::move(inst));
648    parent.mapInstruction(raw_instruction);
649}
650
651__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
652{
653    instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, Op::OpLabel)));
654    instructions.back()->setBlock(this);
655    parent.getParent().mapInstruction(instructions.back().get());
656}
657
658__inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
659{
660    Instruction* raw_instruction = inst.get();
661    instructions.push_back(std::move(inst));
662    raw_instruction->setBlock(this);
663    if (raw_instruction->getResultId())
664        parent.getParent().mapInstruction(raw_instruction);
665}
666
667}  // end spv namespace
668
669#endif // spvIR_H