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