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