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