1 // 2 // Copyright (C) 2014-2015 LunarG, Inc. 3 // Copyright (C) 2015-2020 Google, Inc. 4 // Copyright (C) 2017 ARM Limited. 5 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. 6 // 7 // All rights reserved. 8 // 9 // Redistribution and use in source and binary forms, with or without 10 // modification, are permitted provided that the following conditions 11 // are met: 12 // 13 // Redistributions of source code must retain the above copyright 14 // notice, this list of conditions and the following disclaimer. 15 // 16 // Redistributions in binary form must reproduce the above 17 // copyright notice, this list of conditions and the following 18 // disclaimer in the documentation and/or other materials provided 19 // with the distribution. 20 // 21 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 22 // contributors may be used to endorse or promote products derived 23 // from this software without specific prior written permission. 24 // 25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 // POSSIBILITY OF SUCH DAMAGE. 37 38 // 39 // "Builder" is an interface to fully build SPIR-V IR. Allocate one of 40 // these to build (a thread safe) internal SPIR-V representation (IR), 41 // and then dump it as a binary stream according to the SPIR-V specification. 42 // 43 // A Builder has a 1:1 relationship with a SPIR-V module. 44 // 45 46 #pragma once 47 #ifndef SpvBuilder_H 48 #define SpvBuilder_H 49 50 #include "Logger.h" 51 #include "spirv.hpp" 52 #include "spvIR.h" 53 namespace spv { 54 #include "GLSL.ext.KHR.h" 55 #include "NonSemanticShaderDebugInfo100.h" 56 } 57 58 #include <algorithm> 59 #include <map> 60 #include <memory> 61 #include <set> 62 #include <sstream> 63 #include <stack> 64 #include <unordered_map> 65 #include <map> 66 67 namespace spv { 68 69 typedef enum { 70 Spv_1_0 = (1 << 16), 71 Spv_1_1 = (1 << 16) | (1 << 8), 72 Spv_1_2 = (1 << 16) | (2 << 8), 73 Spv_1_3 = (1 << 16) | (3 << 8), 74 Spv_1_4 = (1 << 16) | (4 << 8), 75 Spv_1_5 = (1 << 16) | (5 << 8), 76 } SpvVersion; 77 78 class Builder { 79 public: 80 Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); 81 virtual ~Builder(); 82 83 static const int maxMatrixSize = 4; 84 getSpvVersion()85 unsigned int getSpvVersion() const { return spvVersion; } 86 setSource(spv::SourceLanguage lang,int version)87 void setSource(spv::SourceLanguage lang, int version) 88 { 89 sourceLang = lang; 90 sourceVersion = version; 91 } getStringId(const std::string & str)92 spv::Id getStringId(const std::string& str) 93 { 94 auto sItr = stringIds.find(str); 95 if (sItr != stringIds.end()) 96 return sItr->second; 97 spv::Id strId = getUniqueId(); 98 Instruction* fileString = new Instruction(strId, NoType, OpString); 99 const char* file_c_str = str.c_str(); 100 fileString->addStringOperand(file_c_str); 101 strings.push_back(std::unique_ptr<Instruction>(fileString)); 102 module.mapInstruction(fileString); 103 stringIds[file_c_str] = strId; 104 return strId; 105 } 106 getMainFileId()107 spv::Id getMainFileId() const { return mainFileId; } 108 109 // Initialize the main source file name setDebugSourceFile(const std::string & file)110 void setDebugSourceFile(const std::string& file) 111 { 112 if (trackDebugInfo) { 113 dirtyLineTracker = true; 114 mainFileId = getStringId(file); 115 currentFileId = mainFileId; 116 } 117 } 118 119 // Set the debug source location tracker in the builder. 120 // The upcoming instructions in basic blocks will be associated to this location. setDebugSourceLocation(int line,const char * filename)121 void setDebugSourceLocation(int line, const char* filename) 122 { 123 if (trackDebugInfo) { 124 dirtyLineTracker = true; 125 if (line != 0) { 126 // TODO: This is special handling of some AST nodes having (untracked) line 0. 127 // But they should have a valid line number. 128 currentLine = line; 129 if (filename) { 130 currentFileId = getStringId(filename); 131 } 132 } 133 } 134 } 135 setSourceText(const std::string & text)136 void setSourceText(const std::string& text) { sourceText = text; } addSourceExtension(const char * ext)137 void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } addModuleProcessed(const std::string & p)138 void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } setEmitSpirvDebugInfo()139 void setEmitSpirvDebugInfo() 140 { 141 trackDebugInfo = true; 142 emitSpirvDebugInfo = true; 143 } setEmitNonSemanticShaderDebugInfo(bool emitSourceText)144 void setEmitNonSemanticShaderDebugInfo(bool emitSourceText) 145 { 146 trackDebugInfo = true; 147 emitNonSemanticShaderDebugInfo = true; 148 importNonSemanticShaderDebugInfoInstructions(); 149 150 if (emitSourceText) { 151 emitNonSemanticShaderDebugSource = emitSourceText; 152 } 153 } addExtension(const char * ext)154 void addExtension(const char* ext) { extensions.insert(ext); } removeExtension(const char * ext)155 void removeExtension(const char* ext) 156 { 157 extensions.erase(ext); 158 } addIncorporatedExtension(const char * ext,SpvVersion incorporatedVersion)159 void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion) 160 { 161 if (getSpvVersion() < static_cast<unsigned>(incorporatedVersion)) 162 addExtension(ext); 163 } promoteIncorporatedExtension(const char * baseExt,const char * promoExt,SpvVersion incorporatedVersion)164 void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion) 165 { 166 removeExtension(baseExt); 167 addIncorporatedExtension(promoExt, incorporatedVersion); 168 } addInclude(const std::string & name,const std::string & text)169 void addInclude(const std::string& name, const std::string& text) 170 { 171 spv::Id incId = getStringId(name); 172 includeFiles[incId] = &text; 173 } 174 Id import(const char*); setMemoryModel(spv::AddressingModel addr,spv::MemoryModel mem)175 void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) 176 { 177 addressModel = addr; 178 memoryModel = mem; 179 } 180 addCapability(spv::Capability cap)181 void addCapability(spv::Capability cap) { capabilities.insert(cap); } 182 183 // To get a new <id> for anything needing a new one. getUniqueId()184 Id getUniqueId() { return ++uniqueId; } 185 186 // To get a set of new <id>s, e.g., for a set of function parameters getUniqueIds(int numIds)187 Id getUniqueIds(int numIds) 188 { 189 Id id = uniqueId + 1; 190 uniqueId += numIds; 191 return id; 192 } 193 194 // For creating new types (will return old type if the requested one was already made). 195 Id makeVoidType(); 196 Id makeBoolType(); 197 Id makePointer(StorageClass, Id pointee); 198 Id makeForwardPointer(StorageClass); 199 Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); 200 Id makeIntegerType(int width, bool hasSign); // generic makeIntType(int width)201 Id makeIntType(int width) { return makeIntegerType(width, true); } makeUintType(int width)202 Id makeUintType(int width) { return makeIntegerType(width, false); } 203 Id makeFloatType(int width); 204 Id makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated = true); 205 Id makeStructResultType(Id type0, Id type1); 206 Id makeVectorType(Id component, int size); 207 Id makeMatrixType(Id component, int cols, int rows); 208 Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration 209 Id makeRuntimeArray(Id element); 210 Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes); 211 Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format); 212 Id makeSamplerType(); 213 Id makeSampledImageType(Id imageType); 214 Id makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use); 215 Id makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols); 216 Id makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType); 217 Id makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands); 218 219 // SPIR-V NonSemantic Shader DebugInfo Instructions 220 struct DebugTypeLoc { 221 std::string name {}; 222 int line {0}; 223 int column {0}; 224 }; 225 std::unordered_map<Id, DebugTypeLoc> debugTypeLocs; 226 Id makeDebugInfoNone(); 227 Id makeBoolDebugType(int const size); 228 Id makeIntegerDebugType(int const width, bool const hasSign); 229 Id makeFloatDebugType(int const width); 230 Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType); 231 Id makeArrayDebugType(Id const baseType, Id const componentCount); 232 Id makeVectorDebugType(Id const baseType, int const componentCount); 233 Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true); 234 Id makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc); 235 Id makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name, 236 NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType = false); 237 Id makePointerDebugType(StorageClass storageClass, Id const baseType); 238 Id makeDebugSource(const Id fileName); 239 Id makeDebugCompilationUnit(); 240 Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable); 241 Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0); 242 Id makeDebugExpression(); 243 Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer); 244 Id makeDebugValue(Id const debugLocalVariable, Id const value); 245 Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes); 246 Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId); 247 Id makeDebugLexicalBlock(uint32_t line); 248 std::string unmangleFunctionName(std::string const& name) const; 249 void setupDebugFunctionEntry(Function* function, const char* name, int line, 250 const std::vector<Id>& paramTypes, 251 const std::vector<char const*>& paramNames); 252 253 // accelerationStructureNV type 254 Id makeAccelerationStructureType(); 255 // rayQueryEXT type 256 Id makeRayQueryType(); 257 // hitObjectNV type 258 Id makeHitObjectNVType(); 259 260 // For querying about types. getTypeId(Id resultId)261 Id getTypeId(Id resultId) const { return module.getTypeId(resultId); } 262 Id getDerefTypeId(Id resultId) const; getOpCode(Id id)263 Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); } getTypeClass(Id typeId)264 Op getTypeClass(Id typeId) const { return getOpCode(typeId); } 265 Op getMostBasicTypeClass(Id typeId) const; getNumComponents(Id resultId)266 int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); } 267 int getNumTypeConstituents(Id typeId) const; getNumTypeComponents(Id typeId)268 int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } 269 Id getScalarTypeId(Id typeId) const; 270 Id getContainedTypeId(Id typeId) const; 271 Id getContainedTypeId(Id typeId, int) const; getTypeStorageClass(Id typeId)272 StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } getImageTypeFormat(Id typeId)273 ImageFormat getImageTypeFormat(Id typeId) const 274 { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } 275 Id getResultingAccessChainType() const; getIdOperand(Id resultId,int idx)276 Id getIdOperand(Id resultId, int idx) { return module.getInstruction(resultId)->getIdOperand(idx); } 277 isPointer(Id resultId)278 bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); } isScalar(Id resultId)279 bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); } isVector(Id resultId)280 bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); } isMatrix(Id resultId)281 bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); } isCooperativeMatrix(Id resultId)282 bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); } isAggregate(Id resultId)283 bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); } isSampledImage(Id resultId)284 bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } 285 isBoolType(Id typeId)286 bool isBoolType(Id typeId) 287 { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } isIntType(Id typeId)288 bool isIntType(Id typeId) const 289 { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } isUintType(Id typeId)290 bool isUintType(Id typeId) const 291 { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } isFloatType(Id typeId)292 bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } isPointerType(Id typeId)293 bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } isScalarType(Id typeId)294 bool isScalarType(Id typeId) const 295 { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || 296 getTypeClass(typeId) == OpTypeBool; } isVectorType(Id typeId)297 bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } isMatrixType(Id typeId)298 bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; } isStructType(Id typeId)299 bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; } isArrayType(Id typeId)300 bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; } isCooperativeMatrixType(Id typeId)301 bool isCooperativeMatrixType(Id typeId)const 302 { 303 return getTypeClass(typeId) == OpTypeCooperativeMatrixKHR || getTypeClass(typeId) == OpTypeCooperativeMatrixNV; 304 } isAggregateType(Id typeId)305 bool isAggregateType(Id typeId) const 306 { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); } isImageType(Id typeId)307 bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; } isSamplerType(Id typeId)308 bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; } isSampledImageType(Id typeId)309 bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } 310 bool containsType(Id typeId, Op typeOp, unsigned int width) const; 311 bool containsPhysicalStorageBufferOrArray(Id typeId) const; 312 313 bool isConstantOpCode(Op opcode) const; 314 bool isSpecConstantOpCode(Op opcode) const; isConstant(Id resultId)315 bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } isConstantScalar(Id resultId)316 bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } isSpecConstant(Id resultId)317 bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } getConstantScalar(Id resultId)318 unsigned int getConstantScalar(Id resultId) const 319 { return module.getInstruction(resultId)->getImmediateOperand(0); } getStorageClass(Id resultId)320 StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } 321 isVariableOpCode(Op opcode)322 bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; } isVariable(Id resultId)323 bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); } isGlobalStorage(Id resultId)324 bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; } isGlobalVariable(Id resultId)325 bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); } 326 // See if a resultId is valid for use as an initializer. isValidInitializer(Id resultId)327 bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); } 328 getScalarTypeWidth(Id typeId)329 int getScalarTypeWidth(Id typeId) const 330 { 331 Id scalarTypeId = getScalarTypeId(typeId); 332 assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); 333 return module.getInstruction(scalarTypeId)->getImmediateOperand(0); 334 } 335 getTypeNumColumns(Id typeId)336 int getTypeNumColumns(Id typeId) const 337 { 338 assert(isMatrixType(typeId)); 339 return getNumTypeConstituents(typeId); 340 } getNumColumns(Id resultId)341 int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); } getTypeNumRows(Id typeId)342 int getTypeNumRows(Id typeId) const 343 { 344 assert(isMatrixType(typeId)); 345 return getNumTypeComponents(getContainedTypeId(typeId)); 346 } getNumRows(Id resultId)347 int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); } 348 getTypeDimensionality(Id typeId)349 Dim getTypeDimensionality(Id typeId) const 350 { 351 assert(isImageType(typeId)); 352 return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); 353 } getImageType(Id resultId)354 Id getImageType(Id resultId) const 355 { 356 Id typeId = getTypeId(resultId); 357 assert(isImageType(typeId) || isSampledImageType(typeId)); 358 return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; 359 } isArrayedImageType(Id typeId)360 bool isArrayedImageType(Id typeId) const 361 { 362 assert(isImageType(typeId)); 363 return module.getInstruction(typeId)->getImmediateOperand(3) != 0; 364 } 365 366 // For making new constants (will return old constant if the requested one was already made). 367 Id makeNullConstant(Id typeId); 368 Id makeBoolConstant(bool b, bool specConstant = false); 369 Id makeInt8Constant(int i, bool specConstant = false) 370 { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } 371 Id makeUint8Constant(unsigned u, bool specConstant = false) 372 { return makeIntConstant(makeUintType(8), u, specConstant); } 373 Id makeInt16Constant(int i, bool specConstant = false) 374 { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } 375 Id makeUint16Constant(unsigned u, bool specConstant = false) 376 { return makeIntConstant(makeUintType(16), u, specConstant); } 377 Id makeIntConstant(int i, bool specConstant = false) 378 { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } 379 Id makeUintConstant(unsigned u, bool specConstant = false) 380 { return makeIntConstant(makeUintType(32), u, specConstant); } 381 Id makeInt64Constant(long long i, bool specConstant = false) 382 { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } 383 Id makeUint64Constant(unsigned long long u, bool specConstant = false) 384 { return makeInt64Constant(makeUintType(64), u, specConstant); } 385 Id makeFloatConstant(float f, bool specConstant = false); 386 Id makeDoubleConstant(double d, bool specConstant = false); 387 Id makeFloat16Constant(float f16, bool specConstant = false); 388 Id makeFpConstant(Id type, double d, bool specConstant = false); 389 390 Id importNonSemanticShaderDebugInfoInstructions(); 391 392 // Turn the array of constants into a proper spv constant of the requested type. 393 Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false); 394 395 // Methods for adding information outside the CFG. 396 Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); 397 void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); 398 void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals); 399 void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds); 400 void addName(Id, const char* name); 401 void addMemberName(Id, int member, const char* name); 402 void addDecoration(Id, Decoration, int num = -1); 403 void addDecoration(Id, Decoration, const char*); 404 void addDecoration(Id, Decoration, const std::vector<unsigned>& literals); 405 void addDecoration(Id, Decoration, const std::vector<const char*>& strings); 406 void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType); 407 void addDecorationId(Id id, Decoration, Id idDecoration); 408 void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds); 409 void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); 410 void addMemberDecoration(Id, unsigned int member, Decoration, const char*); 411 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals); 412 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings); 413 414 // At the end of what block do the next create*() instructions go? 415 // Also reset current last DebugScope and current source line to unknown setBuildPoint(Block * bp)416 void setBuildPoint(Block* bp) { 417 buildPoint = bp; 418 // TODO: Technically, change of build point should set line tracker dirty. But we'll have bad line info for 419 // branch instructions. Commenting this for now because at least this matches the old behavior. 420 dirtyScopeTracker = true; 421 } getBuildPoint()422 Block* getBuildPoint() const { return buildPoint; } 423 424 // Append an instruction to the end of the current build point. 425 // Optionally, additional debug info instructions may also be prepended. 426 void addInstruction(std::unique_ptr<Instruction> inst); 427 428 // Make the entry-point function. The returned pointer is only valid 429 // for the lifetime of this builder. 430 Function* makeEntryPoint(const char*); 431 432 // Make a shader-style function, and create its entry block if entry is non-zero. 433 // Return the function, pass back the entry. 434 // The returned pointer is only valid for the lifetime of this builder. 435 Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType, 436 const std::vector<Id>& paramTypes, 437 const std::vector<std::vector<Decoration>>& precisions, Block** entry = nullptr); 438 439 // Create a return. An 'implicit' return is one not appearing in the source 440 // code. In the case of an implicit return, no post-return block is inserted. 441 void makeReturn(bool implicit, Id retVal = 0); 442 443 // Initialize state and generate instructions for new lexical scope 444 void enterLexicalBlock(uint32_t line); 445 446 // Set state and generate instructions to exit current lexical scope 447 void leaveLexicalBlock(); 448 449 // Prepare builder for generation of instructions for a function. 450 void enterFunction(Function const* function); 451 452 // Generate all the code needed to finish up a function. 453 void leaveFunction(); 454 455 // Create block terminator instruction for certain statements like 456 // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT 457 void makeStatementTerminator(spv::Op opcode, const char *name); 458 459 // Create block terminator instruction for statements that have input operands 460 // such as OpEmitMeshTasksEXT 461 void makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name); 462 463 // Create a global or function local or IO variable. 464 Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr, 465 Id initializer = NoResult, bool const compilerGenerated = true); 466 467 // Create an intermediate with an undefined value. 468 Id createUndefined(Id type); 469 470 // Store into an Id and return the l-value 471 void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 472 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 473 474 // Load from an Id and return it 475 Id createLoad(Id lValue, spv::Decoration precision, 476 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 477 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 478 479 // Create an OpAccessChain instruction 480 Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets); 481 482 // Create an OpArrayLength instruction 483 Id createArrayLength(Id base, unsigned int member); 484 485 // Create an OpCooperativeMatrixLengthKHR instruction 486 Id createCooperativeMatrixLengthKHR(Id type); 487 // Create an OpCooperativeMatrixLengthNV instruction 488 Id createCooperativeMatrixLengthNV(Id type); 489 490 // Create an OpCompositeExtract instruction 491 Id createCompositeExtract(Id composite, Id typeId, unsigned index); 492 Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes); 493 Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); 494 Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes); 495 496 Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); 497 Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); 498 499 void createNoResultOp(Op); 500 void createNoResultOp(Op, Id operand); 501 void createNoResultOp(Op, const std::vector<Id>& operands); 502 void createNoResultOp(Op, const std::vector<IdImmediate>& operands); 503 void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); 504 void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); 505 Id createUnaryOp(Op, Id typeId, Id operand); 506 Id createBinOp(Op, Id typeId, Id operand1, Id operand2); 507 Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); 508 Id createOp(Op, Id typeId, const std::vector<Id>& operands); 509 Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands); 510 Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&); 511 Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals); 512 513 // Take an rvalue (source) and a set of channels to extract from it to 514 // make a new rvalue, which is returned. 515 Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels); 516 517 // Take a copy of an lvalue (target) and a source of components, and set the 518 // source components into the lvalue where the 'channels' say to put them. 519 // An updated version of the target is returned. 520 // (No true lvalue or stores are used.) 521 Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels); 522 523 // If both the id and precision are valid, the id 524 // gets tagged with the requested precision. 525 // The passed in id is always the returned id, to simplify use patterns. setPrecision(Id id,Decoration precision)526 Id setPrecision(Id id, Decoration precision) 527 { 528 if (precision != NoPrecision && id != NoResult) 529 addDecoration(id, precision); 530 531 return id; 532 } 533 534 // Can smear a scalar to a vector for the following forms: 535 // - promoteScalar(scalar, vector) // smear scalar to width of vector 536 // - promoteScalar(vector, scalar) // smear scalar to width of vector 537 // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to 538 // - promoteScalar(scalar, scalar) // do nothing 539 // Other forms are not allowed. 540 // 541 // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. 542 // The type of the created vector is a vector of components of the same type as the scalar. 543 // 544 // Note: One of the arguments will change, with the result coming back that way rather than 545 // through the return value. 546 void promoteScalar(Decoration precision, Id& left, Id& right); 547 548 // Make a value by smearing the scalar to fill the type. 549 // vectorType should be the correct type for making a vector of scalarVal. 550 // (No conversions are done.) 551 Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); 552 553 // Create a call to a built-in function. 554 Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args); 555 556 // List of parameters used to create a texture operation 557 struct TextureParameters { 558 Id sampler; 559 Id coords; 560 Id bias; 561 Id lod; 562 Id Dref; 563 Id offset; 564 Id offsets; 565 Id gradX; 566 Id gradY; 567 Id sample; 568 Id component; 569 Id texelOut; 570 Id lodClamp; 571 Id granularity; 572 Id coarse; 573 bool nonprivate; 574 bool volatil; 575 }; 576 577 // Select the correct texture operation based on all inputs, and emit the correct instruction 578 Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, 579 bool noImplicit, const TextureParameters&, ImageOperandsMask); 580 581 // Emit the OpTextureQuery* instruction that was passed in. 582 // Figure out the right return value and type, and return it. 583 Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); 584 585 Id createSamplePositionCall(Decoration precision, Id, Id); 586 587 Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); 588 Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); 589 590 // Reduction comparison for composites: For equal and not-equal resulting in a scalar. 591 Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); 592 593 // OpCompositeConstruct 594 Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents); 595 596 // vector or scalar constructor 597 Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId); 598 599 // matrix constructor 600 Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee); 601 602 // Helper to use for building nested control flow with if-then-else. 603 class If { 604 public: 605 If(Id condition, unsigned int ctrl, Builder& builder); ~If()606 ~If() {} 607 608 void makeBeginElse(); 609 void makeEndIf(); 610 611 private: 612 If(const If&); 613 If& operator=(If&); 614 615 Builder& builder; 616 Id condition; 617 unsigned int control; 618 Function* function; 619 Block* headerBlock; 620 Block* thenBlock; 621 Block* elseBlock; 622 Block* mergeBlock; 623 }; 624 625 // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing 626 // any case/default labels, all separated by one or more case/default labels. Each possible 627 // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this 628 // number space. How to compute the value is given by 'condition', as in switch(condition). 629 // 630 // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. 631 // 632 // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). 633 // 634 // Returns the right set of basic blocks to start each code segment with, so that the caller's 635 // recursion stack can hold the memory for it. 636 // 637 void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues, 638 const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); 639 640 // Add a branch to the innermost switch's merge block. 641 void addSwitchBreak(); 642 643 // Move to the next code segment, passing in the return argument in makeSwitch() 644 void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment); 645 646 // Finish off the innermost switch. 647 void endSwitch(std::vector<Block*>& segmentBB); 648 649 struct LoopBlocks { LoopBlocksLoopBlocks650 LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : 651 head(head), body(body), merge(merge), continue_target(continue_target) { } 652 Block &head, &body, &merge, &continue_target; 653 private: 654 LoopBlocks(); 655 LoopBlocks& operator=(const LoopBlocks&) = delete; 656 }; 657 658 // Start a new loop and prepare the builder to generate code for it. Until 659 // closeLoop() is called for this loop, createLoopContinue() and 660 // createLoopExit() will target its corresponding blocks. 661 LoopBlocks& makeNewLoop(); 662 663 // Create a new block in the function containing the build point. Memory is 664 // owned by the function object. 665 Block& makeNewBlock(); 666 667 // Add a branch to the continue_target of the current (innermost) loop. 668 void createLoopContinue(); 669 670 // Add an exit (e.g. "break") from the innermost loop that we're currently 671 // in. 672 void createLoopExit(); 673 674 // Close the innermost loop that you're in 675 void closeLoop(); 676 677 // 678 // Access chain design for an R-Value vs. L-Value: 679 // 680 // There is a single access chain the builder is building at 681 // any particular time. Such a chain can be used to either to a load or 682 // a store, when desired. 683 // 684 // Expressions can be r-values, l-values, or both, or only r-values: 685 // a[b.c].d = .... // l-value 686 // ... = a[b.c].d; // r-value, that also looks like an l-value 687 // ++a[b.c].d; // r-value and l-value 688 // (x + y)[2]; // r-value only, can't possibly be l-value 689 // 690 // Computing an r-value means generating code. Hence, 691 // r-values should only be computed when they are needed, not speculatively. 692 // 693 // Computing an l-value means saving away information for later use in the compiler, 694 // no code is generated until the l-value is later dereferenced. It is okay 695 // to speculatively generate an l-value, just not okay to speculatively dereference it. 696 // 697 // The base of the access chain (the left-most variable or expression 698 // from which everything is based) can be set either as an l-value 699 // or as an r-value. Most efficient would be to set an l-value if one 700 // is available. If an expression was evaluated, the resulting r-value 701 // can be set as the chain base. 702 // 703 // The users of this single access chain can save and restore if they 704 // want to nest or manage multiple chains. 705 // 706 707 struct AccessChain { 708 Id base; // for l-values, pointer to the base object, for r-values, the base object 709 std::vector<Id> indexChain; 710 Id instr; // cache the instruction that generates this access chain 711 std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number 712 Id component; // a dynamic component index, can coexist with a swizzle, 713 // done after the swizzle, NoResult if not present 714 Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; 715 // NoType unless a swizzle or component is present 716 bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value 717 unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. 718 // Only tracks base and (optional) component selection alignment. 719 720 // Accumulate whether anything in the chain of structures has coherent decorations. 721 struct CoherentFlags { CoherentFlagsAccessChain::CoherentFlags722 CoherentFlags() { clear(); } isVolatileAccessChain::CoherentFlags723 bool isVolatile() const { return volatil; } isNonUniformAccessChain::CoherentFlags724 bool isNonUniform() const { return nonUniform; } anyCoherentAccessChain::CoherentFlags725 bool anyCoherent() const { 726 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || 727 subgroupcoherent || shadercallcoherent; 728 } 729 730 unsigned coherent : 1; 731 unsigned devicecoherent : 1; 732 unsigned queuefamilycoherent : 1; 733 unsigned workgroupcoherent : 1; 734 unsigned subgroupcoherent : 1; 735 unsigned shadercallcoherent : 1; 736 unsigned nonprivate : 1; 737 unsigned volatil : 1; 738 unsigned isImage : 1; 739 unsigned nonUniform : 1; 740 clearAccessChain::CoherentFlags741 void clear() { 742 coherent = 0; 743 devicecoherent = 0; 744 queuefamilycoherent = 0; 745 workgroupcoherent = 0; 746 subgroupcoherent = 0; 747 shadercallcoherent = 0; 748 nonprivate = 0; 749 volatil = 0; 750 isImage = 0; 751 nonUniform = 0; 752 } 753 754 CoherentFlags operator |=(const CoherentFlags &other) { 755 coherent |= other.coherent; 756 devicecoherent |= other.devicecoherent; 757 queuefamilycoherent |= other.queuefamilycoherent; 758 workgroupcoherent |= other.workgroupcoherent; 759 subgroupcoherent |= other.subgroupcoherent; 760 shadercallcoherent |= other.shadercallcoherent; 761 nonprivate |= other.nonprivate; 762 volatil |= other.volatil; 763 isImage |= other.isImage; 764 nonUniform |= other.nonUniform; 765 return *this; 766 } 767 }; 768 CoherentFlags coherentFlags; 769 }; 770 771 // 772 // the SPIR-V builder maintains a single active chain that 773 // the following methods operate on 774 // 775 776 // for external save and restore getAccessChain()777 AccessChain getAccessChain() { return accessChain; } setAccessChain(AccessChain newChain)778 void setAccessChain(AccessChain newChain) { accessChain = newChain; } 779 780 // clear accessChain 781 void clearAccessChain(); 782 783 // set new base as an l-value base setAccessChainLValue(Id lValue)784 void setAccessChainLValue(Id lValue) 785 { 786 assert(isPointer(lValue)); 787 accessChain.base = lValue; 788 } 789 790 // set new base value as an r-value setAccessChainRValue(Id rValue)791 void setAccessChainRValue(Id rValue) 792 { 793 accessChain.isRValue = true; 794 accessChain.base = rValue; 795 } 796 797 // push offset onto the end of the chain accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)798 void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) 799 { 800 accessChain.indexChain.push_back(offset); 801 accessChain.coherentFlags |= coherentFlags; 802 accessChain.alignment |= alignment; 803 } 804 805 // push new swizzle onto the end of any existing swizzle, merging into a single swizzle 806 void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, 807 AccessChain::CoherentFlags coherentFlags, unsigned int alignment); 808 809 // push a dynamic component selection onto the access chain, only applicable with a 810 // non-trivial swizzle or no swizzle accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)811 void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, 812 unsigned int alignment) 813 { 814 if (accessChain.swizzle.size() != 1) { 815 accessChain.component = component; 816 if (accessChain.preSwizzleBaseType == NoType) 817 accessChain.preSwizzleBaseType = preSwizzleBaseType; 818 } 819 accessChain.coherentFlags |= coherentFlags; 820 accessChain.alignment |= alignment; 821 } 822 823 // use accessChain and swizzle to store value 824 void accessChainStore(Id rvalue, Decoration nonUniform, 825 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 826 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 827 828 // use accessChain and swizzle to load an r-value 829 Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType, 830 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, 831 unsigned int alignment = 0); 832 833 // Return whether or not the access chain can be represented in SPIR-V 834 // as an l-value. 835 // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. isSpvLvalue()836 bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } 837 838 // get the direct pointer for an l-value 839 Id accessChainGetLValue(); 840 841 // Get the inferred SPIR-V type of the result of the current access chain, 842 // based on the type of the base and the chain of dereferences. 843 Id accessChainGetInferredType(); 844 845 // Add capabilities, extensions, remove unneeded decorations, etc., 846 // based on the resulting SPIR-V. 847 void postProcess(bool compileOnly); 848 849 // Prune unreachable blocks in the CFG and remove unneeded decorations. 850 void postProcessCFG(); 851 852 // Add capabilities, extensions based on instructions in the module. 853 void postProcessFeatures(); 854 // Hook to visit each instruction in a block in a function 855 void postProcess(Instruction&); 856 // Hook to visit each non-32-bit sized float/int operation in a block. 857 void postProcessType(const Instruction&, spv::Id typeId); 858 859 void dump(std::vector<unsigned int>&) const; 860 861 void createBranch(Block* block); 862 void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); 863 void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, 864 const std::vector<unsigned int>& operands); 865 866 // Sets to generate opcode for specialization constants. setToSpecConstCodeGenMode()867 void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } 868 // Sets to generate opcode for non-specialization constants (normal mode). setToNormalCodeGenMode()869 void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } 870 // Check if the builder is generating code for spec constants. isInSpecConstCodeGenMode()871 bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } 872 873 protected: 874 Id makeIntConstant(Id typeId, unsigned value, bool specConstant); 875 Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); 876 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); 877 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); 878 Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps); 879 Id findStructConstant(Id typeId, const std::vector<Id>& comps); 880 Id collapseAccessChain(); 881 void remapDynamicSwizzle(); 882 void transferAccessChainSwizzle(bool dynamic); 883 void simplifyAccessChainSwizzle(); 884 void createAndSetNoPredecessorBlock(const char*); 885 void createSelectionMerge(Block* mergeBlock, unsigned int control); 886 void dumpSourceInstructions(std::vector<unsigned int>&) const; 887 void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const; 888 void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const; 889 void dumpModuleProcesses(std::vector<unsigned int>&) const; 890 spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) 891 const; 892 893 unsigned int spvVersion; // the version of SPIR-V to emit in the header 894 SourceLanguage sourceLang; 895 int sourceVersion; 896 spv::Id nonSemanticShaderCompilationUnitId {0}; 897 spv::Id nonSemanticShaderDebugInfo {0}; 898 spv::Id debugInfoNone {0}; 899 spv::Id debugExpression {0}; // Debug expression with zero operations. 900 std::string sourceText; 901 902 // True if an new OpLine/OpDebugLine may need to be inserted. Either: 903 // 1. The current debug location changed 904 // 2. The current build point changed 905 bool dirtyLineTracker; 906 int currentLine = 0; 907 // OpString id of the current file name. Always 0 if debug info is off. 908 spv::Id currentFileId = 0; 909 // OpString id of the main file name. Always 0 if debug info is off. 910 spv::Id mainFileId = 0; 911 912 // True if an new OpDebugScope may need to be inserted. Either: 913 // 1. A new lexical block is pushed 914 // 2. The current build point changed 915 bool dirtyScopeTracker; 916 std::stack<spv::Id> currentDebugScopeId; 917 918 // This flag toggles tracking of debug info while building the SPIR-V. 919 bool trackDebugInfo = false; 920 // This flag toggles emission of SPIR-V debug instructions, like OpLine and OpSource. 921 bool emitSpirvDebugInfo = false; 922 // This flag toggles emission of Non-Semantic Debug extension debug instructions. 923 bool emitNonSemanticShaderDebugInfo = false; 924 bool restoreNonSemanticShaderDebugInfo = false; 925 bool emitNonSemanticShaderDebugSource = false; 926 927 std::set<std::string> extensions; 928 std::vector<const char*> sourceExtensions; 929 std::vector<const char*> moduleProcesses; 930 AddressingModel addressModel; 931 MemoryModel memoryModel; 932 std::set<spv::Capability> capabilities; 933 int builderNumber; 934 Module module; 935 Block* buildPoint; 936 Id uniqueId; 937 Function* entryPointFunction; 938 bool generatingOpCodeForSpecConst; 939 AccessChain accessChain; 940 941 // special blocks of instructions for output 942 std::vector<std::unique_ptr<Instruction> > strings; 943 std::vector<std::unique_ptr<Instruction> > imports; 944 std::vector<std::unique_ptr<Instruction> > entryPoints; 945 std::vector<std::unique_ptr<Instruction> > executionModes; 946 std::vector<std::unique_ptr<Instruction> > names; 947 std::vector<std::unique_ptr<Instruction> > decorations; 948 std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals; 949 std::vector<std::unique_ptr<Instruction> > externals; 950 std::vector<std::unique_ptr<Function> > functions; 951 952 // not output, internally used for quick & dirty canonical (unique) creation 953 954 // map type opcodes to constant inst. 955 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants; 956 // map struct-id to constant instructions 957 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; 958 // map type opcodes to type instructions 959 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes; 960 // map type opcodes to debug type instructions 961 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes; 962 // list of OpConstantNull instructions 963 std::vector<Instruction*> nullConstants; 964 965 // stack of switches 966 std::stack<Block*> switchMerges; 967 968 // Our loop stack. 969 std::stack<LoopBlocks> loops; 970 971 // map from strings to their string ids 972 std::unordered_map<std::string, spv::Id> stringIds; 973 974 // map from include file name ids to their contents 975 std::map<spv::Id, const std::string*> includeFiles; 976 977 // map from core id to debug id 978 std::map <spv::Id, spv::Id> debugId; 979 980 // map from file name string id to DebugSource id 981 std::unordered_map<spv::Id, spv::Id> debugSourceId; 982 983 // The stream for outputting warnings and errors. 984 SpvBuildLogger* logger; 985 }; // end Builder class 986 987 }; // end spv namespace 988 989 #endif // SpvBuilder_H 990