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