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