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 makeBoolConstant(bool b, bool specConstant = false); 297 Id makeInt8Constant(int i, bool specConstant = false) 298 { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } 299 Id makeUint8Constant(unsigned u, bool specConstant = false) 300 { return makeIntConstant(makeUintType(8), u, specConstant); } 301 Id makeInt16Constant(int i, bool specConstant = false) 302 { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } 303 Id makeUint16Constant(unsigned u, bool specConstant = false) 304 { return makeIntConstant(makeUintType(16), u, specConstant); } 305 Id makeIntConstant(int i, bool specConstant = false) 306 { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } 307 Id makeUintConstant(unsigned u, bool specConstant = false) 308 { return makeIntConstant(makeUintType(32), u, specConstant); } 309 Id makeInt64Constant(long long i, bool specConstant = false) 310 { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } 311 Id makeUint64Constant(unsigned long long u, bool specConstant = false) 312 { return makeInt64Constant(makeUintType(64), u, specConstant); } 313 Id makeFloatConstant(float f, bool specConstant = false); 314 Id makeDoubleConstant(double d, bool specConstant = false); 315 Id makeFloat16Constant(float f16, bool specConstant = false); 316 Id makeFpConstant(Id type, double d, bool specConstant = false); 317 318 // Turn the array of constants into a proper spv constant of the requested type. 319 Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false); 320 321 // Methods for adding information outside the CFG. 322 Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); 323 void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); 324 void addExecutionMode(Function*, ExecutionMode mode, const std::vector<unsigned>& literals); 325 void addExecutionModeId(Function*, ExecutionMode mode, const std::vector<Id>& operandIds); 326 void addName(Id, const char* name); 327 void addMemberName(Id, int member, const char* name); 328 void addDecoration(Id, Decoration, int num = -1); 329 void addDecoration(Id, Decoration, const char*); 330 void addDecoration(Id, Decoration, const std::vector<unsigned>& literals); 331 void addDecoration(Id, Decoration, const std::vector<const char*>& strings); 332 void addDecorationId(Id id, Decoration, Id idDecoration); 333 void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds); 334 void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); 335 void addMemberDecoration(Id, unsigned int member, Decoration, const char*); 336 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<unsigned>& literals); 337 void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector<const char*>& strings); 338 339 // At the end of what block do the next create*() instructions go? setBuildPoint(Block * bp)340 void setBuildPoint(Block* bp) { buildPoint = bp; } getBuildPoint()341 Block* getBuildPoint() const { return buildPoint; } 342 343 // Make the entry-point function. The returned pointer is only valid 344 // for the lifetime of this builder. 345 Function* makeEntryPoint(const char*); 346 347 // Make a shader-style function, and create its entry block if entry is non-zero. 348 // Return the function, pass back the entry. 349 // The returned pointer is only valid for the lifetime of this builder. 350 Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, 351 const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0); 352 353 // Create a return. An 'implicit' return is one not appearing in the source 354 // code. In the case of an implicit return, no post-return block is inserted. 355 void makeReturn(bool implicit, Id retVal = 0); 356 357 // Generate all the code needed to finish up a function. 358 void leaveFunction(); 359 360 // Create a discard. 361 void makeDiscard(); 362 363 // Create a global or function local or IO variable. 364 Id createVariable(Decoration precision, StorageClass, Id type, const char* name = nullptr, 365 Id initializer = NoResult); 366 367 // Create an intermediate with an undefined value. 368 Id createUndefined(Id type); 369 370 // Store into an Id and return the l-value 371 void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 372 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 373 374 // Load from an Id and return it 375 Id createLoad(Id lValue, spv::Decoration precision, 376 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 377 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 378 379 // Create an OpAccessChain instruction 380 Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets); 381 382 // Create an OpArrayLength instruction 383 Id createArrayLength(Id base, unsigned int member); 384 385 // Create an OpCooperativeMatrixLengthNV instruction 386 Id createCooperativeMatrixLength(Id type); 387 388 // Create an OpCompositeExtract instruction 389 Id createCompositeExtract(Id composite, Id typeId, unsigned index); 390 Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes); 391 Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); 392 Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes); 393 394 Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); 395 Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); 396 397 void createNoResultOp(Op); 398 void createNoResultOp(Op, Id operand); 399 void createNoResultOp(Op, const std::vector<Id>& operands); 400 void createNoResultOp(Op, const std::vector<IdImmediate>& operands); 401 void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); 402 void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); 403 Id createUnaryOp(Op, Id typeId, Id operand); 404 Id createBinOp(Op, Id typeId, Id operand1, Id operand2); 405 Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); 406 Id createOp(Op, Id typeId, const std::vector<Id>& operands); 407 Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands); 408 Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&); 409 Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals); 410 411 // Take an rvalue (source) and a set of channels to extract from it to 412 // make a new rvalue, which is returned. 413 Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels); 414 415 // Take a copy of an lvalue (target) and a source of components, and set the 416 // source components into the lvalue where the 'channels' say to put them. 417 // An updated version of the target is returned. 418 // (No true lvalue or stores are used.) 419 Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels); 420 421 // If both the id and precision are valid, the id 422 // gets tagged with the requested precision. 423 // The passed in id is always the returned id, to simplify use patterns. setPrecision(Id id,Decoration precision)424 Id setPrecision(Id id, Decoration precision) 425 { 426 if (precision != NoPrecision && id != NoResult) 427 addDecoration(id, precision); 428 429 return id; 430 } 431 432 // Can smear a scalar to a vector for the following forms: 433 // - promoteScalar(scalar, vector) // smear scalar to width of vector 434 // - promoteScalar(vector, scalar) // smear scalar to width of vector 435 // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to 436 // - promoteScalar(scalar, scalar) // do nothing 437 // Other forms are not allowed. 438 // 439 // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. 440 // The type of the created vector is a vector of components of the same type as the scalar. 441 // 442 // Note: One of the arguments will change, with the result coming back that way rather than 443 // through the return value. 444 void promoteScalar(Decoration precision, Id& left, Id& right); 445 446 // Make a value by smearing the scalar to fill the type. 447 // vectorType should be the correct type for making a vector of scalarVal. 448 // (No conversions are done.) 449 Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); 450 451 // Create a call to a built-in function. 452 Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args); 453 454 // List of parameters used to create a texture operation 455 struct TextureParameters { 456 Id sampler; 457 Id coords; 458 Id bias; 459 Id lod; 460 Id Dref; 461 Id offset; 462 Id offsets; 463 Id gradX; 464 Id gradY; 465 Id sample; 466 Id component; 467 Id texelOut; 468 Id lodClamp; 469 Id granularity; 470 Id coarse; 471 bool nonprivate; 472 bool volatil; 473 }; 474 475 // Select the correct texture operation based on all inputs, and emit the correct instruction 476 Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, 477 bool noImplicit, const TextureParameters&, ImageOperandsMask); 478 479 // Emit the OpTextureQuery* instruction that was passed in. 480 // Figure out the right return value and type, and return it. 481 Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); 482 483 Id createSamplePositionCall(Decoration precision, Id, Id); 484 485 Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); 486 Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); 487 488 // Reduction comparison for composites: For equal and not-equal resulting in a scalar. 489 Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); 490 491 // OpCompositeConstruct 492 Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents); 493 494 // vector or scalar constructor 495 Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId); 496 497 // matrix constructor 498 Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee); 499 500 // Helper to use for building nested control flow with if-then-else. 501 class If { 502 public: 503 If(Id condition, unsigned int ctrl, Builder& builder); ~If()504 ~If() {} 505 506 void makeBeginElse(); 507 void makeEndIf(); 508 509 private: 510 If(const If&); 511 If& operator=(If&); 512 513 Builder& builder; 514 Id condition; 515 unsigned int control; 516 Function* function; 517 Block* headerBlock; 518 Block* thenBlock; 519 Block* elseBlock; 520 Block* mergeBlock; 521 }; 522 523 // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing 524 // any case/default labels, all separated by one or more case/default labels. Each possible 525 // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this 526 // number space. How to compute the value is given by 'condition', as in switch(condition). 527 // 528 // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. 529 // 530 // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). 531 // 532 // Returns the right set of basic blocks to start each code segment with, so that the caller's 533 // recursion stack can hold the memory for it. 534 // 535 void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues, 536 const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); 537 538 // Add a branch to the innermost switch's merge block. 539 void addSwitchBreak(); 540 541 // Move to the next code segment, passing in the return argument in makeSwitch() 542 void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment); 543 544 // Finish off the innermost switch. 545 void endSwitch(std::vector<Block*>& segmentBB); 546 547 struct LoopBlocks { LoopBlocksLoopBlocks548 LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : 549 head(head), body(body), merge(merge), continue_target(continue_target) { } 550 Block &head, &body, &merge, &continue_target; 551 private: 552 LoopBlocks(); 553 LoopBlocks& operator=(const LoopBlocks&) = delete; 554 }; 555 556 // Start a new loop and prepare the builder to generate code for it. Until 557 // closeLoop() is called for this loop, createLoopContinue() and 558 // createLoopExit() will target its corresponding blocks. 559 LoopBlocks& makeNewLoop(); 560 561 // Create a new block in the function containing the build point. Memory is 562 // owned by the function object. 563 Block& makeNewBlock(); 564 565 // Add a branch to the continue_target of the current (innermost) loop. 566 void createLoopContinue(); 567 568 // Add an exit (e.g. "break") from the innermost loop that we're currently 569 // in. 570 void createLoopExit(); 571 572 // Close the innermost loop that you're in 573 void closeLoop(); 574 575 // 576 // Access chain design for an R-Value vs. L-Value: 577 // 578 // There is a single access chain the builder is building at 579 // any particular time. Such a chain can be used to either to a load or 580 // a store, when desired. 581 // 582 // Expressions can be r-values, l-values, or both, or only r-values: 583 // a[b.c].d = .... // l-value 584 // ... = a[b.c].d; // r-value, that also looks like an l-value 585 // ++a[b.c].d; // r-value and l-value 586 // (x + y)[2]; // r-value only, can't possibly be l-value 587 // 588 // Computing an r-value means generating code. Hence, 589 // r-values should only be computed when they are needed, not speculatively. 590 // 591 // Computing an l-value means saving away information for later use in the compiler, 592 // no code is generated until the l-value is later dereferenced. It is okay 593 // to speculatively generate an l-value, just not okay to speculatively dereference it. 594 // 595 // The base of the access chain (the left-most variable or expression 596 // from which everything is based) can be set either as an l-value 597 // or as an r-value. Most efficient would be to set an l-value if one 598 // is available. If an expression was evaluated, the resulting r-value 599 // can be set as the chain base. 600 // 601 // The users of this single access chain can save and restore if they 602 // want to nest or manage multiple chains. 603 // 604 605 struct AccessChain { 606 Id base; // for l-values, pointer to the base object, for r-values, the base object 607 std::vector<Id> indexChain; 608 Id instr; // cache the instruction that generates this access chain 609 std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number 610 Id component; // a dynamic component index, can coexist with a swizzle, 611 // done after the swizzle, NoResult if not present 612 Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; 613 // NoType unless a swizzle or component is present 614 bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value 615 unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. 616 // Only tracks base and (optional) component selection alignment. 617 618 // Accumulate whether anything in the chain of structures has coherent decorations. 619 struct CoherentFlags { CoherentFlagsAccessChain::CoherentFlags620 CoherentFlags() { clear(); } 621 #ifdef GLSLANG_WEB clearAccessChain::CoherentFlags622 void clear() { } isVolatileAccessChain::CoherentFlags623 bool isVolatile() const { return false; } 624 CoherentFlags operator |=(const CoherentFlags &other) { return *this; } 625 #else isVolatileAccessChain::CoherentFlags626 bool isVolatile() const { return volatil; } anyCoherentAccessChain::CoherentFlags627 bool anyCoherent() const { 628 return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || 629 subgroupcoherent || shadercallcoherent; 630 } 631 632 unsigned coherent : 1; 633 unsigned devicecoherent : 1; 634 unsigned queuefamilycoherent : 1; 635 unsigned workgroupcoherent : 1; 636 unsigned subgroupcoherent : 1; 637 unsigned shadercallcoherent : 1; 638 unsigned nonprivate : 1; 639 unsigned volatil : 1; 640 unsigned isImage : 1; 641 clearAccessChain::CoherentFlags642 void clear() { 643 coherent = 0; 644 devicecoherent = 0; 645 queuefamilycoherent = 0; 646 workgroupcoherent = 0; 647 subgroupcoherent = 0; 648 shadercallcoherent = 0; 649 nonprivate = 0; 650 volatil = 0; 651 isImage = 0; 652 } 653 654 CoherentFlags operator |=(const CoherentFlags &other) { 655 coherent |= other.coherent; 656 devicecoherent |= other.devicecoherent; 657 queuefamilycoherent |= other.queuefamilycoherent; 658 workgroupcoherent |= other.workgroupcoherent; 659 subgroupcoherent |= other.subgroupcoherent; 660 shadercallcoherent |= other.shadercallcoherent; 661 nonprivate |= other.nonprivate; 662 volatil |= other.volatil; 663 isImage |= other.isImage; 664 return *this; 665 } 666 #endif 667 }; 668 CoherentFlags coherentFlags; 669 }; 670 671 // 672 // the SPIR-V builder maintains a single active chain that 673 // the following methods operate on 674 // 675 676 // for external save and restore getAccessChain()677 AccessChain getAccessChain() { return accessChain; } setAccessChain(AccessChain newChain)678 void setAccessChain(AccessChain newChain) { accessChain = newChain; } 679 680 // clear accessChain 681 void clearAccessChain(); 682 683 // set new base as an l-value base setAccessChainLValue(Id lValue)684 void setAccessChainLValue(Id lValue) 685 { 686 assert(isPointer(lValue)); 687 accessChain.base = lValue; 688 } 689 690 // set new base value as an r-value setAccessChainRValue(Id rValue)691 void setAccessChainRValue(Id rValue) 692 { 693 accessChain.isRValue = true; 694 accessChain.base = rValue; 695 } 696 697 // push offset onto the end of the chain accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)698 void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) 699 { 700 accessChain.indexChain.push_back(offset); 701 accessChain.coherentFlags |= coherentFlags; 702 accessChain.alignment |= alignment; 703 } 704 705 // push new swizzle onto the end of any existing swizzle, merging into a single swizzle 706 void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, 707 AccessChain::CoherentFlags coherentFlags, unsigned int alignment); 708 709 // push a dynamic component selection onto the access chain, only applicable with a 710 // non-trivial swizzle or no swizzle accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)711 void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, 712 unsigned int alignment) 713 { 714 if (accessChain.swizzle.size() != 1) { 715 accessChain.component = component; 716 if (accessChain.preSwizzleBaseType == NoType) 717 accessChain.preSwizzleBaseType = preSwizzleBaseType; 718 } 719 accessChain.coherentFlags |= coherentFlags; 720 accessChain.alignment |= alignment; 721 } 722 723 // use accessChain and swizzle to store value 724 void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, 725 spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 726 727 // use accessChain and swizzle to load an r-value 728 Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, 729 spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, 730 unsigned int alignment = 0); 731 732 // Return whether or not the access chain can be represented in SPIR-V 733 // as an l-value. 734 // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. isSpvLvalue()735 bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } 736 737 // get the direct pointer for an l-value 738 Id accessChainGetLValue(); 739 740 // Get the inferred SPIR-V type of the result of the current access chain, 741 // based on the type of the base and the chain of dereferences. 742 Id accessChainGetInferredType(); 743 744 // Add capabilities, extensions, remove unneeded decorations, etc., 745 // based on the resulting SPIR-V. 746 void postProcess(); 747 748 // Prune unreachable blocks in the CFG and remove unneeded decorations. 749 void postProcessCFG(); 750 751 #ifndef GLSLANG_WEB 752 // Add capabilities, extensions based on instructions in the module. 753 void postProcessFeatures(); 754 // Hook to visit each instruction in a block in a function 755 void postProcess(Instruction&); 756 // Hook to visit each non-32-bit sized float/int operation in a block. 757 void postProcessType(const Instruction&, spv::Id typeId); 758 #endif 759 760 void dump(std::vector<unsigned int>&) const; 761 762 void createBranch(Block* block); 763 void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); 764 void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, 765 const std::vector<unsigned int>& operands); 766 767 // Sets to generate opcode for specialization constants. setToSpecConstCodeGenMode()768 void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } 769 // Sets to generate opcode for non-specialization constants (normal mode). setToNormalCodeGenMode()770 void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } 771 // Check if the builder is generating code for spec constants. isInSpecConstCodeGenMode()772 bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } 773 774 protected: 775 Id makeIntConstant(Id typeId, unsigned value, bool specConstant); 776 Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); 777 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); 778 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); 779 Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps); 780 Id findStructConstant(Id typeId, const std::vector<Id>& comps); 781 Id collapseAccessChain(); 782 void remapDynamicSwizzle(); 783 void transferAccessChainSwizzle(bool dynamic); 784 void simplifyAccessChainSwizzle(); 785 void createAndSetNoPredecessorBlock(const char*); 786 void createSelectionMerge(Block* mergeBlock, unsigned int control); 787 void dumpSourceInstructions(std::vector<unsigned int>&) const; 788 void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const; 789 void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const; 790 void dumpModuleProcesses(std::vector<unsigned int>&) const; 791 spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) 792 const; 793 794 unsigned int spvVersion; // the version of SPIR-V to emit in the header 795 SourceLanguage source; 796 int sourceVersion; 797 spv::Id sourceFileStringId; 798 std::string sourceText; 799 int currentLine; 800 const char* currentFile; 801 bool emitOpLines; 802 std::set<std::string> extensions; 803 std::vector<const char*> sourceExtensions; 804 std::vector<const char*> moduleProcesses; 805 AddressingModel addressModel; 806 MemoryModel memoryModel; 807 std::set<spv::Capability> capabilities; 808 int builderNumber; 809 Module module; 810 Block* buildPoint; 811 Id uniqueId; 812 Function* entryPointFunction; 813 bool generatingOpCodeForSpecConst; 814 AccessChain accessChain; 815 816 // special blocks of instructions for output 817 std::vector<std::unique_ptr<Instruction> > strings; 818 std::vector<std::unique_ptr<Instruction> > imports; 819 std::vector<std::unique_ptr<Instruction> > entryPoints; 820 std::vector<std::unique_ptr<Instruction> > executionModes; 821 std::vector<std::unique_ptr<Instruction> > names; 822 std::vector<std::unique_ptr<Instruction> > decorations; 823 std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals; 824 std::vector<std::unique_ptr<Instruction> > externals; 825 std::vector<std::unique_ptr<Function> > functions; 826 827 // not output, internally used for quick & dirty canonical (unique) creation 828 829 // map type opcodes to constant inst. 830 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants; 831 // map struct-id to constant instructions 832 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; 833 // map type opcodes to type instructions 834 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes; 835 836 // stack of switches 837 std::stack<Block*> switchMerges; 838 839 // Our loop stack. 840 std::stack<LoopBlocks> loops; 841 842 // map from strings to their string ids 843 std::unordered_map<std::string, spv::Id> stringIds; 844 845 // map from include file name ids to their contents 846 std::map<spv::Id, const std::string*> includeFiles; 847 848 // The stream for outputting warnings and errors. 849 SpvBuildLogger* logger; 850 }; // end Builder class 851 852 }; // end spv namespace 853 854 #endif // SpvBuilder_H 855