Home | History | Annotate | Download | only in SPIRV
      1 //
      2 // Copyright (C) 2014-2015 LunarG, Inc.
      3 // Copyright (C) 2015-2018 Google, Inc.
      4 // Copyright (C) 2017 ARM Limited.
      5 //
      6 // All rights reserved.
      7 //
      8 // Redistribution and use in source and binary forms, with or without
      9 // modification, are permitted provided that the following conditions
     10 // are met:
     11 //
     12 //    Redistributions of source code must retain the above copyright
     13 //    notice, this list of conditions and the following disclaimer.
     14 //
     15 //    Redistributions in binary form must reproduce the above
     16 //    copyright notice, this list of conditions and the following
     17 //    disclaimer in the documentation and/or other materials provided
     18 //    with the distribution.
     19 //
     20 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
     21 //    contributors may be used to endorse or promote products derived
     22 //    from this software without specific prior written permission.
     23 //
     24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     27 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     28 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     29 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     31 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     32 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     34 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35 // POSSIBILITY OF SUCH DAMAGE.
     36 
     37 //
     38 // "Builder" is an interface to fully build SPIR-V IR.   Allocate one of
     39 // these to build (a thread safe) internal SPIR-V representation (IR),
     40 // and then dump it as a binary stream according to the SPIR-V specification.
     41 //
     42 // A Builder has a 1:1 relationship with a SPIR-V module.
     43 //
     44 
     45 #pragma once
     46 #ifndef SpvBuilder_H
     47 #define SpvBuilder_H
     48 
     49 #include "Logger.h"
     50 #include "spirv.hpp"
     51 #include "spvIR.h"
     52 
     53 #include <algorithm>
     54 #include <map>
     55 #include <memory>
     56 #include <set>
     57 #include <sstream>
     58 #include <stack>
     59 #include <unordered_map>
     60 #include <map>
     61 
     62 namespace spv {
     63 
     64 class Builder {
     65 public:
     66     Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
     67     virtual ~Builder();
     68 
     69     static const int maxMatrixSize = 4;
     70 
     71     unsigned int getSpvVersion() const { return spvVersion; }
     72 
     73     void setSource(spv::SourceLanguage lang, int version)
     74     {
     75         source = lang;
     76         sourceVersion = version;
     77     }
     78     spv::Id getStringId(const std::string& str)
     79     {
     80         auto sItr = stringIds.find(str);
     81         if (sItr != stringIds.end())
     82             return sItr->second;
     83         spv::Id strId = getUniqueId();
     84         Instruction* fileString = new Instruction(strId, NoType, OpString);
     85         const char* file_c_str = str.c_str();
     86         fileString->addStringOperand(file_c_str);
     87         strings.push_back(std::unique_ptr<Instruction>(fileString));
     88         stringIds[file_c_str] = strId;
     89         return strId;
     90     }
     91     void setSourceFile(const std::string& file)
     92     {
     93         sourceFileStringId = getStringId(file);
     94     }
     95     void setSourceText(const std::string& text) { sourceText = text; }
     96     void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
     97     void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
     98     void setEmitOpLines() { emitOpLines = true; }
     99     void addExtension(const char* ext) { extensions.insert(ext); }
    100     void addInclude(const std::string& name, const std::string& text)
    101     {
    102         spv::Id incId = getStringId(name);
    103         includeFiles[incId] = &text;
    104     }
    105     Id import(const char*);
    106     void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
    107     {
    108         addressModel = addr;
    109         memoryModel = mem;
    110     }
    111 
    112     void addCapability(spv::Capability cap) { capabilities.insert(cap); }
    113 
    114     // To get a new <id> for anything needing a new one.
    115     Id getUniqueId() { return ++uniqueId; }
    116 
    117     // To get a set of new <id>s, e.g., for a set of function parameters
    118     Id getUniqueIds(int numIds)
    119     {
    120         Id id = uniqueId + 1;
    121         uniqueId += numIds;
    122         return id;
    123     }
    124 
    125     // Generate OpLine for non-filename-based #line directives (ie no filename
    126     // seen yet): Log the current line, and if different than the last one,
    127     // issue a new OpLine using the new line and current source file name.
    128     void setLine(int line);
    129 
    130     // If filename null, generate OpLine for non-filename-based line directives,
    131     // else do filename-based: Log the current line and file, and if different
    132     // than the last one, issue a new OpLine using the new line and file
    133     // name.
    134     void setLine(int line, const char* filename);
    135     // Low-level OpLine. See setLine() for a layered helper.
    136     void addLine(Id fileName, int line, int column);
    137 
    138     // For creating new types (will return old type if the requested one was already made).
    139     Id makeVoidType();
    140     Id makeBoolType();
    141     Id makePointer(StorageClass, Id pointee);
    142     Id makeForwardPointer(StorageClass);
    143     Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
    144     Id makeIntegerType(int width, bool hasSign);   // generic
    145     Id makeIntType(int width) { return makeIntegerType(width, true); }
    146     Id makeUintType(int width) { return makeIntegerType(width, false); }
    147     Id makeFloatType(int width);
    148     Id makeStructType(const std::vector<Id>& members, const char*);
    149     Id makeStructResultType(Id type0, Id type1);
    150     Id makeVectorType(Id component, int size);
    151     Id makeMatrixType(Id component, int cols, int rows);
    152     Id makeArrayType(Id element, Id sizeId, int stride);  // 0 stride means no stride decoration
    153     Id makeRuntimeArray(Id element);
    154     Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
    155     Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);
    156     Id makeSamplerType();
    157     Id makeSampledImageType(Id imageType);
    158 
    159     // accelerationStructureNV type
    160     Id makeAccelerationStructureNVType();
    161 
    162     // For querying about types.
    163     Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
    164     Id getDerefTypeId(Id resultId) const;
    165     Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
    166     Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
    167     Op getMostBasicTypeClass(Id typeId) const;
    168     int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
    169     int getNumTypeConstituents(Id typeId) const;
    170     int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
    171     Id getScalarTypeId(Id typeId) const;
    172     Id getContainedTypeId(Id typeId) const;
    173     Id getContainedTypeId(Id typeId, int) const;
    174     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
    175     ImageFormat getImageTypeFormat(Id typeId) const { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
    176 
    177     bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
    178     bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }
    179     bool isVector(Id resultId)       const { return isVectorType(getTypeId(resultId)); }
    180     bool isMatrix(Id resultId)       const { return isMatrixType(getTypeId(resultId)); }
    181     bool isAggregate(Id resultId)    const { return isAggregateType(getTypeId(resultId)); }
    182     bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
    183 
    184     bool isBoolType(Id typeId)               { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
    185     bool isIntType(Id typeId)          const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
    186     bool isUintType(Id typeId)         const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
    187     bool isFloatType(Id typeId)        const { return getTypeClass(typeId) == OpTypeFloat; }
    188     bool isPointerType(Id typeId)      const { return getTypeClass(typeId) == OpTypePointer; }
    189     bool isScalarType(Id typeId)       const { return getTypeClass(typeId) == OpTypeFloat  || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; }
    190     bool isVectorType(Id typeId)       const { return getTypeClass(typeId) == OpTypeVector; }
    191     bool isMatrixType(Id typeId)       const { return getTypeClass(typeId) == OpTypeMatrix; }
    192     bool isStructType(Id typeId)       const { return getTypeClass(typeId) == OpTypeStruct; }
    193     bool isArrayType(Id typeId)        const { return getTypeClass(typeId) == OpTypeArray; }
    194     bool isAggregateType(Id typeId)    const { return isArrayType(typeId) || isStructType(typeId); }
    195     bool isImageType(Id typeId)        const { return getTypeClass(typeId) == OpTypeImage; }
    196     bool isSamplerType(Id typeId)      const { return getTypeClass(typeId) == OpTypeSampler; }
    197     bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
    198     bool containsType(Id typeId, Op typeOp, unsigned int width) const;
    199     bool containsPhysicalStorageBufferOrArray(Id typeId) const;
    200 
    201     bool isConstantOpCode(Op opcode) const;
    202     bool isSpecConstantOpCode(Op opcode) const;
    203     bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
    204     bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
    205     bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
    206     unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
    207     StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
    208 
    209     int getScalarTypeWidth(Id typeId) const
    210     {
    211         Id scalarTypeId = getScalarTypeId(typeId);
    212         assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
    213         return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
    214     }
    215 
    216     int getTypeNumColumns(Id typeId) const
    217     {
    218         assert(isMatrixType(typeId));
    219         return getNumTypeConstituents(typeId);
    220     }
    221     int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
    222     int getTypeNumRows(Id typeId) const
    223     {
    224         assert(isMatrixType(typeId));
    225         return getNumTypeComponents(getContainedTypeId(typeId));
    226     }
    227     int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
    228 
    229     Dim getTypeDimensionality(Id typeId) const
    230     {
    231         assert(isImageType(typeId));
    232         return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
    233     }
    234     Id getImageType(Id resultId) const
    235     {
    236         Id typeId = getTypeId(resultId);
    237         assert(isImageType(typeId) || isSampledImageType(typeId));
    238         return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
    239     }
    240     bool isArrayedImageType(Id typeId) const
    241     {
    242         assert(isImageType(typeId));
    243         return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
    244     }
    245 
    246     // For making new constants (will return old constant if the requested one was already made).
    247     Id makeBoolConstant(bool b, bool specConstant = false);
    248     Id makeInt8Constant(int i, bool specConstant = false)        { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); }
    249     Id makeUint8Constant(unsigned u, bool specConstant = false)  { return makeIntConstant(makeUintType(8),           u, specConstant); }
    250     Id makeInt16Constant(int i, bool specConstant = false)       { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); }
    251     Id makeUint16Constant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(16),           u, specConstant); }
    252     Id makeIntConstant(int i, bool specConstant = false)         { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); }
    253     Id makeUintConstant(unsigned u, bool specConstant = false)   { return makeIntConstant(makeUintType(32),           u, specConstant); }
    254     Id makeInt64Constant(long long i, bool specConstant = false)            { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); }
    255     Id makeUint64Constant(unsigned long long u, bool specConstant = false)  { return makeInt64Constant(makeUintType(64),                     u, specConstant); }
    256     Id makeFloatConstant(float f, bool specConstant = false);
    257     Id makeDoubleConstant(double d, bool specConstant = false);
    258     Id makeFloat16Constant(float f16, bool specConstant = false);
    259     Id makeFpConstant(Id type, double d, bool specConstant = false);
    260 
    261     // Turn the array of constants into a proper spv constant of the requested type.
    262     Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
    263 
    264     // Methods for adding information outside the CFG.
    265     Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
    266     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
    267     void addName(Id, const char* name);
    268     void addMemberName(Id, int member, const char* name);
    269     void addDecoration(Id, Decoration, int num = -1);
    270     void addDecoration(Id, Decoration, const char*);
    271     void addDecorationId(Id id, Decoration, Id idDecoration);
    272     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
    273     void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
    274 
    275     // At the end of what block do the next create*() instructions go?
    276     void setBuildPoint(Block* bp) { buildPoint = bp; }
    277     Block* getBuildPoint() const { return buildPoint; }
    278 
    279     // Make the entry-point function. The returned pointer is only valid
    280     // for the lifetime of this builder.
    281     Function* makeEntryPoint(const char*);
    282 
    283     // Make a shader-style function, and create its entry block if entry is non-zero.
    284     // Return the function, pass back the entry.
    285     // The returned pointer is only valid for the lifetime of this builder.
    286     Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, const std::vector<Id>& paramTypes,
    287                                 const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0);
    288 
    289     // Create a return. An 'implicit' return is one not appearing in the source
    290     // code.  In the case of an implicit return, no post-return block is inserted.
    291     void makeReturn(bool implicit, Id retVal = 0);
    292 
    293     // Generate all the code needed to finish up a function.
    294     void leaveFunction();
    295 
    296     // Create a discard.
    297     void makeDiscard();
    298 
    299     // Create a global or function local or IO variable.
    300     Id createVariable(StorageClass, Id type, const char* name = 0);
    301 
    302     // Create an intermediate with an undefined value.
    303     Id createUndefined(Id type);
    304 
    305     // Store into an Id and return the l-value
    306     void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
    307 
    308     // Load from an Id and return it
    309     Id createLoad(Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
    310 
    311     // Create an OpAccessChain instruction
    312     Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
    313 
    314     // Create an OpArrayLength instruction
    315     Id createArrayLength(Id base, unsigned int member);
    316 
    317     // Create an OpCompositeExtract instruction
    318     Id createCompositeExtract(Id composite, Id typeId, unsigned index);
    319     Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
    320     Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
    321     Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
    322 
    323     Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
    324     Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
    325 
    326     void createNoResultOp(Op);
    327     void createNoResultOp(Op, Id operand);
    328     void createNoResultOp(Op, const std::vector<Id>& operands);
    329     void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
    330     void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
    331     void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
    332     Id createUnaryOp(Op, Id typeId, Id operand);
    333     Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
    334     Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
    335     Id createOp(Op, Id typeId, const std::vector<Id>& operands);
    336     Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
    337     Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
    338     Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
    339 
    340     // Take an rvalue (source) and a set of channels to extract from it to
    341     // make a new rvalue, which is returned.
    342     Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
    343 
    344     // Take a copy of an lvalue (target) and a source of components, and set the
    345     // source components into the lvalue where the 'channels' say to put them.
    346     // An updated version of the target is returned.
    347     // (No true lvalue or stores are used.)
    348     Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
    349 
    350     // If both the id and precision are valid, the id
    351     // gets tagged with the requested precision.
    352     // The passed in id is always the returned id, to simplify use patterns.
    353     Id setPrecision(Id id, Decoration precision)
    354     {
    355         if (precision != NoPrecision && id != NoResult)
    356             addDecoration(id, precision);
    357 
    358         return id;
    359     }
    360 
    361     // Can smear a scalar to a vector for the following forms:
    362     //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
    363     //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
    364     //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
    365     //   - promoteScalar(scalar, scalar)  // do nothing
    366     // Other forms are not allowed.
    367     //
    368     // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
    369     // The type of the created vector is a vector of components of the same type as the scalar.
    370     //
    371     // Note: One of the arguments will change, with the result coming back that way rather than
    372     // through the return value.
    373     void promoteScalar(Decoration precision, Id& left, Id& right);
    374 
    375     // Make a value by smearing the scalar to fill the type.
    376     // vectorType should be the correct type for making a vector of scalarVal.
    377     // (No conversions are done.)
    378     Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
    379 
    380     // Create a call to a built-in function.
    381     Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
    382 
    383     // List of parameters used to create a texture operation
    384     struct TextureParameters {
    385         Id sampler;
    386         Id coords;
    387         Id bias;
    388         Id lod;
    389         Id Dref;
    390         Id offset;
    391         Id offsets;
    392         Id gradX;
    393         Id gradY;
    394         Id sample;
    395         Id component;
    396         Id texelOut;
    397         Id lodClamp;
    398         Id granularity;
    399         Id coarse;
    400         bool nonprivate;
    401         bool volatil;
    402     };
    403 
    404     // Select the correct texture operation based on all inputs, and emit the correct instruction
    405     Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, bool noImplicit, const TextureParameters&);
    406 
    407     // Emit the OpTextureQuery* instruction that was passed in.
    408     // Figure out the right return value and type, and return it.
    409     Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
    410 
    411     Id createSamplePositionCall(Decoration precision, Id, Id);
    412 
    413     Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
    414     Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
    415 
    416     // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
    417     Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
    418 
    419     // OpCompositeConstruct
    420     Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
    421 
    422     // vector or scalar constructor
    423     Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
    424 
    425     // matrix constructor
    426     Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
    427 
    428     // Helper to use for building nested control flow with if-then-else.
    429     class If {
    430     public:
    431         If(Id condition, unsigned int ctrl, Builder& builder);
    432         ~If() {}
    433 
    434         void makeBeginElse();
    435         void makeEndIf();
    436 
    437     private:
    438         If(const If&);
    439         If& operator=(If&);
    440 
    441         Builder& builder;
    442         Id condition;
    443         unsigned int control;
    444         Function* function;
    445         Block* headerBlock;
    446         Block* thenBlock;
    447         Block* elseBlock;
    448         Block* mergeBlock;
    449     };
    450 
    451     // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
    452     // any case/default labels, all separated by one or more case/default labels.  Each possible
    453     // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
    454     // number space.  How to compute the value is given by 'condition', as in switch(condition).
    455     //
    456     // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
    457     //
    458     // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
    459     //
    460     // Returns the right set of basic blocks to start each code segment with, so that the caller's
    461     // recursion stack can hold the memory for it.
    462     //
    463     void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
    464                     const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); // return argument
    465 
    466     // Add a branch to the innermost switch's merge block.
    467     void addSwitchBreak();
    468 
    469     // Move to the next code segment, passing in the return argument in makeSwitch()
    470     void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
    471 
    472     // Finish off the innermost switch.
    473     void endSwitch(std::vector<Block*>& segmentBB);
    474 
    475     struct LoopBlocks {
    476         LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
    477             head(head), body(body), merge(merge), continue_target(continue_target) { }
    478         Block &head, &body, &merge, &continue_target;
    479     private:
    480         LoopBlocks();
    481         LoopBlocks& operator=(const LoopBlocks&);
    482     };
    483 
    484     // Start a new loop and prepare the builder to generate code for it.  Until
    485     // closeLoop() is called for this loop, createLoopContinue() and
    486     // createLoopExit() will target its corresponding blocks.
    487     LoopBlocks& makeNewLoop();
    488 
    489     // Create a new block in the function containing the build point.  Memory is
    490     // owned by the function object.
    491     Block& makeNewBlock();
    492 
    493     // Add a branch to the continue_target of the current (innermost) loop.
    494     void createLoopContinue();
    495 
    496     // Add an exit (e.g. "break") from the innermost loop that we're currently
    497     // in.
    498     void createLoopExit();
    499 
    500     // Close the innermost loop that you're in
    501     void closeLoop();
    502 
    503     //
    504     // Access chain design for an R-Value vs. L-Value:
    505     //
    506     // There is a single access chain the builder is building at
    507     // any particular time.  Such a chain can be used to either to a load or
    508     // a store, when desired.
    509     //
    510     // Expressions can be r-values, l-values, or both, or only r-values:
    511     //    a[b.c].d = ....  // l-value
    512     //    ... = a[b.c].d;  // r-value, that also looks like an l-value
    513     //    ++a[b.c].d;      // r-value and l-value
    514     //    (x + y)[2];      // r-value only, can't possibly be l-value
    515     //
    516     // Computing an r-value means generating code.  Hence,
    517     // r-values should only be computed when they are needed, not speculatively.
    518     //
    519     // Computing an l-value means saving away information for later use in the compiler,
    520     // no code is generated until the l-value is later dereferenced.  It is okay
    521     // to speculatively generate an l-value, just not okay to speculatively dereference it.
    522     //
    523     // The base of the access chain (the left-most variable or expression
    524     // from which everything is based) can be set either as an l-value
    525     // or as an r-value.  Most efficient would be to set an l-value if one
    526     // is available.  If an expression was evaluated, the resulting r-value
    527     // can be set as the chain base.
    528     //
    529     // The users of this single access chain can save and restore if they
    530     // want to nest or manage multiple chains.
    531     //
    532 
    533     struct AccessChain {
    534         Id base;                       // for l-values, pointer to the base object, for r-values, the base object
    535         std::vector<Id> indexChain;
    536         Id instr;                      // cache the instruction that generates this access chain
    537         std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
    538         Id component;                  // a dynamic component index, can coexist with a swizzle, done after the swizzle, NoResult if not present
    539         Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied; NoType unless a swizzle or component is present
    540         bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
    541         unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment. Only tracks base and (optional) component selection alignment.
    542 
    543         // Accumulate whether anything in the chain of structures has coherent decorations.
    544         struct CoherentFlags {
    545             unsigned coherent : 1;
    546             unsigned devicecoherent : 1;
    547             unsigned queuefamilycoherent : 1;
    548             unsigned workgroupcoherent : 1;
    549             unsigned subgroupcoherent : 1;
    550             unsigned nonprivate : 1;
    551             unsigned volatil : 1;
    552             unsigned isImage : 1;
    553 
    554             void clear() {
    555                 coherent = 0;
    556                 devicecoherent = 0;
    557                 queuefamilycoherent = 0;
    558                 workgroupcoherent = 0;
    559                 subgroupcoherent = 0;
    560                 nonprivate = 0;
    561                 volatil = 0;
    562                 isImage = 0;
    563             }
    564 
    565             CoherentFlags() { clear(); }
    566             CoherentFlags operator |=(const CoherentFlags &other) {
    567                 coherent |= other.coherent;
    568                 devicecoherent |= other.devicecoherent;
    569                 queuefamilycoherent |= other.queuefamilycoherent;
    570                 workgroupcoherent |= other.workgroupcoherent;
    571                 subgroupcoherent |= other.subgroupcoherent;
    572                 nonprivate |= other.nonprivate;
    573                 volatil |= other.volatil;
    574                 isImage |= other.isImage;
    575                 return *this;
    576             }
    577         };
    578         CoherentFlags coherentFlags;
    579     };
    580 
    581     //
    582     // the SPIR-V builder maintains a single active chain that
    583     // the following methods operate on
    584     //
    585 
    586     // for external save and restore
    587     AccessChain getAccessChain() { return accessChain; }
    588     void setAccessChain(AccessChain newChain) { accessChain = newChain; }
    589 
    590     // clear accessChain
    591     void clearAccessChain();
    592 
    593     // set new base as an l-value base
    594     void setAccessChainLValue(Id lValue)
    595     {
    596         assert(isPointer(lValue));
    597         accessChain.base = lValue;
    598     }
    599 
    600     // set new base value as an r-value
    601     void setAccessChainRValue(Id rValue)
    602     {
    603         accessChain.isRValue = true;
    604         accessChain.base = rValue;
    605     }
    606 
    607     // push offset onto the end of the chain
    608     void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
    609     {
    610         accessChain.indexChain.push_back(offset);
    611         accessChain.coherentFlags |= coherentFlags;
    612         accessChain.alignment |= alignment;
    613     }
    614 
    615     // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
    616     void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
    617 
    618     // push a dynamic component selection onto the access chain, only applicable with a
    619     // non-trivial swizzle or no swizzle
    620     void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
    621     {
    622         if (accessChain.swizzle.size() != 1) {
    623             accessChain.component = component;
    624             if (accessChain.preSwizzleBaseType == NoType)
    625                 accessChain.preSwizzleBaseType = preSwizzleBaseType;
    626         }
    627         accessChain.coherentFlags |= coherentFlags;
    628         accessChain.alignment |= alignment;
    629     }
    630 
    631     // use accessChain and swizzle to store value
    632     void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
    633 
    634     // use accessChain and swizzle to load an r-value
    635     Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
    636 
    637     // get the direct pointer for an l-value
    638     Id accessChainGetLValue();
    639 
    640     // Get the inferred SPIR-V type of the result of the current access chain,
    641     // based on the type of the base and the chain of dereferences.
    642     Id accessChainGetInferredType();
    643 
    644     // Add capabilities, extensions, remove unneeded decorations, etc.,
    645     // based on the resulting SPIR-V.
    646     void postProcess();
    647 
    648     // Hook to visit each instruction in a block in a function
    649     void postProcess(Instruction&);
    650     // Hook to visit each instruction in a reachable block in a function.
    651     void postProcessReachable(const Instruction&);
    652     // Hook to visit each non-32-bit sized float/int operation in a block.
    653     void postProcessType(const Instruction&, spv::Id typeId);
    654 
    655     void dump(std::vector<unsigned int>&) const;
    656 
    657     void createBranch(Block* block);
    658     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
    659     void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength);
    660 
    661     // Sets to generate opcode for specialization constants.
    662     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
    663     // Sets to generate opcode for non-specialization constants (normal mode).
    664     void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
    665     // Check if the builder is generating code for spec constants.
    666     bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
    667 
    668  protected:
    669     Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
    670     Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
    671     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
    672     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
    673     Id findCompositeConstant(Op typeClass, const std::vector<Id>& comps);
    674     Id findStructConstant(Id typeId, const std::vector<Id>& comps);
    675     Id collapseAccessChain();
    676     void remapDynamicSwizzle();
    677     void transferAccessChainSwizzle(bool dynamic);
    678     void simplifyAccessChainSwizzle();
    679     void createAndSetNoPredecessorBlock(const char*);
    680     void createSelectionMerge(Block* mergeBlock, unsigned int control);
    681     void dumpSourceInstructions(std::vector<unsigned int>&) const;
    682     void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
    683     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
    684     void dumpModuleProcesses(std::vector<unsigned int>&) const;
    685     spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) const;
    686 
    687     unsigned int spvVersion;     // the version of SPIR-V to emit in the header
    688     SourceLanguage source;
    689     int sourceVersion;
    690     spv::Id sourceFileStringId;
    691     std::string sourceText;
    692     int currentLine;
    693     const char* currentFile;
    694     bool emitOpLines;
    695     std::set<std::string> extensions;
    696     std::vector<const char*> sourceExtensions;
    697     std::vector<const char*> moduleProcesses;
    698     AddressingModel addressModel;
    699     MemoryModel memoryModel;
    700     std::set<spv::Capability> capabilities;
    701     int builderNumber;
    702     Module module;
    703     Block* buildPoint;
    704     Id uniqueId;
    705     Function* entryPointFunction;
    706     bool generatingOpCodeForSpecConst;
    707     AccessChain accessChain;
    708 
    709     // special blocks of instructions for output
    710     std::vector<std::unique_ptr<Instruction> > strings;
    711     std::vector<std::unique_ptr<Instruction> > imports;
    712     std::vector<std::unique_ptr<Instruction> > entryPoints;
    713     std::vector<std::unique_ptr<Instruction> > executionModes;
    714     std::vector<std::unique_ptr<Instruction> > names;
    715     std::vector<std::unique_ptr<Instruction> > decorations;
    716     std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
    717     std::vector<std::unique_ptr<Instruction> > externals;
    718     std::vector<std::unique_ptr<Function> > functions;
    719 
    720      // not output, internally used for quick & dirty canonical (unique) creation
    721     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;       // map type opcodes to constant inst.
    722     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; // map struct-id to constant instructions
    723     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes;           // map type opcodes to type instructions
    724 
    725     // stack of switches
    726     std::stack<Block*> switchMerges;
    727 
    728     // Our loop stack.
    729     std::stack<LoopBlocks> loops;
    730 
    731     // map from strings to their string ids
    732     std::unordered_map<std::string, spv::Id> stringIds;
    733 
    734     // map from include file name ids to their contents
    735     std::map<spv::Id, const std::string*> includeFiles;
    736 
    737     // The stream for outputting warnings and errors.
    738     SpvBuildLogger* logger;
    739 };  // end Builder class
    740 
    741 };  // end spv namespace
    742 
    743 #endif // SpvBuilder_H
    744