mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
	
	
		
			839 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			839 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | //
 | ||
|  | // Copyright (C) 2014-2015 LunarG, Inc.
 | ||
|  | // Copyright (C) 2015-2020 Google, Inc.
 | ||
|  | // Copyright (C) 2017 ARM Limited.
 | ||
|  | // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
 | ||
|  | //
 | ||
|  | // 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.
 | ||
|  | 
 | ||
|  | //
 | ||
|  | // "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 "Logger.h"
 | ||
|  | #include "spirv.hpp"
 | ||
|  | #include "spvIR.h"
 | ||
|  | 
 | ||
|  | #include <algorithm>
 | ||
|  | #include <map>
 | ||
|  | #include <memory>
 | ||
|  | #include <set>
 | ||
|  | #include <sstream>
 | ||
|  | #include <stack>
 | ||
|  | #include <unordered_map>
 | ||
|  | #include <map>
 | ||
|  | 
 | ||
|  | namespace spv { | ||
|  | 
 | ||
|  | typedef enum { | ||
|  |     Spv_1_0 = (1 << 16), | ||
|  |     Spv_1_1 = (1 << 16) | (1 << 8), | ||
|  |     Spv_1_2 = (1 << 16) | (2 << 8), | ||
|  |     Spv_1_3 = (1 << 16) | (3 << 8), | ||
|  |     Spv_1_4 = (1 << 16) | (4 << 8), | ||
|  |     Spv_1_5 = (1 << 16) | (5 << 8), | ||
|  | } SpvVersion; | ||
|  | 
 | ||
|  | class Builder { | ||
|  | public: | ||
|  |     Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); | ||
|  |     virtual ~Builder(); | ||
|  | 
 | ||
|  |     static const int maxMatrixSize = 4; | ||
|  | 
 | ||
|  |     unsigned int getSpvVersion() const { return spvVersion; } | ||
|  | 
 | ||
|  |     void setSource(spv::SourceLanguage lang, int version) | ||
|  |     { | ||
|  |         source = lang; | ||
|  |         sourceVersion = version; | ||
|  |     } | ||
|  |     spv::Id getStringId(const std::string& str) | ||
|  |     { | ||
|  |         auto sItr = stringIds.find(str); | ||
|  |         if (sItr != stringIds.end()) | ||
|  |             return sItr->second; | ||
|  |         spv::Id strId = getUniqueId(); | ||
|  |         Instruction* fileString = new Instruction(strId, NoType, OpString); | ||
|  |         const char* file_c_str = str.c_str(); | ||
|  |         fileString->addStringOperand(file_c_str); | ||
|  |         strings.push_back(std::unique_ptr<Instruction>(fileString)); | ||
|  |         module.mapInstruction(fileString); | ||
|  |         stringIds[file_c_str] = strId; | ||
|  |         return strId; | ||
|  |     } | ||
|  |     void setSourceFile(const std::string& file) | ||
|  |     { | ||
|  |         sourceFileStringId = getStringId(file); | ||
|  |     } | ||
|  |     void setSourceText(const std::string& text) { sourceText = text; } | ||
|  |     void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } | ||
|  |     void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } | ||
|  |     void setEmitOpLines() { emitOpLines = true; } | ||
|  |     void addExtension(const char* ext) { extensions.insert(ext); } | ||
|  |     void removeExtension(const char* ext) | ||
|  |     { | ||
|  |         extensions.erase(ext); | ||
|  |     } | ||
|  |     void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion) | ||
|  |     { | ||
|  |         if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion)) | ||
|  |             addExtension(ext); | ||
|  |     } | ||
|  |     void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion) | ||
|  |     { | ||
|  |         removeExtension(baseExt); | ||
|  |         addIncorporatedExtension(promoExt, incorporatedVersion); | ||
|  |     } | ||
|  |     void addInclude(const std::string& name, const std::string& text) | ||
|  |     { | ||
|  |         spv::Id incId = getStringId(name); | ||
|  |         includeFiles[incId] = &text; | ||
|  |     } | ||
|  |     Id import(const char*); | ||
|  |     void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) | ||
|  |     { | ||
|  |         addressModel = addr; | ||
|  |         memoryModel = mem; | ||
|  |     } | ||
|  | 
 | ||
|  |     void addCapability(spv::Capability cap) { capabilities.insert(cap); } | ||
|  | 
 | ||
|  |     // 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; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Generate OpLine for non-filename-based #line directives (ie no filename
 | ||
|  |     // seen yet): Log the current line, and if different than the last one,
 | ||
|  |     // issue a new OpLine using the new line and current source file name.
 | ||
|  |     void setLine(int line); | ||
|  | 
 | ||
|  |     // If filename null, generate OpLine for non-filename-based line directives,
 | ||
|  |     // else do filename-based: Log the current line and file, and if different
 | ||
|  |     // than the last one, issue a new OpLine using the new line and file
 | ||
|  |     // name.
 | ||
|  |     void setLine(int line, const char* filename); | ||
|  |     // Low-level OpLine. See setLine() for a layered helper.
 | ||
|  |     void addLine(Id fileName, int line, int column); | ||
|  | 
 | ||
|  |     // For creating new types (will return old type if the requested one was already made).
 | ||
|  |     Id makeVoidType(); | ||
|  |     Id makeBoolType(); | ||
|  |     Id makePointer(StorageClass, Id pointee); | ||
|  |     Id makeForwardPointer(StorageClass); | ||
|  |     Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); | ||
|  |     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(const std::vector<Id>& members, const char*); | ||
|  |     Id makeStructResultType(Id type0, Id type1); | ||
|  |     Id makeVectorType(Id component, int size); | ||
|  |     Id makeMatrixType(Id component, int cols, int rows); | ||
|  |     Id makeArrayType(Id element, Id sizeId, int stride);  // 0 stride means no stride decoration
 | ||
|  |     Id makeRuntimeArray(Id element); | ||
|  |     Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes); | ||
|  |     Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format); | ||
|  |     Id makeSamplerType(); | ||
|  |     Id makeSampledImageType(Id imageType); | ||
|  |     Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols); | ||
|  | 
 | ||
|  |     // accelerationStructureNV type
 | ||
|  |     Id makeAccelerationStructureType(); | ||
|  |     // rayQueryEXT type
 | ||
|  |     Id makeRayQueryType(); | ||
|  | 
 | ||
|  |     // 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 getNumTypeConstituents(Id typeId) const; | ||
|  |     int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } | ||
|  |     Id getScalarTypeId(Id typeId) const; | ||
|  |     Id getContainedTypeId(Id typeId) const; | ||
|  |     Id getContainedTypeId(Id typeId, int) const; | ||
|  |     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } | ||
|  |     ImageFormat getImageTypeFormat(Id typeId) const | ||
|  |         { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } | ||
|  | 
 | ||
|  |     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 isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); } | ||
|  |     bool isAggregate(Id resultId)    const { return isAggregateType(getTypeId(resultId)); } | ||
|  |     bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } | ||
|  | 
 | ||
|  |     bool isBoolType(Id typeId) | ||
|  |         { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } | ||
|  |     bool isIntType(Id typeId)          const | ||
|  |         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } | ||
|  |     bool isUintType(Id typeId)         const | ||
|  |         { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } | ||
|  |     bool isFloatType(Id typeId)        const { return getTypeClass(typeId) == OpTypeFloat; } | ||
|  |     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; } | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |     bool isCooperativeMatrixType(Id typeId)const { return false; } | ||
|  | #else
 | ||
|  |     bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; } | ||
|  | #endif
 | ||
|  |     bool isAggregateType(Id typeId)    const | ||
|  |         { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); } | ||
|  |     bool isImageType(Id typeId)        const { return getTypeClass(typeId) == OpTypeImage; } | ||
|  |     bool isSamplerType(Id typeId)      const { return getTypeClass(typeId) == OpTypeSampler; } | ||
|  |     bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } | ||
|  |     bool containsType(Id typeId, Op typeOp, unsigned int width) const; | ||
|  |     bool containsPhysicalStorageBufferOrArray(Id typeId) const; | ||
|  | 
 | ||
|  |     bool isConstantOpCode(Op opcode) const; | ||
|  |     bool isSpecConstantOpCode(Op opcode) const; | ||
|  |     bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } | ||
|  |     bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } | ||
|  |     bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } | ||
|  |     unsigned int getConstantScalar(Id resultId) const | ||
|  |         { return module.getInstruction(resultId)->getImmediateOperand(0); } | ||
|  |     StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } | ||
|  | 
 | ||
|  |     int getScalarTypeWidth(Id typeId) const | ||
|  |     { | ||
|  |         Id scalarTypeId = getScalarTypeId(typeId); | ||
|  |         assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); | ||
|  |         return module.getInstruction(scalarTypeId)->getImmediateOperand(0); | ||
|  |     } | ||
|  | 
 | ||
|  |     int getTypeNumColumns(Id typeId) const | ||
|  |     { | ||
|  |         assert(isMatrixType(typeId)); | ||
|  |         return getNumTypeConstituents(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 getTypeDimensionality(Id typeId) const | ||
|  |     { | ||
|  |         assert(isImageType(typeId)); | ||
|  |         return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); | ||
|  |     } | ||
|  |     Id getImageType(Id resultId) const | ||
|  |     { | ||
|  |         Id typeId = getTypeId(resultId); | ||
|  |         assert(isImageType(typeId) || isSampledImageType(typeId)); | ||
|  |         return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; | ||
|  |     } | ||
|  |     bool isArrayedImageType(Id typeId) const | ||
|  |     { | ||
|  |         assert(isImageType(typeId)); | ||
|  |         return module.getInstruction(typeId)->getImmediateOperand(3) != 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     // For making new constants (will return old constant if the requested one was already made).
 | ||
|  |     Id makeBoolConstant(bool b, bool specConstant = false); | ||
|  |     Id makeInt8Constant(int i, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeIntType(8),  (unsigned)i, specConstant); } | ||
|  |     Id makeUint8Constant(unsigned u, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeUintType(8),           u, specConstant); } | ||
|  |     Id makeInt16Constant(int i, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeIntType(16),  (unsigned)i, specConstant); } | ||
|  |     Id makeUint16Constant(unsigned u, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeUintType(16),           u, specConstant); } | ||
|  |     Id makeIntConstant(int i, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeIntType(32),  (unsigned)i, specConstant); } | ||
|  |     Id makeUintConstant(unsigned u, bool specConstant = false) | ||
|  |         { return makeIntConstant(makeUintType(32),           u, specConstant); } | ||
|  |     Id makeInt64Constant(long long i, bool specConstant = false) | ||
|  |         { return makeInt64Constant(makeIntType(64),  (unsigned long long)i, specConstant); } | ||
|  |     Id makeUint64Constant(unsigned long long u, bool specConstant = false) | ||
|  |         { return makeInt64Constant(makeUintType(64),                     u, specConstant); } | ||
|  |     Id makeFloatConstant(float f, bool specConstant = false); | ||
|  |     Id makeDoubleConstant(double d, bool specConstant = false); | ||
|  |     Id makeFloat16Constant(float f16, bool specConstant = false); | ||
|  |     Id makeFpConstant(Id type, double d, bool specConstant = false); | ||
|  | 
 | ||
|  |     // Turn the array of constants into a proper spv constant of the requested type.
 | ||
|  |     Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false); | ||
|  | 
 | ||
|  |     // Methods for adding information outside the CFG.
 | ||
|  |     Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); | ||
|  |     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); | ||
|  |     void addName(Id, const char* name); | ||
|  |     void addMemberName(Id, int member, const char* name); | ||
|  |     void addDecoration(Id, Decoration, int num = -1); | ||
|  |     void addDecoration(Id, Decoration, const char*); | ||
|  |     void addDecorationId(Id id, Decoration, Id idDecoration); | ||
|  |     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); | ||
|  |     void addMemberDecoration(Id, unsigned int member, Decoration, const char*); | ||
|  | 
 | ||
|  |     // 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 entry-point function. The returned pointer is only valid
 | ||
|  |     // for the lifetime of this builder.
 | ||
|  |     Function* makeEntryPoint(const char*); | ||
|  | 
 | ||
|  |     // Make a shader-style function, and create its entry block if entry is non-zero.
 | ||
|  |     // Return the function, pass back the entry.
 | ||
|  |     // The returned pointer is only valid for the lifetime of this builder.
 | ||
|  |     Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, | ||
|  |         const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0); | ||
|  | 
 | ||
|  |     // Create a return. An 'implicit' return is one not appearing in the source
 | ||
|  |     // code.  In the case of an implicit return, no post-return block is inserted.
 | ||
|  |     void makeReturn(bool implicit, Id retVal = 0); | ||
|  | 
 | ||
|  |     // Generate all the code needed to finish up a function.
 | ||
|  |     void leaveFunction(); | ||
|  | 
 | ||
|  |     // Create a discard.
 | ||
|  |     void makeDiscard(); | ||
|  | 
 | ||
|  |     // Create a global or function local or IO variable.
 | ||
|  |     Id createVariable(StorageClass, Id type, const char* name = 0, Id initializer = NoResult); | ||
|  | 
 | ||
|  |     // Create an intermediate with an undefined value.
 | ||
|  |     Id createUndefined(Id type); | ||
|  | 
 | ||
|  |     // Store into an Id and return the l-value
 | ||
|  |     void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, | ||
|  |         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); | ||
|  | 
 | ||
|  |     // Load from an Id and return it
 | ||
|  |     Id createLoad(Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, | ||
|  |         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); | ||
|  | 
 | ||
|  |     // Create an OpAccessChain instruction
 | ||
|  |     Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets); | ||
|  | 
 | ||
|  |     // Create an OpArrayLength instruction
 | ||
|  |     Id createArrayLength(Id base, unsigned int member); | ||
|  | 
 | ||
|  |     // Create an OpCooperativeMatrixLengthNV instruction
 | ||
|  |     Id createCooperativeMatrixLength(Id type); | ||
|  | 
 | ||
|  |     // Create an OpCompositeExtract instruction
 | ||
|  |     Id createCompositeExtract(Id composite, Id typeId, unsigned index); | ||
|  |     Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes); | ||
|  |     Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); | ||
|  |     Id createCompositeInsert(Id object, Id composite, Id typeId, const 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 createNoResultOp(Op, const std::vector<Id>& operands); | ||
|  |     void createNoResultOp(Op, const std::vector<IdImmediate>& operands); | ||
|  |     void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); | ||
|  |     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, const std::vector<Id>& operands); | ||
|  |     Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands); | ||
|  |     Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&); | ||
|  |     Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals); | ||
|  | 
 | ||
|  |     // Take an rvalue (source) and a set of channels to extract from it to
 | ||
|  |     // make a new rvalue, which is returned.
 | ||
|  |     Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const 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, const std::vector<unsigned>& channels); | ||
|  | 
 | ||
|  |     // If both the id and precision are valid, the id
 | ||
|  |     // gets tagged with the requested precision.
 | ||
|  |     // The passed in id is always the returned id, to simplify use patterns.
 | ||
|  |     Id setPrecision(Id id, Decoration precision) | ||
|  |     { | ||
|  |         if (precision != NoPrecision && id != NoResult) | ||
|  |             addDecoration(id, precision); | ||
|  | 
 | ||
|  |         return id; | ||
|  |     } | ||
|  | 
 | ||
|  |     // 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.
 | ||
|  |     //
 | ||
|  |     // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
 | ||
|  |     // The type of the created vector is a vector of components of the same type as the scalar.
 | ||
|  |     //
 | ||
|  |     // 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.
 | ||
|  |     // vectorType should be the correct type for making a vector of scalarVal.
 | ||
|  |     // (No conversions are done.)
 | ||
|  |     Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); | ||
|  | 
 | ||
|  |     // Create a call to a built-in function.
 | ||
|  |     Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const 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 offsets; | ||
|  |         Id gradX; | ||
|  |         Id gradY; | ||
|  |         Id sample; | ||
|  |         Id component; | ||
|  |         Id texelOut; | ||
|  |         Id lodClamp; | ||
|  |         Id granularity; | ||
|  |         Id coarse; | ||
|  |         bool nonprivate; | ||
|  |         bool volatil; | ||
|  |     }; | ||
|  | 
 | ||
|  |     // Select the correct texture operation based on all inputs, and emit the correct instruction
 | ||
|  |     Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, | ||
|  |         bool noImplicit, const TextureParameters&, ImageOperandsMask); | ||
|  | 
 | ||
|  |     // Emit the OpTextureQuery* instruction that was passed in.
 | ||
|  |     // Figure out the right return value and type, and return it.
 | ||
|  |     Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); | ||
|  | 
 | ||
|  |     Id createSamplePositionCall(Decoration precision, Id, Id); | ||
|  | 
 | ||
|  |     Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); | ||
|  |     Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); | ||
|  | 
 | ||
|  |     // Reduction comparison for composites:  For equal and not-equal resulting in a scalar.
 | ||
|  |     Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); | ||
|  | 
 | ||
|  |     // OpCompositeConstruct
 | ||
|  |     Id createCompositeConstruct(Id typeId, const 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, unsigned int ctrl, Builder& builder); | ||
|  |         ~If() {} | ||
|  | 
 | ||
|  |         void makeBeginElse(); | ||
|  |         void makeEndIf(); | ||
|  | 
 | ||
|  |     private: | ||
|  |         If(const If&); | ||
|  |         If& operator=(If&); | ||
|  | 
 | ||
|  |         Builder& builder; | ||
|  |         Id condition; | ||
|  |         unsigned int control; | ||
|  |         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, unsigned int control, int numSegments, const std::vector<int>& caseValues, | ||
|  |                     const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); | ||
|  | 
 | ||
|  |     // 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); | ||
|  | 
 | ||
|  |     struct LoopBlocks { | ||
|  |         LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : | ||
|  |             head(head), body(body), merge(merge), continue_target(continue_target) { } | ||
|  |         Block &head, &body, &merge, &continue_target; | ||
|  |     private: | ||
|  |         LoopBlocks(); | ||
|  |         LoopBlocks& operator=(const LoopBlocks&) = delete; | ||
|  |     }; | ||
|  | 
 | ||
|  |     // Start a new loop and prepare the builder to generate code for it.  Until
 | ||
|  |     // closeLoop() is called for this loop, createLoopContinue() and
 | ||
|  |     // createLoopExit() will target its corresponding blocks.
 | ||
|  |     LoopBlocks& makeNewLoop(); | ||
|  | 
 | ||
|  |     // Create a new block in the function containing the build point.  Memory is
 | ||
|  |     // owned by the function object.
 | ||
|  |     Block& makeNewBlock(); | ||
|  | 
 | ||
|  |     // Add a branch to the continue_target of the current (innermost) loop.
 | ||
|  |     void createLoopContinue(); | ||
|  | 
 | ||
|  |     // Add an exit (e.g. "break") from the innermost loop that we're currently
 | ||
|  |     // 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;                      // cache the instruction that generates this access chain
 | ||
|  |         std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
 | ||
|  |         Id component;                  // a dynamic component index, can coexist with a swizzle,
 | ||
|  |                                        // done after the swizzle, NoResult if not present
 | ||
|  |         Id preSwizzleBaseType;         // dereferenced type, before swizzle or component is applied;
 | ||
|  |                                        // NoType unless a swizzle or component is present
 | ||
|  |         bool isRValue;                 // true if 'base' is an r-value, otherwise, base is an l-value
 | ||
|  |         unsigned int alignment;        // bitwise OR of alignment values passed in. Accumulates worst alignment.
 | ||
|  |                                        // Only tracks base and (optional) component selection alignment.
 | ||
|  | 
 | ||
|  |         // Accumulate whether anything in the chain of structures has coherent decorations.
 | ||
|  |         struct CoherentFlags { | ||
|  |             CoherentFlags() { clear(); } | ||
|  | #ifdef GLSLANG_WEB
 | ||
|  |             void clear() { } | ||
|  |             bool isVolatile() const { return false; } | ||
|  |             CoherentFlags operator |=(const CoherentFlags &other) { return *this; } | ||
|  | #else
 | ||
|  |             bool isVolatile() const { return volatil; } | ||
|  |             bool anyCoherent() const { | ||
|  |                 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || | ||
|  |                     subgroupcoherent || shadercallcoherent; | ||
|  |             } | ||
|  | 
 | ||
|  |             unsigned coherent : 1; | ||
|  |             unsigned devicecoherent : 1; | ||
|  |             unsigned queuefamilycoherent : 1; | ||
|  |             unsigned workgroupcoherent : 1; | ||
|  |             unsigned subgroupcoherent : 1; | ||
|  |             unsigned shadercallcoherent : 1; | ||
|  |             unsigned nonprivate : 1; | ||
|  |             unsigned volatil : 1; | ||
|  |             unsigned isImage : 1; | ||
|  | 
 | ||
|  |             void clear() { | ||
|  |                 coherent = 0; | ||
|  |                 devicecoherent = 0; | ||
|  |                 queuefamilycoherent = 0; | ||
|  |                 workgroupcoherent = 0; | ||
|  |                 subgroupcoherent = 0; | ||
|  |                 shadercallcoherent = 0; | ||
|  |                 nonprivate = 0; | ||
|  |                 volatil = 0; | ||
|  |                 isImage = 0; | ||
|  |             } | ||
|  | 
 | ||
|  |             CoherentFlags operator |=(const CoherentFlags &other) { | ||
|  |                 coherent |= other.coherent; | ||
|  |                 devicecoherent |= other.devicecoherent; | ||
|  |                 queuefamilycoherent |= other.queuefamilycoherent; | ||
|  |                 workgroupcoherent |= other.workgroupcoherent; | ||
|  |                 subgroupcoherent |= other.subgroupcoherent; | ||
|  |                 shadercallcoherent |= other.shadercallcoherent; | ||
|  |                 nonprivate |= other.nonprivate; | ||
|  |                 volatil |= other.volatil; | ||
|  |                 isImage |= other.isImage; | ||
|  |                 return *this; | ||
|  |             } | ||
|  | #endif
 | ||
|  |         }; | ||
|  |         CoherentFlags coherentFlags; | ||
|  |     }; | ||
|  | 
 | ||
|  |     //
 | ||
|  |     // the SPIR-V builder maintains a single active chain that
 | ||
|  |     // the following methods operate 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; | ||
|  |     } | ||
|  | 
 | ||
|  |     // set new base value as an r-value
 | ||
|  |     void setAccessChainRValue(Id rValue) | ||
|  |     { | ||
|  |         accessChain.isRValue = true; | ||
|  |         accessChain.base = rValue; | ||
|  |     } | ||
|  | 
 | ||
|  |     // push offset onto the end of the chain
 | ||
|  |     void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) | ||
|  |     { | ||
|  |         accessChain.indexChain.push_back(offset); | ||
|  |         accessChain.coherentFlags |= coherentFlags; | ||
|  |         accessChain.alignment |= alignment; | ||
|  |     } | ||
|  | 
 | ||
|  |     // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
 | ||
|  |     void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, | ||
|  |         AccessChain::CoherentFlags coherentFlags, unsigned int alignment); | ||
|  | 
 | ||
|  |     // push a dynamic component selection onto the access chain, only applicable with a
 | ||
|  |     // non-trivial swizzle or no swizzle
 | ||
|  |     void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, | ||
|  |         unsigned int alignment) | ||
|  |     { | ||
|  |         if (accessChain.swizzle.size() != 1) { | ||
|  |             accessChain.component = component; | ||
|  |             if (accessChain.preSwizzleBaseType == NoType) | ||
|  |                 accessChain.preSwizzleBaseType = preSwizzleBaseType; | ||
|  |         } | ||
|  |         accessChain.coherentFlags |= coherentFlags; | ||
|  |         accessChain.alignment |= alignment; | ||
|  |     } | ||
|  | 
 | ||
|  |     // use accessChain and swizzle to store value
 | ||
|  |     void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, | ||
|  |         spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); | ||
|  | 
 | ||
|  |     // use accessChain and swizzle to load an r-value
 | ||
|  |     Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, | ||
|  |         spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, | ||
|  |             unsigned int alignment = 0); | ||
|  | 
 | ||
|  |     // Return whether or not the access chain can be represented in SPIR-V
 | ||
|  |     // as an l-value.
 | ||
|  |     // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be.
 | ||
|  |     bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } | ||
|  | 
 | ||
|  |     // get the direct pointer for an l-value
 | ||
|  |     Id accessChainGetLValue(); | ||
|  | 
 | ||
|  |     // Get the inferred SPIR-V type of the result of the current access chain,
 | ||
|  |     // based on the type of the base and the chain of dereferences.
 | ||
|  |     Id accessChainGetInferredType(); | ||
|  | 
 | ||
|  |     // Add capabilities, extensions, remove unneeded decorations, etc.,
 | ||
|  |     // based on the resulting SPIR-V.
 | ||
|  |     void postProcess(); | ||
|  | 
 | ||
|  |     // Prune unreachable blocks in the CFG and remove unneeded decorations.
 | ||
|  |     void postProcessCFG(); | ||
|  | 
 | ||
|  | #ifndef GLSLANG_WEB
 | ||
|  |     // Add capabilities, extensions based on instructions in the module.
 | ||
|  |     void postProcessFeatures(); | ||
|  |     // Hook to visit each instruction in a block in a function
 | ||
|  |     void postProcess(Instruction&); | ||
|  |     // Hook to visit each non-32-bit sized float/int operation in a block.
 | ||
|  |     void postProcessType(const Instruction&, spv::Id typeId); | ||
|  | #endif
 | ||
|  | 
 | ||
|  |     void dump(std::vector<unsigned int>&) const; | ||
|  | 
 | ||
|  |     void createBranch(Block* block); | ||
|  |     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); | ||
|  |     void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, | ||
|  |         const std::vector<unsigned int>& operands); | ||
|  | 
 | ||
|  |     // Sets to generate opcode for specialization constants.
 | ||
|  |     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } | ||
|  |     // Sets to generate opcode for non-specialization constants (normal mode).
 | ||
|  |     void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } | ||
|  |     // Check if the builder is generating code for spec constants.
 | ||
|  |     bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } | ||
|  | 
 | ||
|  |  protected: | ||
|  |     Id makeIntConstant(Id typeId, unsigned value, bool specConstant); | ||
|  |     Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); | ||
|  |     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); | ||
|  |     Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); | ||
|  |     Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps); | ||
|  |     Id findStructConstant(Id typeId, const std::vector<Id>& comps); | ||
|  |     Id collapseAccessChain(); | ||
|  |     void remapDynamicSwizzle(); | ||
|  |     void transferAccessChainSwizzle(bool dynamic); | ||
|  |     void simplifyAccessChainSwizzle(); | ||
|  |     void createAndSetNoPredecessorBlock(const char*); | ||
|  |     void createSelectionMerge(Block* mergeBlock, unsigned int control); | ||
|  |     void dumpSourceInstructions(std::vector<unsigned int>&) const; | ||
|  |     void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const; | ||
|  |     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const; | ||
|  |     void dumpModuleProcesses(std::vector<unsigned int>&) const; | ||
|  |     spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) | ||
|  |         const; | ||
|  | 
 | ||
|  |     unsigned int spvVersion;     // the version of SPIR-V to emit in the header
 | ||
|  |     SourceLanguage source; | ||
|  |     int sourceVersion; | ||
|  |     spv::Id sourceFileStringId; | ||
|  |     std::string sourceText; | ||
|  |     int currentLine; | ||
|  |     const char* currentFile; | ||
|  |     bool emitOpLines; | ||
|  |     std::set<std::string> extensions; | ||
|  |     std::vector<const char*> sourceExtensions; | ||
|  |     std::vector<const char*> moduleProcesses; | ||
|  |     AddressingModel addressModel; | ||
|  |     MemoryModel memoryModel; | ||
|  |     std::set<spv::Capability> capabilities; | ||
|  |     int builderNumber; | ||
|  |     Module module; | ||
|  |     Block* buildPoint; | ||
|  |     Id uniqueId; | ||
|  |     Function* entryPointFunction; | ||
|  |     bool generatingOpCodeForSpecConst; | ||
|  |     AccessChain accessChain; | ||
|  | 
 | ||
|  |     // special blocks of instructions for output
 | ||
|  |     std::vector<std::unique_ptr<Instruction> > strings; | ||
|  |     std::vector<std::unique_ptr<Instruction> > imports; | ||
|  |     std::vector<std::unique_ptr<Instruction> > entryPoints; | ||
|  |     std::vector<std::unique_ptr<Instruction> > executionModes; | ||
|  |     std::vector<std::unique_ptr<Instruction> > names; | ||
|  |     std::vector<std::unique_ptr<Instruction> > decorations; | ||
|  |     std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals; | ||
|  |     std::vector<std::unique_ptr<Instruction> > externals; | ||
|  |     std::vector<std::unique_ptr<Function> > functions; | ||
|  | 
 | ||
|  |     // not output, internally used for quick & dirty canonical (unique) creation
 | ||
|  | 
 | ||
|  |     // map type opcodes to constant inst.
 | ||
|  |     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants; | ||
|  |     // map struct-id to constant instructions
 | ||
|  |     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; | ||
|  |     // map type opcodes to type instructions
 | ||
|  |     std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes; | ||
|  | 
 | ||
|  |     // stack of switches
 | ||
|  |     std::stack<Block*> switchMerges; | ||
|  | 
 | ||
|  |     // Our loop stack.
 | ||
|  |     std::stack<LoopBlocks> loops; | ||
|  | 
 | ||
|  |     // map from strings to their string ids
 | ||
|  |     std::unordered_map<std::string, spv::Id> stringIds; | ||
|  | 
 | ||
|  |     // map from include file name ids to their contents
 | ||
|  |     std::map<spv::Id, const std::string*> includeFiles; | ||
|  | 
 | ||
|  |     // The stream for outputting warnings and errors.
 | ||
|  |     SpvBuildLogger* logger; | ||
|  | };  // end Builder class
 | ||
|  | 
 | ||
|  | };  // end spv namespace
 | ||
|  | 
 | ||
|  | #endif // SpvBuilder_H
 |