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