1 // 2 // Copyright (C) 2014-2015 LunarG, Inc. 3 // Copyright (C) 2015-2018 Google, Inc. 4 // Copyright (C) 2017 ARM Limited. 5 // 6 // All rights reserved. 7 // 8 // Redistribution and use in source and binary forms, with or without 9 // modification, are permitted provided that the following conditions 10 // are met: 11 // 12 // Redistributions of source code must retain the above copyright 13 // notice, this list of conditions and the following disclaimer. 14 // 15 // Redistributions in binary form must reproduce the above 16 // copyright notice, this list of conditions and the following 17 // disclaimer in the documentation and/or other materials provided 18 // with the distribution. 19 // 20 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 21 // contributors may be used to endorse or promote products derived 22 // from this software without specific prior written permission. 23 // 24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 // POSSIBILITY OF SUCH DAMAGE. 36 37 // 38 // "Builder" is an interface to fully build SPIR-V IR. Allocate one of 39 // these to build (a thread safe) internal SPIR-V representation (IR), 40 // and then dump it as a binary stream according to the SPIR-V specification. 41 // 42 // A Builder has a 1:1 relationship with a SPIR-V module. 43 // 44 45 #pragma once 46 #ifndef SpvBuilder_H 47 #define SpvBuilder_H 48 49 #include "Logger.h" 50 #include "spirv.hpp" 51 #include "spvIR.h" 52 53 #include <algorithm> 54 #include <map> 55 #include <memory> 56 #include <set> 57 #include <sstream> 58 #include <stack> 59 #include <unordered_map> 60 #include <map> 61 62 namespace spv { 63 64 class Builder { 65 public: 66 Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); 67 virtual ~Builder(); 68 69 static const int maxMatrixSize = 4; 70 getSpvVersion()71 unsigned int getSpvVersion() const { return spvVersion; } 72 setSource(spv::SourceLanguage lang,int version)73 void setSource(spv::SourceLanguage lang, int version) 74 { 75 source = lang; 76 sourceVersion = version; 77 } getStringId(const std::string & str)78 spv::Id getStringId(const std::string& str) 79 { 80 auto sItr = stringIds.find(str); 81 if (sItr != stringIds.end()) 82 return sItr->second; 83 spv::Id strId = getUniqueId(); 84 Instruction* fileString = new Instruction(strId, NoType, OpString); 85 const char* file_c_str = str.c_str(); 86 fileString->addStringOperand(file_c_str); 87 strings.push_back(std::unique_ptr<Instruction>(fileString)); 88 stringIds[file_c_str] = strId; 89 return strId; 90 } setSourceFile(const std::string & file)91 void setSourceFile(const std::string& file) 92 { 93 sourceFileStringId = getStringId(file); 94 } setSourceText(const std::string & text)95 void setSourceText(const std::string& text) { sourceText = text; } addSourceExtension(const char * ext)96 void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } addModuleProcessed(const std::string & p)97 void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } setEmitOpLines()98 void setEmitOpLines() { emitOpLines = true; } addExtension(const char * ext)99 void addExtension(const char* ext) { extensions.insert(ext); } addInclude(const std::string & name,const std::string & text)100 void addInclude(const std::string& name, const std::string& text) 101 { 102 spv::Id incId = getStringId(name); 103 includeFiles[incId] = &text; 104 } 105 Id import(const char*); setMemoryModel(spv::AddressingModel addr,spv::MemoryModel mem)106 void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) 107 { 108 addressModel = addr; 109 memoryModel = mem; 110 } 111 addCapability(spv::Capability cap)112 void addCapability(spv::Capability cap) { capabilities.insert(cap); } 113 114 // To get a new <id> for anything needing a new one. getUniqueId()115 Id getUniqueId() { return ++uniqueId; } 116 117 // To get a set of new <id>s, e.g., for a set of function parameters getUniqueIds(int numIds)118 Id getUniqueIds(int numIds) 119 { 120 Id id = uniqueId + 1; 121 uniqueId += numIds; 122 return id; 123 } 124 125 // Generate OpLine for non-filename-based #line directives (ie no filename 126 // seen yet): Log the current line, and if different than the last one, 127 // issue a new OpLine using the new line and current source file name. 128 void setLine(int line); 129 130 // If filename null, generate OpLine for non-filename-based line directives, 131 // else do filename-based: Log the current line and file, and if different 132 // than the last one, issue a new OpLine using the new line and file 133 // name. 134 void setLine(int line, const char* filename); 135 // Low-level OpLine. See setLine() for a layered helper. 136 void addLine(Id fileName, int line, int column); 137 138 // For creating new types (will return old type if the requested one was already made). 139 Id makeVoidType(); 140 Id makeBoolType(); 141 Id makePointer(StorageClass, Id pointee); 142 Id makeForwardPointer(StorageClass); 143 Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); 144 Id makeIntegerType(int width, bool hasSign); // generic makeIntType(int width)145 Id makeIntType(int width) { return makeIntegerType(width, true); } makeUintType(int width)146 Id makeUintType(int width) { return makeIntegerType(width, false); } 147 Id makeFloatType(int width); 148 Id makeStructType(const std::vector<Id>& members, const char*); 149 Id makeStructResultType(Id type0, Id type1); 150 Id makeVectorType(Id component, int size); 151 Id makeMatrixType(Id component, int cols, int rows); 152 Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration 153 Id makeRuntimeArray(Id element); 154 Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes); 155 Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format); 156 Id makeSamplerType(); 157 Id makeSampledImageType(Id imageType); 158 159 // accelerationStructureNV type 160 Id makeAccelerationStructureNVType(); 161 162 // For querying about types. getTypeId(Id resultId)163 Id getTypeId(Id resultId) const { return module.getTypeId(resultId); } 164 Id getDerefTypeId(Id resultId) const; getOpCode(Id id)165 Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); } getTypeClass(Id typeId)166 Op getTypeClass(Id typeId) const { return getOpCode(typeId); } 167 Op getMostBasicTypeClass(Id typeId) const; getNumComponents(Id resultId)168 int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); } 169 int getNumTypeConstituents(Id typeId) const; getNumTypeComponents(Id typeId)170 int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } 171 Id getScalarTypeId(Id typeId) const; 172 Id getContainedTypeId(Id typeId) const; 173 Id getContainedTypeId(Id typeId, int) const; getTypeStorageClass(Id typeId)174 StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } getImageTypeFormat(Id typeId)175 ImageFormat getImageTypeFormat(Id typeId) const { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } 176 isPointer(Id resultId)177 bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); } isScalar(Id resultId)178 bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); } isVector(Id resultId)179 bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); } isMatrix(Id resultId)180 bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); } isAggregate(Id resultId)181 bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); } isSampledImage(Id resultId)182 bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } 183 isBoolType(Id typeId)184 bool isBoolType(Id typeId) { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } isIntType(Id typeId)185 bool isIntType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } isUintType(Id typeId)186 bool isUintType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } isFloatType(Id typeId)187 bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } isPointerType(Id typeId)188 bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } isScalarType(Id typeId)189 bool isScalarType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; } isVectorType(Id typeId)190 bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } isMatrixType(Id typeId)191 bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; } isStructType(Id typeId)192 bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; } isArrayType(Id typeId)193 bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; } isAggregateType(Id typeId)194 bool isAggregateType(Id typeId) const { return isArrayType(typeId) || isStructType(typeId); } isImageType(Id typeId)195 bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; } isSamplerType(Id typeId)196 bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; } isSampledImageType(Id typeId)197 bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } 198 bool containsType(Id typeId, Op typeOp, unsigned int width) const; 199 bool containsPhysicalStorageBufferOrArray(Id typeId) const; 200 201 bool isConstantOpCode(Op opcode) const; 202 bool isSpecConstantOpCode(Op opcode) const; isConstant(Id resultId)203 bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } isConstantScalar(Id resultId)204 bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } isSpecConstant(Id resultId)205 bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } getConstantScalar(Id resultId)206 unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); } getStorageClass(Id resultId)207 StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } 208 getScalarTypeWidth(Id typeId)209 int getScalarTypeWidth(Id typeId) const 210 { 211 Id scalarTypeId = getScalarTypeId(typeId); 212 assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); 213 return module.getInstruction(scalarTypeId)->getImmediateOperand(0); 214 } 215 getTypeNumColumns(Id typeId)216 int getTypeNumColumns(Id typeId) const 217 { 218 assert(isMatrixType(typeId)); 219 return getNumTypeConstituents(typeId); 220 } getNumColumns(Id resultId)221 int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); } getTypeNumRows(Id typeId)222 int getTypeNumRows(Id typeId) const 223 { 224 assert(isMatrixType(typeId)); 225 return getNumTypeComponents(getContainedTypeId(typeId)); 226 } getNumRows(Id resultId)227 int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); } 228 getTypeDimensionality(Id typeId)229 Dim getTypeDimensionality(Id typeId) const 230 { 231 assert(isImageType(typeId)); 232 return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); 233 } getImageType(Id resultId)234 Id getImageType(Id resultId) const 235 { 236 Id typeId = getTypeId(resultId); 237 assert(isImageType(typeId) || isSampledImageType(typeId)); 238 return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; 239 } isArrayedImageType(Id typeId)240 bool isArrayedImageType(Id typeId) const 241 { 242 assert(isImageType(typeId)); 243 return module.getInstruction(typeId)->getImmediateOperand(3) != 0; 244 } 245 246 // For making new constants (will return old constant if the requested one was already made). 247 Id makeBoolConstant(bool b, bool specConstant = false); 248 Id makeInt8Constant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } 249 Id makeUint8Constant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(8), u, specConstant); } 250 Id makeInt16Constant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } 251 Id makeUint16Constant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(16), u, specConstant); } 252 Id makeIntConstant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } 253 Id makeUintConstant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(32), u, specConstant); } 254 Id makeInt64Constant(long long i, bool specConstant = false) { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } 255 Id makeUint64Constant(unsigned long long u, bool specConstant = false) { return makeInt64Constant(makeUintType(64), u, specConstant); } 256 Id makeFloatConstant(float f, bool specConstant = false); 257 Id makeDoubleConstant(double d, bool specConstant = false); 258 Id makeFloat16Constant(float f16, bool specConstant = false); 259 Id makeFpConstant(Id type, double d, bool specConstant = false); 260 261 // Turn the array of constants into a proper spv constant of the requested type. 262 Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false); 263 264 // Methods for adding information outside the CFG. 265 Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); 266 void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); 267 void addName(Id, const char* name); 268 void addMemberName(Id, int member, const char* name); 269 void addDecoration(Id, Decoration, int num = -1); 270 void addDecoration(Id, Decoration, const char*); 271 void addDecorationId(Id id, Decoration, Id idDecoration); 272 void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); 273 void addMemberDecoration(Id, unsigned int member, Decoration, const char*); 274 275 // At the end of what block do the next create*() instructions go? setBuildPoint(Block * bp)276 void setBuildPoint(Block* bp) { buildPoint = bp; } getBuildPoint()277 Block* getBuildPoint() const { return buildPoint; } 278 279 // Make the entry-point function. The returned pointer is only valid 280 // for the lifetime of this builder. 281 Function* makeEntryPoint(const char*); 282 283 // Make a shader-style function, and create its entry block if entry is non-zero. 284 // Return the function, pass back the entry. 285 // The returned pointer is only valid for the lifetime of this builder. 286 Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, const std::vector<Id>& paramTypes, 287 const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0); 288 289 // Create a return. An 'implicit' return is one not appearing in the source 290 // code. In the case of an implicit return, no post-return block is inserted. 291 void makeReturn(bool implicit, Id retVal = 0); 292 293 // Generate all the code needed to finish up a function. 294 void leaveFunction(); 295 296 // Create a discard. 297 void makeDiscard(); 298 299 // Create a global or function local or IO variable. 300 Id createVariable(StorageClass, Id type, const char* name = 0); 301 302 // Create an intermediate with an undefined value. 303 Id createUndefined(Id type); 304 305 // Store into an Id and return the l-value 306 void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 307 308 // Load from an Id and return it 309 Id createLoad(Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 310 311 // Create an OpAccessChain instruction 312 Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets); 313 314 // Create an OpArrayLength instruction 315 Id createArrayLength(Id base, unsigned int member); 316 317 // Create an OpCompositeExtract instruction 318 Id createCompositeExtract(Id composite, Id typeId, unsigned index); 319 Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes); 320 Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); 321 Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes); 322 323 Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); 324 Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); 325 326 void createNoResultOp(Op); 327 void createNoResultOp(Op, Id operand); 328 void createNoResultOp(Op, const std::vector<Id>& operands); 329 void createNoResultOp(Op, const std::vector<IdImmediate>& operands); 330 void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); 331 void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); 332 Id createUnaryOp(Op, Id typeId, Id operand); 333 Id createBinOp(Op, Id typeId, Id operand1, Id operand2); 334 Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); 335 Id createOp(Op, Id typeId, const std::vector<Id>& operands); 336 Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands); 337 Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&); 338 Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals); 339 340 // Take an rvalue (source) and a set of channels to extract from it to 341 // make a new rvalue, which is returned. 342 Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels); 343 344 // Take a copy of an lvalue (target) and a source of components, and set the 345 // source components into the lvalue where the 'channels' say to put them. 346 // An updated version of the target is returned. 347 // (No true lvalue or stores are used.) 348 Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels); 349 350 // If both the id and precision are valid, the id 351 // gets tagged with the requested precision. 352 // The passed in id is always the returned id, to simplify use patterns. setPrecision(Id id,Decoration precision)353 Id setPrecision(Id id, Decoration precision) 354 { 355 if (precision != NoPrecision && id != NoResult) 356 addDecoration(id, precision); 357 358 return id; 359 } 360 361 // Can smear a scalar to a vector for the following forms: 362 // - promoteScalar(scalar, vector) // smear scalar to width of vector 363 // - promoteScalar(vector, scalar) // smear scalar to width of vector 364 // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to 365 // - promoteScalar(scalar, scalar) // do nothing 366 // Other forms are not allowed. 367 // 368 // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. 369 // The type of the created vector is a vector of components of the same type as the scalar. 370 // 371 // Note: One of the arguments will change, with the result coming back that way rather than 372 // through the return value. 373 void promoteScalar(Decoration precision, Id& left, Id& right); 374 375 // Make a value by smearing the scalar to fill the type. 376 // vectorType should be the correct type for making a vector of scalarVal. 377 // (No conversions are done.) 378 Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); 379 380 // Create a call to a built-in function. 381 Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args); 382 383 // List of parameters used to create a texture operation 384 struct TextureParameters { 385 Id sampler; 386 Id coords; 387 Id bias; 388 Id lod; 389 Id Dref; 390 Id offset; 391 Id offsets; 392 Id gradX; 393 Id gradY; 394 Id sample; 395 Id component; 396 Id texelOut; 397 Id lodClamp; 398 Id granularity; 399 Id coarse; 400 bool nonprivate; 401 bool volatil; 402 }; 403 404 // Select the correct texture operation based on all inputs, and emit the correct instruction 405 Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, bool noImplicit, const TextureParameters&); 406 407 // Emit the OpTextureQuery* instruction that was passed in. 408 // Figure out the right return value and type, and return it. 409 Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); 410 411 Id createSamplePositionCall(Decoration precision, Id, Id); 412 413 Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); 414 Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); 415 416 // Reduction comparison for composites: For equal and not-equal resulting in a scalar. 417 Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); 418 419 // OpCompositeConstruct 420 Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents); 421 422 // vector or scalar constructor 423 Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId); 424 425 // matrix constructor 426 Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee); 427 428 // Helper to use for building nested control flow with if-then-else. 429 class If { 430 public: 431 If(Id condition, unsigned int ctrl, Builder& builder); ~If()432 ~If() {} 433 434 void makeBeginElse(); 435 void makeEndIf(); 436 437 private: 438 If(const If&); 439 If& operator=(If&); 440 441 Builder& builder; 442 Id condition; 443 unsigned int control; 444 Function* function; 445 Block* headerBlock; 446 Block* thenBlock; 447 Block* elseBlock; 448 Block* mergeBlock; 449 }; 450 451 // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing 452 // any case/default labels, all separated by one or more case/default labels. Each possible 453 // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this 454 // number space. How to compute the value is given by 'condition', as in switch(condition). 455 // 456 // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. 457 // 458 // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). 459 // 460 // Returns the right set of basic blocks to start each code segment with, so that the caller's 461 // recursion stack can hold the memory for it. 462 // 463 void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues, 464 const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); // return argument 465 466 // Add a branch to the innermost switch's merge block. 467 void addSwitchBreak(); 468 469 // Move to the next code segment, passing in the return argument in makeSwitch() 470 void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment); 471 472 // Finish off the innermost switch. 473 void endSwitch(std::vector<Block*>& segmentBB); 474 475 struct LoopBlocks { LoopBlocksLoopBlocks476 LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : 477 head(head), body(body), merge(merge), continue_target(continue_target) { } 478 Block &head, &body, &merge, &continue_target; 479 private: 480 LoopBlocks(); 481 LoopBlocks& operator=(const LoopBlocks&); 482 }; 483 484 // Start a new loop and prepare the builder to generate code for it. Until 485 // closeLoop() is called for this loop, createLoopContinue() and 486 // createLoopExit() will target its corresponding blocks. 487 LoopBlocks& makeNewLoop(); 488 489 // Create a new block in the function containing the build point. Memory is 490 // owned by the function object. 491 Block& makeNewBlock(); 492 493 // Add a branch to the continue_target of the current (innermost) loop. 494 void createLoopContinue(); 495 496 // Add an exit (e.g. "break") from the innermost loop that we're currently 497 // in. 498 void createLoopExit(); 499 500 // Close the innermost loop that you're in 501 void closeLoop(); 502 503 // 504 // Access chain design for an R-Value vs. L-Value: 505 // 506 // There is a single access chain the builder is building at 507 // any particular time. Such a chain can be used to either to a load or 508 // a store, when desired. 509 // 510 // Expressions can be r-values, l-values, or both, or only r-values: 511 // a[b.c].d = .... // l-value 512 // ... = a[b.c].d; // r-value, that also looks like an l-value 513 // ++a[b.c].d; // r-value and l-value 514 // (x + y)[2]; // r-value only, can't possibly be l-value 515 // 516 // Computing an r-value means generating code. Hence, 517 // r-values should only be computed when they are needed, not speculatively. 518 // 519 // Computing an l-value means saving away information for later use in the compiler, 520 // no code is generated until the l-value is later dereferenced. It is okay 521 // to speculatively generate an l-value, just not okay to speculatively dereference it. 522 // 523 // The base of the access chain (the left-most variable or expression 524 // from which everything is based) can be set either as an l-value 525 // or as an r-value. Most efficient would be to set an l-value if one 526 // is available. If an expression was evaluated, the resulting r-value 527 // can be set as the chain base. 528 // 529 // The users of this single access chain can save and restore if they 530 // want to nest or manage multiple chains. 531 // 532 533 struct AccessChain { 534 Id base; // for l-values, pointer to the base object, for r-values, the base object 535 std::vector<Id> indexChain; 536 Id instr; // cache the instruction that generates this access chain 537 std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number 538 Id component; // a dynamic component index, can coexist with a swizzle, done after the swizzle, NoResult if not present 539 Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; NoType unless a swizzle or component is present 540 bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value 541 unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. Only tracks base and (optional) component selection alignment. 542 543 // Accumulate whether anything in the chain of structures has coherent decorations. 544 struct CoherentFlags { 545 unsigned coherent : 1; 546 unsigned devicecoherent : 1; 547 unsigned queuefamilycoherent : 1; 548 unsigned workgroupcoherent : 1; 549 unsigned subgroupcoherent : 1; 550 unsigned nonprivate : 1; 551 unsigned volatil : 1; 552 unsigned isImage : 1; 553 clearAccessChain::CoherentFlags554 void clear() { 555 coherent = 0; 556 devicecoherent = 0; 557 queuefamilycoherent = 0; 558 workgroupcoherent = 0; 559 subgroupcoherent = 0; 560 nonprivate = 0; 561 volatil = 0; 562 isImage = 0; 563 } 564 CoherentFlagsAccessChain::CoherentFlags565 CoherentFlags() { clear(); } 566 CoherentFlags operator |=(const CoherentFlags &other) { 567 coherent |= other.coherent; 568 devicecoherent |= other.devicecoherent; 569 queuefamilycoherent |= other.queuefamilycoherent; 570 workgroupcoherent |= other.workgroupcoherent; 571 subgroupcoherent |= other.subgroupcoherent; 572 nonprivate |= other.nonprivate; 573 volatil |= other.volatil; 574 isImage |= other.isImage; 575 return *this; 576 } 577 }; 578 CoherentFlags coherentFlags; 579 }; 580 581 // 582 // the SPIR-V builder maintains a single active chain that 583 // the following methods operate on 584 // 585 586 // for external save and restore getAccessChain()587 AccessChain getAccessChain() { return accessChain; } setAccessChain(AccessChain newChain)588 void setAccessChain(AccessChain newChain) { accessChain = newChain; } 589 590 // clear accessChain 591 void clearAccessChain(); 592 593 // set new base as an l-value base setAccessChainLValue(Id lValue)594 void setAccessChainLValue(Id lValue) 595 { 596 assert(isPointer(lValue)); 597 accessChain.base = lValue; 598 } 599 600 // set new base value as an r-value setAccessChainRValue(Id rValue)601 void setAccessChainRValue(Id rValue) 602 { 603 accessChain.isRValue = true; 604 accessChain.base = rValue; 605 } 606 607 // push offset onto the end of the chain accessChainPush(Id offset,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)608 void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) 609 { 610 accessChain.indexChain.push_back(offset); 611 accessChain.coherentFlags |= coherentFlags; 612 accessChain.alignment |= alignment; 613 } 614 615 // push new swizzle onto the end of any existing swizzle, merging into a single swizzle 616 void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment); 617 618 // push a dynamic component selection onto the access chain, only applicable with a 619 // non-trivial swizzle or no swizzle accessChainPushComponent(Id component,Id preSwizzleBaseType,AccessChain::CoherentFlags coherentFlags,unsigned int alignment)620 void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) 621 { 622 if (accessChain.swizzle.size() != 1) { 623 accessChain.component = component; 624 if (accessChain.preSwizzleBaseType == NoType) 625 accessChain.preSwizzleBaseType = preSwizzleBaseType; 626 } 627 accessChain.coherentFlags |= coherentFlags; 628 accessChain.alignment |= alignment; 629 } 630 631 // use accessChain and swizzle to store value 632 void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 633 634 // use accessChain and swizzle to load an r-value 635 Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); 636 637 // get the direct pointer for an l-value 638 Id accessChainGetLValue(); 639 640 // Get the inferred SPIR-V type of the result of the current access chain, 641 // based on the type of the base and the chain of dereferences. 642 Id accessChainGetInferredType(); 643 644 // Add capabilities, extensions, remove unneeded decorations, etc., 645 // based on the resulting SPIR-V. 646 void postProcess(); 647 648 // Hook to visit each instruction in a block in a function 649 void postProcess(Instruction&); 650 // Hook to visit each instruction in a reachable block in a function. 651 void postProcessReachable(const Instruction&); 652 // Hook to visit each non-32-bit sized float/int operation in a block. 653 void postProcessType(const Instruction&, spv::Id typeId); 654 655 void dump(std::vector<unsigned int>&) const; 656 657 void createBranch(Block* block); 658 void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); 659 void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength); 660 661 // Sets to generate opcode for specialization constants. setToSpecConstCodeGenMode()662 void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } 663 // Sets to generate opcode for non-specialization constants (normal mode). setToNormalCodeGenMode()664 void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } 665 // Check if the builder is generating code for spec constants. isInSpecConstCodeGenMode()666 bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } 667 668 protected: 669 Id makeIntConstant(Id typeId, unsigned value, bool specConstant); 670 Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); 671 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); 672 Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); 673 Id findCompositeConstant(Op typeClass, const std::vector<Id>& comps); 674 Id findStructConstant(Id typeId, const std::vector<Id>& comps); 675 Id collapseAccessChain(); 676 void remapDynamicSwizzle(); 677 void transferAccessChainSwizzle(bool dynamic); 678 void simplifyAccessChainSwizzle(); 679 void createAndSetNoPredecessorBlock(const char*); 680 void createSelectionMerge(Block* mergeBlock, unsigned int control); 681 void dumpSourceInstructions(std::vector<unsigned int>&) const; 682 void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const; 683 void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const; 684 void dumpModuleProcesses(std::vector<unsigned int>&) const; 685 spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) const; 686 687 unsigned int spvVersion; // the version of SPIR-V to emit in the header 688 SourceLanguage source; 689 int sourceVersion; 690 spv::Id sourceFileStringId; 691 std::string sourceText; 692 int currentLine; 693 const char* currentFile; 694 bool emitOpLines; 695 std::set<std::string> extensions; 696 std::vector<const char*> sourceExtensions; 697 std::vector<const char*> moduleProcesses; 698 AddressingModel addressModel; 699 MemoryModel memoryModel; 700 std::set<spv::Capability> capabilities; 701 int builderNumber; 702 Module module; 703 Block* buildPoint; 704 Id uniqueId; 705 Function* entryPointFunction; 706 bool generatingOpCodeForSpecConst; 707 AccessChain accessChain; 708 709 // special blocks of instructions for output 710 std::vector<std::unique_ptr<Instruction> > strings; 711 std::vector<std::unique_ptr<Instruction> > imports; 712 std::vector<std::unique_ptr<Instruction> > entryPoints; 713 std::vector<std::unique_ptr<Instruction> > executionModes; 714 std::vector<std::unique_ptr<Instruction> > names; 715 std::vector<std::unique_ptr<Instruction> > decorations; 716 std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals; 717 std::vector<std::unique_ptr<Instruction> > externals; 718 std::vector<std::unique_ptr<Function> > functions; 719 720 // not output, internally used for quick & dirty canonical (unique) creation 721 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants; // map type opcodes to constant inst. 722 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; // map struct-id to constant instructions 723 std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes; // map type opcodes to type instructions 724 725 // stack of switches 726 std::stack<Block*> switchMerges; 727 728 // Our loop stack. 729 std::stack<LoopBlocks> loops; 730 731 // map from strings to their string ids 732 std::unordered_map<std::string, spv::Id> stringIds; 733 734 // map from include file name ids to their contents 735 std::map<spv::Id, const std::string*> includeFiles; 736 737 // The stream for outputting warnings and errors. 738 SpvBuildLogger* logger; 739 }; // end Builder class 740 741 }; // end spv namespace 742 743 #endif // SpvBuilder_H 744