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