576 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//
 | 
						|
//Copyright (C) 2014 LunarG, Inc.
 | 
						|
//
 | 
						|
//All rights reserved.
 | 
						|
//
 | 
						|
//Redistribution and use in source and binary forms, with or without
 | 
						|
//modification, are permitted provided that the following conditions
 | 
						|
//are met:
 | 
						|
//
 | 
						|
//    Redistributions of source code must retain the above copyright
 | 
						|
//    notice, this list of conditions and the following disclaimer.
 | 
						|
//
 | 
						|
//    Redistributions in binary form must reproduce the above
 | 
						|
//    copyright notice, this list of conditions and the following
 | 
						|
//    disclaimer in the documentation and/or other materials provided
 | 
						|
//    with the distribution.
 | 
						|
//
 | 
						|
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
 | 
						|
//    contributors may be used to endorse or promote products derived
 | 
						|
//    from this software without specific prior written permission.
 | 
						|
//
 | 
						|
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
						|
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
						|
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
						|
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 | 
						|
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
						|
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | 
						|
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
						|
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | 
						|
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 | 
						|
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
						|
//POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 | 
						|
//
 | 
						|
// Author: John Kessenich, LunarG
 | 
						|
//
 | 
						|
 | 
						|
//
 | 
						|
// "Builder" is an interface to fully build SPIR-V IR.   Allocate one of
 | 
						|
// these to build (a thread safe) internal SPIR-V representation (IR),
 | 
						|
// and then dump it as a binary stream according to the SPIR-V specification.
 | 
						|
//
 | 
						|
// A Builder has a 1:1 relationship with a SPIR-V module.
 | 
						|
//
 | 
						|
 | 
						|
#pragma once
 | 
						|
#ifndef SpvBuilder_H
 | 
						|
#define SpvBuilder_H
 | 
						|
 | 
						|
#include "spirv.h"
 | 
						|
#include "spvIR.h"
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <stack>
 | 
						|
#include <map>
 | 
						|
 | 
						|
namespace spv {
 | 
						|
 | 
						|
class Builder {
 | 
						|
public:
 | 
						|
    Builder(unsigned int userNumber);
 | 
						|
    virtual ~Builder();
 | 
						|
 | 
						|
    static const int maxMatrixSize = 4;
 | 
						|
 | 
						|
    void setSource(spv::SourceLanguage lang, int version)
 | 
						|
    {
 | 
						|
        source = lang;
 | 
						|
        sourceVersion = version;
 | 
						|
    }
 | 
						|
    void addSourceExtension(const char* ext) { extensions.push_back(ext); }
 | 
						|
    Id import(const char*);
 | 
						|
    void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
 | 
						|
    {
 | 
						|
        addressModel = addr;
 | 
						|
        memoryModel = mem;
 | 
						|
    }
 | 
						|
 | 
						|
    // To get a new <id> for anything needing a new one.
 | 
						|
    Id getUniqueId() { return ++uniqueId; }
 | 
						|
 | 
						|
    // To get a set of new <id>s, e.g., for a set of function parameters
 | 
						|
    Id getUniqueIds(int numIds)
 | 
						|
    {
 | 
						|
        Id id = uniqueId + 1;
 | 
						|
        uniqueId += numIds;
 | 
						|
        return id;
 | 
						|
    }
 | 
						|
 | 
						|
    // For creating new types (will return old type if the requested one was already made).
 | 
						|
    Id makeVoidType();
 | 
						|
    Id makeBoolType();
 | 
						|
    Id makePointer(StorageClass, Id type);
 | 
						|
    Id makeIntegerType(int width, bool hasSign);   // generic
 | 
						|
    Id makeIntType(int width) { return makeIntegerType(width, true); }
 | 
						|
    Id makeUintType(int width) { return makeIntegerType(width, false); }
 | 
						|
    Id makeFloatType(int width);
 | 
						|
    Id makeStructType(std::vector<Id>& members, const char*);
 | 
						|
    Id makeVectorType(Id component, int size);
 | 
						|
    Id makeMatrixType(Id component, int cols, int rows);
 | 
						|
    Id makeArrayType(Id element, unsigned size);
 | 
						|
    Id makeFunctionType(Id returnType, std::vector<Id>& paramTypes);
 | 
						|
    enum samplerContent {
 | 
						|
        samplerContentTexture,
 | 
						|
        samplerContentImage,
 | 
						|
        samplerContentTextureFilter
 | 
						|
    };
 | 
						|
    Id makeSampler(Id sampledType, Dim, samplerContent, bool arrayed, bool shadow, bool ms);
 | 
						|
 | 
						|
    // For querying about types.
 | 
						|
    Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
 | 
						|
    Id getDerefTypeId(Id resultId) const;
 | 
						|
    Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
 | 
						|
    Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
 | 
						|
    Op getMostBasicTypeClass(Id typeId) const;
 | 
						|
    int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
 | 
						|
    int getNumTypeComponents(Id typeId) const;
 | 
						|
    Id getScalarTypeId(Id typeId) const;
 | 
						|
    Id getContainedTypeId(Id typeId) const;
 | 
						|
    Id getContainedTypeId(Id typeId, int) const;
 | 
						|
 | 
						|
    bool isPointer(Id resultId)     const { return isPointerType(getTypeId(resultId)); }
 | 
						|
    bool isScalar(Id resultId)      const { return isScalarType(getTypeId(resultId)); }
 | 
						|
    bool isVector(Id resultId)      const { return isVectorType(getTypeId(resultId)); }
 | 
						|
    bool isMatrix(Id resultId)      const { return isMatrixType(getTypeId(resultId)); }
 | 
						|
    bool isAggregate(Id resultId)   const { return isAggregateType(getTypeId(resultId)); }
 | 
						|
 | 
						|
    bool isPointerType(Id typeId)   const { return getTypeClass(typeId) == OpTypePointer; }
 | 
						|
    bool isScalarType(Id typeId)    const { return getTypeClass(typeId) == OpTypeFloat  || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; }
 | 
						|
    bool isVectorType(Id typeId)    const { return getTypeClass(typeId) == OpTypeVector; }
 | 
						|
    bool isMatrixType(Id typeId)    const { return getTypeClass(typeId) == OpTypeMatrix; }
 | 
						|
    bool isStructType(Id typeId)    const { return getTypeClass(typeId) == OpTypeStruct; }
 | 
						|
    bool isArrayType(Id typeId)     const { return getTypeClass(typeId) == OpTypeArray; }
 | 
						|
    bool isAggregateType(Id typeId) const { return isArrayType(typeId) || isStructType(typeId); }
 | 
						|
    bool isSamplerType(Id typeId)   const { return getTypeClass(typeId) == OpTypeSampler; }
 | 
						|
 | 
						|
    bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
 | 
						|
    unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
 | 
						|
 | 
						|
    int getTypeNumColumns(Id typeId) const
 | 
						|
    {
 | 
						|
        assert(isMatrixType(typeId));
 | 
						|
        return getNumTypeComponents(typeId);
 | 
						|
    }
 | 
						|
    int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
 | 
						|
    int getTypeNumRows(Id typeId) const
 | 
						|
    {
 | 
						|
        assert(isMatrixType(typeId));
 | 
						|
        return getNumTypeComponents(getContainedTypeId(typeId));
 | 
						|
    }
 | 
						|
    int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
 | 
						|
 | 
						|
    Dim getDimensionality(Id resultId) const
 | 
						|
    {
 | 
						|
        assert(isSamplerType(getTypeId(resultId)));
 | 
						|
        return (Dim)module.getInstruction(getTypeId(resultId))->getImmediateOperand(1);
 | 
						|
    }
 | 
						|
    bool isArrayedSampler(Id resultId) const
 | 
						|
    {
 | 
						|
        assert(isSamplerType(getTypeId(resultId)));
 | 
						|
        return module.getInstruction(getTypeId(resultId))->getImmediateOperand(3) != 0;
 | 
						|
    }
 | 
						|
 | 
						|
    // For making new constants (will return old constant if the requested one was already made).
 | 
						|
    Id makeBoolConstant(bool b);
 | 
						|
    Id makeIntConstant(Id typeId, unsigned value);
 | 
						|
    Id makeIntConstant(int i)         { return makeIntConstant(makeIntType(32),  (unsigned)i); }
 | 
						|
    Id makeUintConstant(unsigned u)   { return makeIntConstant(makeUintType(32),           u); }
 | 
						|
    Id makeFloatConstant(float f);
 | 
						|
    Id makeDoubleConstant(double d);
 | 
						|
 | 
						|
    // Turn the array of constants into a proper spv constant of the requested type.
 | 
						|
    Id makeCompositeConstant(Id type, std::vector<Id>& comps);
 | 
						|
 | 
						|
    // Methods for adding information outside the CFG.
 | 
						|
    void addEntryPoint(ExecutionModel, Function*);
 | 
						|
    void addExecutionMode(Function*, ExecutionMode mode, int value = -1);
 | 
						|
    void addName(Id, const char* name);
 | 
						|
    void addMemberName(Id, int member, const char* name);
 | 
						|
    void addLine(Id target, Id fileName, int line, int column);
 | 
						|
    void addDecoration(Id, Decoration, int num = -1);
 | 
						|
    void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
 | 
						|
 | 
						|
    // At the end of what block do the next create*() instructions go?
 | 
						|
    void setBuildPoint(Block* bp) { buildPoint = bp; }
 | 
						|
    Block* getBuildPoint() const { return buildPoint; }
 | 
						|
 | 
						|
    // Make the main function.
 | 
						|
    Function* makeMain();
 | 
						|
 | 
						|
    // Return from main. Implicit denotes a return at the very end of main.
 | 
						|
    void makeMainReturn(bool implicit = false) { makeReturn(implicit, 0, true); }
 | 
						|
 | 
						|
    // Close the main function.
 | 
						|
    void closeMain();
 | 
						|
 | 
						|
    // Make a shader-style function, and create its entry block if entry is non-zero.
 | 
						|
    // Return the function, pass back the entry.
 | 
						|
    Function* makeFunctionEntry(Id returnType, const char* name, std::vector<Id>& paramTypes, Block **entry = 0);
 | 
						|
 | 
						|
    // Create a return. Pass whether it is a return form main, and the return
 | 
						|
    // value (if applicable). In the case of an implicit return, no post-return
 | 
						|
    // block is inserted.
 | 
						|
    void makeReturn(bool implicit = false, Id retVal = 0, bool isMain = false);
 | 
						|
 | 
						|
    // Generate all the code needed to finish up a function.
 | 
						|
    void leaveFunction(bool main);
 | 
						|
 | 
						|
    // Create a discard.
 | 
						|
    void makeDiscard();
 | 
						|
 | 
						|
    // Create a global or function local or IO variable.
 | 
						|
    Id createVariable(StorageClass, Id type, const char* name = 0);
 | 
						|
 | 
						|
    // Store into an Id and return the l-value
 | 
						|
    void createStore(Id rValue, Id lValue);
 | 
						|
 | 
						|
    // Load from an Id and return it
 | 
						|
    Id createLoad(Id lValue);
 | 
						|
 | 
						|
    // Create an OpAccessChain instruction
 | 
						|
    Id createAccessChain(StorageClass, Id base, std::vector<Id>& offsets);
 | 
						|
 | 
						|
    // Create an OpCompositeExtract instruction
 | 
						|
    Id createCompositeExtract(Id composite, Id typeId, unsigned index);
 | 
						|
    Id createCompositeExtract(Id composite, Id typeId, std::vector<unsigned>& indexes);
 | 
						|
    Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
 | 
						|
    Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector<unsigned>& indexes);
 | 
						|
 | 
						|
    Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
 | 
						|
    Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
 | 
						|
 | 
						|
    void createNoResultOp(Op);
 | 
						|
    void createNoResultOp(Op, Id operand);
 | 
						|
    void createControlBarrier(unsigned executionScope);
 | 
						|
    void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
 | 
						|
    Id createUnaryOp(Op, Id typeId, Id operand);
 | 
						|
    Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
 | 
						|
    Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
 | 
						|
    Id createOp(Op, Id typeId, std::vector<Id>& operands);
 | 
						|
    Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
 | 
						|
 | 
						|
    // Take an rvalue (source) and a set of channels to extract from it to
 | 
						|
    // make a new rvalue, which is returned.
 | 
						|
    Id createRvalueSwizzle(Id typeId, Id source, std::vector<unsigned>& channels);
 | 
						|
 | 
						|
    // Take a copy of an lvalue (target) and a source of components, and set the
 | 
						|
    // source components into the lvalue where the 'channels' say to put them.
 | 
						|
    // An updated version of the target is returned.
 | 
						|
    // (No true lvalue or stores are used.)
 | 
						|
    Id createLvalueSwizzle(Id typeId, Id target, Id source, std::vector<unsigned>& channels);
 | 
						|
 | 
						|
    // If the value passed in is an instruction and the precision is not EMpNone,
 | 
						|
    // it gets tagged with the requested precision.
 | 
						|
    void setPrecision(Id /* value */, Decoration /* precision */)
 | 
						|
    {
 | 
						|
        // TODO
 | 
						|
    }
 | 
						|
 | 
						|
    // Can smear a scalar to a vector for the following forms:
 | 
						|
    //   - promoteScalar(scalar, vector)  // smear scalar to width of vector
 | 
						|
    //   - promoteScalar(vector, scalar)  // smear scalar to width of vector
 | 
						|
    //   - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
 | 
						|
    //   - promoteScalar(scalar, scalar)  // do nothing
 | 
						|
    // Other forms are not allowed.
 | 
						|
    //
 | 
						|
    // Note: One of the arguments will change, with the result coming back that way rather than 
 | 
						|
    // through the return value.
 | 
						|
    void promoteScalar(Decoration precision, Id& left, Id& right);
 | 
						|
 | 
						|
    // make a value by smearing the scalar to fill the type
 | 
						|
    Id smearScalar(Decoration precision, Id scalarVal, Id);
 | 
						|
 | 
						|
    // Create a call to a built-in function.
 | 
						|
    Id createBuiltinCall(Decoration precision, Id resultType, Id builtins, int entryPoint, std::vector<Id>& args);
 | 
						|
 | 
						|
    // List of parameters used to create a texture operation
 | 
						|
    struct TextureParameters {
 | 
						|
        Id sampler;
 | 
						|
        Id coords;
 | 
						|
        Id bias;
 | 
						|
        Id lod;
 | 
						|
        Id Dref;
 | 
						|
        Id offset;
 | 
						|
        Id gradX;
 | 
						|
        Id gradY;
 | 
						|
    };
 | 
						|
 | 
						|
    // Select the correct texture operation based on all inputs, and emit the correct instruction
 | 
						|
    Id createTextureCall(Decoration precision, Id resultType, bool proj, const TextureParameters&);
 | 
						|
 | 
						|
    // Emit the OpTextureQuery* instruction that was passed in.
 | 
						|
    // Figure out the right return value and type, and return it.
 | 
						|
    Id createTextureQueryCall(Op, const TextureParameters&);
 | 
						|
 | 
						|
    Id createSamplePositionCall(Decoration precision, Id, Id);
 | 
						|
 | 
						|
    Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
 | 
						|
    Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
 | 
						|
 | 
						|
    // Reduction comparision for composites:  For equal and not-equal resulting in a scalar.
 | 
						|
    Id createCompare(Decoration precision, Id, Id, bool /* true if for equal, fales if for not-equal */);
 | 
						|
 | 
						|
    // OpCompositeConstruct
 | 
						|
    Id createCompositeConstruct(Id typeId, std::vector<Id>& constituents);
 | 
						|
 | 
						|
    // vector or scalar constructor
 | 
						|
    Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
 | 
						|
 | 
						|
    // matrix constructor
 | 
						|
    Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
 | 
						|
 | 
						|
    // Helper to use for building nested control flow with if-then-else.
 | 
						|
    class If {
 | 
						|
    public:
 | 
						|
        If(Id condition, Builder& builder);
 | 
						|
        ~If() {}
 | 
						|
 | 
						|
        void makeBeginElse();
 | 
						|
        void makeEndIf();
 | 
						|
 | 
						|
    private:
 | 
						|
        If(const If&);
 | 
						|
        If& operator=(If&);
 | 
						|
 | 
						|
        Builder& builder;
 | 
						|
        Id condition;
 | 
						|
        Function* function;
 | 
						|
        Block* headerBlock;
 | 
						|
        Block* thenBlock;
 | 
						|
        Block* elseBlock;
 | 
						|
        Block* mergeBlock;
 | 
						|
    };
 | 
						|
 | 
						|
    // Make a switch statement.  A switch has 'numSegments' of pieces of code, not containing
 | 
						|
    // any case/default labels, all separated by one or more case/default labels.  Each possible
 | 
						|
    // case value v is a jump to the caseValues[v] segment.  The defaultSegment is also in this
 | 
						|
    // number space.  How to compute the value is given by 'condition', as in switch(condition).
 | 
						|
    //
 | 
						|
    // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
 | 
						|
    //
 | 
						|
    // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
 | 
						|
    //
 | 
						|
    // Returns the right set of basic blocks to start each code segment with, so that the caller's
 | 
						|
    // recursion stack can hold the memory for it.
 | 
						|
    //
 | 
						|
    void makeSwitch(Id condition, int numSegments, std::vector<int>& caseValues, std::vector<int>& valueToSegment, int defaultSegment,
 | 
						|
                    std::vector<Block*>& segmentBB);  // return argument
 | 
						|
 | 
						|
    // Add a branch to the innermost switch's merge block.
 | 
						|
    void addSwitchBreak();
 | 
						|
 | 
						|
    // Move to the next code segment, passing in the return argument in makeSwitch()
 | 
						|
    void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
 | 
						|
 | 
						|
    // Finish off the innermost switch.
 | 
						|
    void endSwitch(std::vector<Block*>& segmentBB);
 | 
						|
 | 
						|
    // Start the beginning of a new loop, and prepare the builder to
 | 
						|
    // generate code for the loop test.
 | 
						|
    // The loopTestFirst parameter is true when the loop test executes before
 | 
						|
    // the body.  (It is false for do-while loops.)
 | 
						|
    void makeNewLoop(bool loopTestFirst);
 | 
						|
 | 
						|
    // Add the branch for the loop test, based on the given condition.
 | 
						|
    // The true branch goes to the first block in the loop body, and
 | 
						|
    // the false branch goes to the loop's merge block.  The builder insertion
 | 
						|
    // point will be placed at the start of the body.
 | 
						|
    void createLoopTestBranch(Id condition);
 | 
						|
 | 
						|
    // Generate an unconditional branch to the loop body.  The builder insertion
 | 
						|
    // point will be placed at the start of the body.  Use this when there is
 | 
						|
    // no loop test.
 | 
						|
    void createBranchToBody();
 | 
						|
 | 
						|
    // Add a branch to the test of the current (innermost) loop.
 | 
						|
    // The way we generate code, that's also the loop header.
 | 
						|
    void createLoopContinue();
 | 
						|
 | 
						|
    // Add an exit (e.g. "break") for the innermost loop that you're in
 | 
						|
    void createLoopExit();
 | 
						|
 | 
						|
    // Close the innermost loop that you're in
 | 
						|
    void closeLoop();
 | 
						|
 | 
						|
    //
 | 
						|
    // Access chain design for an R-Value vs. L-Value:
 | 
						|
    //
 | 
						|
    // There is a single access chain the builder is building at
 | 
						|
    // any particular time.  Such a chain can be used to either to a load or
 | 
						|
    // a store, when desired.
 | 
						|
    //
 | 
						|
    // Expressions can be r-values, l-values, or both, or only r-values:
 | 
						|
    //    a[b.c].d = ....  // l-value
 | 
						|
    //    ... = a[b.c].d;  // r-value, that also looks like an l-value
 | 
						|
    //    ++a[b.c].d;      // r-value and l-value
 | 
						|
    //    (x + y)[2];      // r-value only, can't possibly be l-value
 | 
						|
    //
 | 
						|
    // Computing an r-value means generating code.  Hence,
 | 
						|
    // r-values should only be computed when they are needed, not speculatively.
 | 
						|
    //
 | 
						|
    // Computing an l-value means saving away information for later use in the compiler,
 | 
						|
    // no code is generated until the l-value is later dereferenced.  It is okay
 | 
						|
    // to speculatively generate an l-value, just not okay to speculatively dereference it.
 | 
						|
    //
 | 
						|
    // The base of the access chain (the left-most variable or expression
 | 
						|
    // from which everything is based) can be set either as an l-value
 | 
						|
    // or as an r-value.  Most efficient would be to set an l-value if one
 | 
						|
    // is available.  If an expression was evaluated, the resulting r-value
 | 
						|
    // can be set as the chain base.
 | 
						|
    //
 | 
						|
    // The users of this single access chain can save and restore if they
 | 
						|
    // want to nest or manage multiple chains.
 | 
						|
    //
 | 
						|
 | 
						|
    struct AccessChain {
 | 
						|
        Id base;                     // for l-values, pointer to the base object, for r-values, the base object
 | 
						|
        std::vector<Id> indexChain;
 | 
						|
        Id instr;                    // the instruction that generates this access chain
 | 
						|
        std::vector<unsigned> swizzle;
 | 
						|
        Id component;                // a dynamic component index, can coexist with a swizzle, done after the swizzle
 | 
						|
        Id resultType;               // dereferenced type, to be exclusive of swizzles
 | 
						|
        bool isRValue;
 | 
						|
    };
 | 
						|
 | 
						|
    //
 | 
						|
    // the SPIR-V builder maintains a single active chain that
 | 
						|
    // the following methods operated on
 | 
						|
    //
 | 
						|
 | 
						|
    // for external save and restore
 | 
						|
    AccessChain getAccessChain() { return accessChain; }
 | 
						|
    void setAccessChain(AccessChain newChain) { accessChain = newChain; }
 | 
						|
 | 
						|
    // clear accessChain
 | 
						|
    void clearAccessChain();
 | 
						|
 | 
						|
    // set new base as an l-value base
 | 
						|
    void setAccessChainLValue(Id lValue)
 | 
						|
    {
 | 
						|
        assert(isPointer(lValue));
 | 
						|
        accessChain.base = lValue;
 | 
						|
        accessChain.resultType = getContainedTypeId(getTypeId(lValue));
 | 
						|
    }
 | 
						|
 | 
						|
    // set new base value as an r-value
 | 
						|
    void setAccessChainRValue(Id rValue)
 | 
						|
    {
 | 
						|
        accessChain.isRValue = true;
 | 
						|
        accessChain.base = rValue;
 | 
						|
        accessChain.resultType = getTypeId(rValue);
 | 
						|
    }
 | 
						|
 | 
						|
    // push offset onto the end of the chain
 | 
						|
    void accessChainPush(Id offset, Id newType)
 | 
						|
    {
 | 
						|
        accessChain.indexChain.push_back(offset);
 | 
						|
        accessChain.resultType = newType;
 | 
						|
    }
 | 
						|
 | 
						|
    // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
 | 
						|
    void accessChainPushSwizzle(std::vector<unsigned>& swizzle);
 | 
						|
 | 
						|
    // push a variable component selection onto the access chain; supporting only one, so unsided
 | 
						|
    void accessChainPushComponent(Id component) { accessChain.component = component; }
 | 
						|
 | 
						|
    // use accessChain and swizzle to store value
 | 
						|
    void accessChainStore(Id rvalue);
 | 
						|
 | 
						|
    // use accessChain and swizzle to load an r-value
 | 
						|
    Id accessChainLoad(Decoration precision);
 | 
						|
 | 
						|
    // get the direct pointer for an l-value
 | 
						|
    Id accessChainGetLValue();
 | 
						|
 | 
						|
    void dump(std::vector<unsigned int>&) const;
 | 
						|
 | 
						|
protected:
 | 
						|
    Id findScalarConstant(Op typeClass, Id typeId, unsigned value) const;
 | 
						|
    Id findScalarConstant(Op typeClass, Id typeId, unsigned v1, unsigned v2) const;
 | 
						|
    Id findCompositeConstant(Op typeClass, std::vector<Id>& comps) const;
 | 
						|
    Id collapseAccessChain();
 | 
						|
    void simplifyAccessChainSwizzle();
 | 
						|
    void mergeAccessChainSwizzle();
 | 
						|
    void createAndSetNoPredecessorBlock(const char*);
 | 
						|
    void createBranch(Block* block);
 | 
						|
    void createMerge(Op, Block*, unsigned int control);
 | 
						|
    void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
 | 
						|
    void dumpInstructions(std::vector<unsigned int>&, const std::vector<Instruction*>&) const;
 | 
						|
 | 
						|
    struct Loop; // Defined below.
 | 
						|
    void createBranchToLoopHeaderFromInside(const Loop& loop);
 | 
						|
 | 
						|
    SourceLanguage source;
 | 
						|
    int sourceVersion;
 | 
						|
    std::vector<const char*> extensions;
 | 
						|
    AddressingModel addressModel;
 | 
						|
    MemoryModel memoryModel;
 | 
						|
    int builderNumber;
 | 
						|
    Module module;
 | 
						|
    Block* buildPoint;
 | 
						|
    Id uniqueId;
 | 
						|
    Function* mainFunction;
 | 
						|
    Block* stageExit;
 | 
						|
    AccessChain accessChain;
 | 
						|
 | 
						|
    // special blocks of instructions for output
 | 
						|
    std::vector<Instruction*> imports;
 | 
						|
    std::vector<Instruction*> entryPoints;
 | 
						|
    std::vector<Instruction*> executionModes;
 | 
						|
    std::vector<Instruction*> names;
 | 
						|
    std::vector<Instruction*> lines;
 | 
						|
    std::vector<Instruction*> decorations;
 | 
						|
    std::vector<Instruction*> constantsTypesGlobals;
 | 
						|
    std::vector<Instruction*> externals;
 | 
						|
 | 
						|
     // not output, internally used for quick & dirty canonical (unique) creation
 | 
						|
    std::vector<Instruction*> groupedConstants[OpConstant];  // all types appear before OpConstant
 | 
						|
    std::vector<Instruction*> groupedTypes[OpConstant];
 | 
						|
 | 
						|
    // stack of switches
 | 
						|
    std::stack<Block*> switchMerges;
 | 
						|
 | 
						|
    // Data that needs to be kept in order to properly handle loops.
 | 
						|
    struct Loop {
 | 
						|
        // Constructs a default Loop structure containing new header, merge, and
 | 
						|
        // body blocks for the current function.
 | 
						|
        // The testFirst argument indicates whether the loop test executes at
 | 
						|
        // the top of the loop rather than at the bottom.  In the latter case,
 | 
						|
        // also create a phi instruction whose value indicates whether we're on
 | 
						|
        // the first iteration of the loop.  The phi instruction is initialized
 | 
						|
        // with no values or predecessor operands.
 | 
						|
        Loop(Builder& builder, bool testFirst);
 | 
						|
 | 
						|
        // The function containing the loop.
 | 
						|
        Function* const function;
 | 
						|
        // The header is the first block generated for the loop.
 | 
						|
        // It dominates all the blocks in the loop, i.e. it is always
 | 
						|
        // executed before any others.
 | 
						|
        // If the loop test is executed before the body (as in "while" and
 | 
						|
        // "for" loops), then the header begins with the test code.
 | 
						|
        // Otherwise, the loop is a "do-while" loop and the header contains the
 | 
						|
        // start of the body of the loop (if the body exists).
 | 
						|
        Block* const header;
 | 
						|
        // The merge block marks the end of the loop.  Control is transferred
 | 
						|
        // to the merge block when either the loop test fails, or when a
 | 
						|
        // nested "break" is encountered.
 | 
						|
        Block* const merge;
 | 
						|
        // The body block is the first basic block in the body of the loop, i.e.
 | 
						|
        // the code that is to be repeatedly executed, aside from loop control.
 | 
						|
        // This member is null until we generate code that references the loop
 | 
						|
        // body block.
 | 
						|
        Block* const body;
 | 
						|
        // True when the loop test executes before the body.
 | 
						|
        const bool testFirst;
 | 
						|
        // When the test executes after the body, this is defined as the phi
 | 
						|
        // instruction that tells us whether we are on the first iteration of
 | 
						|
        // the loop.  Otherwise this is null.
 | 
						|
        Instruction* const isFirstIteration;
 | 
						|
    };
 | 
						|
 | 
						|
    // Our loop stack.
 | 
						|
    std::stack<Loop> loops;
 | 
						|
};  // end Builder class
 | 
						|
 | 
						|
// Use for non-fatal notes about what's not complete
 | 
						|
void TbdFunctionality(const char*);
 | 
						|
 | 
						|
// Use for fatal missing functionality
 | 
						|
void MissingFunctionality(const char*);
 | 
						|
 | 
						|
};  // end spv namespace
 | 
						|
 | 
						|
#endif // SpvBuilder_H
 |