/src/shaderc/third_party/glslang/SPIRV/SpvBuilder.cpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (C) 2014-2015 LunarG, Inc. |
3 | | // Copyright (C) 2015-2018 Google, Inc. |
4 | | // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
5 | | // |
6 | | // All rights reserved. |
7 | | // |
8 | | // Redistribution and use in source and binary forms, with or without |
9 | | // modification, are permitted provided that the following conditions |
10 | | // are met: |
11 | | // |
12 | | // Redistributions of source code must retain the above copyright |
13 | | // notice, this list of conditions and the following disclaimer. |
14 | | // |
15 | | // Redistributions in binary form must reproduce the above |
16 | | // copyright notice, this list of conditions and the following |
17 | | // disclaimer in the documentation and/or other materials provided |
18 | | // with the distribution. |
19 | | // |
20 | | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
21 | | // contributors may be used to endorse or promote products derived |
22 | | // from this software without specific prior written permission. |
23 | | // |
24 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
25 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
26 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
27 | | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
28 | | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
29 | | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
30 | | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
31 | | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
32 | | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
33 | | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
34 | | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
35 | | // POSSIBILITY OF SUCH DAMAGE. |
36 | | |
37 | | // |
38 | | // Helper for making SPIR-V IR. Generally, this is documented in the header |
39 | | // SpvBuilder.h. |
40 | | // |
41 | | |
42 | | #include <cassert> |
43 | | #include <cstdlib> |
44 | | |
45 | | #include <unordered_set> |
46 | | #include <algorithm> |
47 | | |
48 | | #include "SpvBuilder.h" |
49 | | #include "spvUtil.h" |
50 | | #include "hex_float.h" |
51 | | |
52 | | #ifndef _WIN32 |
53 | | #include <cstdio> |
54 | | #endif |
55 | | |
56 | | namespace spv { |
57 | | |
58 | | Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : |
59 | 442 | spvVersion(spvVersion), |
60 | 442 | sourceLang(SourceLanguage::Unknown), |
61 | 442 | sourceVersion(0), |
62 | 442 | addressModel(AddressingModel::Logical), |
63 | 442 | memoryModel(MemoryModel::GLSL450), |
64 | 442 | builderNumber(magicNumber), |
65 | 442 | buildPoint(nullptr), |
66 | 442 | uniqueId(0), |
67 | 442 | entryPointFunction(nullptr), |
68 | 442 | generatingOpCodeForSpecConst(false), |
69 | 442 | logger(buildLogger) |
70 | 442 | { |
71 | 442 | clearAccessChain(); |
72 | 442 | } |
73 | | |
74 | | Builder::~Builder() |
75 | 442 | { |
76 | 442 | } |
77 | | |
78 | | Id Builder::import(const char* name) |
79 | 460 | { |
80 | 460 | Instruction* import = new Instruction(getUniqueId(), NoType, Op::OpExtInstImport); |
81 | 460 | import->addStringOperand(name); |
82 | 460 | module.mapInstruction(import); |
83 | | |
84 | 460 | imports.push_back(std::unique_ptr<Instruction>(import)); |
85 | 460 | return import->getResultId(); |
86 | 460 | } |
87 | | |
88 | | // For creating new groupedTypes (will return old type if the requested one was already made). |
89 | | Id Builder::makeVoidType() |
90 | 1.82k | { |
91 | 1.82k | Instruction* type; |
92 | 1.82k | if (groupedTypes[enumCast(Op::OpTypeVoid)].size() == 0) { |
93 | 442 | Id typeId = getUniqueId(); |
94 | 442 | type = new Instruction(typeId, NoType, Op::OpTypeVoid); |
95 | 442 | groupedTypes[enumCast(Op::OpTypeVoid)].push_back(type); |
96 | 442 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
97 | 442 | module.mapInstruction(type); |
98 | | // Core OpTypeVoid used for debug void type |
99 | 442 | if (emitNonSemanticShaderDebugInfo) |
100 | 0 | debugTypeIdLookup[typeId] = typeId; |
101 | 442 | } else |
102 | 1.38k | type = groupedTypes[enumCast(Op::OpTypeVoid)].back(); |
103 | | |
104 | 1.82k | return type->getResultId(); |
105 | 1.82k | } |
106 | | |
107 | | Id Builder::makeBoolType() |
108 | 6.69k | { |
109 | 6.69k | Instruction* type; |
110 | 6.69k | if (groupedTypes[enumCast(Op::OpTypeBool)].size() == 0) { |
111 | 174 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeBool); |
112 | 174 | groupedTypes[enumCast(Op::OpTypeBool)].push_back(type); |
113 | 174 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
114 | 174 | module.mapInstruction(type); |
115 | | |
116 | 174 | if (emitNonSemanticShaderDebugInfo) { |
117 | 0 | auto const debugResultId = makeBoolDebugType(32); |
118 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
119 | 0 | } |
120 | | |
121 | 174 | } else |
122 | 6.51k | type = groupedTypes[enumCast(Op::OpTypeBool)].back(); |
123 | | |
124 | | |
125 | 6.69k | return type->getResultId(); |
126 | 6.69k | } |
127 | | |
128 | | Id Builder::makeSamplerType(const char* debugName) |
129 | 115 | { |
130 | 115 | Instruction* type; |
131 | 115 | if (groupedTypes[enumCast(Op::OpTypeSampler)].size() == 0) { |
132 | 88 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeSampler); |
133 | 88 | groupedTypes[enumCast(Op::OpTypeSampler)].push_back(type); |
134 | 88 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
135 | 88 | module.mapInstruction(type); |
136 | 88 | } else |
137 | 27 | type = groupedTypes[enumCast(Op::OpTypeSampler)].back(); |
138 | | |
139 | 115 | if (emitNonSemanticShaderDebugInfo) |
140 | 0 | { |
141 | 0 | auto const debugResultId = makeOpaqueDebugType(debugName); |
142 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
143 | 0 | } |
144 | | |
145 | 115 | return type->getResultId(); |
146 | 115 | } |
147 | | |
148 | | Id Builder::makePointer(StorageClass storageClass, Id pointee) |
149 | 17.4k | { |
150 | | // try to find it |
151 | 17.4k | Instruction* type; |
152 | 118k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) { |
153 | 114k | type = groupedTypes[enumCast(Op::OpTypePointer)][t]; |
154 | 114k | if (type->getImmediateOperand(0) == (unsigned)storageClass && |
155 | 51.5k | type->getIdOperand(1) == pointee) |
156 | 13.1k | return type->getResultId(); |
157 | 114k | } |
158 | | |
159 | | // not found, make it |
160 | 4.30k | type = new Instruction(getUniqueId(), NoType, Op::OpTypePointer); |
161 | 4.30k | type->reserveOperands(2); |
162 | 4.30k | type->addImmediateOperand(storageClass); |
163 | 4.30k | type->addIdOperand(pointee); |
164 | 4.30k | groupedTypes[enumCast(Op::OpTypePointer)].push_back(type); |
165 | 4.30k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
166 | 4.30k | module.mapInstruction(type); |
167 | | |
168 | 4.30k | if (emitNonSemanticShaderDebugInfo) { |
169 | 0 | const Id debugResultId = makePointerDebugType(storageClass, pointee); |
170 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
171 | 0 | } |
172 | | |
173 | 4.30k | return type->getResultId(); |
174 | 17.4k | } |
175 | | |
176 | | Id Builder::makeForwardPointer(StorageClass storageClass) |
177 | 54 | { |
178 | | // Caching/uniquifying doesn't work here, because we don't know the |
179 | | // pointee type and there can be multiple forward pointers of the same |
180 | | // storage type. Somebody higher up in the stack must keep track. |
181 | 54 | Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeForwardPointer); |
182 | 54 | type->addImmediateOperand(storageClass); |
183 | 54 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
184 | 54 | module.mapInstruction(type); |
185 | | |
186 | 54 | if (emitNonSemanticShaderDebugInfo) { |
187 | 0 | const Id debugResultId = makeForwardPointerDebugType(storageClass); |
188 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
189 | 0 | } |
190 | 54 | return type->getResultId(); |
191 | 54 | } |
192 | | |
193 | | Id Builder::makeUntypedPointer(StorageClass storageClass, bool setBufferPointer) |
194 | 0 | { |
195 | | // try to find it |
196 | 0 | Instruction* type; |
197 | | // both typeBufferEXT and UntypedPointer only contains storage class info. |
198 | 0 | spv::Op typeOp = setBufferPointer ? Op::OpTypeBufferEXT : Op::OpTypeUntypedPointerKHR; |
199 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(typeOp)].size(); ++t) { |
200 | 0 | type = groupedTypes[enumCast(typeOp)][t]; |
201 | 0 | if (type->getImmediateOperand(0) == (unsigned)storageClass) |
202 | 0 | return type->getResultId(); |
203 | 0 | } |
204 | | |
205 | | // not found, make it |
206 | 0 | type = new Instruction(getUniqueId(), NoType, typeOp); |
207 | 0 | type->addImmediateOperand(storageClass); |
208 | 0 | groupedTypes[enumCast(typeOp)].push_back(type); |
209 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
210 | 0 | module.mapInstruction(type); |
211 | 0 | return type->getResultId(); |
212 | 0 | } |
213 | | |
214 | | Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) |
215 | 387 | { |
216 | | // try to find it |
217 | 387 | Instruction* type; |
218 | 1.31k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypePointer)].size(); ++t) { |
219 | 1.26k | type = groupedTypes[enumCast(Op::OpTypePointer)][t]; |
220 | 1.26k | if (type->getImmediateOperand(0) == (unsigned)storageClass && |
221 | 414 | type->getIdOperand(1) == pointee) |
222 | 333 | return type->getResultId(); |
223 | 1.26k | } |
224 | | |
225 | 54 | type = new Instruction(forwardPointerType, NoType, Op::OpTypePointer); |
226 | 54 | type->reserveOperands(2); |
227 | 54 | type->addImmediateOperand(storageClass); |
228 | 54 | type->addIdOperand(pointee); |
229 | 54 | groupedTypes[enumCast(Op::OpTypePointer)].push_back(type); |
230 | 54 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
231 | 54 | module.mapInstruction(type); |
232 | | |
233 | | // If we are emitting nonsemantic debuginfo, we need to patch the debug pointer type |
234 | | // that was emitted alongside the forward pointer, now that we have a pointee debug |
235 | | // type for it to point to. |
236 | 54 | if (emitNonSemanticShaderDebugInfo) { |
237 | 0 | Instruction *debugForwardPointer = module.getInstruction(getDebugType(forwardPointerType)); |
238 | 0 | assert(getDebugType(pointee)); |
239 | 0 | debugForwardPointer->setIdOperand(2, getDebugType(pointee)); |
240 | 0 | } |
241 | | |
242 | 54 | return type->getResultId(); |
243 | 387 | } |
244 | | |
245 | | Id Builder::makeIntegerType(int width, bool hasSign) |
246 | 51.0k | { |
247 | | // try to find it |
248 | 51.0k | Instruction* type; |
249 | 115k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeInt)].size(); ++t) { |
250 | 114k | type = groupedTypes[enumCast(Op::OpTypeInt)][t]; |
251 | 114k | if (type->getImmediateOperand(0) == (unsigned)width && |
252 | 68.6k | type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) |
253 | 49.9k | return type->getResultId(); |
254 | 114k | } |
255 | | |
256 | | // not found, make it |
257 | 1.14k | type = new Instruction(getUniqueId(), NoType, Op::OpTypeInt); |
258 | 1.14k | type->reserveOperands(2); |
259 | 1.14k | type->addImmediateOperand(width); |
260 | 1.14k | type->addImmediateOperand(hasSign ? 1 : 0); |
261 | 1.14k | groupedTypes[enumCast(Op::OpTypeInt)].push_back(type); |
262 | 1.14k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
263 | 1.14k | module.mapInstruction(type); |
264 | | |
265 | | // deal with capabilities |
266 | 1.14k | switch (width) { |
267 | 106 | case 8: |
268 | 274 | case 16: |
269 | | // these are currently handled by storage-type declarations and post processing |
270 | 274 | break; |
271 | 177 | case 64: |
272 | 177 | addCapability(Capability::Int64); |
273 | 177 | break; |
274 | 698 | default: |
275 | 698 | break; |
276 | 1.14k | } |
277 | | |
278 | 1.14k | if (emitNonSemanticShaderDebugInfo) |
279 | 0 | { |
280 | 0 | auto const debugResultId = makeIntegerDebugType(width, hasSign); |
281 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
282 | 0 | } |
283 | | |
284 | 1.14k | return type->getResultId(); |
285 | 1.14k | } |
286 | | |
287 | | Id Builder::makeFloatType(int width) |
288 | 23.8k | { |
289 | | // try to find it |
290 | 23.8k | Instruction* type; |
291 | 24.7k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) { |
292 | 24.3k | type = groupedTypes[enumCast(Op::OpTypeFloat)][t]; |
293 | 24.3k | if (type->getNumOperands() != 1) { |
294 | 0 | continue; |
295 | 0 | } |
296 | 24.3k | if (type->getImmediateOperand(0) == (unsigned)width) |
297 | 23.4k | return type->getResultId(); |
298 | 24.3k | } |
299 | | |
300 | | // not found, make it |
301 | 400 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat); |
302 | 400 | type->addImmediateOperand(width); |
303 | 400 | groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type); |
304 | 400 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
305 | 400 | module.mapInstruction(type); |
306 | | |
307 | | // deal with capabilities |
308 | 400 | switch (width) { |
309 | 70 | case 16: |
310 | | // currently handled by storage-type declarations and post processing |
311 | 70 | break; |
312 | 36 | case 64: |
313 | 36 | addCapability(Capability::Float64); |
314 | 36 | break; |
315 | 294 | default: |
316 | 294 | break; |
317 | 400 | } |
318 | | |
319 | 400 | if (emitNonSemanticShaderDebugInfo) |
320 | 0 | { |
321 | 0 | auto const debugResultId = makeFloatDebugType(width); |
322 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
323 | 0 | } |
324 | | |
325 | 400 | return type->getResultId(); |
326 | 400 | } |
327 | | |
328 | | Id Builder::makeBFloat16Type() |
329 | 0 | { |
330 | | // try to find it |
331 | 0 | Instruction* type; |
332 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) { |
333 | 0 | type = groupedTypes[enumCast(Op::OpTypeFloat)][t]; |
334 | 0 | if (type->getNumOperands() != 2) { |
335 | 0 | continue; |
336 | 0 | } |
337 | 0 | if (type->getImmediateOperand(0) == (unsigned)16 && |
338 | 0 | type->getImmediateOperand(1) == FPEncoding::BFloat16KHR) |
339 | 0 | return type->getResultId(); |
340 | 0 | } |
341 | | |
342 | | // not found, make it |
343 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat); |
344 | 0 | type->addImmediateOperand(16); |
345 | 0 | type->addImmediateOperand(FPEncoding::BFloat16KHR); |
346 | 0 | groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type); |
347 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
348 | 0 | module.mapInstruction(type); |
349 | |
|
350 | 0 | addExtension(spv::E_SPV_KHR_bfloat16); |
351 | 0 | addCapability(Capability::BFloat16TypeKHR); |
352 | |
|
353 | 0 | if (emitNonSemanticShaderDebugInfo) { |
354 | 0 | auto const debugResultId = makeFloatDebugType(16, makeUintConstant((unsigned)FPEncoding::BFloat16KHR)); |
355 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
356 | 0 | } |
357 | |
|
358 | 0 | return type->getResultId(); |
359 | 0 | } |
360 | | |
361 | | Id Builder::makeFloatE5M2Type() |
362 | 0 | { |
363 | | // try to find it |
364 | 0 | Instruction* type; |
365 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) { |
366 | 0 | type = groupedTypes[enumCast(Op::OpTypeFloat)][t]; |
367 | 0 | if (type->getNumOperands() != 2) { |
368 | 0 | continue; |
369 | 0 | } |
370 | 0 | if (type->getImmediateOperand(0) == (unsigned)8 && |
371 | 0 | type->getImmediateOperand(1) == FPEncoding::Float8E5M2EXT) |
372 | 0 | return type->getResultId(); |
373 | 0 | } |
374 | | |
375 | | // not found, make it |
376 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat); |
377 | 0 | type->addImmediateOperand(8); |
378 | 0 | type->addImmediateOperand(FPEncoding::Float8E5M2EXT); |
379 | 0 | groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type); |
380 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
381 | 0 | module.mapInstruction(type); |
382 | |
|
383 | 0 | addExtension(spv::E_SPV_EXT_float8); |
384 | 0 | addCapability(Capability::Float8EXT); |
385 | |
|
386 | 0 | if (emitNonSemanticShaderDebugInfo) { |
387 | 0 | auto const debugResultId = makeFloatDebugType(8, makeUintConstant((unsigned)FPEncoding::Float8E5M2EXT)); |
388 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
389 | 0 | } |
390 | |
|
391 | 0 | return type->getResultId(); |
392 | 0 | } |
393 | | |
394 | | Id Builder::makeFloatE4M3Type() |
395 | 0 | { |
396 | | // try to find it |
397 | 0 | Instruction* type; |
398 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFloat)].size(); ++t) { |
399 | 0 | type = groupedTypes[enumCast(Op::OpTypeFloat)][t]; |
400 | 0 | if (type->getNumOperands() != 2) { |
401 | 0 | continue; |
402 | 0 | } |
403 | 0 | if (type->getImmediateOperand(0) == (unsigned)8 && |
404 | 0 | type->getImmediateOperand(1) == FPEncoding::Float8E4M3EXT) |
405 | 0 | return type->getResultId(); |
406 | 0 | } |
407 | | |
408 | | // not found, make it |
409 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeFloat); |
410 | 0 | type->addImmediateOperand(8); |
411 | 0 | type->addImmediateOperand(FPEncoding::Float8E4M3EXT); |
412 | 0 | groupedTypes[enumCast(Op::OpTypeFloat)].push_back(type); |
413 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
414 | 0 | module.mapInstruction(type); |
415 | |
|
416 | 0 | addExtension(spv::E_SPV_EXT_float8); |
417 | 0 | addCapability(Capability::Float8EXT); |
418 | |
|
419 | 0 | if (emitNonSemanticShaderDebugInfo) { |
420 | 0 | auto const debugResultId = makeFloatDebugType(8, makeUintConstant((unsigned)FPEncoding::Float8E4M3EXT)); |
421 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
422 | 0 | } |
423 | |
|
424 | 0 | return type->getResultId(); |
425 | 0 | } |
426 | | |
427 | | // Make a struct without checking for duplication. |
428 | | // See makeStructResultType() for non-decorated structs |
429 | | // needed as the result of some instructions, which does |
430 | | // check for duplicates. |
431 | | // For compiler-generated structs, debug info is ignored. |
432 | | Id Builder::makeStructType(const std::vector<Id>& members, const std::vector<spv::StructMemberDebugInfo>& memberDebugInfo, |
433 | | const char* name, bool const compilerGenerated) |
434 | 810 | { |
435 | | // Don't look for previous one, because in the general case, |
436 | | // structs can be duplicated except for decorations. |
437 | | |
438 | | // not found, make it |
439 | 810 | Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeStruct); |
440 | 3.53k | for (int op = 0; op < (int)members.size(); ++op) |
441 | 2.72k | type->addIdOperand(members[op]); |
442 | 810 | groupedTypes[enumCast(Op::OpTypeStruct)].push_back(type); |
443 | 810 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
444 | 810 | module.mapInstruction(type); |
445 | 810 | addName(type->getResultId(), name); |
446 | | |
447 | 810 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) { |
448 | 0 | assert(members.size() == memberDebugInfo.size()); |
449 | 0 | auto const debugResultId = |
450 | 0 | makeCompositeDebugType(members, memberDebugInfo, name, NonSemanticShaderDebugInfoStructure); |
451 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
452 | 0 | } |
453 | | |
454 | 810 | return type->getResultId(); |
455 | 810 | } |
456 | | |
457 | | // Make a struct for the simple results of several instructions, |
458 | | // checking for duplication. |
459 | | Id Builder::makeStructResultType(Id type0, Id type1) |
460 | 553 | { |
461 | | // try to find it |
462 | 553 | Instruction* type; |
463 | 815 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeStruct)].size(); ++t) { |
464 | 756 | type = groupedTypes[enumCast(Op::OpTypeStruct)][t]; |
465 | 756 | if (type->getNumOperands() != 2) |
466 | 87 | continue; |
467 | 669 | if (type->getIdOperand(0) != type0 || |
468 | 651 | type->getIdOperand(1) != type1) |
469 | 175 | continue; |
470 | 494 | return type->getResultId(); |
471 | 669 | } |
472 | | |
473 | | // not found, make it |
474 | 59 | std::vector<spv::Id> members; |
475 | 59 | members.push_back(type0); |
476 | 59 | members.push_back(type1); |
477 | | |
478 | 59 | return makeStructType(members, {}, "ResType"); |
479 | 553 | } |
480 | | |
481 | | Id Builder::makeVectorType(Id component, int size) |
482 | 25.3k | { |
483 | | // try to find it |
484 | 25.3k | Instruction* type; |
485 | 84.8k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeVector)].size(); ++t) { |
486 | 82.9k | type = groupedTypes[enumCast(Op::OpTypeVector)][t]; |
487 | 82.9k | if (type->getIdOperand(0) == component && |
488 | 39.1k | type->getImmediateOperand(1) == (unsigned)size) |
489 | 23.4k | return type->getResultId(); |
490 | 82.9k | } |
491 | | |
492 | | // not found, make it |
493 | 1.90k | type = new Instruction(getUniqueId(), NoType, Op::OpTypeVector); |
494 | 1.90k | type->reserveOperands(2); |
495 | 1.90k | type->addIdOperand(component); |
496 | 1.90k | type->addImmediateOperand(size); |
497 | 1.90k | groupedTypes[enumCast(Op::OpTypeVector)].push_back(type); |
498 | 1.90k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
499 | 1.90k | module.mapInstruction(type); |
500 | | |
501 | 1.90k | if (emitNonSemanticShaderDebugInfo) |
502 | 0 | { |
503 | 0 | auto const debugResultId = makeVectorDebugType(component, size); |
504 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
505 | 0 | } |
506 | | |
507 | 1.90k | return type->getResultId(); |
508 | 25.3k | } |
509 | | |
510 | | Id Builder::makeMatrixType(Id component, int cols, int rows) |
511 | 957 | { |
512 | 957 | assert(cols <= maxMatrixSize && rows <= maxMatrixSize); |
513 | | |
514 | 957 | Id column = makeVectorType(component, rows); |
515 | | |
516 | | // try to find it |
517 | 957 | Instruction* type; |
518 | 1.39k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeMatrix)].size(); ++t) { |
519 | 1.20k | type = groupedTypes[enumCast(Op::OpTypeMatrix)][t]; |
520 | 1.20k | if (type->getIdOperand(0) == column && |
521 | 767 | type->getImmediateOperand(1) == (unsigned)cols) |
522 | 767 | return type->getResultId(); |
523 | 1.20k | } |
524 | | |
525 | | // not found, make it |
526 | 190 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeMatrix); |
527 | 190 | type->reserveOperands(2); |
528 | 190 | type->addIdOperand(column); |
529 | 190 | type->addImmediateOperand(cols); |
530 | 190 | groupedTypes[enumCast(Op::OpTypeMatrix)].push_back(type); |
531 | 190 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
532 | 190 | module.mapInstruction(type); |
533 | | |
534 | 190 | if (emitNonSemanticShaderDebugInfo) |
535 | 0 | { |
536 | 0 | auto const debugResultId = makeMatrixDebugType(column, cols); |
537 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
538 | 0 | } |
539 | | |
540 | 190 | return type->getResultId(); |
541 | 957 | } |
542 | | |
543 | | Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use) |
544 | 0 | { |
545 | | // try to find it |
546 | 0 | Instruction* type; |
547 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)].size(); ++t) { |
548 | 0 | type = groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)][t]; |
549 | 0 | if (type->getIdOperand(0) == component && |
550 | 0 | type->getIdOperand(1) == scope && |
551 | 0 | type->getIdOperand(2) == rows && |
552 | 0 | type->getIdOperand(3) == cols && |
553 | 0 | type->getIdOperand(4) == use) |
554 | 0 | return type->getResultId(); |
555 | 0 | } |
556 | | |
557 | | // not found, make it |
558 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeMatrixKHR); |
559 | 0 | type->reserveOperands(5); |
560 | 0 | type->addIdOperand(component); |
561 | 0 | type->addIdOperand(scope); |
562 | 0 | type->addIdOperand(rows); |
563 | 0 | type->addIdOperand(cols); |
564 | 0 | type->addIdOperand(use); |
565 | 0 | groupedTypes[enumCast(Op::OpTypeCooperativeMatrixKHR)].push_back(type); |
566 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
567 | 0 | module.mapInstruction(type); |
568 | |
|
569 | 0 | if (emitNonSemanticShaderDebugInfo) { |
570 | 0 | auto const debugResultId = makeCooperativeMatrixDebugTypeKHR(component, scope, rows, cols, use); |
571 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
572 | 0 | } |
573 | |
|
574 | 0 | return type->getResultId(); |
575 | 0 | } |
576 | | |
577 | | Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols) |
578 | 0 | { |
579 | | // try to find it |
580 | 0 | Instruction* type; |
581 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)].size(); ++t) { |
582 | 0 | type = groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)][t]; |
583 | 0 | if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows && |
584 | 0 | type->getIdOperand(3) == cols) |
585 | 0 | return type->getResultId(); |
586 | 0 | } |
587 | | |
588 | | // not found, make it |
589 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeMatrixNV); |
590 | 0 | type->reserveOperands(4); |
591 | 0 | type->addIdOperand(component); |
592 | 0 | type->addIdOperand(scope); |
593 | 0 | type->addIdOperand(rows); |
594 | 0 | type->addIdOperand(cols); |
595 | 0 | groupedTypes[enumCast(Op::OpTypeCooperativeMatrixNV)].push_back(type); |
596 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
597 | 0 | module.mapInstruction(type); |
598 | |
|
599 | 0 | return type->getResultId(); |
600 | 0 | } |
601 | | |
602 | | Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType) |
603 | 0 | { |
604 | 0 | Instruction* instr = module.getInstruction(otherType); |
605 | 0 | if (instr->getOpCode() == Op::OpTypeCooperativeMatrixNV) { |
606 | 0 | return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3)); |
607 | 0 | } else { |
608 | 0 | assert(instr->getOpCode() == Op::OpTypeCooperativeMatrixKHR); |
609 | 0 | return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4)); |
610 | 0 | } |
611 | 0 | } |
612 | | |
613 | | Id Builder::makeCooperativeVectorTypeNV(Id componentType, Id components) |
614 | 0 | { |
615 | | // try to find it |
616 | 0 | Instruction* type; |
617 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)].size(); ++t) { |
618 | 0 | type = groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)][t]; |
619 | 0 | if (type->getIdOperand(0) == componentType && |
620 | 0 | type->getIdOperand(1) == components) |
621 | 0 | return type->getResultId(); |
622 | 0 | } |
623 | | |
624 | | // not found, make it |
625 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeCooperativeVectorNV); |
626 | 0 | type->addIdOperand(componentType); |
627 | 0 | type->addIdOperand(components); |
628 | 0 | groupedTypes[enumCast(Op::OpTypeCooperativeVectorNV)].push_back(type); |
629 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
630 | 0 | module.mapInstruction(type); |
631 | |
|
632 | 0 | if (emitNonSemanticShaderDebugInfo) { |
633 | 0 | auto const debugResultId = makeVectorIdDebugType(componentType, components); |
634 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
635 | 0 | } |
636 | |
|
637 | 0 | return type->getResultId(); |
638 | 0 | } |
639 | | |
640 | | Id Builder::makeTensorTypeARM(Id elementType, Id rank) |
641 | 0 | { |
642 | | // See if an OpTypeTensorARM with same element type and rank already exists. |
643 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeTensorARM)].size(); ++t) { |
644 | 0 | const Instruction *type = groupedTypes[enumCast(Op::OpTypeTensorARM)][t]; |
645 | 0 | if (type->getIdOperand(0) == elementType && type->getIdOperand(1) == rank) |
646 | 0 | return type->getResultId(); |
647 | 0 | } |
648 | | |
649 | | // Not found, make it. |
650 | 0 | std::unique_ptr<Instruction> type(new Instruction(getUniqueId(), NoType, Op::OpTypeTensorARM)); |
651 | 0 | type->addIdOperand(elementType); |
652 | 0 | type->addIdOperand(rank); |
653 | 0 | groupedTypes[enumCast(Op::OpTypeTensorARM)].push_back(type.get()); |
654 | 0 | module.mapInstruction(type.get()); |
655 | 0 | Id resultID = type->getResultId(); |
656 | 0 | constantsTypesGlobals.push_back(std::move(type)); |
657 | 0 | return resultID; |
658 | 0 | } |
659 | | |
660 | | Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands) |
661 | 0 | { |
662 | | // try to find it |
663 | 0 | Instruction* type; |
664 | 0 | for (int t = 0; t < (int)groupedTypes[enumCast(opcode)].size(); ++t) { |
665 | 0 | type = groupedTypes[enumCast(opcode)][t]; |
666 | 0 | if (static_cast<size_t>(type->getNumOperands()) != operands.size()) |
667 | 0 | continue; // Number mismatch, find next |
668 | | |
669 | 0 | bool match = true; |
670 | 0 | for (int op = 0; match && op < (int)operands.size(); ++op) { |
671 | 0 | match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word; |
672 | 0 | } |
673 | 0 | if (match) |
674 | 0 | return type->getResultId(); |
675 | 0 | } |
676 | | |
677 | | // not found, make it |
678 | 0 | type = new Instruction(getUniqueId(), NoType, opcode); |
679 | 0 | type->reserveOperands(operands.size()); |
680 | 0 | for (size_t op = 0; op < operands.size(); ++op) { |
681 | 0 | if (operands[op].isId) |
682 | 0 | type->addIdOperand(operands[op].word); |
683 | 0 | else |
684 | 0 | type->addImmediateOperand(operands[op].word); |
685 | 0 | } |
686 | 0 | groupedTypes[enumCast(opcode)].push_back(type); |
687 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
688 | 0 | module.mapInstruction(type); |
689 | |
|
690 | 0 | return type->getResultId(); |
691 | 0 | } |
692 | | |
693 | | // TODO: performance: track arrays per stride |
694 | | // If a stride is supplied (non-zero) make an array. |
695 | | // If no stride (0), reuse previous array types. |
696 | | // 'size' is an Id of a constant or specialization constant of the array size |
697 | | Id Builder::makeArrayType(Id element, Id sizeId, int stride) |
698 | 382 | { |
699 | 382 | Instruction* type; |
700 | 382 | if (stride == 0) { |
701 | | // try to find existing type |
702 | 392 | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeArray)].size(); ++t) { |
703 | 261 | type = groupedTypes[enumCast(Op::OpTypeArray)][t]; |
704 | 261 | if (type->getIdOperand(0) == element && |
705 | 156 | type->getIdOperand(1) == sizeId && |
706 | 121 | explicitlyLaidOut.find(type->getResultId()) == explicitlyLaidOut.end()) |
707 | 121 | return type->getResultId(); |
708 | 261 | } |
709 | 252 | } |
710 | | |
711 | | // not found, make it |
712 | 261 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeArray); |
713 | 261 | type->reserveOperands(2); |
714 | 261 | type->addIdOperand(element); |
715 | 261 | type->addIdOperand(sizeId); |
716 | 261 | groupedTypes[enumCast(Op::OpTypeArray)].push_back(type); |
717 | 261 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
718 | 261 | module.mapInstruction(type); |
719 | | |
720 | 261 | if (stride != 0) { |
721 | 130 | explicitlyLaidOut.insert(type->getResultId()); |
722 | 130 | } |
723 | | |
724 | 261 | if (emitNonSemanticShaderDebugInfo) |
725 | 0 | { |
726 | 0 | auto const debugResultId = makeArrayDebugType(element, sizeId); |
727 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
728 | 0 | } |
729 | | |
730 | 261 | return type->getResultId(); |
731 | 382 | } |
732 | | |
733 | | Id Builder::makeRuntimeArray(Id element) |
734 | 51 | { |
735 | 51 | Instruction* type = new Instruction(getUniqueId(), NoType, Op::OpTypeRuntimeArray); |
736 | 51 | type->addIdOperand(element); |
737 | 51 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
738 | 51 | module.mapInstruction(type); |
739 | | |
740 | 51 | if (emitNonSemanticShaderDebugInfo) |
741 | 0 | { |
742 | 0 | auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0)); |
743 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
744 | 0 | } |
745 | | |
746 | 51 | return type->getResultId(); |
747 | 51 | } |
748 | | |
749 | | Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
750 | 914 | { |
751 | | // try to find it |
752 | 914 | Instruction* type; |
753 | 1.51k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeFunction)].size(); ++t) { |
754 | 824 | type = groupedTypes[enumCast(Op::OpTypeFunction)][t]; |
755 | 824 | if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) |
756 | 540 | continue; |
757 | 284 | bool mismatch = false; |
758 | 284 | for (int p = 0; p < (int)paramTypes.size(); ++p) { |
759 | 63 | if (paramTypes[p] != type->getIdOperand(p + 1)) { |
760 | 63 | mismatch = true; |
761 | 63 | break; |
762 | 63 | } |
763 | 63 | } |
764 | 284 | if (! mismatch) |
765 | 221 | { |
766 | | // If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void) |
767 | | // function type is created for the wrapper function. However, nonsemantic shader debug information is disabled |
768 | | // while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create |
769 | | // the associated debug function type if it hasn't been created yet. |
770 | 221 | if(emitNonSemanticShaderDebugInfo && getDebugType(type->getResultId()) == NoType) { |
771 | 0 | assert(sourceLang == spv::SourceLanguage::HLSL); |
772 | 0 | assert(getTypeClass(returnType) == Op::OpTypeVoid && paramTypes.size() == 0); |
773 | |
|
774 | 0 | Id id = makeDebugFunctionType(returnType, {}); |
775 | 0 | debugTypeIdLookup[type->getResultId()] = id; |
776 | 0 | } |
777 | 221 | return type->getResultId(); |
778 | 221 | } |
779 | 284 | } |
780 | | |
781 | | // not found, make it |
782 | 693 | Id typeId = getUniqueId(); |
783 | 693 | type = new Instruction(typeId, NoType, Op::OpTypeFunction); |
784 | 693 | type->reserveOperands(paramTypes.size() + 1); |
785 | 693 | type->addIdOperand(returnType); |
786 | 1.13k | for (int p = 0; p < (int)paramTypes.size(); ++p) |
787 | 443 | type->addIdOperand(paramTypes[p]); |
788 | 693 | groupedTypes[enumCast(Op::OpTypeFunction)].push_back(type); |
789 | 693 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
790 | 693 | module.mapInstruction(type); |
791 | | |
792 | | // make debug type and map it |
793 | 693 | if (emitNonSemanticShaderDebugInfo) { |
794 | 0 | Id debugTypeId = makeDebugFunctionType(returnType, paramTypes); |
795 | 0 | debugTypeIdLookup[typeId] = debugTypeId; |
796 | 0 | } |
797 | | |
798 | 693 | return type->getResultId(); |
799 | 914 | } |
800 | | |
801 | | Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
802 | 0 | { |
803 | 0 | assert(getDebugType(returnType) != NoType); |
804 | |
|
805 | 0 | Id typeId = getUniqueId(); |
806 | 0 | auto type = new Instruction(typeId, makeVoidType(), Op::OpExtInst); |
807 | 0 | type->reserveOperands(paramTypes.size() + 4); |
808 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
809 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeFunction); |
810 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsPublic)); |
811 | 0 | type->addIdOperand(getDebugType(returnType)); |
812 | 0 | for (auto const paramType : paramTypes) { |
813 | 0 | if (isPointerType(paramType) || isArrayType(paramType)) { |
814 | 0 | type->addIdOperand(getDebugType(getContainedTypeId(paramType))); |
815 | 0 | } |
816 | 0 | else { |
817 | 0 | type->addIdOperand(getDebugType(paramType)); |
818 | 0 | } |
819 | 0 | } |
820 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
821 | 0 | module.mapInstruction(type); |
822 | 0 | return typeId; |
823 | 0 | } |
824 | | |
825 | | Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, |
826 | | ImageFormat format, const char* debugName) |
827 | 1.49k | { |
828 | 1.49k | assert(sampled == 1 || sampled == 2); |
829 | | |
830 | | // try to find it |
831 | 1.49k | Instruction* type; |
832 | 7.30k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeImage)].size(); ++t) { |
833 | 6.40k | type = groupedTypes[enumCast(Op::OpTypeImage)][t]; |
834 | 6.40k | if (type->getIdOperand(0) == sampledType && |
835 | 5.32k | type->getImmediateOperand(1) == (unsigned int)dim && |
836 | 1.61k | type->getImmediateOperand(2) == ( depth ? 1u : 0u) && |
837 | 1.44k | type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && |
838 | 840 | type->getImmediateOperand(4) == ( ms ? 1u : 0u) && |
839 | 781 | type->getImmediateOperand(5) == sampled && |
840 | 748 | type->getImmediateOperand(6) == (unsigned int)format) |
841 | 594 | return type->getResultId(); |
842 | 6.40k | } |
843 | | |
844 | | // not found, make it |
845 | 899 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeImage); |
846 | 899 | type->reserveOperands(7); |
847 | 899 | type->addIdOperand(sampledType); |
848 | 899 | type->addImmediateOperand( dim); |
849 | 899 | type->addImmediateOperand( depth ? 1 : 0); |
850 | 899 | type->addImmediateOperand(arrayed ? 1 : 0); |
851 | 899 | type->addImmediateOperand( ms ? 1 : 0); |
852 | 899 | type->addImmediateOperand(sampled); |
853 | 899 | type->addImmediateOperand((unsigned int)format); |
854 | | |
855 | 899 | groupedTypes[enumCast(Op::OpTypeImage)].push_back(type); |
856 | 899 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
857 | 899 | module.mapInstruction(type); |
858 | | |
859 | | // deal with capabilities |
860 | 899 | switch (dim) { |
861 | 19 | case Dim::Buffer: |
862 | 19 | if (sampled == 1) |
863 | 3 | addCapability(Capability::SampledBuffer); |
864 | 16 | else |
865 | 16 | addCapability(Capability::ImageBuffer); |
866 | 19 | break; |
867 | 172 | case Dim::Dim1D: |
868 | 172 | if (sampled == 1) |
869 | 16 | addCapability(Capability::Sampled1D); |
870 | 156 | else |
871 | 156 | addCapability(Capability::Image1D); |
872 | 172 | break; |
873 | 168 | case Dim::Cube: |
874 | 168 | if (arrayed) { |
875 | 84 | if (sampled == 1) |
876 | 6 | addCapability(Capability::SampledCubeArray); |
877 | 78 | else |
878 | 78 | addCapability(Capability::ImageCubeArray); |
879 | 84 | } |
880 | 168 | break; |
881 | 22 | case Dim::Rect: |
882 | 22 | if (sampled == 1) |
883 | 6 | addCapability(Capability::SampledRect); |
884 | 16 | else |
885 | 16 | addCapability(Capability::ImageRect); |
886 | 22 | break; |
887 | 6 | case Dim::SubpassData: |
888 | 6 | addCapability(Capability::InputAttachment); |
889 | 6 | break; |
890 | 512 | default: |
891 | 512 | break; |
892 | 899 | } |
893 | | |
894 | 899 | if (ms) { |
895 | 41 | if (sampled == 2) { |
896 | | // Images used with subpass data are not storage |
897 | | // images, so don't require the capability for them. |
898 | 35 | if (dim != Dim::SubpassData) |
899 | 32 | addCapability(Capability::StorageImageMultisample); |
900 | 35 | if (arrayed) |
901 | 16 | addCapability(Capability::ImageMSArray); |
902 | 35 | } |
903 | 41 | } |
904 | | |
905 | 899 | if (emitNonSemanticShaderDebugInfo) |
906 | 0 | { |
907 | 0 | auto const debugResultId = makeOpaqueDebugType(debugName); |
908 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
909 | 0 | } |
910 | | |
911 | 899 | return type->getResultId(); |
912 | 899 | } |
913 | | |
914 | | Id Builder::makeSampledImageType(Id imageType, const char* debugName) |
915 | 587 | { |
916 | | // try to find it |
917 | 587 | Instruction* type; |
918 | 1.58k | for (int t = 0; t < (int)groupedTypes[enumCast(Op::OpTypeSampledImage)].size(); ++t) { |
919 | 1.36k | type = groupedTypes[enumCast(Op::OpTypeSampledImage)][t]; |
920 | 1.36k | if (type->getIdOperand(0) == imageType) |
921 | 367 | return type->getResultId(); |
922 | 1.36k | } |
923 | | |
924 | | // not found, make it |
925 | 220 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeSampledImage); |
926 | 220 | type->addIdOperand(imageType); |
927 | | |
928 | 220 | groupedTypes[enumCast(Op::OpTypeSampledImage)].push_back(type); |
929 | 220 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
930 | 220 | module.mapInstruction(type); |
931 | | |
932 | 220 | if (emitNonSemanticShaderDebugInfo) |
933 | 0 | { |
934 | 0 | auto const debugResultId = makeOpaqueDebugType(debugName); |
935 | 0 | debugTypeIdLookup[type->getResultId()] = debugResultId; |
936 | 0 | } |
937 | | |
938 | 220 | return type->getResultId(); |
939 | 587 | } |
940 | | |
941 | | Id Builder::makeDebugInfoNone() |
942 | 0 | { |
943 | 0 | if (debugInfoNone != 0) |
944 | 0 | return debugInfoNone; |
945 | | |
946 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
947 | 0 | inst->reserveOperands(2); |
948 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
949 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugInfoNone); |
950 | |
|
951 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
952 | 0 | module.mapInstruction(inst); |
953 | |
|
954 | 0 | debugInfoNone = inst->getResultId(); |
955 | |
|
956 | 0 | return debugInfoNone; |
957 | 0 | } |
958 | | |
959 | | Id Builder::makeBoolDebugType(int const size) |
960 | 0 | { |
961 | | // try to find it |
962 | 0 | Instruction* type; |
963 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].size(); ++t) { |
964 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic][t]; |
965 | 0 | if (type->getIdOperand(0) == getStringId("bool") && |
966 | 0 | type->getIdOperand(1) == static_cast<unsigned int>(size) && |
967 | 0 | type->getIdOperand(2) == NonSemanticShaderDebugInfoBoolean) |
968 | 0 | return type->getResultId(); |
969 | 0 | } |
970 | | |
971 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
972 | 0 | type->reserveOperands(6); |
973 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
974 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeBasic); |
975 | |
|
976 | 0 | type->addIdOperand(getStringId("bool")); // name id |
977 | 0 | type->addIdOperand(makeUintConstant(size)); // size id |
978 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoBoolean)); // encoding id |
979 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoNone)); // flags id |
980 | |
|
981 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].push_back(type); |
982 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
983 | 0 | module.mapInstruction(type); |
984 | |
|
985 | 0 | return type->getResultId(); |
986 | 0 | } |
987 | | |
988 | | Id Builder::makeIntegerDebugType(int const width, bool const hasSign) |
989 | 0 | { |
990 | 0 | const char* typeName = nullptr; |
991 | 0 | switch (width) { |
992 | 0 | case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break; |
993 | 0 | case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break; |
994 | 0 | case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break; |
995 | 0 | default: typeName = hasSign ? "int" : "uint"; |
996 | 0 | } |
997 | 0 | auto nameId = getStringId(typeName); |
998 | | // try to find it |
999 | 0 | Instruction* type; |
1000 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].size(); ++t) { |
1001 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic][t]; |
1002 | 0 | if (type->getIdOperand(0) == nameId && |
1003 | 0 | type->getIdOperand(1) == static_cast<unsigned int>(width) && |
1004 | 0 | type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfoSigned : NonSemanticShaderDebugInfoUnsigned)) |
1005 | 0 | return type->getResultId(); |
1006 | 0 | } |
1007 | | |
1008 | | // not found, make it |
1009 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1010 | 0 | type->reserveOperands(6); |
1011 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1012 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeBasic); |
1013 | 0 | type->addIdOperand(nameId); // name id |
1014 | 0 | type->addIdOperand(makeUintConstant(width)); // size id |
1015 | 0 | if(hasSign == true) { |
1016 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoSigned)); // encoding id |
1017 | 0 | } else { |
1018 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoUnsigned)); // encoding id |
1019 | 0 | } |
1020 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoNone)); // flags id |
1021 | |
|
1022 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].push_back(type); |
1023 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1024 | 0 | module.mapInstruction(type); |
1025 | |
|
1026 | 0 | return type->getResultId(); |
1027 | 0 | } |
1028 | | |
1029 | | Id Builder::makeFloatDebugType(int const width, Id const fpEncoding) |
1030 | 0 | { |
1031 | 0 | if (fpEncoding != NoType) |
1032 | 0 | requireNonSemanticShaderDebugInfoVersion(101); |
1033 | | // Determine the debug type name. FP-encoded variants have distinct names. |
1034 | | // Id comparison works here because makeUintConstant deduplicates: two calls |
1035 | | // with the same value return the same Id. |
1036 | 0 | const char* typeName = nullptr; |
1037 | 0 | if (fpEncoding != NoType) { |
1038 | 0 | if (fpEncoding == makeUintConstant((unsigned)FPEncoding::BFloat16KHR)) |
1039 | 0 | typeName = "bfloat16_t"; |
1040 | 0 | else if (fpEncoding == makeUintConstant((unsigned)FPEncoding::Float8E4M3EXT)) |
1041 | 0 | typeName = "floate4m3_t"; |
1042 | 0 | else if (fpEncoding == makeUintConstant((unsigned)FPEncoding::Float8E5M2EXT)) |
1043 | 0 | typeName = "floate5m2_t"; |
1044 | 0 | else |
1045 | 0 | typeName = "float"; |
1046 | 0 | } else { |
1047 | 0 | switch (width) { |
1048 | 0 | case 16: typeName = "float16_t"; break; |
1049 | 0 | case 64: typeName = "double"; break; |
1050 | 0 | default: typeName = "float"; break; |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | auto nameId = getStringId(typeName); |
1054 | | // try to find it |
1055 | 0 | Instruction* type; |
1056 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].size(); ++t) { |
1057 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic][t]; |
1058 | 0 | if (type->getIdOperand(0) == nameId && |
1059 | 0 | type->getIdOperand(1) == static_cast<unsigned int>(width) && |
1060 | 0 | type->getIdOperand(2) == NonSemanticShaderDebugInfoFloat) { |
1061 | 0 | if (fpEncoding == NoType) { |
1062 | 0 | if (type->getNumOperands() == 6) |
1063 | 0 | return type->getResultId(); |
1064 | 0 | } else { |
1065 | 0 | if (type->getNumOperands() == 7 && type->getIdOperand(6) == fpEncoding) |
1066 | 0 | return type->getResultId(); |
1067 | 0 | } |
1068 | 0 | } |
1069 | 0 | } |
1070 | | |
1071 | | // not found, make it |
1072 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1073 | 0 | type->reserveOperands(fpEncoding == NoType ? 6 : 7); |
1074 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1075 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeBasic); |
1076 | 0 | type->addIdOperand(nameId); // name id |
1077 | 0 | type->addIdOperand(makeUintConstant(width)); // size id |
1078 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFloat)); // encoding id |
1079 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoNone)); // flags id |
1080 | 0 | if (fpEncoding != NoType) |
1081 | 0 | type->addIdOperand(fpEncoding); // optional FPEncoding id (NSDI.101) |
1082 | |
|
1083 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeBasic].push_back(type); |
1084 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1085 | 0 | module.mapInstruction(type); |
1086 | |
|
1087 | 0 | return type->getResultId(); |
1088 | 0 | } |
1089 | | |
1090 | | Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfoInstructions const sequenceType) |
1091 | 0 | { |
1092 | 0 | assert(sequenceType == NonSemanticShaderDebugInfoDebugTypeArray || |
1093 | 0 | sequenceType == NonSemanticShaderDebugInfoDebugTypeVector); |
1094 | | |
1095 | | // try to find it |
1096 | 0 | Instruction* type; |
1097 | 0 | for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) { |
1098 | 0 | type = groupedDebugTypes[sequenceType][t]; |
1099 | 0 | if (type->getIdOperand(0) == baseType && |
1100 | 0 | type->getIdOperand(1) == makeUintConstant(componentCount)) |
1101 | 0 | return type->getResultId(); |
1102 | 0 | } |
1103 | | |
1104 | | // not found, make it |
1105 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1106 | 0 | type->reserveOperands(4); |
1107 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1108 | 0 | type->addImmediateOperand(sequenceType); |
1109 | 0 | type->addIdOperand(getDebugType(baseType)); // base type |
1110 | 0 | type->addIdOperand(componentCount); // component count |
1111 | |
|
1112 | 0 | groupedDebugTypes[sequenceType].push_back(type); |
1113 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1114 | 0 | module.mapInstruction(type); |
1115 | |
|
1116 | 0 | return type->getResultId(); |
1117 | 0 | } |
1118 | | |
1119 | | Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount) |
1120 | 0 | { |
1121 | 0 | return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfoDebugTypeArray); |
1122 | 0 | } |
1123 | | |
1124 | | Id Builder::makeVectorDebugType(Id const baseType, int const componentCount) |
1125 | 0 | { |
1126 | 0 | return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfoDebugTypeVector); |
1127 | 0 | } |
1128 | | |
1129 | | Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor) |
1130 | 0 | { |
1131 | | // try to find it |
1132 | 0 | Instruction* type; |
1133 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeMatrix].size(); ++t) { |
1134 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeMatrix][t]; |
1135 | 0 | if (type->getIdOperand(0) == vectorType && |
1136 | 0 | type->getIdOperand(1) == makeUintConstant(vectorCount)) |
1137 | 0 | return type->getResultId(); |
1138 | 0 | } |
1139 | | |
1140 | | // not found, make it |
1141 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1142 | 0 | type->reserveOperands(5); |
1143 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1144 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeMatrix); |
1145 | 0 | type->addIdOperand(getDebugType(vectorType)); // vector type id |
1146 | 0 | type->addIdOperand(makeUintConstant(vectorCount)); // component count id |
1147 | 0 | type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id |
1148 | |
|
1149 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeMatrix].push_back(type); |
1150 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1151 | 0 | module.mapInstruction(type); |
1152 | |
|
1153 | 0 | return type->getResultId(); |
1154 | 0 | } |
1155 | | |
1156 | | // DebugTypeVectorIdEXT (NSDI.101 opcode 109): describes OpTypeVectorIdEXT and |
1157 | | // OpTypeCooperativeVectorNV types whose component count is a SPIR-V Id (not a |
1158 | | // literal), including specialization-constant component counts. |
1159 | | Id Builder::makeVectorIdDebugType(Id const componentType, Id const componentCount) |
1160 | 0 | { |
1161 | 0 | requireNonSemanticShaderDebugInfoVersion(101); |
1162 | | // try to find it |
1163 | 0 | Instruction* type; |
1164 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeVectorIdEXT].size(); ++t) { |
1165 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeVectorIdEXT][t]; |
1166 | 0 | if (type->getIdOperand(2) == getDebugType(componentType) && |
1167 | 0 | type->getIdOperand(3) == componentCount) |
1168 | 0 | return type->getResultId(); |
1169 | 0 | } |
1170 | | |
1171 | | // not found, make it |
1172 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1173 | 0 | type->reserveOperands(4); |
1174 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1175 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeVectorIdEXT); |
1176 | 0 | type->addIdOperand(getDebugType(componentType)); // component debug type |
1177 | 0 | type->addIdOperand(componentCount); // component count (constant instruction Id) |
1178 | |
|
1179 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeVectorIdEXT].push_back(type); |
1180 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1181 | 0 | module.mapInstruction(type); |
1182 | |
|
1183 | 0 | return type->getResultId(); |
1184 | 0 | } |
1185 | | |
1186 | | // DebugTypeCooperativeMatrixKHR (NSDI.101 opcode 110): describes |
1187 | | // OpTypeCooperativeMatrixKHR types. |
1188 | | Id Builder::makeCooperativeMatrixDebugTypeKHR(Id const componentType, Id const scope, |
1189 | | Id const rows, Id const cols, Id const use) |
1190 | 0 | { |
1191 | 0 | requireNonSemanticShaderDebugInfoVersion(101); |
1192 | | // try to find it |
1193 | 0 | Instruction* type; |
1194 | 0 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR].size(); ++t) { |
1195 | 0 | type = groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR][t]; |
1196 | 0 | if (type->getIdOperand(2) == getDebugType(componentType) && |
1197 | 0 | type->getIdOperand(3) == scope && |
1198 | 0 | type->getIdOperand(4) == rows && |
1199 | 0 | type->getIdOperand(5) == cols && |
1200 | 0 | type->getIdOperand(6) == use) |
1201 | 0 | return type->getResultId(); |
1202 | 0 | } |
1203 | | |
1204 | | // not found, make it |
1205 | 0 | type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1206 | 0 | type->reserveOperands(7); |
1207 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1208 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR); |
1209 | 0 | type->addIdOperand(getDebugType(componentType)); // component debug type |
1210 | 0 | type->addIdOperand(scope); // scope |
1211 | 0 | type->addIdOperand(rows); // rows |
1212 | 0 | type->addIdOperand(cols); // columns |
1213 | 0 | type->addIdOperand(use); // use (MatrixA, MatrixB, Accumulator) |
1214 | |
|
1215 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR].push_back(type); |
1216 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1217 | 0 | module.mapInstruction(type); |
1218 | |
|
1219 | 0 | return type->getResultId(); |
1220 | 0 | } |
1221 | | |
1222 | | Id Builder::makeMemberDebugType(Id const memberType, StructMemberDebugInfo const& debugTypeLoc) |
1223 | 0 | { |
1224 | 0 | assert(getDebugType(memberType) != NoType); |
1225 | |
|
1226 | 0 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1227 | 0 | type->reserveOperands(10); |
1228 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1229 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeMember); |
1230 | 0 | type->addIdOperand(getStringId(debugTypeLoc.name)); // name id |
1231 | 0 | type->addIdOperand(debugTypeLoc.debugTypeOverride != 0 ? debugTypeLoc.debugTypeOverride |
1232 | 0 | : getDebugType(memberType)); // type id |
1233 | 0 | type->addIdOperand(makeDebugSource(currentFileId)); // source id |
1234 | 0 | type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero |
1235 | 0 | type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id |
1236 | 0 | type->addIdOperand(makeUintConstant(0)); // TODO: offset id |
1237 | 0 | type->addIdOperand(makeUintConstant(0)); // TODO: size id |
1238 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsPublic)); // flags id |
1239 | |
|
1240 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeMember].push_back(type); |
1241 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1242 | 0 | module.mapInstruction(type); |
1243 | |
|
1244 | 0 | return type->getResultId(); |
1245 | 0 | } |
1246 | | |
1247 | | Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, std::vector<StructMemberDebugInfo> const& memberDebugInfo, |
1248 | | char const* const name, NonSemanticShaderDebugInfoDebugCompositeType const tag) |
1249 | 0 | { |
1250 | | // Create the debug member types. |
1251 | 0 | std::vector<Id> memberDebugTypes; |
1252 | 0 | assert(memberTypes.size() == memberDebugInfo.size()); |
1253 | 0 | for (size_t i = 0; i < memberTypes.size(); i++) { |
1254 | 0 | if (getDebugType(memberTypes[i]) != NoType) { |
1255 | 0 | memberDebugTypes.emplace_back(makeMemberDebugType(memberTypes[i], memberDebugInfo[i])); |
1256 | 0 | } |
1257 | 0 | } |
1258 | | |
1259 | | // Create The structure debug type. |
1260 | 0 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1261 | 0 | type->reserveOperands(memberDebugTypes.size() + 11); |
1262 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1263 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeComposite); |
1264 | 0 | type->addIdOperand(getStringId(name)); // name id |
1265 | 0 | type->addIdOperand(makeUintConstant(tag)); // tag id |
1266 | 0 | type->addIdOperand(makeDebugSource(currentFileId)); // source id |
1267 | 0 | type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? |
1268 | 0 | type->addIdOperand(makeUintConstant(0)); // TODO: column id |
1269 | 0 | type->addIdOperand(makeDebugCompilationUnit()); // scope id |
1270 | 0 | type->addIdOperand(getStringId(name)); // linkage name id |
1271 | 0 | type->addIdOperand(makeUintConstant(0)); // TODO: size id |
1272 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsPublic)); // flags id |
1273 | 0 | for(auto const memberDebugType : memberDebugTypes) { |
1274 | 0 | type->addIdOperand(memberDebugType); |
1275 | 0 | } |
1276 | |
|
1277 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeComposite].push_back(type); |
1278 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1279 | 0 | module.mapInstruction(type); |
1280 | |
|
1281 | 0 | return type->getResultId(); |
1282 | 0 | } |
1283 | | |
1284 | | // The NonSemantic Shader Debug Info doesn't really have a dedicated opcode for opaque types. Instead, we use DebugTypeComposite. |
1285 | | // To represent a source language opaque type, this instruction must have no Members operands, Size operand must be |
1286 | | // DebugInfoNone, and Name must start with @ to avoid clashes with user defined names. |
1287 | | Id Builder::makeOpaqueDebugType(char const* const name) |
1288 | 0 | { |
1289 | | // Create The structure debug type. |
1290 | 0 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1291 | 0 | type->reserveOperands(11); |
1292 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1293 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypeComposite); |
1294 | 0 | type->addIdOperand(getStringId(name)); // name id |
1295 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoStructure)); // tag id |
1296 | 0 | type->addIdOperand(makeDebugSource(currentFileId)); // source id |
1297 | 0 | type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? |
1298 | 0 | type->addIdOperand(makeUintConstant(0)); // TODO: column id |
1299 | 0 | type->addIdOperand(makeDebugCompilationUnit()); // scope id |
1300 | | // Prepend '@' to opaque types. |
1301 | 0 | type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id |
1302 | 0 | type->addIdOperand(makeDebugInfoNone()); // size id |
1303 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsPublic)); // flags id |
1304 | |
|
1305 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypeComposite].push_back(type); |
1306 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1307 | 0 | module.mapInstruction(type); |
1308 | |
|
1309 | 0 | return type->getResultId(); |
1310 | 0 | } |
1311 | | |
1312 | | Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType) |
1313 | 0 | { |
1314 | 0 | const Id debugBaseType = getDebugType(baseType); |
1315 | 0 | if (!debugBaseType) { |
1316 | 0 | return makeDebugInfoNone(); |
1317 | 0 | } |
1318 | 0 | const Id scID = makeUintConstant(storageClass); |
1319 | 0 | for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypePointer]) { |
1320 | 0 | if (otherType->getIdOperand(2) == debugBaseType && |
1321 | 0 | otherType->getIdOperand(3) == scID) { |
1322 | 0 | return otherType->getResultId(); |
1323 | 0 | } |
1324 | 0 | } |
1325 | | |
1326 | 0 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1327 | 0 | type->reserveOperands(5); |
1328 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1329 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypePointer); |
1330 | 0 | type->addIdOperand(debugBaseType); |
1331 | 0 | type->addIdOperand(scID); |
1332 | 0 | type->addIdOperand(makeUintConstant(0)); |
1333 | |
|
1334 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypePointer].push_back(type); |
1335 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1336 | 0 | module.mapInstruction(type); |
1337 | |
|
1338 | 0 | return type->getResultId(); |
1339 | 0 | } |
1340 | | |
1341 | | // Emit a OpExtInstWithForwardRefsKHR nonsemantic instruction for a pointer debug type |
1342 | | // where we don't have the pointee yet. Since we don't have the pointee yet, it just |
1343 | | // points to itself and we rely on patching it later. |
1344 | | Id Builder::makeForwardPointerDebugType(StorageClass storageClass) |
1345 | 0 | { |
1346 | 0 | const Id scID = makeUintConstant(storageClass); |
1347 | |
|
1348 | 0 | this->addExtension(spv::E_SPV_KHR_relaxed_extended_instruction); |
1349 | |
|
1350 | 0 | Instruction *type = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInstWithForwardRefsKHR); |
1351 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
1352 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugTypePointer); |
1353 | 0 | type->addIdOperand(type->getResultId()); |
1354 | 0 | type->addIdOperand(scID); |
1355 | 0 | type->addIdOperand(makeUintConstant(0)); |
1356 | |
|
1357 | 0 | groupedDebugTypes[NonSemanticShaderDebugInfoDebugTypePointer].push_back(type); |
1358 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1359 | 0 | module.mapInstruction(type); |
1360 | |
|
1361 | 0 | return type->getResultId(); |
1362 | 0 | } |
1363 | | |
1364 | 0 | Id Builder::makeDebugSource(const Id fileName) { |
1365 | 0 | if (debugSourceId.find(fileName) != debugSourceId.end()) |
1366 | 0 | return debugSourceId[fileName]; |
1367 | 0 | spv::Id resultId = getUniqueId(); |
1368 | 0 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst); |
1369 | 0 | sourceInst->reserveOperands(3); |
1370 | 0 | sourceInst->addIdOperand(nonSemanticShaderDebugInfo); |
1371 | 0 | sourceInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugSource); |
1372 | 0 | sourceInst->addIdOperand(fileName); |
1373 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst)); |
1374 | 0 | module.mapInstruction(sourceInst); |
1375 | 0 | if (emitNonSemanticShaderDebugSource) { |
1376 | 0 | const int maxWordCount = 0xFFFF; |
1377 | 0 | const int opSourceWordCount = 4; |
1378 | 0 | const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; |
1379 | 0 | auto processDebugSource = [&](std::string source) { |
1380 | 0 | if (source.size() > 0) { |
1381 | 0 | int nextByte = 0; |
1382 | 0 | while ((int)source.size() - nextByte > 0) { |
1383 | 0 | auto subString = source.substr(nextByte, nonNullBytesPerInstruction); |
1384 | 0 | auto sourceId = getStringId(subString); |
1385 | 0 | if (nextByte == 0) { |
1386 | | // DebugSource |
1387 | 0 | sourceInst->addIdOperand(sourceId); |
1388 | 0 | } else { |
1389 | | // DebugSourceContinued |
1390 | 0 | Instruction* sourceContinuedInst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1391 | 0 | sourceContinuedInst->reserveOperands(2); |
1392 | 0 | sourceContinuedInst->addIdOperand(nonSemanticShaderDebugInfo); |
1393 | 0 | sourceContinuedInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugSourceContinued); |
1394 | 0 | sourceContinuedInst->addIdOperand(sourceId); |
1395 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceContinuedInst)); |
1396 | 0 | module.mapInstruction(sourceContinuedInst); |
1397 | 0 | } |
1398 | 0 | nextByte += nonNullBytesPerInstruction; |
1399 | 0 | } |
1400 | 0 | } else { |
1401 | 0 | auto sourceId = getStringId(source); |
1402 | 0 | sourceInst->addIdOperand(sourceId); |
1403 | 0 | } |
1404 | 0 | }; |
1405 | 0 | if (fileName == mainFileId) { |
1406 | 0 | processDebugSource(sourceText); |
1407 | 0 | } else { |
1408 | 0 | auto incItr = includeFiles.find(fileName); |
1409 | 0 | if (incItr != includeFiles.end()) { |
1410 | 0 | processDebugSource(*incItr->second); |
1411 | 0 | } else { |
1412 | | // We omit the optional source text item if not available in glslang |
1413 | 0 | } |
1414 | 0 | } |
1415 | 0 | } |
1416 | 0 | debugSourceId[fileName] = resultId; |
1417 | 0 | return resultId; |
1418 | 0 | } |
1419 | | |
1420 | 0 | Id Builder::makeDebugCompilationUnit() { |
1421 | 0 | if (nonSemanticShaderCompilationUnitId != 0) |
1422 | 0 | return nonSemanticShaderCompilationUnitId; |
1423 | 0 | spv::Id resultId = getUniqueId(); |
1424 | 0 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst); |
1425 | 0 | sourceInst->reserveOperands(6); |
1426 | 0 | sourceInst->addIdOperand(nonSemanticShaderDebugInfo); |
1427 | 0 | sourceInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugCompilationUnit); |
1428 | 0 | sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number |
1429 | 0 | sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number |
1430 | 0 | sourceInst->addIdOperand(makeDebugSource(mainFileId)); |
1431 | 0 | sourceInst->addIdOperand(makeUintConstant(sourceLang)); |
1432 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(sourceInst)); |
1433 | 0 | module.mapInstruction(sourceInst); |
1434 | 0 | nonSemanticShaderCompilationUnitId = resultId; |
1435 | | |
1436 | | // In the case of non-semantic shader debug info, preserve source text for every include |
1437 | | // even if no debug scope or line record ends up referencing that file. |
1438 | 0 | if (emitNonSemanticShaderDebugSource) { |
1439 | 0 | for (const auto& includeFile : includeFiles) { |
1440 | 0 | makeDebugSource(includeFile.first); |
1441 | 0 | } |
1442 | 0 | } |
1443 | | |
1444 | | // We can reasonably assume that makeDebugCompilationUnit will be called before any of |
1445 | | // debug-scope stack. Function scopes and lexical scopes will occur afterward. |
1446 | 0 | assert(currentDebugScopeId.empty()); |
1447 | 0 | currentDebugScopeId.push(nonSemanticShaderCompilationUnitId); |
1448 | |
|
1449 | 0 | return resultId; |
1450 | 0 | } |
1451 | | |
1452 | | Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable) |
1453 | 0 | { |
1454 | 0 | assert(type != 0); |
1455 | |
|
1456 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1457 | 0 | inst->reserveOperands(11); |
1458 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1459 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugGlobalVariable); |
1460 | 0 | inst->addIdOperand(getStringId(name)); // name id |
1461 | 0 | inst->addIdOperand(type); // type id |
1462 | 0 | inst->addIdOperand(makeDebugSource(currentFileId)); // source id |
1463 | 0 | inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? |
1464 | 0 | inst->addIdOperand(makeUintConstant(0)); // TODO: column id |
1465 | 0 | inst->addIdOperand(makeDebugCompilationUnit()); // scope id |
1466 | 0 | inst->addIdOperand(getStringId(name)); // linkage name id |
1467 | 0 | inst->addIdOperand(variable); // variable id |
1468 | 0 | inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsDefinition)); // flags id |
1469 | |
|
1470 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1471 | 0 | module.mapInstruction(inst); |
1472 | |
|
1473 | 0 | return inst->getResultId(); |
1474 | 0 | } |
1475 | | |
1476 | | Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber) |
1477 | 0 | { |
1478 | 0 | assert(name != nullptr); |
1479 | 0 | assert(!currentDebugScopeId.empty()); |
1480 | |
|
1481 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1482 | 0 | inst->reserveOperands(9); |
1483 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1484 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugLocalVariable); |
1485 | 0 | inst->addIdOperand(getStringId(name)); // name id |
1486 | 0 | inst->addIdOperand(type); // type id |
1487 | 0 | inst->addIdOperand(makeDebugSource(currentFileId)); // source id |
1488 | 0 | inst->addIdOperand(makeUintConstant(currentLine)); // line id |
1489 | 0 | inst->addIdOperand(makeUintConstant(0)); // TODO: column id |
1490 | 0 | inst->addIdOperand(currentDebugScopeId.top()); // scope id |
1491 | 0 | inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsLocal)); // flags id |
1492 | 0 | if(argNumber != 0) { |
1493 | 0 | inst->addIdOperand(makeUintConstant(static_cast<unsigned int>(argNumber))); |
1494 | 0 | } |
1495 | |
|
1496 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1497 | 0 | module.mapInstruction(inst); |
1498 | |
|
1499 | 0 | return inst->getResultId(); |
1500 | 0 | } |
1501 | | |
1502 | | Id Builder::makeDebugExpression() |
1503 | 0 | { |
1504 | 0 | if (debugExpression != 0) |
1505 | 0 | return debugExpression; |
1506 | | |
1507 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1508 | 0 | inst->reserveOperands(2); |
1509 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1510 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugExpression); |
1511 | |
|
1512 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
1513 | 0 | module.mapInstruction(inst); |
1514 | |
|
1515 | 0 | debugExpression = inst->getResultId(); |
1516 | |
|
1517 | 0 | return debugExpression; |
1518 | 0 | } |
1519 | | |
1520 | | Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer) |
1521 | 0 | { |
1522 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1523 | 0 | inst->reserveOperands(5); |
1524 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1525 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugDeclare); |
1526 | 0 | inst->addIdOperand(debugLocalVariable); // debug local variable id |
1527 | 0 | inst->addIdOperand(pointer); // pointer to local variable id |
1528 | 0 | inst->addIdOperand(makeDebugExpression()); // expression id |
1529 | 0 | addInstruction(std::unique_ptr<Instruction>(inst)); |
1530 | |
|
1531 | 0 | return inst->getResultId(); |
1532 | 0 | } |
1533 | | |
1534 | | Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value) |
1535 | 0 | { |
1536 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), Op::OpExtInst); |
1537 | 0 | inst->reserveOperands(5); |
1538 | 0 | inst->addIdOperand(nonSemanticShaderDebugInfo); |
1539 | 0 | inst->addImmediateOperand(NonSemanticShaderDebugInfoDebugValue); |
1540 | 0 | inst->addIdOperand(debugLocalVariable); // debug local variable id |
1541 | 0 | inst->addIdOperand(value); // value of local variable id |
1542 | 0 | inst->addIdOperand(makeDebugExpression()); // expression id |
1543 | 0 | addInstruction(std::unique_ptr<Instruction>(inst)); |
1544 | |
|
1545 | 0 | return inst->getResultId(); |
1546 | 0 | } |
1547 | | |
1548 | | Id Builder::makeAccelerationStructureType() |
1549 | 37 | { |
1550 | 37 | Instruction *type; |
1551 | 37 | if (groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].size() == 0) { |
1552 | 37 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeAccelerationStructureKHR); |
1553 | 37 | groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].push_back(type); |
1554 | 37 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1555 | 37 | module.mapInstruction(type); |
1556 | 37 | if (emitNonSemanticShaderDebugInfo) { |
1557 | 0 | spv::Id debugType = makeOpaqueDebugType("accelerationStructure"); |
1558 | 0 | debugTypeIdLookup[type->getResultId()] = debugType; |
1559 | 0 | } |
1560 | 37 | } else { |
1561 | 0 | type = groupedTypes[enumCast(Op::OpTypeAccelerationStructureKHR)].back(); |
1562 | 0 | } |
1563 | | |
1564 | 37 | return type->getResultId(); |
1565 | 37 | } |
1566 | | |
1567 | | Id Builder::makeRayQueryType() |
1568 | 37 | { |
1569 | 37 | Instruction *type; |
1570 | 37 | if (groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].size() == 0) { |
1571 | 37 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeRayQueryKHR); |
1572 | 37 | groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].push_back(type); |
1573 | 37 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1574 | 37 | module.mapInstruction(type); |
1575 | 37 | if (emitNonSemanticShaderDebugInfo) { |
1576 | 0 | spv::Id debugType = makeOpaqueDebugType("rayQuery"); |
1577 | 0 | debugTypeIdLookup[type->getResultId()] = debugType; |
1578 | 0 | } |
1579 | 37 | } else { |
1580 | 0 | type = groupedTypes[enumCast(Op::OpTypeRayQueryKHR)].back(); |
1581 | 0 | } |
1582 | | |
1583 | 37 | return type->getResultId(); |
1584 | 37 | } |
1585 | | |
1586 | | Id Builder::makeHitObjectEXTType() |
1587 | 0 | { |
1588 | 0 | Instruction *type; |
1589 | 0 | if (groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].size() == 0) { |
1590 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeHitObjectEXT); |
1591 | 0 | groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].push_back(type); |
1592 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1593 | 0 | module.mapInstruction(type); |
1594 | 0 | } else { |
1595 | 0 | type = groupedTypes[enumCast(Op::OpTypeHitObjectEXT)].back(); |
1596 | 0 | } |
1597 | |
|
1598 | 0 | return type->getResultId(); |
1599 | 0 | } |
1600 | | Id Builder::makeHitObjectNVType() |
1601 | 0 | { |
1602 | 0 | Instruction *type; |
1603 | 0 | if (groupedTypes[enumCast(Op::OpTypeHitObjectNV)].size() == 0) { |
1604 | 0 | type = new Instruction(getUniqueId(), NoType, Op::OpTypeHitObjectNV); |
1605 | 0 | groupedTypes[enumCast(Op::OpTypeHitObjectNV)].push_back(type); |
1606 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
1607 | 0 | module.mapInstruction(type); |
1608 | 0 | if (emitNonSemanticShaderDebugInfo) { |
1609 | 0 | spv::Id debugType = makeOpaqueDebugType("hitObjectNV"); |
1610 | 0 | debugTypeIdLookup[type->getResultId()] = debugType; |
1611 | 0 | } |
1612 | 0 | } else { |
1613 | 0 | type = groupedTypes[enumCast(Op::OpTypeHitObjectNV)].back(); |
1614 | 0 | } |
1615 | |
|
1616 | 0 | return type->getResultId(); |
1617 | 0 | } |
1618 | | |
1619 | | Id Builder::getDerefTypeId(Id resultId) const |
1620 | 22.6k | { |
1621 | 22.6k | Id typeId = getTypeId(resultId); |
1622 | 22.6k | assert(isPointerType(typeId)); |
1623 | | |
1624 | 22.6k | return module.getInstruction(typeId)->getIdOperand(1); |
1625 | 22.6k | } |
1626 | | |
1627 | | Op Builder::getMostBasicTypeClass(Id typeId) const |
1628 | 296k | { |
1629 | 296k | Instruction* instr = module.getInstruction(typeId); |
1630 | | |
1631 | 296k | Op typeClass = instr->getOpCode(); |
1632 | 296k | switch (typeClass) |
1633 | 296k | { |
1634 | 71.8k | case Op::OpTypeVector: |
1635 | 73.9k | case Op::OpTypeMatrix: |
1636 | 74.2k | case Op::OpTypeArray: |
1637 | 74.2k | case Op::OpTypeRuntimeArray: |
1638 | 74.2k | return getMostBasicTypeClass(instr->getIdOperand(0)); |
1639 | 56.1k | case Op::OpTypePointer: |
1640 | 56.1k | return getMostBasicTypeClass(instr->getIdOperand(1)); |
1641 | 165k | default: |
1642 | 165k | return typeClass; |
1643 | 296k | } |
1644 | 296k | } |
1645 | | |
1646 | | unsigned int Builder::getNumTypeConstituents(Id typeId) const |
1647 | 8.29k | { |
1648 | 8.29k | Instruction* instr = module.getInstruction(typeId); |
1649 | | |
1650 | 8.29k | switch (instr->getOpCode()) |
1651 | 8.29k | { |
1652 | 0 | case Op::OpTypeBool: |
1653 | 88 | case Op::OpTypeInt: |
1654 | 99 | case Op::OpTypeFloat: |
1655 | 99 | case Op::OpTypePointer: |
1656 | 99 | return 1; |
1657 | 8.11k | case Op::OpTypeVector: |
1658 | 8.19k | case Op::OpTypeMatrix: |
1659 | 8.19k | return instr->getImmediateOperand(1); |
1660 | 0 | case Op::OpTypeCooperativeVectorNV: |
1661 | 0 | case Op::OpTypeArray: |
1662 | 0 | { |
1663 | 0 | Id lengthId = instr->getIdOperand(1); |
1664 | 0 | return module.getInstruction(lengthId)->getImmediateOperand(0); |
1665 | 0 | } |
1666 | 0 | case Op::OpTypeStruct: |
1667 | 0 | return instr->getNumOperands(); |
1668 | 0 | case Op::OpTypeCooperativeMatrixKHR: |
1669 | 0 | case Op::OpTypeCooperativeMatrixNV: |
1670 | | // has only one constituent when used with OpCompositeConstruct. |
1671 | 0 | return 1; |
1672 | 0 | default: |
1673 | 0 | assert(0); |
1674 | 0 | return 1; |
1675 | 8.29k | } |
1676 | 8.29k | } |
1677 | | |
1678 | | // Return the lowest-level type of scalar that an homogeneous composite is made out of. |
1679 | | // Typically, this is just to find out if something is made out of ints or floats. |
1680 | | // However, it includes returning a structure, if say, it is an array of structure. |
1681 | | Id Builder::getScalarTypeId(Id typeId) const |
1682 | 261k | { |
1683 | 261k | Instruction* instr = module.getInstruction(typeId); |
1684 | | |
1685 | 261k | Op typeClass = instr->getOpCode(); |
1686 | 261k | switch (typeClass) |
1687 | 261k | { |
1688 | 0 | case Op::OpTypeVoid: |
1689 | 36 | case Op::OpTypeBool: |
1690 | 65.5k | case Op::OpTypeInt: |
1691 | 141k | case Op::OpTypeFloat: |
1692 | 141k | case Op::OpTypeStruct: |
1693 | 141k | return instr->getResultId(); |
1694 | 76.3k | case Op::OpTypeVector: |
1695 | 78.5k | case Op::OpTypeMatrix: |
1696 | 78.6k | case Op::OpTypeArray: |
1697 | 78.6k | case Op::OpTypeRuntimeArray: |
1698 | 119k | case Op::OpTypePointer: |
1699 | 119k | case Op::OpTypeCooperativeVectorNV: |
1700 | 119k | return getScalarTypeId(getContainedTypeId(typeId)); |
1701 | 0 | default: |
1702 | 0 | assert(0); |
1703 | 0 | return NoResult; |
1704 | 261k | } |
1705 | 261k | } |
1706 | | |
1707 | | // Return the type of 'member' of a composite. |
1708 | | Id Builder::getContainedTypeId(Id typeId, int member) const |
1709 | 191k | { |
1710 | 191k | Instruction* instr = module.getInstruction(typeId); |
1711 | | |
1712 | 191k | Op typeClass = instr->getOpCode(); |
1713 | 191k | switch (typeClass) |
1714 | 191k | { |
1715 | 86.0k | case Op::OpTypeVector: |
1716 | 89.1k | case Op::OpTypeMatrix: |
1717 | 90.7k | case Op::OpTypeArray: |
1718 | 90.8k | case Op::OpTypeRuntimeArray: |
1719 | 90.8k | case Op::OpTypeCooperativeMatrixKHR: |
1720 | 90.8k | case Op::OpTypeCooperativeMatrixNV: |
1721 | 90.8k | case Op::OpTypeCooperativeVectorNV: |
1722 | 90.8k | return instr->getIdOperand(0); |
1723 | 89.9k | case Op::OpTypePointer: |
1724 | 89.9k | return instr->getIdOperand(1); |
1725 | 10.4k | case Op::OpTypeStruct: |
1726 | 10.4k | return instr->getIdOperand(member); |
1727 | 0 | default: |
1728 | 0 | assert(0); |
1729 | 0 | return NoResult; |
1730 | 191k | } |
1731 | 191k | } |
1732 | | |
1733 | | // Figure out the final resulting type of the access chain. |
1734 | | Id Builder::getResultingAccessChainType() const |
1735 | 9.56k | { |
1736 | 9.56k | assert(accessChain.base != NoResult); |
1737 | 9.56k | Id typeId = getTypeId(accessChain.base); |
1738 | | |
1739 | 9.56k | assert(isPointerType(typeId)); |
1740 | 9.56k | typeId = getContainedTypeId(typeId); |
1741 | | |
1742 | 19.7k | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
1743 | 10.1k | if (isStructType(typeId)) { |
1744 | 6.60k | assert(isConstantScalar(accessChain.indexChain[i])); |
1745 | 6.60k | typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i])); |
1746 | 6.60k | } else |
1747 | 3.55k | typeId = getContainedTypeId(typeId, accessChain.indexChain[i]); |
1748 | 10.1k | } |
1749 | | |
1750 | 9.56k | return typeId; |
1751 | 9.56k | } |
1752 | | |
1753 | | // Return the immediately contained type of a given composite type. |
1754 | | Id Builder::getContainedTypeId(Id typeId) const |
1755 | 177k | { |
1756 | 177k | return getContainedTypeId(typeId, 0); |
1757 | 177k | } |
1758 | | |
1759 | | // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' |
1760 | | // of width 'width'. The 'width' is only consumed for int and float types. |
1761 | | // Returns false otherwise. |
1762 | | bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const |
1763 | 14.8k | { |
1764 | 14.8k | const Instruction& instr = *module.getInstruction(typeId); |
1765 | | |
1766 | 14.8k | Op typeClass = instr.getOpCode(); |
1767 | 14.8k | switch (typeClass) |
1768 | 14.8k | { |
1769 | 4.23k | case Op::OpTypeInt: |
1770 | 7.01k | case Op::OpTypeFloat: |
1771 | 7.01k | return typeClass == typeOp && instr.getImmediateOperand(0) == width; |
1772 | 1.10k | case Op::OpTypeStruct: |
1773 | 3.87k | for (int m = 0; m < instr.getNumOperands(); ++m) { |
1774 | 2.79k | if (containsType(instr.getIdOperand(m), typeOp, width)) |
1775 | 18 | return true; |
1776 | 2.79k | } |
1777 | 1.08k | return false; |
1778 | 3.03k | case Op::OpTypePointer: |
1779 | 3.03k | return false; |
1780 | 3.04k | case Op::OpTypeVector: |
1781 | 3.41k | case Op::OpTypeMatrix: |
1782 | 3.55k | case Op::OpTypeArray: |
1783 | 3.55k | case Op::OpTypeRuntimeArray: |
1784 | 3.55k | return containsType(getContainedTypeId(typeId), typeOp, width); |
1785 | 150 | default: |
1786 | 150 | return typeClass == typeOp; |
1787 | 14.8k | } |
1788 | 14.8k | } |
1789 | | |
1790 | | // return true if the type is a pointer to PhysicalStorageBufferEXT or an |
1791 | | // contains such a pointer. These require restrict/aliased decorations. |
1792 | | bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const |
1793 | 4.24k | { |
1794 | 4.24k | const Instruction& instr = *module.getInstruction(typeId); |
1795 | | |
1796 | 4.24k | Op typeClass = instr.getOpCode(); |
1797 | 4.24k | switch (typeClass) |
1798 | 4.24k | { |
1799 | 108 | case Op::OpTypePointer: |
1800 | 108 | return getTypeStorageClass(typeId) == StorageClass::PhysicalStorageBufferEXT; |
1801 | 81 | case Op::OpTypeArray: |
1802 | 81 | return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); |
1803 | 265 | case Op::OpTypeStruct: |
1804 | 1.11k | for (int m = 0; m < instr.getNumOperands(); ++m) { |
1805 | 847 | if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m))) |
1806 | 0 | return true; |
1807 | 847 | } |
1808 | 265 | return false; |
1809 | 3.79k | default: |
1810 | 3.79k | return false; |
1811 | 4.24k | } |
1812 | 4.24k | } |
1813 | | |
1814 | | // See if a scalar constant of this type has already been created, so it |
1815 | | // can be reused rather than duplicated. (Required by the specification). |
1816 | | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) |
1817 | 33.3k | { |
1818 | 33.3k | ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, value, 0 }; |
1819 | 33.3k | auto it = groupedScalarConstantResultIDs.find(key); |
1820 | 33.3k | return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0; |
1821 | 33.3k | } |
1822 | | |
1823 | | // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). |
1824 | | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) |
1825 | 2.01k | { |
1826 | 2.01k | ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, v1, v2 }; |
1827 | 2.01k | auto it = groupedScalarConstantResultIDs.find(key); |
1828 | 2.01k | return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0; |
1829 | 2.01k | } |
1830 | | |
1831 | | // Return true if consuming 'opcode' means consuming a constant. |
1832 | | // "constant" here means after final transform to executable code, |
1833 | | // the value consumed will be a constant, so includes specialization. |
1834 | | bool Builder::isConstantOpCode(Op opcode) const |
1835 | 530 | { |
1836 | 530 | switch (opcode) { |
1837 | 0 | case Op::OpUndef: |
1838 | 0 | case Op::OpConstantTrue: |
1839 | 0 | case Op::OpConstantFalse: |
1840 | 110 | case Op::OpConstant: |
1841 | 110 | case Op::OpConstantDataKHR: |
1842 | 530 | case Op::OpConstantComposite: |
1843 | 530 | case Op::OpConstantCompositeReplicateEXT: |
1844 | 530 | case Op::OpConstantSampler: |
1845 | 530 | case Op::OpConstantNull: |
1846 | 530 | case Op::OpSpecConstantTrue: |
1847 | 530 | case Op::OpSpecConstantFalse: |
1848 | 530 | case Op::OpSpecConstant: |
1849 | 530 | case Op::OpSpecConstantComposite: |
1850 | 530 | case Op::OpSpecConstantCompositeReplicateEXT: |
1851 | 530 | case Op::OpSpecConstantDataKHR: |
1852 | 530 | case Op::OpSpecConstantOp: |
1853 | 530 | case Op::OpConstantSizeOfEXT: |
1854 | 530 | return true; |
1855 | 0 | default: |
1856 | 0 | return false; |
1857 | 530 | } |
1858 | 530 | } |
1859 | | |
1860 | | // Return true if consuming 'opcode' means consuming a specialization constant. |
1861 | | bool Builder::isSpecConstantOpCode(Op opcode) const |
1862 | 0 | { |
1863 | 0 | switch (opcode) { |
1864 | 0 | case Op::OpSpecConstantTrue: |
1865 | 0 | case Op::OpSpecConstantFalse: |
1866 | 0 | case Op::OpSpecConstant: |
1867 | 0 | case Op::OpSpecConstantComposite: |
1868 | 0 | case Op::OpSpecConstantDataKHR: |
1869 | 0 | case Op::OpSpecConstantOp: |
1870 | 0 | case Op::OpSpecConstantCompositeReplicateEXT: |
1871 | 0 | return true; |
1872 | 0 | default: |
1873 | 0 | return false; |
1874 | 0 | } |
1875 | 0 | } |
1876 | | |
1877 | | Id Builder::makeNullConstant(Id typeId) |
1878 | 0 | { |
1879 | 0 | Instruction* constant; |
1880 | | |
1881 | | // See if we already made it. |
1882 | 0 | Id existing = NoResult; |
1883 | 0 | for (int i = 0; i < (int)nullConstants.size(); ++i) { |
1884 | 0 | constant = nullConstants[i]; |
1885 | 0 | if (constant->getTypeId() == typeId) |
1886 | 0 | existing = constant->getResultId(); |
1887 | 0 | } |
1888 | |
|
1889 | 0 | if (existing != NoResult) |
1890 | 0 | return existing; |
1891 | | |
1892 | | // Make it |
1893 | 0 | Instruction* c = new Instruction(getUniqueId(), typeId, Op::OpConstantNull); |
1894 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1895 | 0 | nullConstants.push_back(c); |
1896 | 0 | module.mapInstruction(c); |
1897 | |
|
1898 | 0 | return c->getResultId(); |
1899 | 0 | } |
1900 | | |
1901 | | Id Builder::makeBoolConstant(bool b, bool specConstant) |
1902 | 1.12k | { |
1903 | 1.12k | Id typeId = makeBoolType(); |
1904 | 1.12k | Op opcode = specConstant ? (b ? Op::OpSpecConstantTrue : Op::OpSpecConstantFalse) : (b ? Op::OpConstantTrue : Op::OpConstantFalse); |
1905 | | |
1906 | | // See if we already made it. Applies only to regular constants, because specialization constants |
1907 | | // must remain distinct for the purpose of applying a SpecId decoration. |
1908 | 1.12k | if (!specConstant) { |
1909 | 1.08k | Id existing = findScalarConstant(Op::OpTypeBool, opcode, typeId, 0); |
1910 | 1.08k | if (existing) |
1911 | 913 | return existing; |
1912 | 1.08k | } |
1913 | | |
1914 | | // Make it |
1915 | 211 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1916 | 211 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1917 | 211 | module.mapInstruction(c); |
1918 | | |
1919 | 211 | Id resultId = c->getResultId(); |
1920 | 211 | if (!specConstant) { |
1921 | 173 | ScalarConstantKey key{enumCast(Op::OpTypeBool), enumCast(opcode), typeId, 0, 0}; |
1922 | 173 | groupedScalarConstantResultIDs[key] = resultId; |
1923 | 173 | } |
1924 | 211 | return resultId; |
1925 | 1.12k | } |
1926 | | |
1927 | | Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) |
1928 | 28.4k | { |
1929 | 28.4k | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
1930 | | |
1931 | | // See if we already made it. Applies only to regular constants, because specialization constants |
1932 | | // must remain distinct for the purpose of applying a SpecId decoration. |
1933 | 28.4k | if (! specConstant) { |
1934 | 28.2k | Id existing = findScalarConstant(Op::OpTypeInt, opcode, typeId, value); |
1935 | 28.2k | if (existing) |
1936 | 25.9k | return existing; |
1937 | 28.2k | } |
1938 | | |
1939 | 2.45k | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1940 | 2.45k | c->addImmediateOperand(value); |
1941 | 2.45k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1942 | 2.45k | module.mapInstruction(c); |
1943 | | |
1944 | 2.45k | Id resultId = c->getResultId(); |
1945 | 2.45k | if (!specConstant) { |
1946 | 2.25k | ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, value, 0 }; |
1947 | 2.25k | groupedScalarConstantResultIDs[key] = resultId; |
1948 | 2.25k | } |
1949 | 2.45k | return resultId; |
1950 | 28.4k | } |
1951 | | |
1952 | | Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) |
1953 | 2.04k | { |
1954 | 2.04k | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
1955 | | |
1956 | 2.04k | unsigned op1 = value & 0xFFFFFFFF; |
1957 | 2.04k | unsigned op2 = value >> 32; |
1958 | | |
1959 | | // See if we already made it. Applies only to regular constants, because specialization constants |
1960 | | // must remain distinct for the purpose of applying a SpecId decoration. |
1961 | 2.04k | if (! specConstant) { |
1962 | 1.99k | Id existing = findScalarConstant(Op::OpTypeInt, opcode, typeId, op1, op2); |
1963 | 1.99k | if (existing) |
1964 | 1.69k | return existing; |
1965 | 1.99k | } |
1966 | | |
1967 | 356 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1968 | 356 | c->reserveOperands(2); |
1969 | 356 | c->addImmediateOperand(op1); |
1970 | 356 | c->addImmediateOperand(op2); |
1971 | 356 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
1972 | 356 | module.mapInstruction(c); |
1973 | | |
1974 | 356 | Id resultId = c->getResultId(); |
1975 | 356 | if (!specConstant) { |
1976 | 306 | ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, op1, op2 }; |
1977 | 306 | groupedScalarConstantResultIDs[key] = resultId; |
1978 | 306 | } |
1979 | 356 | return resultId; |
1980 | 2.04k | } |
1981 | | |
1982 | | Id Builder::makeFloatConstant(float f, bool specConstant) |
1983 | 3.16k | { |
1984 | 3.16k | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
1985 | 3.16k | Id typeId = makeFloatType(32); |
1986 | 3.16k | union { float fl; unsigned int ui; } u; |
1987 | 3.16k | u.fl = f; |
1988 | 3.16k | unsigned value = u.ui; |
1989 | | |
1990 | | // See if we already made it. Applies only to regular constants, because specialization constants |
1991 | | // must remain distinct for the purpose of applying a SpecId decoration. |
1992 | 3.16k | if (! specConstant) { |
1993 | 3.16k | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value); |
1994 | 3.16k | if (existing) |
1995 | 2.24k | return existing; |
1996 | 3.16k | } |
1997 | | |
1998 | 917 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1999 | 917 | c->addImmediateOperand(value); |
2000 | 917 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2001 | 917 | module.mapInstruction(c); |
2002 | | |
2003 | 917 | Id resultId = c->getResultId(); |
2004 | 917 | if (!specConstant) { |
2005 | 917 | ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 }; |
2006 | 917 | groupedScalarConstantResultIDs[key] = resultId; |
2007 | 917 | } |
2008 | 917 | return resultId; |
2009 | 3.16k | } |
2010 | | |
2011 | | Id Builder::makeDoubleConstant(double d, bool specConstant) |
2012 | 18 | { |
2013 | 18 | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
2014 | 18 | Id typeId = makeFloatType(64); |
2015 | 18 | union { double db; unsigned long long ull; } u; |
2016 | 18 | u.db = d; |
2017 | 18 | unsigned long long value = u.ull; |
2018 | 18 | unsigned op1 = value & 0xFFFFFFFF; |
2019 | 18 | unsigned op2 = value >> 32; |
2020 | | |
2021 | | // See if we already made it. Applies only to regular constants, because specialization constants |
2022 | | // must remain distinct for the purpose of applying a SpecId decoration. |
2023 | 18 | if (! specConstant) { |
2024 | 18 | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, op1, op2); |
2025 | 18 | if (existing) |
2026 | 0 | return existing; |
2027 | 18 | } |
2028 | | |
2029 | 18 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2030 | 18 | c->reserveOperands(2); |
2031 | 18 | c->addImmediateOperand(op1); |
2032 | 18 | c->addImmediateOperand(op2); |
2033 | 18 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2034 | 18 | module.mapInstruction(c); |
2035 | | |
2036 | 18 | Id resultId = c->getResultId(); |
2037 | 18 | if (!specConstant) { |
2038 | 18 | ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, op1, op2 }; |
2039 | 18 | groupedScalarConstantResultIDs[key] = resultId; |
2040 | 18 | } |
2041 | 18 | return resultId; |
2042 | 18 | } |
2043 | | |
2044 | | Id Builder::makeFloat16Constant(float f16, bool specConstant) |
2045 | 846 | { |
2046 | 846 | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
2047 | 846 | Id typeId = makeFloatType(16); |
2048 | | |
2049 | 846 | spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16); |
2050 | 846 | spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0); |
2051 | 846 | fVal.castTo(f16Val, spvutils::kRoundToZero); |
2052 | | |
2053 | 846 | unsigned value = f16Val.value().getAsFloat().get_value(); |
2054 | | |
2055 | | // See if we already made it. Applies only to regular constants, because specialization constants |
2056 | | // must remain distinct for the purpose of applying a SpecId decoration. |
2057 | 846 | if (!specConstant) { |
2058 | 846 | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value); |
2059 | 846 | if (existing) |
2060 | 783 | return existing; |
2061 | 846 | } |
2062 | | |
2063 | 63 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2064 | 63 | c->addImmediateOperand(value); |
2065 | 63 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2066 | 63 | module.mapInstruction(c); |
2067 | | |
2068 | 63 | Id resultId = c->getResultId(); |
2069 | 63 | if (!specConstant) { |
2070 | 63 | ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 }; |
2071 | 63 | groupedScalarConstantResultIDs[key] = resultId; |
2072 | 63 | } |
2073 | 63 | return resultId; |
2074 | 846 | } |
2075 | | |
2076 | | Id Builder::makeBFloat16Constant(float bf16, bool specConstant) |
2077 | 0 | { |
2078 | 0 | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
2079 | 0 | Id typeId = makeBFloat16Type(); |
2080 | |
|
2081 | 0 | union { |
2082 | 0 | float f; |
2083 | 0 | uint32_t u; |
2084 | 0 | } un; |
2085 | 0 | un.f = bf16; |
2086 | | |
2087 | | // take high 16b of fp32 value. This is effectively round-to-zero, other than certain NaNs. |
2088 | 0 | unsigned value = un.u >> 16; |
2089 | | |
2090 | | // See if we already made it. Applies only to regular constants, because specialization constants |
2091 | | // must remain distinct for the purpose of applying a SpecId decoration. |
2092 | 0 | if (!specConstant) { |
2093 | 0 | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value); |
2094 | 0 | if (existing) |
2095 | 0 | return existing; |
2096 | 0 | } |
2097 | | |
2098 | 0 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2099 | 0 | c->addImmediateOperand(value); |
2100 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2101 | 0 | module.mapInstruction(c); |
2102 | |
|
2103 | 0 | Id resultId = c->getResultId(); |
2104 | 0 | if (!specConstant) { |
2105 | 0 | ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 }; |
2106 | 0 | groupedScalarConstantResultIDs[key] = resultId; |
2107 | 0 | } |
2108 | 0 | return resultId; |
2109 | 0 | } |
2110 | | |
2111 | | Id Builder::makeFloatE5M2Constant(float fe5m2, bool specConstant) |
2112 | 0 | { |
2113 | 0 | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
2114 | 0 | Id typeId = makeFloatE5M2Type(); |
2115 | |
|
2116 | 0 | spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(fe5m2); |
2117 | 0 | spvutils::HexFloat<spvutils::FloatProxy<spvutils::FloatE5M2>> fe5m2Val(0); |
2118 | 0 | fVal.castTo(fe5m2Val, spvutils::kRoundToZero); |
2119 | |
|
2120 | 0 | unsigned value = fe5m2Val.value().getAsFloat().get_value(); |
2121 | | |
2122 | | // See if we already made it. Applies only to regular constants, because specialization constants |
2123 | | // must remain distinct for the purpose of applying a SpecId decoration. |
2124 | 0 | if (!specConstant) { |
2125 | 0 | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value); |
2126 | 0 | if (existing) |
2127 | 0 | return existing; |
2128 | 0 | } |
2129 | | |
2130 | 0 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2131 | 0 | c->addImmediateOperand(value); |
2132 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2133 | 0 | module.mapInstruction(c); |
2134 | |
|
2135 | 0 | Id resultId = c->getResultId(); |
2136 | 0 | if (!specConstant) { |
2137 | 0 | ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0}; |
2138 | 0 | groupedScalarConstantResultIDs[key] = resultId; |
2139 | 0 | } |
2140 | 0 | return resultId; |
2141 | 0 | } |
2142 | | |
2143 | | Id Builder::makeFloatE4M3Constant(float fe4m3, bool specConstant) |
2144 | 0 | { |
2145 | 0 | Op opcode = specConstant ? Op::OpSpecConstant : Op::OpConstant; |
2146 | 0 | Id typeId = makeFloatE4M3Type(); |
2147 | |
|
2148 | 0 | spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(fe4m3); |
2149 | 0 | spvutils::HexFloat<spvutils::FloatProxy<spvutils::FloatE4M3>> fe4m3Val(0); |
2150 | 0 | fVal.castTo(fe4m3Val, spvutils::kRoundToZero); |
2151 | |
|
2152 | 0 | unsigned value = fe4m3Val.value().getAsFloat().get_value(); |
2153 | | |
2154 | | // See if we already made it. Applies only to regular constants, because specialization constants |
2155 | | // must remain distinct for the purpose of applying a SpecId decoration. |
2156 | 0 | if (!specConstant) { |
2157 | 0 | Id existing = findScalarConstant(Op::OpTypeFloat, opcode, typeId, value); |
2158 | 0 | if (existing) |
2159 | 0 | return existing; |
2160 | 0 | } |
2161 | | |
2162 | 0 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2163 | 0 | c->addImmediateOperand(value); |
2164 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2165 | 0 | module.mapInstruction(c); |
2166 | |
|
2167 | 0 | Id resultId = c->getResultId(); |
2168 | 0 | if (!specConstant) { |
2169 | 0 | ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0}; |
2170 | 0 | groupedScalarConstantResultIDs[key] = resultId; |
2171 | 0 | } |
2172 | 0 | return resultId; |
2173 | 0 | } |
2174 | | |
2175 | | Id Builder::makeFpConstant(Id type, double d, bool specConstant) |
2176 | 0 | { |
2177 | 0 | const int width = getScalarTypeWidth(type); |
2178 | |
|
2179 | 0 | assert(isFloatType(type)); |
2180 | |
|
2181 | 0 | switch (width) { |
2182 | 0 | case 16: |
2183 | 0 | return makeFloat16Constant((float)d, specConstant); |
2184 | 0 | case 32: |
2185 | 0 | return makeFloatConstant((float)d, specConstant); |
2186 | 0 | case 64: |
2187 | 0 | return makeDoubleConstant(d, specConstant); |
2188 | 0 | default: |
2189 | 0 | break; |
2190 | 0 | } |
2191 | | |
2192 | 0 | assert(false); |
2193 | 0 | return NoResult; |
2194 | 0 | } |
2195 | | |
2196 | | Id Builder::importNonSemanticShaderDebugInfoInstructions() |
2197 | 0 | { |
2198 | 0 | assert(emitNonSemanticShaderDebugInfo == true); |
2199 | |
|
2200 | 0 | if(nonSemanticShaderDebugInfo == 0) |
2201 | 0 | { |
2202 | 0 | this->addExtension(spv::E_SPV_KHR_non_semantic_info); |
2203 | | // Create the import instruction directly so we can save a pointer for later |
2204 | | // patching by requireNonSemanticShaderDebugInfoVersion(). |
2205 | 0 | std::string importName = "NonSemantic.Shader.DebugInfo." + std::to_string(nonSemanticShaderDebugInfoVersion); |
2206 | 0 | auto* importInst = new Instruction(getUniqueId(), NoType, Op::OpExtInstImport); |
2207 | 0 | importInst->addStringOperand(importName.c_str()); |
2208 | 0 | module.mapInstruction(importInst); |
2209 | 0 | imports.push_back(std::unique_ptr<Instruction>(importInst)); |
2210 | 0 | nonSemanticShaderDebugInfo = importInst->getResultId(); |
2211 | 0 | nonSemanticShaderDebugInfoImportInst = importInst; |
2212 | 0 | } |
2213 | |
|
2214 | 0 | return nonSemanticShaderDebugInfo; |
2215 | 0 | } |
2216 | | |
2217 | | void Builder::requireNonSemanticShaderDebugInfoVersion(unsigned version) |
2218 | 0 | { |
2219 | 0 | if (nonSemanticShaderDebugInfoVersion >= version) |
2220 | 0 | return; |
2221 | 0 | nonSemanticShaderDebugInfoVersion = version; |
2222 | 0 | if (nonSemanticShaderDebugInfoImportInst != nullptr) { |
2223 | | // The import instruction was already emitted with an older version string. |
2224 | | // Rebuild the string operands in place so all referencing OpExtInst instructions |
2225 | | // automatically pick up the new name without needing new IDs. |
2226 | 0 | std::string importName = "NonSemantic.Shader.DebugInfo." + std::to_string(version); |
2227 | 0 | nonSemanticShaderDebugInfoImportInst->clearOperands(); |
2228 | 0 | nonSemanticShaderDebugInfoImportInst->addStringOperand(importName.c_str()); |
2229 | 0 | } |
2230 | 0 | } |
2231 | | |
2232 | | Id Builder::findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std::vector<Id>& comps, size_t numMembers) |
2233 | 4.81k | { |
2234 | 4.81k | Instruction* constant = nullptr; |
2235 | 4.81k | bool found = false; |
2236 | 33.7k | for (int i = 0; i < (int)groupedCompositeConstants[enumCast(typeClass)].size(); ++i) { |
2237 | 32.7k | constant = groupedCompositeConstants[enumCast(typeClass)][i]; |
2238 | | |
2239 | 32.7k | if (constant->getTypeId() != typeId) |
2240 | 28.0k | continue; |
2241 | | |
2242 | 4.71k | if (constant->getOpCode() != opcode) { |
2243 | 0 | continue; |
2244 | 0 | } |
2245 | | |
2246 | 4.71k | if (constant->getNumOperands() != (int)numMembers) |
2247 | 0 | continue; |
2248 | | |
2249 | | // same contents? |
2250 | 4.71k | bool mismatch = false; |
2251 | 15.5k | for (int op = 0; op < constant->getNumOperands(); ++op) { |
2252 | 11.7k | if (constant->getIdOperand(op) != comps[op]) { |
2253 | 896 | mismatch = true; |
2254 | 896 | break; |
2255 | 896 | } |
2256 | 11.7k | } |
2257 | 4.71k | if (! mismatch) { |
2258 | 3.82k | found = true; |
2259 | 3.82k | break; |
2260 | 3.82k | } |
2261 | 4.71k | } |
2262 | | |
2263 | 4.81k | return found ? constant->getResultId() : NoResult; |
2264 | 4.81k | } |
2265 | | |
2266 | | Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps) |
2267 | 216 | { |
2268 | 216 | Instruction* constant = nullptr; |
2269 | 216 | bool found = false; |
2270 | 549 | for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { |
2271 | 378 | constant = groupedStructConstants[typeId][i]; |
2272 | | |
2273 | | // same contents? |
2274 | 378 | bool mismatch = false; |
2275 | 468 | for (int op = 0; op < constant->getNumOperands(); ++op) { |
2276 | 423 | if (constant->getIdOperand(op) != comps[op]) { |
2277 | 333 | mismatch = true; |
2278 | 333 | break; |
2279 | 333 | } |
2280 | 423 | } |
2281 | 378 | if (! mismatch) { |
2282 | 45 | found = true; |
2283 | 45 | break; |
2284 | 45 | } |
2285 | 378 | } |
2286 | | |
2287 | 216 | return found ? constant->getResultId() : NoResult; |
2288 | 216 | } |
2289 | | |
2290 | | // Comments in header |
2291 | | Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant) |
2292 | 5.02k | { |
2293 | 5.02k | assert(typeId); |
2294 | 5.02k | Op typeClass = getTypeClass(typeId); |
2295 | | |
2296 | 5.02k | bool replicate = false; |
2297 | 5.02k | size_t numMembers = members.size(); |
2298 | 5.02k | if (useReplicatedComposites || typeClass == Op::OpTypeCooperativeVectorNV) { |
2299 | | // use replicate if all members are the same |
2300 | 0 | replicate = numMembers > 0 && |
2301 | 0 | std::equal(members.begin() + 1, members.end(), members.begin()); |
2302 | |
|
2303 | 0 | if (replicate) { |
2304 | 0 | numMembers = 1; |
2305 | 0 | addCapability(spv::Capability::ReplicatedCompositesEXT); |
2306 | 0 | addExtension(spv::E_SPV_EXT_replicated_composites); |
2307 | 0 | } |
2308 | 0 | } |
2309 | | |
2310 | 5.02k | Op opcode = replicate ? |
2311 | 0 | (specConstant ? Op::OpSpecConstantCompositeReplicateEXT : Op::OpConstantCompositeReplicateEXT) : |
2312 | 5.02k | (specConstant ? Op::OpSpecConstantComposite : Op::OpConstantComposite); |
2313 | | |
2314 | 5.02k | switch (typeClass) { |
2315 | 4.61k | case Op::OpTypeVector: |
2316 | 4.73k | case Op::OpTypeArray: |
2317 | 4.81k | case Op::OpTypeMatrix: |
2318 | 4.81k | case Op::OpTypeCooperativeMatrixKHR: |
2319 | 4.81k | case Op::OpTypeCooperativeMatrixNV: |
2320 | 4.81k | case Op::OpTypeCooperativeVectorNV: |
2321 | 4.81k | if (! specConstant) { |
2322 | 4.81k | Id existing = findCompositeConstant(typeClass, opcode, typeId, members, numMembers); |
2323 | 4.81k | if (existing) |
2324 | 3.82k | return existing; |
2325 | 4.81k | } |
2326 | 987 | break; |
2327 | 987 | case Op::OpTypeStruct: |
2328 | 216 | if (! specConstant) { |
2329 | 216 | Id existing = findStructConstant(typeId, members); |
2330 | 216 | if (existing) |
2331 | 45 | return existing; |
2332 | 216 | } |
2333 | 171 | break; |
2334 | 171 | default: |
2335 | 0 | assert(0); |
2336 | 0 | return makeFloatConstant(0.0); |
2337 | 5.02k | } |
2338 | | |
2339 | 1.15k | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
2340 | 1.15k | c->reserveOperands(members.size()); |
2341 | 4.34k | for (size_t op = 0; op < numMembers; ++op) |
2342 | 3.18k | c->addIdOperand(members[op]); |
2343 | 1.15k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); |
2344 | 1.15k | if (typeClass == Op::OpTypeStruct) |
2345 | 171 | groupedStructConstants[typeId].push_back(c); |
2346 | 987 | else |
2347 | 987 | groupedCompositeConstants[enumCast(typeClass)].push_back(c); |
2348 | 1.15k | module.mapInstruction(c); |
2349 | | |
2350 | 1.15k | return c->getResultId(); |
2351 | 5.02k | } |
2352 | | |
2353 | | Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) |
2354 | 442 | { |
2355 | 442 | Instruction* entryPoint = new Instruction(Op::OpEntryPoint); |
2356 | 442 | entryPoint->reserveOperands(3); |
2357 | 442 | entryPoint->addImmediateOperand(model); |
2358 | 442 | entryPoint->addIdOperand(function->getId()); |
2359 | 442 | entryPoint->addStringOperand(name); |
2360 | | |
2361 | 442 | entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint)); |
2362 | | |
2363 | 442 | return entryPoint; |
2364 | 442 | } |
2365 | | |
2366 | | // Currently relying on the fact that all 'value' of interest are small non-negative values. |
2367 | | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) |
2368 | 31 | { |
2369 | | // entryPoint can be null if we are in compile-only mode |
2370 | 31 | if (!entryPoint) |
2371 | 0 | return; |
2372 | | |
2373 | 31 | Instruction* instr = new Instruction(Op::OpExecutionMode); |
2374 | 31 | instr->reserveOperands(3); |
2375 | 31 | instr->addIdOperand(entryPoint->getId()); |
2376 | 31 | instr->addImmediateOperand(mode); |
2377 | 31 | if (value1 >= 0) |
2378 | 0 | instr->addImmediateOperand(value1); |
2379 | 31 | if (value2 >= 0) |
2380 | 0 | instr->addImmediateOperand(value2); |
2381 | 31 | if (value3 >= 0) |
2382 | 0 | instr->addImmediateOperand(value3); |
2383 | | |
2384 | 31 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
2385 | 31 | } |
2386 | | |
2387 | | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals) |
2388 | 0 | { |
2389 | | // entryPoint can be null if we are in compile-only mode |
2390 | 0 | if (!entryPoint) |
2391 | 0 | return; |
2392 | | |
2393 | 0 | Instruction* instr = new Instruction(Op::OpExecutionMode); |
2394 | 0 | instr->reserveOperands(literals.size() + 2); |
2395 | 0 | instr->addIdOperand(entryPoint->getId()); |
2396 | 0 | instr->addImmediateOperand(mode); |
2397 | 0 | for (auto literal : literals) |
2398 | 0 | instr->addImmediateOperand(literal); |
2399 | |
|
2400 | 0 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
2401 | 0 | } |
2402 | | |
2403 | | void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds) |
2404 | 0 | { |
2405 | | // entryPoint can be null if we are in compile-only mode |
2406 | 0 | if (!entryPoint) |
2407 | 0 | return; |
2408 | | |
2409 | 0 | Instruction* instr = new Instruction(Op::OpExecutionModeId); |
2410 | 0 | instr->reserveOperands(operandIds.size() + 2); |
2411 | 0 | instr->addIdOperand(entryPoint->getId()); |
2412 | 0 | instr->addImmediateOperand(mode); |
2413 | 0 | for (auto operandId : operandIds) |
2414 | 0 | instr->addIdOperand(operandId); |
2415 | |
|
2416 | 0 | executionModes.push_back(std::unique_ptr<Instruction>(instr)); |
2417 | 0 | } |
2418 | | |
2419 | | void Builder::addName(Id id, const char* string) |
2420 | 8.94k | { |
2421 | 8.94k | Instruction* name = new Instruction(Op::OpName); |
2422 | 8.94k | name->reserveOperands(2); |
2423 | 8.94k | name->addIdOperand(id); |
2424 | 8.94k | name->addStringOperand(string); |
2425 | | |
2426 | 8.94k | names.push_back(std::unique_ptr<Instruction>(name)); |
2427 | 8.94k | } |
2428 | | |
2429 | | void Builder::addMemberName(Id id, int memberNumber, const char* string) |
2430 | 2.60k | { |
2431 | 2.60k | Instruction* name = new Instruction(Op::OpMemberName); |
2432 | 2.60k | name->reserveOperands(3); |
2433 | 2.60k | name->addIdOperand(id); |
2434 | 2.60k | name->addImmediateOperand(memberNumber); |
2435 | 2.60k | name->addStringOperand(string); |
2436 | | |
2437 | 2.60k | names.push_back(std::unique_ptr<Instruction>(name)); |
2438 | 2.60k | } |
2439 | | |
2440 | | void Builder::addDecoration(Id id, Decoration decoration, int num) |
2441 | 99.3k | { |
2442 | 99.3k | if (decoration == spv::Decoration::Max) |
2443 | 92.6k | return; |
2444 | | |
2445 | 6.71k | Instruction* dec = new Instruction(Op::OpDecorate); |
2446 | 6.71k | dec->reserveOperands(2); |
2447 | 6.71k | dec->addIdOperand(id); |
2448 | 6.71k | dec->addImmediateOperand(decoration); |
2449 | 6.71k | if (num >= 0) |
2450 | 4.51k | dec->addImmediateOperand(num); |
2451 | | |
2452 | 6.71k | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2453 | 6.71k | } |
2454 | | |
2455 | | void Builder::addDecoration(Id id, Decoration decoration, const char* s) |
2456 | 0 | { |
2457 | 0 | if (decoration == spv::Decoration::Max) |
2458 | 0 | return; |
2459 | | |
2460 | 0 | Instruction* dec = new Instruction(Op::OpDecorateString); |
2461 | 0 | dec->reserveOperands(3); |
2462 | 0 | dec->addIdOperand(id); |
2463 | 0 | dec->addImmediateOperand(decoration); |
2464 | 0 | dec->addStringOperand(s); |
2465 | |
|
2466 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2467 | 0 | } |
2468 | | |
2469 | | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals) |
2470 | 126 | { |
2471 | 126 | if (decoration == spv::Decoration::Max) |
2472 | 0 | return; |
2473 | | |
2474 | 126 | Instruction* dec = new Instruction(Op::OpDecorate); |
2475 | 126 | dec->reserveOperands(literals.size() + 2); |
2476 | 126 | dec->addIdOperand(id); |
2477 | 126 | dec->addImmediateOperand(decoration); |
2478 | 126 | for (auto literal : literals) |
2479 | 126 | dec->addImmediateOperand(literal); |
2480 | | |
2481 | 126 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2482 | 126 | } |
2483 | | |
2484 | | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings) |
2485 | 0 | { |
2486 | 0 | if (decoration == spv::Decoration::Max) |
2487 | 0 | return; |
2488 | | |
2489 | 0 | Instruction* dec = new Instruction(Op::OpDecorateString); |
2490 | 0 | dec->reserveOperands(strings.size() + 2); |
2491 | 0 | dec->addIdOperand(id); |
2492 | 0 | dec->addImmediateOperand(decoration); |
2493 | 0 | for (auto string : strings) |
2494 | 0 | dec->addStringOperand(string); |
2495 | |
|
2496 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2497 | 0 | } |
2498 | | |
2499 | 0 | void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) { |
2500 | 0 | Instruction* dec = new Instruction(Op::OpDecorate); |
2501 | 0 | dec->reserveOperands(4); |
2502 | 0 | dec->addIdOperand(id); |
2503 | 0 | dec->addImmediateOperand(spv::Decoration::LinkageAttributes); |
2504 | 0 | dec->addStringOperand(name); |
2505 | 0 | dec->addImmediateOperand(linkType); |
2506 | |
|
2507 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2508 | 0 | } |
2509 | | |
2510 | | void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) |
2511 | 0 | { |
2512 | 0 | if (decoration == spv::Decoration::Max) |
2513 | 0 | return; |
2514 | | |
2515 | 0 | Instruction* dec = new Instruction(Op::OpDecorateId); |
2516 | 0 | dec->reserveOperands(3); |
2517 | 0 | dec->addIdOperand(id); |
2518 | 0 | dec->addImmediateOperand(decoration); |
2519 | 0 | dec->addIdOperand(idDecoration); |
2520 | |
|
2521 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2522 | 0 | } |
2523 | | |
2524 | | void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds) |
2525 | 0 | { |
2526 | 0 | if(decoration == spv::Decoration::Max) |
2527 | 0 | return; |
2528 | | |
2529 | 0 | Instruction* dec = new Instruction(Op::OpDecorateId); |
2530 | 0 | dec->reserveOperands(operandIds.size() + 2); |
2531 | 0 | dec->addIdOperand(id); |
2532 | 0 | dec->addImmediateOperand(decoration); |
2533 | |
|
2534 | 0 | for (auto operandId : operandIds) |
2535 | 0 | dec->addIdOperand(operandId); |
2536 | |
|
2537 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2538 | 0 | } |
2539 | | |
2540 | | void Builder::addMemberDecorationIdEXT(Id id, unsigned int member, Decoration decoration, |
2541 | | const std::vector<unsigned>& operands) |
2542 | 0 | { |
2543 | 0 | if (decoration == spv::Decoration::Max) |
2544 | 0 | return; |
2545 | | |
2546 | 0 | Instruction* dec = new Instruction(Op::OpMemberDecorateIdEXT); |
2547 | 0 | dec->reserveOperands(operands.size() + 3); |
2548 | 0 | dec->addIdOperand(id); |
2549 | 0 | dec->addImmediateOperand(member); |
2550 | 0 | dec->addImmediateOperand(decoration); |
2551 | 0 | for (auto operand : operands) |
2552 | 0 | dec->addIdOperand(operand); |
2553 | |
|
2554 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2555 | 0 | } |
2556 | | |
2557 | | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) |
2558 | 12.6k | { |
2559 | 12.6k | if (decoration == spv::Decoration::Max) |
2560 | 10.3k | return; |
2561 | | |
2562 | 2.30k | Instruction* dec = new Instruction(Op::OpMemberDecorate); |
2563 | 2.30k | dec->reserveOperands(3); |
2564 | 2.30k | dec->addIdOperand(id); |
2565 | 2.30k | dec->addImmediateOperand(member); |
2566 | 2.30k | dec->addImmediateOperand(decoration); |
2567 | 2.30k | if (num >= 0) |
2568 | 2.22k | dec->addImmediateOperand(num); |
2569 | | |
2570 | 2.30k | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2571 | 2.30k | } |
2572 | | |
2573 | | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) |
2574 | 0 | { |
2575 | 0 | if (decoration == spv::Decoration::Max) |
2576 | 0 | return; |
2577 | | |
2578 | 0 | Instruction* dec = new Instruction(Op::OpMemberDecorateStringGOOGLE); |
2579 | 0 | dec->reserveOperands(4); |
2580 | 0 | dec->addIdOperand(id); |
2581 | 0 | dec->addImmediateOperand(member); |
2582 | 0 | dec->addImmediateOperand(decoration); |
2583 | 0 | dec->addStringOperand(s); |
2584 | |
|
2585 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2586 | 0 | } |
2587 | | |
2588 | | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals) |
2589 | 0 | { |
2590 | 0 | if (decoration == spv::Decoration::Max) |
2591 | 0 | return; |
2592 | | |
2593 | 0 | Instruction* dec = new Instruction(Op::OpMemberDecorate); |
2594 | 0 | dec->reserveOperands(literals.size() + 3); |
2595 | 0 | dec->addIdOperand(id); |
2596 | 0 | dec->addImmediateOperand(member); |
2597 | 0 | dec->addImmediateOperand(decoration); |
2598 | 0 | for (auto literal : literals) |
2599 | 0 | dec->addImmediateOperand(literal); |
2600 | |
|
2601 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2602 | 0 | } |
2603 | | |
2604 | | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings) |
2605 | 0 | { |
2606 | 0 | if (decoration == spv::Decoration::Max) |
2607 | 0 | return; |
2608 | | |
2609 | 0 | Instruction* dec = new Instruction(Op::OpMemberDecorateString); |
2610 | 0 | dec->reserveOperands(strings.size() + 3); |
2611 | 0 | dec->addIdOperand(id); |
2612 | 0 | dec->addImmediateOperand(member); |
2613 | 0 | dec->addImmediateOperand(decoration); |
2614 | 0 | for (auto string : strings) |
2615 | 0 | dec->addStringOperand(string); |
2616 | |
|
2617 | 0 | decorations.insert(std::unique_ptr<Instruction>(dec)); |
2618 | 0 | } |
2619 | | |
2620 | 66.7k | void Builder::addInstruction(std::unique_ptr<Instruction> inst) { |
2621 | | // Phis must appear first in their block, don't insert line tracking instructions |
2622 | | // in front of them, just add the OpPhi and return. |
2623 | 66.7k | if (inst->getOpCode() == Op::OpPhi) { |
2624 | 104 | buildPoint->addInstruction(std::move(inst)); |
2625 | 104 | return; |
2626 | 104 | } |
2627 | | // Optionally insert OpDebugScope |
2628 | 66.6k | if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) { |
2629 | 0 | if (buildPoint->updateDebugScope(currentDebugScopeId.top())) { |
2630 | 0 | auto scopeInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), Op::OpExtInst); |
2631 | 0 | scopeInst->reserveOperands(3); |
2632 | 0 | scopeInst->addIdOperand(nonSemanticShaderDebugInfo); |
2633 | 0 | scopeInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugScope); |
2634 | 0 | scopeInst->addIdOperand(currentDebugScopeId.top()); |
2635 | 0 | buildPoint->addInstruction(std::move(scopeInst)); |
2636 | 0 | } |
2637 | |
|
2638 | 0 | dirtyScopeTracker = false; |
2639 | 0 | } |
2640 | | |
2641 | | // Insert OpLine/OpDebugLine if the debug source location has changed |
2642 | 66.6k | if (trackDebugInfo && dirtyLineTracker) { |
2643 | 0 | if (buildPoint->updateDebugSourceLocation(currentLine, 0, currentFileId)) { |
2644 | 0 | if (emitSpirvDebugInfo) { |
2645 | 0 | auto lineInst = std::make_unique<Instruction>(Op::OpLine); |
2646 | 0 | lineInst->reserveOperands(3); |
2647 | 0 | lineInst->addIdOperand(currentFileId); |
2648 | 0 | lineInst->addImmediateOperand(currentLine); |
2649 | 0 | lineInst->addImmediateOperand(0); |
2650 | 0 | buildPoint->addInstruction(std::move(lineInst)); |
2651 | 0 | } |
2652 | 0 | if (emitNonSemanticShaderDebugInfo) { |
2653 | 0 | auto lineInst = std::make_unique<Instruction>(getUniqueId(), makeVoidType(), Op::OpExtInst); |
2654 | 0 | lineInst->reserveOperands(7); |
2655 | 0 | lineInst->addIdOperand(nonSemanticShaderDebugInfo); |
2656 | 0 | lineInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugLine); |
2657 | 0 | lineInst->addIdOperand(makeDebugSource(currentFileId)); |
2658 | 0 | lineInst->addIdOperand(makeUintConstant(currentLine)); |
2659 | 0 | lineInst->addIdOperand(makeUintConstant(currentLine)); |
2660 | 0 | lineInst->addIdOperand(makeUintConstant(0)); |
2661 | 0 | lineInst->addIdOperand(makeUintConstant(0)); |
2662 | 0 | buildPoint->addInstruction(std::move(lineInst)); |
2663 | 0 | } |
2664 | 0 | } |
2665 | |
|
2666 | 0 | dirtyLineTracker = false; |
2667 | 0 | } |
2668 | | |
2669 | 66.6k | buildPoint->addInstruction(std::move(inst)); |
2670 | 66.6k | } |
2671 | | |
2672 | 4.60k | void Builder::addInstructionNoDebugInfo(std::unique_ptr<Instruction> inst) { |
2673 | 4.60k | buildPoint->addInstruction(std::move(inst)); |
2674 | 4.60k | } |
2675 | | |
2676 | | // Comments in header |
2677 | | Function* Builder::makeEntryPoint(const char* entryPoint) |
2678 | 442 | { |
2679 | 442 | assert(! entryPointFunction); |
2680 | | |
2681 | 442 | auto const returnType = makeVoidType(); |
2682 | | |
2683 | 442 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2684 | 442 | if(sourceLang == spv::SourceLanguage::HLSL) { |
2685 | 0 | emitNonSemanticShaderDebugInfo = false; |
2686 | 0 | } |
2687 | | |
2688 | 442 | Block* entry = nullptr; |
2689 | 442 | entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageType::Max, {}, {}, &entry); |
2690 | | |
2691 | 442 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2692 | | |
2693 | 442 | return entryPointFunction; |
2694 | 442 | } |
2695 | | |
2696 | | // Comments in header |
2697 | | Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType, |
2698 | | const std::vector<Id>& paramTypes, |
2699 | | const std::vector<std::vector<Decoration>>& decorations, Block** entry) |
2700 | 914 | { |
2701 | | // Make the function and initial instructions in it |
2702 | 914 | Id typeId = makeFunctionType(returnType, paramTypes); |
2703 | 914 | Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); |
2704 | 914 | Id funcId = getUniqueId(); |
2705 | 914 | Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module); |
2706 | | |
2707 | | // Set up the precisions |
2708 | 914 | setPrecision(function->getId(), precision); |
2709 | 914 | function->setReturnPrecision(precision); |
2710 | 1.35k | for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { |
2711 | 443 | for (int d = 0; d < (int)decorations[p].size(); ++d) { |
2712 | 0 | addDecoration(firstParamId + p, decorations[p][d]); |
2713 | 0 | function->addParamPrecision(p, decorations[p][d]); |
2714 | 0 | } |
2715 | 443 | } |
2716 | | |
2717 | | // reset last debug scope |
2718 | 914 | if (emitNonSemanticShaderDebugInfo) { |
2719 | 0 | dirtyScopeTracker = true; |
2720 | 0 | } |
2721 | | |
2722 | | // CFG |
2723 | 914 | assert(entry != nullptr); |
2724 | 914 | *entry = new Block(getUniqueId(), *function); |
2725 | 914 | function->addBlock(*entry); |
2726 | 914 | setBuildPoint(*entry); |
2727 | | |
2728 | 914 | if (name) |
2729 | 914 | addName(function->getId(), name); |
2730 | | |
2731 | 914 | functions.push_back(std::unique_ptr<Function>(function)); |
2732 | | |
2733 | 914 | return function; |
2734 | 914 | } |
2735 | | |
2736 | | void Builder::setupFunctionDebugInfo(Function* function, const char* name, const std::vector<Id>& paramTypes, |
2737 | | const std::vector<char const*>& paramNames) |
2738 | 914 | { |
2739 | | |
2740 | 914 | if (!emitNonSemanticShaderDebugInfo) |
2741 | 914 | return; |
2742 | | |
2743 | 0 | Id nameId = getStringId(unmangleFunctionName(name)); |
2744 | 0 | Id funcTypeId = function->getFuncTypeId(); |
2745 | 0 | assert(getDebugType(funcTypeId) != NoType); |
2746 | 0 | Id funcId = function->getId(); |
2747 | |
|
2748 | 0 | assert(funcId != 0); |
2749 | | |
2750 | | // Make the debug function instruction |
2751 | 0 | Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId); |
2752 | 0 | debugFuncIdLookup[funcId] = debugFuncId; |
2753 | 0 | currentDebugScopeId.push(debugFuncId); |
2754 | | |
2755 | | // DebugScope and DebugLine for parameter DebugDeclares |
2756 | 0 | assert(paramTypes.size() == paramNames.size()); |
2757 | 0 | if ((int)paramTypes.size() > 0) { |
2758 | 0 | Id firstParamId = function->getParamId(0); |
2759 | |
|
2760 | 0 | for (size_t p = 0; p < paramTypes.size(); ++p) { |
2761 | 0 | bool passByRef = false; |
2762 | 0 | Id paramTypeId = paramTypes[p]; |
2763 | | |
2764 | | // For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type. |
2765 | 0 | if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) { |
2766 | 0 | passByRef = true; |
2767 | 0 | paramTypeId = getContainedTypeId(paramTypeId); |
2768 | 0 | } |
2769 | |
|
2770 | 0 | auto const& paramName = paramNames[p]; |
2771 | 0 | auto const debugLocalVariableId = createDebugLocalVariable(getDebugType(paramTypeId), paramName, p + 1); |
2772 | 0 | auto const paramId = static_cast<Id>(firstParamId + p); |
2773 | |
|
2774 | 0 | if (passByRef) { |
2775 | 0 | makeDebugDeclare(debugLocalVariableId, paramId); |
2776 | 0 | } else { |
2777 | 0 | makeDebugValue(debugLocalVariableId, paramId); |
2778 | 0 | } |
2779 | 0 | } |
2780 | 0 | } |
2781 | | |
2782 | | // Clear debug scope stack |
2783 | 0 | if (emitNonSemanticShaderDebugInfo) |
2784 | 0 | currentDebugScopeId.pop(); |
2785 | 0 | } |
2786 | | |
2787 | | Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId) |
2788 | 0 | { |
2789 | 0 | assert(function != nullptr); |
2790 | 0 | assert(nameId != 0); |
2791 | 0 | assert(funcTypeId != 0); |
2792 | 0 | assert(getDebugType(funcTypeId) != NoType); |
2793 | |
|
2794 | 0 | Id funcId = getUniqueId(); |
2795 | 0 | auto type = new Instruction(funcId, makeVoidType(), Op::OpExtInst); |
2796 | 0 | type->reserveOperands(11); |
2797 | 0 | type->addIdOperand(nonSemanticShaderDebugInfo); |
2798 | 0 | type->addImmediateOperand(NonSemanticShaderDebugInfoDebugFunction); |
2799 | 0 | type->addIdOperand(nameId); |
2800 | 0 | type->addIdOperand(getDebugType(funcTypeId)); |
2801 | 0 | type->addIdOperand(makeDebugSource(currentFileId)); // TODO: This points to file of definition instead of declaration |
2802 | 0 | type->addIdOperand(makeUintConstant(currentLine)); // TODO: This points to line of definition instead of declaration |
2803 | 0 | type->addIdOperand(makeUintConstant(0)); // column |
2804 | 0 | type->addIdOperand(makeDebugCompilationUnit()); // scope |
2805 | 0 | type->addIdOperand(nameId); // linkage name |
2806 | 0 | type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfoFlagIsPublic)); |
2807 | 0 | type->addIdOperand(makeUintConstant(currentLine)); |
2808 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); |
2809 | 0 | module.mapInstruction(type); |
2810 | 0 | return funcId; |
2811 | 0 | } |
2812 | | |
2813 | 0 | Id Builder::makeDebugLexicalBlock(uint32_t line, uint32_t column) { |
2814 | 0 | assert(!currentDebugScopeId.empty()); |
2815 | |
|
2816 | 0 | Id lexId = getUniqueId(); |
2817 | 0 | auto lex = new Instruction(lexId, makeVoidType(), Op::OpExtInst); |
2818 | 0 | lex->reserveOperands(6); |
2819 | 0 | lex->addIdOperand(nonSemanticShaderDebugInfo); |
2820 | 0 | lex->addImmediateOperand(NonSemanticShaderDebugInfoDebugLexicalBlock); |
2821 | 0 | lex->addIdOperand(makeDebugSource(currentFileId)); |
2822 | 0 | lex->addIdOperand(makeUintConstant(line)); |
2823 | 0 | lex->addIdOperand(makeUintConstant(column)); // column |
2824 | 0 | lex->addIdOperand(currentDebugScopeId.top()); // scope |
2825 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(lex)); |
2826 | 0 | module.mapInstruction(lex); |
2827 | 0 | return lexId; |
2828 | 0 | } |
2829 | | |
2830 | | std::string Builder::unmangleFunctionName(std::string const& name) const |
2831 | 0 | { |
2832 | 0 | assert(name.length() > 0); |
2833 | |
|
2834 | 0 | if(name.rfind('(') != std::string::npos) { |
2835 | 0 | return name.substr(0, name.rfind('(')); |
2836 | 0 | } else { |
2837 | 0 | return name; |
2838 | 0 | } |
2839 | 0 | } |
2840 | | |
2841 | | // Comments in header |
2842 | | void Builder::makeReturn(bool implicit, Id retVal) |
2843 | 1.27k | { |
2844 | 1.27k | if (retVal) { |
2845 | 648 | Instruction* inst = new Instruction(NoResult, NoType, Op::OpReturnValue); |
2846 | 648 | inst->addIdOperand(retVal); |
2847 | 648 | addInstruction(std::unique_ptr<Instruction>(inst)); |
2848 | 648 | } else |
2849 | 624 | addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, Op::OpReturn))); |
2850 | | |
2851 | 1.27k | if (! implicit) |
2852 | 358 | createAndSetNoPredecessorBlock("post-return"); |
2853 | 1.27k | } |
2854 | | |
2855 | | // Comments in header |
2856 | | void Builder::enterLexicalBlock(uint32_t line, uint32_t column) |
2857 | 0 | { |
2858 | 0 | if (!emitNonSemanticShaderDebugInfo) { |
2859 | 0 | return; |
2860 | 0 | } |
2861 | | |
2862 | | // Generate new lexical scope debug instruction |
2863 | 0 | Id lexId = makeDebugLexicalBlock(line, column); |
2864 | 0 | currentDebugScopeId.push(lexId); |
2865 | 0 | dirtyScopeTracker = true; |
2866 | 0 | } |
2867 | | |
2868 | | // Comments in header |
2869 | | void Builder::leaveLexicalBlock() |
2870 | 0 | { |
2871 | 0 | if (!emitNonSemanticShaderDebugInfo) { |
2872 | 0 | return; |
2873 | 0 | } |
2874 | | |
2875 | | // Pop current scope from stack and clear current scope |
2876 | 0 | currentDebugScopeId.pop(); |
2877 | 0 | dirtyScopeTracker = true; |
2878 | 0 | } |
2879 | | |
2880 | | // Comments in header |
2881 | | void Builder::enterFunction(Function const* function) |
2882 | 914 | { |
2883 | 914 | currentFunction = function; |
2884 | | |
2885 | | // Save and disable debugInfo for HLSL entry point function. It is a wrapper |
2886 | | // function with no user code in it. |
2887 | 914 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2888 | 914 | if (sourceLang == spv::SourceLanguage::HLSL && function == entryPointFunction) { |
2889 | 0 | emitNonSemanticShaderDebugInfo = false; |
2890 | 0 | } |
2891 | | |
2892 | 914 | if (emitNonSemanticShaderDebugInfo) { |
2893 | | // Initialize scope state |
2894 | 0 | Id funcId = function->getFuncId(); |
2895 | 0 | Id debugFuncId = getDebugFunction(funcId); |
2896 | 0 | currentDebugScopeId.push(debugFuncId); |
2897 | | // Create DebugFunctionDefinition |
2898 | 0 | spv::Id resultId = getUniqueId(); |
2899 | 0 | Instruction* defInst = new Instruction(resultId, makeVoidType(), Op::OpExtInst); |
2900 | 0 | defInst->reserveOperands(4); |
2901 | 0 | defInst->addIdOperand(nonSemanticShaderDebugInfo); |
2902 | 0 | defInst->addImmediateOperand(NonSemanticShaderDebugInfoDebugFunctionDefinition); |
2903 | 0 | defInst->addIdOperand(debugFuncId); |
2904 | 0 | defInst->addIdOperand(funcId); |
2905 | 0 | addInstruction(std::unique_ptr<Instruction>(defInst)); |
2906 | 0 | } |
2907 | | |
2908 | 914 | if (auto linkType = function->getLinkType(); linkType != LinkageType::Max) { |
2909 | 0 | Id funcId = function->getFuncId(); |
2910 | 0 | addCapability(Capability::Linkage); |
2911 | 0 | addLinkageDecoration(funcId, function->getExportName(), linkType); |
2912 | 0 | } |
2913 | 914 | } |
2914 | | |
2915 | | // Comments in header |
2916 | | void Builder::leaveFunction() |
2917 | 914 | { |
2918 | 914 | Block* block = buildPoint; |
2919 | 914 | Function& function = buildPoint->getParent(); |
2920 | 914 | assert(block); |
2921 | | |
2922 | | // If our function did not contain a return, add a return void now. |
2923 | 914 | if (! block->isTerminated()) { |
2924 | 914 | if (function.getReturnType() == makeVoidType()) |
2925 | 590 | makeReturn(true); |
2926 | 324 | else { |
2927 | 324 | makeReturn(true, createUndefined(function.getReturnType())); |
2928 | 324 | } |
2929 | 914 | } |
2930 | | |
2931 | | // Clear function scope from debug scope stack |
2932 | 914 | if (emitNonSemanticShaderDebugInfo) |
2933 | 0 | currentDebugScopeId.pop(); |
2934 | | |
2935 | 914 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2936 | | |
2937 | | // Clear current function record |
2938 | 914 | currentFunction = nullptr; |
2939 | 914 | } |
2940 | | |
2941 | | // Comments in header |
2942 | | void Builder::makeStatementTerminator(spv::Op opcode, const char *name) |
2943 | 0 | { |
2944 | 0 | addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode))); |
2945 | 0 | createAndSetNoPredecessorBlock(name); |
2946 | 0 | } |
2947 | | |
2948 | | // Comments in header |
2949 | | void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name) |
2950 | 0 | { |
2951 | | // It's assumed that the terminator instruction is always of void return type |
2952 | | // However in future if there is a need for non void return type, new helper |
2953 | | // methods can be created. |
2954 | 0 | createNoResultOp(opcode, operands); |
2955 | 0 | createAndSetNoPredecessorBlock(name); |
2956 | 0 | } |
2957 | | |
2958 | | void Builder::createConstVariable(Id type, const char* name, Id constant, bool isGlobal) |
2959 | 758 | { |
2960 | 758 | if (emitNonSemanticShaderDebugInfo) { |
2961 | 0 | Id debugType = getDebugType(type); |
2962 | 0 | if (isGlobal) { |
2963 | 0 | createDebugGlobalVariable(debugType, name, constant); |
2964 | 0 | } |
2965 | 0 | else { |
2966 | 0 | auto debugLocal = createDebugLocalVariable(debugType, name); |
2967 | 0 | makeDebugValue(debugLocal, constant); |
2968 | 0 | } |
2969 | 0 | } |
2970 | 758 | } |
2971 | | |
2972 | | // Comments in header |
2973 | | Id Builder::createUntypedVariable(Decoration precision, StorageClass storageClass, const char* name, Id dataType, |
2974 | | Id initializer) |
2975 | 0 | { |
2976 | 0 | Id resultUntypedPointerType = makeUntypedPointer(storageClass); |
2977 | 0 | Instruction* inst = new Instruction(getUniqueId(), resultUntypedPointerType, Op::OpUntypedVariableKHR); |
2978 | 0 | inst->addImmediateOperand(storageClass); |
2979 | 0 | if (dataType != NoResult) { |
2980 | 0 | Id dataPointerType = makePointer(storageClass, dataType); |
2981 | 0 | inst->addIdOperand(dataPointerType); |
2982 | 0 | } |
2983 | 0 | if (initializer != NoResult) |
2984 | 0 | inst->addIdOperand(initializer); |
2985 | |
|
2986 | 0 | switch (storageClass) { |
2987 | 0 | case StorageClass::Function: |
2988 | | // Validation rules require the declaration in the entry block |
2989 | 0 | buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst)); |
2990 | 0 | break; |
2991 | 0 | default: |
2992 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
2993 | 0 | module.mapInstruction(inst); |
2994 | 0 | break; |
2995 | 0 | } |
2996 | | |
2997 | 0 | if (name) |
2998 | 0 | addName(inst->getResultId(), name); |
2999 | 0 | setPrecision(inst->getResultId(), precision); |
3000 | |
|
3001 | 0 | return inst->getResultId(); |
3002 | 0 | } |
3003 | | |
3004 | | // Comments in header |
3005 | | Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer, |
3006 | | bool const compilerGenerated) |
3007 | 6.06k | { |
3008 | 6.06k | Id pointerType = makePointer(storageClass, type); |
3009 | 6.06k | Instruction* inst = new Instruction(getUniqueId(), pointerType, Op::OpVariable); |
3010 | 6.06k | inst->addImmediateOperand(storageClass); |
3011 | 6.06k | if (initializer != NoResult) |
3012 | 0 | inst->addIdOperand(initializer); |
3013 | | |
3014 | 6.06k | if (storageClass == StorageClass::Function) { |
3015 | | // Validation rules require the declaration in the entry block |
3016 | 3.31k | buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst)); |
3017 | 3.31k | } |
3018 | 2.74k | else { |
3019 | 2.74k | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
3020 | 2.74k | module.mapInstruction(inst); |
3021 | 2.74k | } |
3022 | | |
3023 | 6.06k | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
3024 | 0 | { |
3025 | | // For debug info, we prefer respecting how the variable is declared in source code. |
3026 | | // We may emulate some local variables as global variable with private storage in SPIR-V, but we still want to |
3027 | | // treat them as local variables in debug info. |
3028 | 0 | if (storageClass == StorageClass::Function || (currentFunction && storageClass == StorageClass::Private)) { |
3029 | 0 | auto const debugLocalVariableId = createDebugLocalVariable(getDebugType(type), name); |
3030 | 0 | makeDebugDeclare(debugLocalVariableId, inst->getResultId()); |
3031 | 0 | } |
3032 | 0 | else { |
3033 | 0 | createDebugGlobalVariable(getDebugType(type), name, inst->getResultId()); |
3034 | 0 | } |
3035 | 0 | } |
3036 | | |
3037 | 6.06k | if (name) |
3038 | 6.05k | addName(inst->getResultId(), name); |
3039 | 6.06k | setPrecision(inst->getResultId(), precision); |
3040 | | |
3041 | 6.06k | return inst->getResultId(); |
3042 | 6.06k | } |
3043 | | |
3044 | | // Comments in header |
3045 | | Id Builder::createUndefined(Id type) |
3046 | 324 | { |
3047 | 324 | Instruction* inst = new Instruction(getUniqueId(), type, Op::OpUndef); |
3048 | 324 | addInstruction(std::unique_ptr<Instruction>(inst)); |
3049 | 324 | return inst->getResultId(); |
3050 | 324 | } |
3051 | | |
3052 | | // av/vis/nonprivate are unnecessary and illegal for some storage classes. |
3053 | | spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) |
3054 | | const |
3055 | 29.2k | { |
3056 | 29.2k | switch (sc) { |
3057 | 5.77k | case spv::StorageClass::Uniform: |
3058 | 5.77k | case spv::StorageClass::Workgroup: |
3059 | 5.79k | case spv::StorageClass::StorageBuffer: |
3060 | 5.86k | case spv::StorageClass::PhysicalStorageBufferEXT: |
3061 | 5.86k | break; |
3062 | 23.4k | default: |
3063 | 23.4k | memoryAccess = spv::MemoryAccessMask(memoryAccess & |
3064 | 23.4k | ~(spv::MemoryAccessMask::MakePointerAvailableKHR | |
3065 | 23.4k | spv::MemoryAccessMask::MakePointerVisibleKHR | |
3066 | 23.4k | spv::MemoryAccessMask::NonPrivatePointerKHR)); |
3067 | 23.4k | break; |
3068 | 29.2k | } |
3069 | 29.2k | return memoryAccess; |
3070 | 29.2k | } |
3071 | | |
3072 | | // Comments in header |
3073 | | void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, |
3074 | | unsigned int alignment) |
3075 | 10.4k | { |
3076 | 10.4k | Instruction* store = nullptr; |
3077 | 10.4k | if (isUntypedPointer(lValue)) |
3078 | 0 | store = createDescHeapLoadStoreBaseRemap(lValue, Op::OpStore); |
3079 | 10.4k | else { |
3080 | 10.4k | store = new Instruction(Op::OpStore); |
3081 | 10.4k | store->reserveOperands(2); |
3082 | 10.4k | store->addIdOperand(lValue); |
3083 | 10.4k | } |
3084 | 10.4k | store->addIdOperand(rValue); |
3085 | | |
3086 | 10.4k | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); |
3087 | | |
3088 | 10.4k | if (memoryAccess != MemoryAccessMask::MaskNone) { |
3089 | 54 | store->addImmediateOperand(memoryAccess); |
3090 | 54 | if (anySet(memoryAccess, spv::MemoryAccessMask::Aligned)) { |
3091 | 54 | store->addImmediateOperand(alignment); |
3092 | 54 | } |
3093 | 54 | if (anySet(memoryAccess, spv::MemoryAccessMask::MakePointerAvailableKHR)) { |
3094 | 0 | store->addIdOperand(makeUintConstant(scope)); |
3095 | 0 | } |
3096 | 54 | } |
3097 | | |
3098 | 10.4k | addInstruction(std::unique_ptr<Instruction>(store)); |
3099 | 10.4k | } |
3100 | | |
3101 | | // Comments in header |
3102 | | Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess, |
3103 | | spv::Scope scope, unsigned int alignment) |
3104 | 18.8k | { |
3105 | 18.8k | Instruction* load = nullptr; |
3106 | 18.8k | if (isUntypedPointer(lValue)) |
3107 | 0 | load = createDescHeapLoadStoreBaseRemap(lValue, Op::OpLoad); |
3108 | 18.8k | else { |
3109 | 18.8k | load = new Instruction(getUniqueId(), getDerefTypeId(lValue), Op::OpLoad); |
3110 | 18.8k | load->addIdOperand(lValue); |
3111 | 18.8k | } |
3112 | | |
3113 | 18.8k | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); |
3114 | | |
3115 | 18.8k | if (memoryAccess != MemoryAccessMask::MaskNone) { |
3116 | 18 | load->addImmediateOperand(memoryAccess); |
3117 | 18 | if (anySet(memoryAccess, spv::MemoryAccessMask::Aligned)) { |
3118 | 18 | load->addImmediateOperand(alignment); |
3119 | 18 | } |
3120 | 18 | if (anySet(memoryAccess, spv::MemoryAccessMask::MakePointerVisibleKHR)) { |
3121 | 0 | load->addIdOperand(makeUintConstant(scope)); |
3122 | 0 | } |
3123 | 18 | } |
3124 | | |
3125 | 18.8k | addInstruction(std::unique_ptr<Instruction>(load)); |
3126 | 18.8k | setPrecision(load->getResultId(), precision); |
3127 | | |
3128 | 18.8k | return load->getResultId(); |
3129 | 18.8k | } |
3130 | | |
3131 | | Instruction* Builder::createDescHeapLoadStoreBaseRemap(Id baseId, Op op) |
3132 | 0 | { |
3133 | | // could only be untypedAccessChain op. |
3134 | 0 | spv::Op instOp = module.getInstruction(baseId)->getOpCode(); |
3135 | 0 | spv::Id baseVal = baseId; |
3136 | | // base type (from run time array) |
3137 | 0 | spv::Id resultTy = getIdOperand(baseId, 0); |
3138 | | // Descriptor heap using run time array. |
3139 | 0 | if (accessChain.descHeapInfo.descHeapStorageClass != StorageClass::Max) |
3140 | 0 | resultTy = getIdOperand(resultTy, 0); |
3141 | 0 | if (instOp != Op::OpUntypedAccessChainKHR) { |
3142 | 0 | assert(false && "Not a untyped load type"); |
3143 | 0 | } |
3144 | |
|
3145 | 0 | Instruction* inst = nullptr; |
3146 | 0 | if (op == Op::OpStore) |
3147 | 0 | inst = new Instruction(Op::OpStore); |
3148 | 0 | else { |
3149 | 0 | inst = new Instruction(getUniqueId(), resultTy, Op::OpLoad); |
3150 | 0 | accessChain.descHeapInfo.descHeapInstId.push_back(inst); |
3151 | 0 | } |
3152 | 0 | inst->addIdOperand(baseVal); |
3153 | 0 | return inst; |
3154 | 0 | } |
3155 | | |
3156 | | uint32_t Builder::isStructureHeapMember(Id id, std::vector<Id> indexChain, |
3157 | | unsigned int idx, spv::BuiltIn* bt, uint32_t* firstArrIndex) |
3158 | 41.0k | { |
3159 | 41.0k | unsigned currentIdx = idx; |
3160 | | // Process types, only array types could contain no constant id operands. |
3161 | 41.0k | Id baseId = id; |
3162 | 41.0k | if (baseId == NoType) |
3163 | 0 | return 0; |
3164 | 41.0k | if (isPointerType(baseId)) |
3165 | 19.2k | baseId = getContainedTypeId(baseId); |
3166 | 41.0k | auto baseInst = module.getInstruction(baseId); |
3167 | 41.0k | if (baseInst->getOpCode() == spv::Op::OpTypeArray || |
3168 | 40.5k | baseInst->getOpCode() == spv::Op::OpTypeRuntimeArray) { |
3169 | 507 | if (firstArrIndex) |
3170 | 0 | *firstArrIndex = currentIdx; |
3171 | 507 | baseId = getContainedTypeId(baseId); |
3172 | 507 | baseInst = module.getInstruction(baseId); |
3173 | 507 | currentIdx++; |
3174 | 507 | } |
3175 | 41.0k | if (currentIdx >= indexChain.size()) |
3176 | 36.9k | return 0; |
3177 | | // Process index op. |
3178 | 4.05k | auto indexInst = module.getInstruction(indexChain[currentIdx]); |
3179 | 4.05k | if (indexInst->getOpCode() != spv::Op::OpConstant) |
3180 | 0 | return 0; |
3181 | 4.05k | auto index = indexInst->getImmediateOperand(0); |
3182 | 134k | for (auto dec = decorations.begin(); dec != decorations.end(); dec++) { |
3183 | 130k | if (dec->get()->getOpCode() == spv::Op::OpMemberDecorate && dec->get()->getIdOperand(0) == baseId && |
3184 | 17.4k | dec->get()->getImmediateOperand(1) == index && |
3185 | 3.83k | dec->get()->getImmediateOperand(2) == spv::Decoration::BuiltIn && |
3186 | 0 | (dec->get()->getImmediateOperand(3) == (unsigned)spv::BuiltIn::ResourceHeapEXT || |
3187 | 0 | dec->get()->getImmediateOperand(3) == (unsigned)spv::BuiltIn::SamplerHeapEXT)) { |
3188 | 0 | if (bt) |
3189 | 0 | *bt = (spv::BuiltIn)dec->get()->getImmediateOperand(3); |
3190 | 0 | return currentIdx; |
3191 | 0 | } |
3192 | 130k | } |
3193 | | // New base. |
3194 | 4.05k | if (baseInst->getOpCode() == spv::Op::OpTypeStruct) { |
3195 | 3.86k | if (!baseInst->isIdOperand(index) || idx == indexChain.size() - 1) |
3196 | 3.38k | return 0; |
3197 | 480 | return isStructureHeapMember(baseInst->getIdOperand(index), indexChain, currentIdx + 1, bt, firstArrIndex); |
3198 | 3.86k | } |
3199 | | |
3200 | 184 | return 0; |
3201 | 4.05k | } |
3202 | | |
3203 | | // Comments in header |
3204 | | Id Builder::createDescHeapAccessChain() |
3205 | 0 | { |
3206 | 0 | uint32_t rsrcOffsetIdx = accessChain.descHeapInfo.structRsrcTyOffsetCount; |
3207 | 0 | if (rsrcOffsetIdx != 0) |
3208 | 0 | accessChain.base = accessChain.descHeapInfo.structRemappedBase; |
3209 | 0 | Id base = accessChain.base; |
3210 | 0 | Id baseTy = accessChain.descHeapInfo.descHeapBaseTy; |
3211 | 0 | uint32_t explicitArrayStride = accessChain.descHeapInfo.descHeapBaseArrayStride; |
3212 | 0 | std::vector<Id>& offsets = accessChain.indexChain; |
3213 | 0 | uint32_t firstArrIndex = accessChain.descHeapInfo.structRsrcTyFirstArrIndex; |
3214 | | // both typeBufferEXT and UntypedPointer only contains storage class info. |
3215 | 0 | StorageClass storageClass = (StorageClass)accessChain.descHeapInfo.descHeapStorageClass; |
3216 | | // Make the untyped access chain instruction |
3217 | 0 | Instruction* chain = new Instruction(getUniqueId(), makeUntypedPointer(getStorageClass(base)), Op::OpUntypedAccessChainKHR); |
3218 | |
|
3219 | 0 | if (storageClass == spv::StorageClass::Uniform || storageClass == spv::StorageClass::StorageBuffer) { |
3220 | | // For buffer and uniform heap, split first index as heap array index |
3221 | | // Insert BufferPointer op and construct another access chain with following indexes. |
3222 | 0 | Id bufferTy = makeUntypedPointer(storageClass, true); |
3223 | 0 | Id strideId = NoResult; |
3224 | 0 | if (explicitArrayStride == 0) { |
3225 | 0 | strideId = createConstantSizeOfEXT(bufferTy); |
3226 | 0 | } else { |
3227 | 0 | strideId = makeUintConstant(explicitArrayStride); |
3228 | 0 | } |
3229 | 0 | Id runtimeArrTy = makeRuntimeArray(bufferTy); |
3230 | 0 | addDecorationId(runtimeArrTy, spv::Decoration::ArrayStrideIdEXT, strideId); |
3231 | 0 | chain->addIdOperand(runtimeArrTy); |
3232 | 0 | chain->addIdOperand(base); |
3233 | | // We would only re-target current member resource directly to resource/sampler heap base. |
3234 | | // So the previous access chain index towards final resource type is not needed? |
3235 | | // In current draft, only keep the first 'array index' into last access chain index. |
3236 | | // As those resource can't be declared as an array, in current first draft, array index will |
3237 | | // be the second index. This will be refined later. |
3238 | 0 | chain->addIdOperand(offsets[firstArrIndex]); |
3239 | 0 | if (rsrcOffsetIdx != 0) { |
3240 | 0 | for (uint32_t i = 0; i < rsrcOffsetIdx + 1; i++) { |
3241 | 0 | if (rsrcOffsetIdx + i + 1 < offsets.size()) |
3242 | 0 | offsets[i] = offsets[i + rsrcOffsetIdx + 1]; |
3243 | 0 | } |
3244 | 0 | } else { |
3245 | 0 | for (uint32_t i = 0; i < offsets.size() - 1; i++) { |
3246 | 0 | offsets[i] = offsets[i + 1]; |
3247 | 0 | } |
3248 | 0 | } |
3249 | 0 | for (uint32_t i = 0; i < rsrcOffsetIdx + 1; i++) |
3250 | 0 | offsets.pop_back(); |
3251 | 0 | addInstruction(std::unique_ptr<Instruction>(chain)); |
3252 | | // Create OpBufferPointer for loading target buffer descriptor. |
3253 | 0 | Id bufferPtrTy = makePointer(storageClass, baseTy); |
3254 | 0 | Instruction* bufferDataPtr = new Instruction(getUniqueId(), bufferPtrTy, Op::OpBufferPointerEXT); |
3255 | 0 | bufferDataPtr->addIdOperand(chain->getResultId()); |
3256 | 0 | addInstruction(std::unique_ptr<Instruction>(bufferDataPtr)); |
3257 | | |
3258 | | // Form a second, typed access chain for accessing buffer data. |
3259 | 0 | Id resultTy = baseTy; |
3260 | 0 | for (int i = 0; i < (int)offsets.size(); ++i) { |
3261 | 0 | if (isStructType(resultTy)) { |
3262 | 0 | assert(isConstantScalar(offsets[i])); |
3263 | 0 | resultTy = getContainedTypeId(resultTy, getConstantScalar(offsets[i])); |
3264 | 0 | } else |
3265 | 0 | resultTy = getContainedTypeId(resultTy, offsets[i]); |
3266 | 0 | } |
3267 | 0 | resultTy = makePointer(storageClass, resultTy); |
3268 | |
|
3269 | 0 | Instruction* bufferChain = new Instruction(getUniqueId(), resultTy, Op::OpAccessChain); |
3270 | 0 | bufferChain->reserveOperands(offsets.size() + 1); |
3271 | 0 | bufferChain->addIdOperand(bufferDataPtr->getResultId()); |
3272 | 0 | for (int i = 0; i < (int)offsets.size(); ++i) |
3273 | 0 | bufferChain->addIdOperand(offsets[i]); |
3274 | 0 | addInstruction(std::unique_ptr<Instruction>(bufferChain)); |
3275 | 0 | return bufferChain->getResultId(); |
3276 | 0 | } else { |
3277 | | // image/sampler heap |
3278 | 0 | Id strideId = NoResult; |
3279 | 0 | if (explicitArrayStride == 0) { |
3280 | 0 | strideId = createConstantSizeOfEXT(baseTy); |
3281 | 0 | } else { |
3282 | 0 | strideId = makeUintConstant(explicitArrayStride); |
3283 | 0 | } |
3284 | 0 | Id runtimeArrTy = makeRuntimeArray(baseTy); |
3285 | 0 | addDecorationId(runtimeArrTy, spv::Decoration::ArrayStrideIdEXT, strideId); |
3286 | 0 | chain->addIdOperand(runtimeArrTy); |
3287 | 0 | chain->addIdOperand(base); |
3288 | 0 | for (int i = 0; i < (int)offsets.size(); ++i) |
3289 | 0 | chain->addIdOperand(offsets[i]); |
3290 | 0 | addInstruction(std::unique_ptr<Instruction>(chain)); |
3291 | 0 | return chain->getResultId(); |
3292 | 0 | } |
3293 | 0 | } |
3294 | | |
3295 | | // Comments in header |
3296 | | Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets) |
3297 | 9.23k | { |
3298 | | // Figure out the final resulting type. |
3299 | 9.23k | Id typeId = getResultingAccessChainType(); |
3300 | 9.23k | typeId = makePointer(storageClass, typeId); |
3301 | | |
3302 | | // Make the instruction |
3303 | 9.23k | Instruction* chain = new Instruction(getUniqueId(), typeId, Op::OpAccessChain); |
3304 | 9.23k | chain->reserveOperands(offsets.size() + 1); |
3305 | 9.23k | chain->addIdOperand(base); |
3306 | 19.3k | for (int i = 0; i < (int)offsets.size(); ++i) |
3307 | 10.1k | chain->addIdOperand(offsets[i]); |
3308 | 9.23k | addInstruction(std::unique_ptr<Instruction>(chain)); |
3309 | | |
3310 | 9.23k | return chain->getResultId(); |
3311 | 9.23k | } |
3312 | | |
3313 | | Id Builder::createArrayLength(Id base, unsigned int member, unsigned int bits) |
3314 | 0 | { |
3315 | 0 | spv::Id intType = makeUintType(bits); |
3316 | 0 | Instruction* length = new Instruction(getUniqueId(), intType, Op::OpArrayLength); |
3317 | 0 | length->reserveOperands(2); |
3318 | 0 | length->addIdOperand(base); |
3319 | 0 | length->addImmediateOperand(member); |
3320 | 0 | addInstruction(std::unique_ptr<Instruction>(length)); |
3321 | |
|
3322 | 0 | return length->getResultId(); |
3323 | 0 | } |
3324 | | |
3325 | | Id Builder::createCooperativeMatrixLengthKHR(Id type) |
3326 | 0 | { |
3327 | 0 | spv::Id intType = makeUintType(32); |
3328 | | |
3329 | | // Generate code for spec constants if in spec constant operation |
3330 | | // generation mode. |
3331 | 0 | if (generatingOpCodeForSpecConst) { |
3332 | 0 | return createSpecConstantOp(Op::OpCooperativeMatrixLengthKHR, intType, std::vector<Id>(1, type), std::vector<Id>()); |
3333 | 0 | } |
3334 | | |
3335 | 0 | Instruction* length = new Instruction(getUniqueId(), intType, Op::OpCooperativeMatrixLengthKHR); |
3336 | 0 | length->addIdOperand(type); |
3337 | 0 | addInstruction(std::unique_ptr<Instruction>(length)); |
3338 | |
|
3339 | 0 | return length->getResultId(); |
3340 | 0 | } |
3341 | | |
3342 | | Id Builder::createCooperativeMatrixLengthNV(Id type) |
3343 | 0 | { |
3344 | 0 | spv::Id intType = makeUintType(32); |
3345 | | |
3346 | | // Generate code for spec constants if in spec constant operation |
3347 | | // generation mode. |
3348 | 0 | if (generatingOpCodeForSpecConst) { |
3349 | 0 | return createSpecConstantOp(Op::OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>()); |
3350 | 0 | } |
3351 | | |
3352 | 0 | Instruction* length = new Instruction(getUniqueId(), intType, Op::OpCooperativeMatrixLengthNV); |
3353 | 0 | length->addIdOperand(type); |
3354 | 0 | addInstruction(std::unique_ptr<Instruction>(length)); |
3355 | |
|
3356 | 0 | return length->getResultId(); |
3357 | 0 | } |
3358 | | |
3359 | | Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) |
3360 | 2.36k | { |
3361 | | // Generate code for spec constants if in spec constant operation |
3362 | | // generation mode. |
3363 | 2.36k | if (generatingOpCodeForSpecConst) { |
3364 | 0 | return createSpecConstantOp(Op::OpCompositeExtract, typeId, std::vector<Id>(1, composite), |
3365 | 0 | std::vector<Id>(1, index)); |
3366 | 0 | } |
3367 | 2.36k | Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpCompositeExtract); |
3368 | 2.36k | extract->reserveOperands(2); |
3369 | 2.36k | extract->addIdOperand(composite); |
3370 | 2.36k | extract->addImmediateOperand(index); |
3371 | 2.36k | addInstruction(std::unique_ptr<Instruction>(extract)); |
3372 | | |
3373 | 2.36k | return extract->getResultId(); |
3374 | 2.36k | } |
3375 | | |
3376 | | Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes) |
3377 | 653 | { |
3378 | | // Generate code for spec constants if in spec constant operation |
3379 | | // generation mode. |
3380 | 653 | if (generatingOpCodeForSpecConst) { |
3381 | 0 | return createSpecConstantOp(Op::OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes); |
3382 | 0 | } |
3383 | 653 | Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpCompositeExtract); |
3384 | 653 | extract->reserveOperands(indexes.size() + 1); |
3385 | 653 | extract->addIdOperand(composite); |
3386 | 1.38k | for (int i = 0; i < (int)indexes.size(); ++i) |
3387 | 734 | extract->addImmediateOperand(indexes[i]); |
3388 | 653 | addInstruction(std::unique_ptr<Instruction>(extract)); |
3389 | | |
3390 | 653 | return extract->getResultId(); |
3391 | 653 | } |
3392 | | |
3393 | | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) |
3394 | 80 | { |
3395 | 80 | Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpCompositeInsert); |
3396 | 80 | insert->reserveOperands(3); |
3397 | 80 | insert->addIdOperand(object); |
3398 | 80 | insert->addIdOperand(composite); |
3399 | 80 | insert->addImmediateOperand(index); |
3400 | 80 | addInstruction(std::unique_ptr<Instruction>(insert)); |
3401 | | |
3402 | 80 | return insert->getResultId(); |
3403 | 80 | } |
3404 | | |
3405 | | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes) |
3406 | 0 | { |
3407 | 0 | Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpCompositeInsert); |
3408 | 0 | insert->reserveOperands(indexes.size() + 2); |
3409 | 0 | insert->addIdOperand(object); |
3410 | 0 | insert->addIdOperand(composite); |
3411 | 0 | for (int i = 0; i < (int)indexes.size(); ++i) |
3412 | 0 | insert->addImmediateOperand(indexes[i]); |
3413 | 0 | addInstruction(std::unique_ptr<Instruction>(insert)); |
3414 | |
|
3415 | 0 | return insert->getResultId(); |
3416 | 0 | } |
3417 | | |
3418 | | Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) |
3419 | 0 | { |
3420 | 0 | Instruction* extract = new Instruction(getUniqueId(), typeId, Op::OpVectorExtractDynamic); |
3421 | 0 | extract->reserveOperands(2); |
3422 | 0 | extract->addIdOperand(vector); |
3423 | 0 | extract->addIdOperand(componentIndex); |
3424 | 0 | addInstruction(std::unique_ptr<Instruction>(extract)); |
3425 | |
|
3426 | 0 | return extract->getResultId(); |
3427 | 0 | } |
3428 | | |
3429 | | Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) |
3430 | 0 | { |
3431 | 0 | Instruction* insert = new Instruction(getUniqueId(), typeId, Op::OpVectorInsertDynamic); |
3432 | 0 | insert->reserveOperands(3); |
3433 | 0 | insert->addIdOperand(vector); |
3434 | 0 | insert->addIdOperand(component); |
3435 | 0 | insert->addIdOperand(componentIndex); |
3436 | 0 | addInstruction(std::unique_ptr<Instruction>(insert)); |
3437 | |
|
3438 | 0 | return insert->getResultId(); |
3439 | 0 | } |
3440 | | |
3441 | | // An opcode that has no operands, no result id, and no type |
3442 | | void Builder::createNoResultOp(Op opCode) |
3443 | 0 | { |
3444 | 0 | Instruction* op = new Instruction(opCode); |
3445 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
3446 | 0 | } |
3447 | | |
3448 | | // An opcode that has one id operand, no result id, and no type |
3449 | | void Builder::createNoResultOp(Op opCode, Id operand) |
3450 | 111 | { |
3451 | 111 | Instruction* op = new Instruction(opCode); |
3452 | 111 | op->addIdOperand(operand); |
3453 | 111 | addInstruction(std::unique_ptr<Instruction>(op)); |
3454 | 111 | } |
3455 | | |
3456 | | // An opcode that has one or more operands, no result id, and no type |
3457 | | void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands) |
3458 | 100 | { |
3459 | 100 | Instruction* op = new Instruction(opCode); |
3460 | 100 | op->reserveOperands(operands.size()); |
3461 | 474 | for (auto id : operands) { |
3462 | 474 | op->addIdOperand(id); |
3463 | 474 | } |
3464 | 100 | addInstruction(std::unique_ptr<Instruction>(op)); |
3465 | 100 | } |
3466 | | |
3467 | | // An opcode that has multiple operands, no result id, and no type |
3468 | | void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands) |
3469 | 134 | { |
3470 | 134 | Instruction* op = new Instruction(opCode); |
3471 | 134 | op->reserveOperands(operands.size()); |
3472 | 646 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
3473 | 512 | if (it->isId) |
3474 | 457 | op->addIdOperand(it->word); |
3475 | 55 | else |
3476 | 55 | op->addImmediateOperand(it->word); |
3477 | 512 | } |
3478 | 134 | addInstruction(std::unique_ptr<Instruction>(op)); |
3479 | 134 | } |
3480 | | |
3481 | | void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) |
3482 | 0 | { |
3483 | 0 | Instruction* op = new Instruction(Op::OpControlBarrier); |
3484 | 0 | op->reserveOperands(3); |
3485 | 0 | op->addIdOperand(makeUintConstant(execution)); |
3486 | 0 | op->addIdOperand(makeUintConstant(memory)); |
3487 | 0 | op->addIdOperand(makeUintConstant(semantics)); |
3488 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
3489 | 0 | } |
3490 | | |
3491 | | void Builder::createMemoryBarrier(Scope executionScope, MemorySemanticsMask memorySemantics) |
3492 | 0 | { |
3493 | 0 | Instruction* op = new Instruction(Op::OpMemoryBarrier); |
3494 | 0 | op->reserveOperands(2); |
3495 | 0 | op->addIdOperand(makeUintConstant((unsigned)executionScope)); |
3496 | 0 | op->addIdOperand(makeUintConstant((unsigned)memorySemantics)); |
3497 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
3498 | 0 | } |
3499 | | |
3500 | | // An opcode that has one operands, a result id, and a type |
3501 | | Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) |
3502 | 3.26k | { |
3503 | | // Generate code for spec constants if in spec constant operation |
3504 | | // generation mode. |
3505 | 3.26k | if (generatingOpCodeForSpecConst) { |
3506 | 264 | return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>()); |
3507 | 264 | } |
3508 | 3.00k | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3509 | 3.00k | op->addIdOperand(operand); |
3510 | 3.00k | addInstruction(std::unique_ptr<Instruction>(op)); |
3511 | | |
3512 | 3.00k | return op->getResultId(); |
3513 | 3.26k | } |
3514 | | |
3515 | | Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) |
3516 | 7.12k | { |
3517 | | // Generate code for spec constants if in spec constant operation |
3518 | | // generation mode. |
3519 | 7.12k | if (generatingOpCodeForSpecConst) { |
3520 | 245 | std::vector<Id> operands(2); |
3521 | 245 | operands[0] = left; operands[1] = right; |
3522 | 245 | return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>()); |
3523 | 245 | } |
3524 | 6.88k | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3525 | 6.88k | op->reserveOperands(2); |
3526 | 6.88k | op->addIdOperand(left); |
3527 | 6.88k | op->addIdOperand(right); |
3528 | 6.88k | addInstruction(std::unique_ptr<Instruction>(op)); |
3529 | | |
3530 | 6.88k | return op->getResultId(); |
3531 | 7.12k | } |
3532 | | |
3533 | | Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) |
3534 | 89 | { |
3535 | | // Generate code for spec constants if in spec constant operation |
3536 | | // generation mode. |
3537 | 89 | if (generatingOpCodeForSpecConst) { |
3538 | 50 | std::vector<Id> operands(3); |
3539 | 50 | operands[0] = op1; |
3540 | 50 | operands[1] = op2; |
3541 | 50 | operands[2] = op3; |
3542 | 50 | return createSpecConstantOp( |
3543 | 50 | opCode, typeId, operands, std::vector<Id>()); |
3544 | 50 | } |
3545 | 39 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3546 | 39 | op->reserveOperands(3); |
3547 | 39 | op->addIdOperand(op1); |
3548 | 39 | op->addIdOperand(op2); |
3549 | 39 | op->addIdOperand(op3); |
3550 | 39 | addInstruction(std::unique_ptr<Instruction>(op)); |
3551 | | |
3552 | 39 | return op->getResultId(); |
3553 | 89 | } |
3554 | | |
3555 | | Id Builder::createConstData(Op opCode, Id typeId, const std::vector<const char*> operands) |
3556 | 0 | { |
3557 | 0 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3558 | 0 | op->reserveOperands(operands.size()); |
3559 | 0 | for (auto id : operands) |
3560 | 0 | op->addStringOperand(id); |
3561 | 0 | module.mapInstruction(op); |
3562 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op)); |
3563 | |
|
3564 | 0 | return op->getResultId(); |
3565 | 0 | } |
3566 | | |
3567 | | Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands) |
3568 | 2.40k | { |
3569 | 2.40k | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3570 | 2.40k | op->reserveOperands(operands.size()); |
3571 | 2.40k | for (auto id : operands) |
3572 | 9.23k | op->addIdOperand(id); |
3573 | 2.40k | addInstruction(std::unique_ptr<Instruction>(op)); |
3574 | | |
3575 | 2.40k | return op->getResultId(); |
3576 | 2.40k | } |
3577 | | |
3578 | | Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands) |
3579 | 3.04k | { |
3580 | 3.04k | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
3581 | 3.04k | op->reserveOperands(operands.size()); |
3582 | 11.6k | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
3583 | 8.63k | if (it->isId) |
3584 | 8.43k | op->addIdOperand(it->word); |
3585 | 198 | else |
3586 | 198 | op->addImmediateOperand(it->word); |
3587 | 8.63k | } |
3588 | 3.04k | addInstruction(std::unique_ptr<Instruction>(op)); |
3589 | | |
3590 | 3.04k | return op->getResultId(); |
3591 | 3.04k | } |
3592 | | |
3593 | | Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, |
3594 | | const std::vector<unsigned>& literals) |
3595 | 559 | { |
3596 | 559 | Instruction* op = new Instruction(getUniqueId(), typeId, Op::OpSpecConstantOp); |
3597 | 559 | op->reserveOperands(operands.size() + literals.size() + 1); |
3598 | 559 | op->addImmediateOperand((unsigned) opCode); |
3599 | 1.46k | for (auto it = operands.cbegin(); it != operands.cend(); ++it) |
3600 | 904 | op->addIdOperand(*it); |
3601 | 559 | for (auto it = literals.cbegin(); it != literals.cend(); ++it) |
3602 | 0 | op->addImmediateOperand(*it); |
3603 | 559 | module.mapInstruction(op); |
3604 | 559 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op)); |
3605 | | |
3606 | | // OpSpecConstantOp's using 8 or 16 bit types require the associated capability |
3607 | 559 | if (containsType(typeId, Op::OpTypeInt, 8)) |
3608 | 0 | addCapability(Capability::Int8); |
3609 | 559 | if (containsType(typeId, Op::OpTypeInt, 16)) |
3610 | 144 | addCapability(Capability::Int16); |
3611 | 559 | if (containsType(typeId, Op::OpTypeFloat, 16)) |
3612 | 0 | addCapability(Capability::Float16); |
3613 | | |
3614 | 559 | return op->getResultId(); |
3615 | 559 | } |
3616 | | |
3617 | | Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args) |
3618 | 1.39k | { |
3619 | 1.39k | Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), Op::OpFunctionCall); |
3620 | 1.39k | op->reserveOperands(args.size() + 1); |
3621 | 1.39k | op->addIdOperand(function->getId()); |
3622 | 1.95k | for (int a = 0; a < (int)args.size(); ++a) |
3623 | 565 | op->addIdOperand(args[a]); |
3624 | 1.39k | addInstruction(std::unique_ptr<Instruction>(op)); |
3625 | | |
3626 | 1.39k | return op->getResultId(); |
3627 | 1.39k | } |
3628 | | |
3629 | | // Comments in header |
3630 | | Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels) |
3631 | 1.57k | { |
3632 | 1.57k | if (channels.size() == 1) |
3633 | 337 | return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); |
3634 | | |
3635 | 1.23k | if (generatingOpCodeForSpecConst) { |
3636 | 0 | std::vector<Id> operands(2); |
3637 | 0 | operands[0] = operands[1] = source; |
3638 | 0 | return setPrecision(createSpecConstantOp(Op::OpVectorShuffle, typeId, operands, channels), precision); |
3639 | 0 | } |
3640 | 1.23k | Instruction* swizzle = new Instruction(getUniqueId(), typeId, Op::OpVectorShuffle); |
3641 | 1.23k | assert(isVector(source)); |
3642 | 1.23k | swizzle->reserveOperands(channels.size() + 2); |
3643 | 1.23k | swizzle->addIdOperand(source); |
3644 | 1.23k | swizzle->addIdOperand(source); |
3645 | 4.45k | for (int i = 0; i < (int)channels.size(); ++i) |
3646 | 3.22k | swizzle->addImmediateOperand(channels[i]); |
3647 | 1.23k | addInstruction(std::unique_ptr<Instruction>(swizzle)); |
3648 | | |
3649 | 1.23k | return setPrecision(swizzle->getResultId(), precision); |
3650 | 1.23k | } |
3651 | | |
3652 | | // Comments in header |
3653 | | Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels) |
3654 | 0 | { |
3655 | 0 | if (channels.size() == 1 && getNumComponents(source) == 1) |
3656 | 0 | return createCompositeInsert(source, target, typeId, channels.front()); |
3657 | | |
3658 | 0 | Instruction* swizzle = new Instruction(getUniqueId(), typeId, Op::OpVectorShuffle); |
3659 | |
|
3660 | 0 | assert(isVector(target)); |
3661 | 0 | swizzle->reserveOperands(2); |
3662 | 0 | swizzle->addIdOperand(target); |
3663 | |
|
3664 | 0 | assert(getNumComponents(source) == channels.size()); |
3665 | 0 | assert(isVector(source)); |
3666 | 0 | swizzle->addIdOperand(source); |
3667 | | |
3668 | | // Set up an identity shuffle from the base value to the result value |
3669 | 0 | unsigned int components[4]; |
3670 | 0 | int numTargetComponents = getNumComponents(target); |
3671 | 0 | for (int i = 0; i < numTargetComponents; ++i) |
3672 | 0 | components[i] = i; |
3673 | | |
3674 | | // Punch in the l-value swizzle |
3675 | 0 | for (int i = 0; i < (int)channels.size(); ++i) |
3676 | 0 | components[channels[i]] = numTargetComponents + i; |
3677 | | |
3678 | | // finish the instruction with these components selectors |
3679 | 0 | swizzle->reserveOperands(numTargetComponents); |
3680 | 0 | for (int i = 0; i < numTargetComponents; ++i) |
3681 | 0 | swizzle->addImmediateOperand(components[i]); |
3682 | 0 | addInstruction(std::unique_ptr<Instruction>(swizzle)); |
3683 | |
|
3684 | 0 | return swizzle->getResultId(); |
3685 | 0 | } |
3686 | | |
3687 | | // Comments in header |
3688 | | void Builder::promoteScalar(Decoration precision, Id& left, Id& right) |
3689 | 4.79k | { |
3690 | | // choose direction of promotion (+1 for left to right, -1 for right to left) |
3691 | 4.79k | int direction = !isScalar(right) - !isScalar(left); |
3692 | | |
3693 | 4.79k | auto const &makeVec = [&](Id component, Id other) { |
3694 | 236 | if (isCooperativeVector(other)) { |
3695 | 0 | return makeCooperativeVectorTypeNV(getTypeId(component), getCooperativeVectorNumComponents(getTypeId(other))); |
3696 | 236 | } else { |
3697 | 236 | return makeVectorType(getTypeId(component), getNumComponents(other)); |
3698 | 236 | } |
3699 | 236 | }; |
3700 | | |
3701 | 4.79k | if (direction > 0) |
3702 | 0 | left = smearScalar(precision, left, makeVec(left, right)); |
3703 | 4.79k | else if (direction < 0) |
3704 | 236 | right = smearScalar(precision, right, makeVec(right, left)); |
3705 | | |
3706 | 4.79k | return; |
3707 | 4.79k | } |
3708 | | |
3709 | | // Comments in header |
3710 | | Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) |
3711 | 502 | { |
3712 | 502 | assert(getNumComponents(scalar) == 1); |
3713 | 502 | assert(getTypeId(scalar) == getScalarTypeId(vectorType)); |
3714 | | |
3715 | 502 | int numComponents = getNumTypeComponents(vectorType); |
3716 | 502 | if (numComponents == 1 && !isCooperativeVectorType(vectorType) && !isVectorType(vectorType)) |
3717 | 0 | return scalar; |
3718 | | |
3719 | 502 | Instruction* smear = nullptr; |
3720 | 502 | if (generatingOpCodeForSpecConst) { |
3721 | 0 | auto members = std::vector<spv::Id>(numComponents, scalar); |
3722 | | // Sometime even in spec-constant-op mode, the temporary vector created by |
3723 | | // promoting a scalar might not be a spec constant. This should depend on |
3724 | | // the scalar. |
3725 | | // e.g.: |
3726 | | // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; |
3727 | | // In such cases, the temporary vector created from a_front_end_const_scalar |
3728 | | // is not a spec constant vector, even though the binary operation node is marked |
3729 | | // as 'specConstant' and we are in spec-constant-op mode. |
3730 | 0 | auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); |
3731 | 0 | smear = module.getInstruction(result_id); |
3732 | 502 | } else { |
3733 | 502 | bool replicate = (useReplicatedComposites || isCooperativeVectorType(vectorType)) && (numComponents > 0); |
3734 | | |
3735 | 502 | if (replicate) { |
3736 | 0 | numComponents = 1; |
3737 | 0 | addCapability(spv::Capability::ReplicatedCompositesEXT); |
3738 | 0 | addExtension(spv::E_SPV_EXT_replicated_composites); |
3739 | 0 | } |
3740 | | |
3741 | 502 | Op opcode = replicate ? Op::OpCompositeConstructReplicateEXT : Op::OpCompositeConstruct; |
3742 | | |
3743 | 502 | smear = new Instruction(getUniqueId(), vectorType, opcode); |
3744 | 502 | smear->reserveOperands(numComponents); |
3745 | 1.92k | for (int c = 0; c < numComponents; ++c) |
3746 | 1.42k | smear->addIdOperand(scalar); |
3747 | 502 | addInstruction(std::unique_ptr<Instruction>(smear)); |
3748 | 502 | } |
3749 | | |
3750 | 502 | return setPrecision(smear->getResultId(), precision); |
3751 | 502 | } |
3752 | | |
3753 | | // Comments in header |
3754 | | Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args) |
3755 | 549 | { |
3756 | 549 | Instruction* inst = new Instruction(getUniqueId(), resultType, Op::OpExtInst); |
3757 | 549 | inst->reserveOperands(args.size() + 2); |
3758 | 549 | inst->addIdOperand(builtins); |
3759 | 549 | inst->addImmediateOperand(entryPoint); |
3760 | 1.54k | for (int arg = 0; arg < (int)args.size(); ++arg) |
3761 | 992 | inst->addIdOperand(args[arg]); |
3762 | | |
3763 | 549 | addInstruction(std::unique_ptr<Instruction>(inst)); |
3764 | | |
3765 | 549 | return inst->getResultId(); |
3766 | 549 | } |
3767 | | |
3768 | | // Accept all parameters needed to create a texture instruction. |
3769 | | // Create the correct instruction based on the inputs, and make the call. |
3770 | | Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, |
3771 | | bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) |
3772 | 1.35k | { |
3773 | 1.35k | std::vector<Id> texArgs; |
3774 | | |
3775 | | // |
3776 | | // Set up the fixed arguments |
3777 | | // |
3778 | 1.35k | bool explicitLod = false; |
3779 | 1.35k | texArgs.push_back(parameters.sampler); |
3780 | 1.35k | texArgs.push_back(parameters.coords); |
3781 | 1.35k | if (parameters.Dref != NoResult) |
3782 | 432 | texArgs.push_back(parameters.Dref); |
3783 | 1.35k | if (parameters.component != NoResult) |
3784 | 152 | texArgs.push_back(parameters.component); |
3785 | | |
3786 | 1.35k | if (parameters.granularity != NoResult) |
3787 | 0 | texArgs.push_back(parameters.granularity); |
3788 | 1.35k | if (parameters.coarse != NoResult) |
3789 | 0 | texArgs.push_back(parameters.coarse); |
3790 | | |
3791 | | // |
3792 | | // Set up the optional arguments |
3793 | | // |
3794 | 1.35k | size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any. |
3795 | 1.35k | ImageOperandsMask mask = ImageOperandsMask::MaskNone; // the mask operand |
3796 | 1.35k | if (parameters.bias) { |
3797 | 94 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Bias); |
3798 | 94 | texArgs.push_back(parameters.bias); |
3799 | 94 | } |
3800 | 1.35k | if (parameters.lod) { |
3801 | 252 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Lod); |
3802 | 252 | texArgs.push_back(parameters.lod); |
3803 | 252 | explicitLod = true; |
3804 | 1.10k | } else if (parameters.gradX) { |
3805 | 304 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Grad); |
3806 | 304 | texArgs.push_back(parameters.gradX); |
3807 | 304 | texArgs.push_back(parameters.gradY); |
3808 | 304 | explicitLod = true; |
3809 | 799 | } else if (noImplicitLod && ! fetch && ! gather) { |
3810 | | // have to explicitly use lod of 0 if not allowed to have them be implicit, and |
3811 | | // we would otherwise be about to issue an implicit instruction |
3812 | 160 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Lod); |
3813 | 160 | texArgs.push_back(makeFloatConstant(0.0)); |
3814 | 160 | explicitLod = true; |
3815 | 160 | } |
3816 | 1.35k | if (parameters.offset) { |
3817 | 466 | if (isConstant(parameters.offset)) |
3818 | 466 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::ConstOffset); |
3819 | 0 | else { |
3820 | 0 | addCapability(Capability::ImageGatherExtended); |
3821 | 0 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Offset); |
3822 | 0 | } |
3823 | 466 | texArgs.push_back(parameters.offset); |
3824 | 466 | } |
3825 | 1.35k | if (parameters.offsets) { |
3826 | 64 | if (!isConstant(parameters.offsets) && sourceLang == spv::SourceLanguage::GLSL) { |
3827 | 0 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Offsets); |
3828 | 64 | } else { |
3829 | 64 | addCapability(Capability::ImageGatherExtended); |
3830 | 64 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::ConstOffsets); |
3831 | 64 | } |
3832 | 64 | texArgs.push_back(parameters.offsets); |
3833 | 64 | } |
3834 | 1.35k | if (parameters.sample) { |
3835 | 12 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::Sample); |
3836 | 12 | texArgs.push_back(parameters.sample); |
3837 | 12 | } |
3838 | 1.35k | if (parameters.lodClamp) { |
3839 | | // capability if this bit is used |
3840 | 200 | addCapability(Capability::MinLod); |
3841 | | |
3842 | 200 | mask = (ImageOperandsMask)(mask | ImageOperandsMask::MinLod); |
3843 | 200 | texArgs.push_back(parameters.lodClamp); |
3844 | 200 | } |
3845 | 1.35k | if (parameters.nonprivate) { |
3846 | 0 | mask = mask | ImageOperandsMask::NonPrivateTexelKHR; |
3847 | 0 | } |
3848 | 1.35k | if (parameters.volatil) { |
3849 | 0 | mask = mask | ImageOperandsMask::VolatileTexelKHR; |
3850 | 0 | } |
3851 | 1.35k | if (parameters.nontemporal) { |
3852 | 0 | mask = mask | ImageOperandsMask::Nontemporal; |
3853 | 0 | } |
3854 | 1.35k | mask = mask | signExtensionMask; |
3855 | | // insert the operand for the mask, if any bits were set. |
3856 | 1.35k | if (mask != ImageOperandsMask::MaskNone) |
3857 | 1.09k | texArgs.insert(texArgs.begin() + optArgNum, (Id)mask); |
3858 | | |
3859 | | // |
3860 | | // Set up the instruction |
3861 | | // |
3862 | 1.35k | Op opCode = Op::OpNop; // All paths below need to set this |
3863 | 1.35k | if (fetch) { |
3864 | 75 | if (sparse) |
3865 | 30 | opCode = Op::OpImageSparseFetch; |
3866 | 45 | else |
3867 | 45 | opCode = Op::OpImageFetch; |
3868 | 1.28k | } else if (parameters.granularity && parameters.coarse) { |
3869 | 0 | opCode = Op::OpImageSampleFootprintNV; |
3870 | 1.28k | } else if (gather) { |
3871 | 240 | if (parameters.Dref) |
3872 | 88 | if (sparse) |
3873 | 44 | opCode = Op::OpImageSparseDrefGather; |
3874 | 44 | else |
3875 | 44 | opCode = Op::OpImageDrefGather; |
3876 | 152 | else |
3877 | 152 | if (sparse) |
3878 | 76 | opCode = Op::OpImageSparseGather; |
3879 | 76 | else |
3880 | 76 | opCode = Op::OpImageGather; |
3881 | 1.04k | } else if (explicitLod) { |
3882 | 604 | if (parameters.Dref) { |
3883 | 160 | if (proj) |
3884 | 32 | if (sparse) |
3885 | 0 | opCode = Op::OpImageSparseSampleProjDrefExplicitLod; |
3886 | 32 | else |
3887 | 32 | opCode = Op::OpImageSampleProjDrefExplicitLod; |
3888 | 128 | else |
3889 | 128 | if (sparse) |
3890 | 44 | opCode = Op::OpImageSparseSampleDrefExplicitLod; |
3891 | 84 | else |
3892 | 84 | opCode = Op::OpImageSampleDrefExplicitLod; |
3893 | 444 | } else { |
3894 | 444 | if (proj) |
3895 | 76 | if (sparse) |
3896 | 0 | opCode = Op::OpImageSparseSampleProjExplicitLod; |
3897 | 76 | else |
3898 | 76 | opCode = Op::OpImageSampleProjExplicitLod; |
3899 | 368 | else |
3900 | 368 | if (sparse) |
3901 | 84 | opCode = Op::OpImageSparseSampleExplicitLod; |
3902 | 284 | else |
3903 | 284 | opCode = Op::OpImageSampleExplicitLod; |
3904 | 444 | } |
3905 | 604 | } else { |
3906 | 436 | if (parameters.Dref) { |
3907 | 184 | if (proj) |
3908 | 12 | if (sparse) |
3909 | 0 | opCode = Op::OpImageSparseSampleProjDrefImplicitLod; |
3910 | 12 | else |
3911 | 12 | opCode = Op::OpImageSampleProjDrefImplicitLod; |
3912 | 172 | else |
3913 | 172 | if (sparse) |
3914 | 56 | opCode = Op::OpImageSparseSampleDrefImplicitLod; |
3915 | 116 | else |
3916 | 116 | opCode = Op::OpImageSampleDrefImplicitLod; |
3917 | 252 | } else { |
3918 | 252 | if (proj) |
3919 | 28 | if (sparse) |
3920 | 0 | opCode = Op::OpImageSparseSampleProjImplicitLod; |
3921 | 28 | else |
3922 | 28 | opCode = Op::OpImageSampleProjImplicitLod; |
3923 | 224 | else |
3924 | 224 | if (sparse) |
3925 | 72 | opCode = Op::OpImageSparseSampleImplicitLod; |
3926 | 152 | else |
3927 | 152 | opCode = Op::OpImageSampleImplicitLod; |
3928 | 252 | } |
3929 | 436 | } |
3930 | | |
3931 | | // See if the result type is expecting a smeared result. |
3932 | | // This happens when a legacy shadow*() call is made, which |
3933 | | // gets a vec4 back instead of a float. |
3934 | 1.35k | Id smearedType = resultType; |
3935 | 1.35k | if (! isScalarType(resultType)) { |
3936 | 705 | switch (opCode) { |
3937 | 0 | case Op::OpImageSampleDrefImplicitLod: |
3938 | 0 | case Op::OpImageSampleDrefExplicitLod: |
3939 | 0 | case Op::OpImageSampleProjDrefImplicitLod: |
3940 | 0 | case Op::OpImageSampleProjDrefExplicitLod: |
3941 | 0 | resultType = getScalarTypeId(resultType); |
3942 | 0 | break; |
3943 | 705 | default: |
3944 | 705 | break; |
3945 | 705 | } |
3946 | 705 | } |
3947 | | |
3948 | 1.35k | Id typeId0 = 0; |
3949 | 1.35k | Id typeId1 = 0; |
3950 | | |
3951 | 1.35k | if (sparse) { |
3952 | 406 | typeId0 = resultType; |
3953 | 406 | typeId1 = getDerefTypeId(parameters.texelOut); |
3954 | 406 | resultType = makeStructResultType(typeId0, typeId1); |
3955 | 406 | } |
3956 | | |
3957 | | // Build the SPIR-V instruction |
3958 | 1.35k | Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); |
3959 | 1.35k | textureInst->reserveOperands(optArgNum + (texArgs.size() - (optArgNum + 1))); |
3960 | 4.64k | for (size_t op = 0; op < optArgNum; ++op) |
3961 | 3.29k | textureInst->addIdOperand(texArgs[op]); |
3962 | 1.35k | if (optArgNum < texArgs.size()) |
3963 | 1.09k | textureInst->addImmediateOperand(texArgs[optArgNum]); |
3964 | 3.21k | for (size_t op = optArgNum + 1; op < texArgs.size(); ++op) |
3965 | 1.85k | textureInst->addIdOperand(texArgs[op]); |
3966 | 1.35k | setPrecision(textureInst->getResultId(), precision); |
3967 | 1.35k | addInstruction(std::unique_ptr<Instruction>(textureInst)); |
3968 | | |
3969 | 1.35k | Id resultId = textureInst->getResultId(); |
3970 | | |
3971 | 1.35k | if (sparse) { |
3972 | | // set capability |
3973 | 406 | addCapability(Capability::SparseResidency); |
3974 | | |
3975 | | // Decode the return type that was a special structure |
3976 | 406 | createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); |
3977 | 406 | resultId = createCompositeExtract(resultId, typeId0, 0); |
3978 | 406 | setPrecision(resultId, precision); |
3979 | 949 | } else { |
3980 | | // When a smear is needed, do it, as per what was computed |
3981 | | // above when resultType was changed to a scalar type. |
3982 | 949 | if (resultType != smearedType) |
3983 | 0 | resultId = smearScalar(precision, resultId, smearedType); |
3984 | 949 | } |
3985 | | |
3986 | 1.35k | return resultId; |
3987 | 1.35k | } |
3988 | | |
3989 | | // Comments in header |
3990 | | Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) |
3991 | 184 | { |
3992 | | // Figure out the result type |
3993 | 184 | Id resultType = 0; |
3994 | 184 | switch (opCode) { |
3995 | 15 | case Op::OpImageQuerySize: |
3996 | 87 | case Op::OpImageQuerySizeLod: |
3997 | 87 | { |
3998 | 87 | int numComponents = 0; |
3999 | 87 | switch (getTypeDimensionality(getImageType(parameters.sampler))) { |
4000 | 12 | case Dim::Dim1D: |
4001 | 15 | case Dim::Buffer: |
4002 | 15 | numComponents = 1; |
4003 | 15 | break; |
4004 | 51 | case Dim::Dim2D: |
4005 | 63 | case Dim::Cube: |
4006 | 69 | case Dim::Rect: |
4007 | 69 | case Dim::SubpassData: |
4008 | 69 | numComponents = 2; |
4009 | 69 | break; |
4010 | 3 | case Dim::Dim3D: |
4011 | 3 | numComponents = 3; |
4012 | 3 | break; |
4013 | | |
4014 | 0 | default: |
4015 | 0 | assert(0); |
4016 | 0 | break; |
4017 | 87 | } |
4018 | 87 | if (isArrayedImageType(getImageType(parameters.sampler))) |
4019 | 54 | ++numComponents; |
4020 | | |
4021 | 87 | Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); |
4022 | 87 | if (numComponents == 1) |
4023 | 9 | resultType = intType; |
4024 | 78 | else |
4025 | 78 | resultType = makeVectorType(intType, numComponents); |
4026 | | |
4027 | 87 | break; |
4028 | 87 | } |
4029 | 52 | case Op::OpImageQueryLod: |
4030 | 52 | resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); |
4031 | 52 | break; |
4032 | 39 | case Op::OpImageQueryLevels: |
4033 | 45 | case Op::OpImageQuerySamples: |
4034 | 45 | resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); |
4035 | 45 | break; |
4036 | 0 | default: |
4037 | 0 | assert(0); |
4038 | 0 | break; |
4039 | 184 | } |
4040 | | |
4041 | 184 | Instruction* query = new Instruction(getUniqueId(), resultType, opCode); |
4042 | 184 | query->addIdOperand(parameters.sampler); |
4043 | 184 | if (parameters.coords) |
4044 | 52 | query->addIdOperand(parameters.coords); |
4045 | 184 | if (parameters.lod) |
4046 | 72 | query->addIdOperand(parameters.lod); |
4047 | 184 | addInstruction(std::unique_ptr<Instruction>(query)); |
4048 | 184 | addCapability(Capability::ImageQuery); |
4049 | | |
4050 | 184 | return query->getResultId(); |
4051 | 184 | } |
4052 | | |
4053 | | // External comments in header. |
4054 | | // Operates recursively to visit the composite's hierarchy. |
4055 | | Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) |
4056 | 0 | { |
4057 | 0 | Id boolType = makeBoolType(); |
4058 | 0 | Id valueType = getTypeId(value1); |
4059 | |
|
4060 | 0 | Id resultId = NoResult; |
4061 | |
|
4062 | 0 | int numConstituents = getNumTypeConstituents(valueType); |
4063 | | |
4064 | | // Scalars and Vectors |
4065 | |
|
4066 | 0 | if (isScalarType(valueType) || isVectorType(valueType)) { |
4067 | 0 | assert(valueType == getTypeId(value2)); |
4068 | | // These just need a single comparison, just have |
4069 | | // to figure out what it is. |
4070 | 0 | Op op; |
4071 | 0 | switch (getMostBasicTypeClass(valueType)) { |
4072 | 0 | case Op::OpTypeFloat: |
4073 | 0 | op = equal ? Op::OpFOrdEqual : Op::OpFUnordNotEqual; |
4074 | 0 | break; |
4075 | 0 | case Op::OpTypeInt: |
4076 | 0 | default: |
4077 | 0 | op = equal ? Op::OpIEqual : Op::OpINotEqual; |
4078 | 0 | break; |
4079 | 0 | case Op::OpTypeBool: |
4080 | 0 | op = equal ? Op::OpLogicalEqual : Op::OpLogicalNotEqual; |
4081 | 0 | precision = NoPrecision; |
4082 | 0 | break; |
4083 | 0 | } |
4084 | | |
4085 | 0 | if (isScalarType(valueType)) { |
4086 | | // scalar |
4087 | 0 | resultId = createBinOp(op, boolType, value1, value2); |
4088 | 0 | } else { |
4089 | | // vector |
4090 | 0 | resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); |
4091 | 0 | setPrecision(resultId, precision); |
4092 | | // reduce vector compares... |
4093 | 0 | resultId = createUnaryOp(equal ? Op::OpAll : Op::OpAny, boolType, resultId); |
4094 | 0 | } |
4095 | |
|
4096 | 0 | return setPrecision(resultId, precision); |
4097 | 0 | } |
4098 | | |
4099 | | // Only structs, arrays, and matrices should be left. |
4100 | | // They share in common the reduction operation across their constituents. |
4101 | 0 | assert(isAggregateType(valueType) || isMatrixType(valueType)); |
4102 | | |
4103 | | // Compare each pair of constituents |
4104 | 0 | for (int constituent = 0; constituent < numConstituents; ++constituent) { |
4105 | 0 | std::vector<unsigned> indexes(1, constituent); |
4106 | 0 | Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); |
4107 | 0 | Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); |
4108 | 0 | Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); |
4109 | 0 | Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); |
4110 | |
|
4111 | 0 | Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); |
4112 | |
|
4113 | 0 | if (constituent == 0) |
4114 | 0 | resultId = subResultId; |
4115 | 0 | else |
4116 | 0 | resultId = setPrecision(createBinOp(equal ? Op::OpLogicalAnd : Op::OpLogicalOr, boolType, resultId, subResultId), |
4117 | 0 | precision); |
4118 | 0 | } |
4119 | |
|
4120 | 0 | return resultId; |
4121 | 0 | } |
4122 | | |
4123 | | // OpCompositeConstruct |
4124 | | Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents) |
4125 | 291 | { |
4126 | 291 | assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && |
4127 | 291 | getNumTypeConstituents(typeId) == constituents.size()) || |
4128 | 291 | ((isCooperativeVectorType(typeId) || isVectorType(typeId)) && constituents.size() == 1)); |
4129 | | |
4130 | 291 | if (generatingOpCodeForSpecConst) { |
4131 | | // Sometime, even in spec-constant-op mode, the constant composite to be |
4132 | | // constructed may not be a specialization constant. |
4133 | | // e.g.: |
4134 | | // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); |
4135 | | // The first column vector should be a spec constant one, as a_spec_const is a spec constant. |
4136 | | // The second column vector should NOT be spec constant, as it does not contain any spec constants. |
4137 | | // To handle such cases, we check the constituents of the constant vector to determine whether this |
4138 | | // vector should be created as a spec constant. |
4139 | 0 | return makeCompositeConstant(typeId, constituents, |
4140 | 0 | std::any_of(constituents.begin(), constituents.end(), |
4141 | 0 | [&](spv::Id id) { return isSpecConstant(id); })); |
4142 | 0 | } |
4143 | | |
4144 | 291 | bool replicate = false; |
4145 | 291 | size_t numConstituents = constituents.size(); |
4146 | | |
4147 | 291 | if (useReplicatedComposites || isCooperativeVectorType(typeId)) { |
4148 | 0 | replicate = numConstituents > 0 && |
4149 | 0 | std::equal(constituents.begin() + 1, constituents.end(), constituents.begin()); |
4150 | 0 | } |
4151 | | |
4152 | 291 | if (replicate) { |
4153 | 0 | numConstituents = 1; |
4154 | 0 | addCapability(spv::Capability::ReplicatedCompositesEXT); |
4155 | 0 | addExtension(spv::E_SPV_EXT_replicated_composites); |
4156 | 0 | } |
4157 | | |
4158 | 291 | Op opcode = replicate ? Op::OpCompositeConstructReplicateEXT : Op::OpCompositeConstruct; |
4159 | | |
4160 | 291 | Instruction* op = new Instruction(getUniqueId(), typeId, opcode); |
4161 | 291 | op->reserveOperands(constituents.size()); |
4162 | 1.19k | for (size_t c = 0; c < numConstituents; ++c) |
4163 | 900 | op->addIdOperand(constituents[c]); |
4164 | 291 | addInstruction(std::unique_ptr<Instruction>(op)); |
4165 | | |
4166 | 291 | return op->getResultId(); |
4167 | 291 | } |
4168 | | |
4169 | | // coopmat conversion |
4170 | | Id Builder::createCooperativeMatrixConversion(Id typeId, Id source) |
4171 | 0 | { |
4172 | 0 | Instruction* op = new Instruction(getUniqueId(), typeId, Op::OpCooperativeMatrixConvertNV); |
4173 | 0 | op->addIdOperand(source); |
4174 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
4175 | |
|
4176 | 0 | return op->getResultId(); |
4177 | 0 | } |
4178 | | |
4179 | | // coopmat reduce |
4180 | | Id Builder::createCooperativeMatrixReduce(Op opcode, Id typeId, Id source, unsigned int mask, Id func) |
4181 | 0 | { |
4182 | 0 | Instruction* op = new Instruction(getUniqueId(), typeId, opcode); |
4183 | 0 | op->addIdOperand(source); |
4184 | 0 | op->addImmediateOperand(mask); |
4185 | 0 | op->addIdOperand(func); |
4186 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
4187 | |
|
4188 | 0 | return op->getResultId(); |
4189 | 0 | } |
4190 | | |
4191 | | // coopmat per-element operation |
4192 | | Id Builder::createCooperativeMatrixPerElementOp(Id typeId, const std::vector<Id>& operands) |
4193 | 0 | { |
4194 | 0 | Instruction* op = new Instruction(getUniqueId(), typeId, spv::Op::OpCooperativeMatrixPerElementOpNV); |
4195 | | // skip operand[0], which is where the result is stored |
4196 | 0 | for (uint32_t i = 1; i < operands.size(); ++i) { |
4197 | 0 | op->addIdOperand(operands[i]); |
4198 | 0 | } |
4199 | 0 | addInstruction(std::unique_ptr<Instruction>(op)); |
4200 | |
|
4201 | 0 | return op->getResultId(); |
4202 | 0 | } |
4203 | | |
4204 | | // Vector or scalar constructor |
4205 | | Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
4206 | 533 | { |
4207 | 533 | Id result = NoResult; |
4208 | 533 | unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); |
4209 | 533 | unsigned int targetComponent = 0; |
4210 | | |
4211 | | // Special case: when calling a vector constructor with a single scalar |
4212 | | // argument, smear the scalar |
4213 | 533 | if (sources.size() == 1 && isScalar(sources[0]) && (numTargetComponents > 1 || isCooperativeVectorType(resultTypeId))) |
4214 | 266 | return smearScalar(precision, sources[0], resultTypeId); |
4215 | | |
4216 | | // Special case: 2 vectors of equal size |
4217 | 267 | if (sources.size() == 1 && |
4218 | 102 | (isVector(sources[0]) || isCooperativeVector(sources[0])) && |
4219 | 83 | numTargetComponents == getNumComponents(sources[0])) { |
4220 | 65 | if (isCooperativeVector(sources[0]) != isCooperativeVectorType(resultTypeId)) { |
4221 | 0 | assert(isVector(sources[0]) != isVectorType(resultTypeId)); |
4222 | 0 | return createUnaryOp(spv::Op::OpBitcast, resultTypeId, sources[0]); |
4223 | 65 | } else { |
4224 | 65 | assert(resultTypeId == getTypeId(sources[0])); |
4225 | 65 | return sources[0]; |
4226 | 65 | } |
4227 | 65 | } |
4228 | | |
4229 | | // accumulate the arguments for OpCompositeConstruct |
4230 | 202 | std::vector<Id> constituents; |
4231 | 202 | Id scalarTypeId = getScalarTypeId(resultTypeId); |
4232 | | |
4233 | | // lambda to store the result of visiting an argument component |
4234 | 604 | const auto latchResult = [&](Id comp) { |
4235 | 604 | if (numTargetComponents > 1 || isVectorType(resultTypeId)) |
4236 | 585 | constituents.push_back(comp); |
4237 | 19 | else |
4238 | 19 | result = comp; |
4239 | 604 | ++targetComponent; |
4240 | 604 | }; |
4241 | | |
4242 | | // lambda to visit a vector argument's components |
4243 | 202 | const auto accumulateVectorConstituents = [&](Id sourceArg) { |
4244 | 130 | unsigned int sourceSize = getNumComponents(sourceArg); |
4245 | 130 | unsigned int sourcesToUse = sourceSize; |
4246 | 130 | if (sourcesToUse + targetComponent > numTargetComponents) |
4247 | 18 | sourcesToUse = numTargetComponents - targetComponent; |
4248 | | |
4249 | 467 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
4250 | 337 | std::vector<unsigned> swiz; |
4251 | 337 | swiz.push_back(s); |
4252 | 337 | latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); |
4253 | 337 | } |
4254 | 130 | }; |
4255 | | |
4256 | | // lambda to visit a matrix argument's components |
4257 | 202 | const auto accumulateMatrixConstituents = [&](Id sourceArg) { |
4258 | 0 | unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); |
4259 | 0 | unsigned int sourcesToUse = sourceSize; |
4260 | 0 | if (sourcesToUse + targetComponent > numTargetComponents) |
4261 | 0 | sourcesToUse = numTargetComponents - targetComponent; |
4262 | |
|
4263 | 0 | unsigned int col = 0; |
4264 | 0 | unsigned int row = 0; |
4265 | 0 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
4266 | 0 | if (row >= getNumRows(sourceArg)) { |
4267 | 0 | row = 0; |
4268 | 0 | col++; |
4269 | 0 | } |
4270 | 0 | std::vector<Id> indexes; |
4271 | 0 | indexes.push_back(col); |
4272 | 0 | indexes.push_back(row); |
4273 | 0 | latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); |
4274 | 0 | row++; |
4275 | 0 | } |
4276 | 0 | }; |
4277 | | |
4278 | | // Go through the source arguments, each one could have either |
4279 | | // a single or multiple components to contribute. |
4280 | 397 | for (unsigned int i = 0; i < sources.size(); ++i) { |
4281 | | |
4282 | 397 | if (isScalar(sources[i]) || isPointer(sources[i])) |
4283 | 267 | latchResult(sources[i]); |
4284 | 130 | else if (isVector(sources[i]) || isCooperativeVector(sources[i])) |
4285 | 130 | accumulateVectorConstituents(sources[i]); |
4286 | 0 | else if (isMatrix(sources[i])) |
4287 | 0 | accumulateMatrixConstituents(sources[i]); |
4288 | 0 | else |
4289 | 0 | assert(0); |
4290 | | |
4291 | 397 | if (targetComponent >= numTargetComponents) |
4292 | 202 | break; |
4293 | 397 | } |
4294 | | |
4295 | | // If the result is a vector, make it from the gathered constituents. |
4296 | 202 | if (constituents.size() > 0) { |
4297 | 183 | result = createCompositeConstruct(resultTypeId, constituents); |
4298 | 183 | return setPrecision(result, precision); |
4299 | 183 | } else { |
4300 | | // Precision was set when generating this component. |
4301 | 19 | return result; |
4302 | 19 | } |
4303 | 202 | } |
4304 | | |
4305 | | // Comments in header |
4306 | | Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
4307 | 36 | { |
4308 | 36 | Id componentTypeId = getScalarTypeId(resultTypeId); |
4309 | 36 | unsigned int numCols = getTypeNumColumns(resultTypeId); |
4310 | 36 | unsigned int numRows = getTypeNumRows(resultTypeId); |
4311 | | |
4312 | 36 | Instruction* instr = module.getInstruction(componentTypeId); |
4313 | 36 | const unsigned bitCount = instr->getImmediateOperand(0); |
4314 | | |
4315 | | // Optimize matrix constructed from a bigger matrix |
4316 | 36 | if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { |
4317 | | // To truncate the matrix to a smaller number of rows/columns, we need to: |
4318 | | // 1. For each column, extract the column and truncate it to the required size using shuffle |
4319 | | // 2. Assemble the resulting matrix from all columns |
4320 | 18 | Id matrix = sources[0]; |
4321 | 18 | Id columnTypeId = getContainedTypeId(resultTypeId); |
4322 | 18 | Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); |
4323 | | |
4324 | 18 | std::vector<unsigned> channels; |
4325 | 63 | for (unsigned int row = 0; row < numRows; ++row) |
4326 | 45 | channels.push_back(row); |
4327 | | |
4328 | 18 | std::vector<Id> matrixColumns; |
4329 | 63 | for (unsigned int col = 0; col < numCols; ++col) { |
4330 | 45 | std::vector<unsigned> indexes; |
4331 | 45 | indexes.push_back(col); |
4332 | 45 | Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); |
4333 | 45 | setPrecision(colv, precision); |
4334 | | |
4335 | 45 | if (numRows != getNumRows(matrix)) { |
4336 | 27 | matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); |
4337 | 27 | } else { |
4338 | 18 | matrixColumns.push_back(colv); |
4339 | 18 | } |
4340 | 45 | } |
4341 | | |
4342 | 18 | return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); |
4343 | 18 | } |
4344 | | |
4345 | | // Detect a matrix being constructed from a repeated vector of the correct size. |
4346 | | // Create the composite directly from it. |
4347 | 18 | if (sources.size() == numCols && isVector(sources[0]) && getNumComponents(sources[0]) == numRows && |
4348 | 0 | std::equal(sources.begin() + 1, sources.end(), sources.begin())) { |
4349 | 0 | return setPrecision(createCompositeConstruct(resultTypeId, sources), precision); |
4350 | 0 | } |
4351 | | |
4352 | | // Otherwise, will use a two step process |
4353 | | // 1. make a compile-time 2D array of values |
4354 | | // 2. construct a matrix from that array |
4355 | | |
4356 | | // Step 1. |
4357 | | |
4358 | | // initialize the array to the identity matrix |
4359 | 18 | Id ids[maxMatrixSize][maxMatrixSize]; |
4360 | 18 | Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); |
4361 | 18 | Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); |
4362 | 90 | for (int col = 0; col < 4; ++col) { |
4363 | 360 | for (int row = 0; row < 4; ++row) { |
4364 | 288 | if (col == row) |
4365 | 72 | ids[col][row] = one; |
4366 | 216 | else |
4367 | 216 | ids[col][row] = zero; |
4368 | 288 | } |
4369 | 72 | } |
4370 | | |
4371 | | // modify components as dictated by the arguments |
4372 | 18 | if (sources.size() == 1 && isScalar(sources[0])) { |
4373 | | // a single scalar; resets the diagonals |
4374 | 45 | for (int col = 0; col < 4; ++col) |
4375 | 36 | ids[col][col] = sources[0]; |
4376 | 9 | } else if (isMatrix(sources[0])) { |
4377 | | // constructing from another matrix; copy over the parts that exist in both the argument and constructee |
4378 | 9 | Id matrix = sources[0]; |
4379 | 9 | unsigned int minCols = std::min(numCols, getNumColumns(matrix)); |
4380 | 9 | unsigned int minRows = std::min(numRows, getNumRows(matrix)); |
4381 | 36 | for (unsigned int col = 0; col < minCols; ++col) { |
4382 | 27 | std::vector<unsigned> indexes; |
4383 | 27 | indexes.push_back(col); |
4384 | 108 | for (unsigned int row = 0; row < minRows; ++row) { |
4385 | 81 | indexes.push_back(row); |
4386 | 81 | ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); |
4387 | 81 | indexes.pop_back(); |
4388 | 81 | setPrecision(ids[col][row], precision); |
4389 | 81 | } |
4390 | 27 | } |
4391 | 9 | } else { |
4392 | | // fill in the matrix in column-major order with whatever argument components are available |
4393 | 0 | unsigned int row = 0; |
4394 | 0 | unsigned int col = 0; |
4395 | |
|
4396 | 0 | for (unsigned int arg = 0; arg < sources.size() && col < numCols; ++arg) { |
4397 | 0 | Id argComp = sources[arg]; |
4398 | 0 | for (unsigned int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { |
4399 | 0 | if (getNumComponents(sources[arg]) > 1) { |
4400 | 0 | argComp = createCompositeExtract(sources[arg], componentTypeId, comp); |
4401 | 0 | setPrecision(argComp, precision); |
4402 | 0 | } |
4403 | 0 | ids[col][row++] = argComp; |
4404 | 0 | if (row == numRows) { |
4405 | 0 | row = 0; |
4406 | 0 | col++; |
4407 | 0 | } |
4408 | 0 | if (col == numCols) { |
4409 | | // If more components are provided than fit the matrix, discard the rest. |
4410 | 0 | break; |
4411 | 0 | } |
4412 | 0 | } |
4413 | 0 | } |
4414 | 0 | } |
4415 | | |
4416 | | // Step 2: Construct a matrix from that array. |
4417 | | // First make the column vectors, then make the matrix. |
4418 | | |
4419 | | // make the column vectors |
4420 | 18 | Id columnTypeId = getContainedTypeId(resultTypeId); |
4421 | 18 | std::vector<Id> matrixColumns; |
4422 | 72 | for (unsigned int col = 0; col < numCols; ++col) { |
4423 | 54 | std::vector<Id> vectorComponents; |
4424 | 234 | for (unsigned int row = 0; row < numRows; ++row) |
4425 | 180 | vectorComponents.push_back(ids[col][row]); |
4426 | 54 | Id column = createCompositeConstruct(columnTypeId, vectorComponents); |
4427 | 54 | setPrecision(column, precision); |
4428 | 54 | matrixColumns.push_back(column); |
4429 | 54 | } |
4430 | | |
4431 | | // make the matrix |
4432 | 18 | return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); |
4433 | 18 | } |
4434 | | |
4435 | | // Comments in header |
4436 | | Builder::If::If(Id cond, SelectionControlMask ctrl, Builder& gb) : |
4437 | 1.23k | builder(gb), |
4438 | 1.23k | condition(cond), |
4439 | 1.23k | control(ctrl), |
4440 | 1.23k | elseBlock(nullptr) |
4441 | 1.23k | { |
4442 | 1.23k | function = &builder.getBuildPoint()->getParent(); |
4443 | | |
4444 | | // make the blocks, but only put the then-block into the function, |
4445 | | // the else-block and merge-block will be added later, in order, after |
4446 | | // earlier code is emitted |
4447 | 1.23k | thenBlock = new Block(builder.getUniqueId(), *function); |
4448 | 1.23k | mergeBlock = new Block(builder.getUniqueId(), *function); |
4449 | | |
4450 | | // Save the current block, so that we can add in the flow control split when |
4451 | | // makeEndIf is called. |
4452 | 1.23k | headerBlock = builder.getBuildPoint(); |
4453 | 1.23k | builder.createSelectionMerge(mergeBlock, control); |
4454 | | |
4455 | 1.23k | function->addBlock(thenBlock); |
4456 | 1.23k | builder.setBuildPoint(thenBlock); |
4457 | 1.23k | } |
4458 | | |
4459 | | // Comments in header |
4460 | | void Builder::If::makeBeginElse() |
4461 | 8 | { |
4462 | | // Close out the "then" by having it jump to the mergeBlock |
4463 | 8 | builder.createBranch(true, mergeBlock); |
4464 | | |
4465 | | // Make the first else block and add it to the function |
4466 | 8 | elseBlock = new Block(builder.getUniqueId(), *function); |
4467 | 8 | function->addBlock(elseBlock); |
4468 | | |
4469 | | // Start building the else block |
4470 | 8 | builder.setBuildPoint(elseBlock); |
4471 | 8 | } |
4472 | | |
4473 | | // Comments in header |
4474 | | void Builder::If::makeEndIf() |
4475 | 1.23k | { |
4476 | | // jump to the merge block |
4477 | 1.23k | builder.createBranch(true, mergeBlock); |
4478 | | |
4479 | | // Go back to the headerBlock and make the flow control split |
4480 | 1.23k | builder.setBuildPoint(headerBlock); |
4481 | 1.23k | if (elseBlock) |
4482 | 8 | builder.createConditionalBranch(condition, thenBlock, elseBlock); |
4483 | 1.22k | else |
4484 | 1.22k | builder.createConditionalBranch(condition, thenBlock, mergeBlock); |
4485 | | |
4486 | | // add the merge block to the function |
4487 | 1.23k | function->addBlock(mergeBlock); |
4488 | 1.23k | builder.setBuildPoint(mergeBlock); |
4489 | 1.23k | } |
4490 | | |
4491 | | // Comments in header |
4492 | | void Builder::makeSwitch(Id selector, SelectionControlMask control, int numSegments, const std::vector<int>& caseValues, |
4493 | | const std::vector<int>& valueIndexToSegment, int defaultSegment, |
4494 | | std::vector<Block*>& segmentBlocks) |
4495 | 164 | { |
4496 | 164 | Function& function = buildPoint->getParent(); |
4497 | | |
4498 | | // make all the blocks |
4499 | 578 | for (int s = 0; s < numSegments; ++s) |
4500 | 414 | segmentBlocks.push_back(new Block(getUniqueId(), function)); |
4501 | | |
4502 | 164 | Block* mergeBlock = new Block(getUniqueId(), function); |
4503 | | |
4504 | | // make and insert the switch's selection-merge instruction |
4505 | 164 | createSelectionMerge(mergeBlock, control); |
4506 | | |
4507 | | // make the switch instruction |
4508 | 164 | Instruction* switchInst = new Instruction(NoResult, NoType, Op::OpSwitch); |
4509 | 164 | switchInst->reserveOperands((caseValues.size() * 2) + 2); |
4510 | 164 | switchInst->addIdOperand(selector); |
4511 | 164 | auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; |
4512 | 164 | switchInst->addIdOperand(defaultOrMerge->getId()); |
4513 | 164 | defaultOrMerge->addPredecessor(buildPoint); |
4514 | 576 | for (int i = 0; i < (int)caseValues.size(); ++i) { |
4515 | 412 | switchInst->addImmediateOperand(caseValues[i]); |
4516 | 412 | switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); |
4517 | 412 | segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); |
4518 | 412 | } |
4519 | 164 | addInstruction(std::unique_ptr<Instruction>(switchInst)); |
4520 | | |
4521 | | // push the merge block |
4522 | 164 | switchMerges.push(mergeBlock); |
4523 | 164 | } |
4524 | | |
4525 | | // Comments in header |
4526 | | void Builder::addSwitchBreak(bool implicit) |
4527 | 575 | { |
4528 | | // branch to the top of the merge block stack |
4529 | 575 | createBranch(implicit, switchMerges.top()); |
4530 | 575 | createAndSetNoPredecessorBlock("post-switch-break"); |
4531 | 575 | } |
4532 | | |
4533 | | // Comments in header |
4534 | | void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment) |
4535 | 414 | { |
4536 | 414 | int lastSegment = nextSegment - 1; |
4537 | 414 | if (lastSegment >= 0) { |
4538 | | // Close out previous segment by jumping, if necessary, to next segment |
4539 | 250 | if (! buildPoint->isTerminated()) |
4540 | 250 | createBranch(true, segmentBlock[nextSegment]); |
4541 | 250 | } |
4542 | 414 | Block* block = segmentBlock[nextSegment]; |
4543 | 414 | block->getParent().addBlock(block); |
4544 | 414 | setBuildPoint(block); |
4545 | 414 | } |
4546 | | |
4547 | | // Comments in header |
4548 | | void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/) |
4549 | 164 | { |
4550 | | // Close out previous segment by jumping, if necessary, to next segment |
4551 | 164 | if (! buildPoint->isTerminated()) |
4552 | 164 | addSwitchBreak(true); |
4553 | | |
4554 | 164 | switchMerges.top()->getParent().addBlock(switchMerges.top()); |
4555 | 164 | setBuildPoint(switchMerges.top()); |
4556 | | |
4557 | 164 | switchMerges.pop(); |
4558 | 164 | } |
4559 | | |
4560 | | Block& Builder::makeNewBlock() |
4561 | 1.71k | { |
4562 | 1.71k | Function& function = buildPoint->getParent(); |
4563 | 1.71k | auto block = new Block(getUniqueId(), function); |
4564 | 1.71k | function.addBlock(block); |
4565 | 1.71k | return *block; |
4566 | 1.71k | } |
4567 | | |
4568 | | Builder::LoopBlocks& Builder::makeNewLoop() |
4569 | 353 | { |
4570 | | // This verbosity is needed to simultaneously get the same behavior |
4571 | | // everywhere (id's in the same order), have a syntax that works |
4572 | | // across lots of versions of C++, have no warnings from pedantic |
4573 | | // compilation modes, and leave the rest of the code alone. |
4574 | 353 | Block& head = makeNewBlock(); |
4575 | 353 | Block& body = makeNewBlock(); |
4576 | 353 | Block& merge = makeNewBlock(); |
4577 | 353 | Block& continue_target = makeNewBlock(); |
4578 | 353 | LoopBlocks blocks(head, body, merge, continue_target); |
4579 | 353 | loops.push(blocks); |
4580 | 353 | return loops.top(); |
4581 | 353 | } |
4582 | | |
4583 | | void Builder::createLoopContinue() |
4584 | 0 | { |
4585 | 0 | createBranch(false, &loops.top().continue_target); |
4586 | | // Set up a block for dead code. |
4587 | 0 | createAndSetNoPredecessorBlock("post-loop-continue"); |
4588 | 0 | } |
4589 | | |
4590 | | void Builder::createLoopExit() |
4591 | 0 | { |
4592 | 0 | createBranch(false, &loops.top().merge); |
4593 | | // Set up a block for dead code. |
4594 | 0 | createAndSetNoPredecessorBlock("post-loop-break"); |
4595 | 0 | } |
4596 | | |
4597 | | void Builder::closeLoop() |
4598 | 353 | { |
4599 | 353 | loops.pop(); |
4600 | 353 | } |
4601 | | |
4602 | | void Builder::clearAccessChain() |
4603 | 113k | { |
4604 | 113k | accessChain.base = NoResult; |
4605 | 113k | accessChain.indexChain.clear(); |
4606 | 113k | accessChain.instr = NoResult; |
4607 | 113k | accessChain.swizzle.clear(); |
4608 | 113k | accessChain.component = NoResult; |
4609 | 113k | accessChain.preSwizzleBaseType = NoType; |
4610 | 113k | accessChain.isRValue = false; |
4611 | 113k | accessChain.coherentFlags.clear(); |
4612 | 113k | accessChain.alignment = 0; |
4613 | 113k | accessChain.descHeapInfo.descHeapBaseTy = NoResult; |
4614 | 113k | accessChain.descHeapInfo.descHeapStorageClass = StorageClass::Max; |
4615 | 113k | accessChain.descHeapInfo.descHeapInstId.clear(); |
4616 | 113k | accessChain.descHeapInfo.descHeapBaseArrayStride = NoResult; |
4617 | 113k | accessChain.descHeapInfo.structRemappedBase = NoResult; |
4618 | 113k | accessChain.descHeapInfo.structRsrcTyOffsetCount = 0; |
4619 | 113k | accessChain.descHeapInfo.structRsrcTyFirstArrIndex = 0; |
4620 | 113k | } |
4621 | | |
4622 | | // Comments in header |
4623 | | void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, |
4624 | | AccessChain::CoherentFlags coherentFlags, unsigned int alignment) |
4625 | 3.60k | { |
4626 | 3.60k | accessChain.coherentFlags |= coherentFlags; |
4627 | 3.60k | accessChain.alignment |= alignment; |
4628 | | |
4629 | | // swizzles can be stacked in GLSL, but simplified to a single |
4630 | | // one here; the base type doesn't change |
4631 | 3.60k | if (accessChain.preSwizzleBaseType == NoType) |
4632 | 3.60k | accessChain.preSwizzleBaseType = preSwizzleBaseType; |
4633 | | |
4634 | | // if needed, propagate the swizzle for the current access chain |
4635 | 3.60k | if (accessChain.swizzle.size() > 0) { |
4636 | 0 | std::vector<unsigned> oldSwizzle = accessChain.swizzle; |
4637 | 0 | accessChain.swizzle.resize(0); |
4638 | 0 | for (unsigned int i = 0; i < swizzle.size(); ++i) { |
4639 | 0 | assert(swizzle[i] < oldSwizzle.size()); |
4640 | 0 | accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); |
4641 | 0 | } |
4642 | 0 | } else |
4643 | 3.60k | accessChain.swizzle = swizzle; |
4644 | | |
4645 | | // determine if we need to track this swizzle anymore |
4646 | 3.60k | simplifyAccessChainSwizzle(); |
4647 | 3.60k | } |
4648 | | |
4649 | | // Comments in header |
4650 | | void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) |
4651 | 9.39k | { |
4652 | 9.39k | assert(accessChain.isRValue == false); |
4653 | | |
4654 | 9.39k | transferAccessChainSwizzle(true); |
4655 | | |
4656 | | // MeshShadingEXT outputs don't support loads, so split swizzled stores |
4657 | 9.39k | bool isMeshOutput = getStorageClass(accessChain.base) == StorageClass::Output && |
4658 | 478 | capabilities.find(spv::Capability::MeshShadingEXT) != capabilities.end(); |
4659 | | |
4660 | | // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores. |
4661 | 9.39k | if (accessChain.swizzle.size() > 0 && |
4662 | 324 | ((getNumTypeComponents(getResultingAccessChainType()) != accessChain.swizzle.size() && accessChain.component == NoResult) || isMeshOutput)) { |
4663 | 1.16k | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
4664 | 836 | accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i])); |
4665 | 836 | accessChain.instr = NoResult; |
4666 | | |
4667 | 836 | Id base = collapseAccessChain(); |
4668 | 836 | addDecoration(base, nonUniform); |
4669 | | |
4670 | 836 | accessChain.indexChain.pop_back(); |
4671 | 836 | accessChain.instr = NoResult; |
4672 | | |
4673 | | // dynamic component should be gone |
4674 | 836 | assert(accessChain.component == NoResult); |
4675 | | |
4676 | 836 | Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i); |
4677 | | |
4678 | | // take LSB of alignment |
4679 | 836 | alignment = alignment & ~(alignment & (alignment-1)); |
4680 | 836 | if (getStorageClass(base) == StorageClass::PhysicalStorageBufferEXT) { |
4681 | 0 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned); |
4682 | 0 | } |
4683 | | |
4684 | 836 | createStore(source, base, memoryAccess, scope, alignment); |
4685 | 836 | } |
4686 | 324 | } |
4687 | 9.07k | else { |
4688 | 9.07k | Id base = collapseAccessChain(); |
4689 | 9.07k | addDecoration(base, nonUniform); |
4690 | | |
4691 | 9.07k | Id source = rvalue; |
4692 | | |
4693 | | // dynamic component should be gone |
4694 | 9.07k | assert(accessChain.component == NoResult); |
4695 | | |
4696 | | // If swizzle still exists, it may be out-of-order, we must load the target vector, |
4697 | | // extract and insert elements to perform writeMask and/or swizzle. |
4698 | 9.07k | if (accessChain.swizzle.size() > 0) { |
4699 | 0 | Id tempBaseId = createLoad(base, spv::NoPrecision); |
4700 | 0 | source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); |
4701 | 0 | } |
4702 | | |
4703 | | // take LSB of alignment |
4704 | 9.07k | alignment = alignment & ~(alignment & (alignment-1)); |
4705 | 9.07k | if (getStorageClass(base) == StorageClass::PhysicalStorageBufferEXT) { |
4706 | 54 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned); |
4707 | 54 | } |
4708 | | |
4709 | 9.07k | createStore(source, base, memoryAccess, scope, alignment); |
4710 | 9.07k | } |
4711 | 9.39k | } |
4712 | | |
4713 | | // Comments in header |
4714 | | Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform, |
4715 | | Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, |
4716 | | spv::Scope scope, unsigned int alignment) |
4717 | 40.3k | { |
4718 | 40.3k | Id id; |
4719 | | |
4720 | 40.3k | if (accessChain.isRValue) { |
4721 | | // transfer access chain, but try to stay in registers |
4722 | 21.5k | transferAccessChainSwizzle(false); |
4723 | 21.5k | if (accessChain.indexChain.size() > 0) { |
4724 | 284 | Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; |
4725 | | |
4726 | | // if all the accesses are constants, we can use OpCompositeExtract |
4727 | 284 | std::vector<unsigned> indexes; |
4728 | 284 | bool constant = true; |
4729 | 550 | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
4730 | 284 | if (isConstantScalar(accessChain.indexChain[i])) |
4731 | 266 | indexes.push_back(getConstantScalar(accessChain.indexChain[i])); |
4732 | 18 | else { |
4733 | 18 | constant = false; |
4734 | 18 | break; |
4735 | 18 | } |
4736 | 284 | } |
4737 | | |
4738 | 284 | if (constant) { |
4739 | 266 | id = createCompositeExtract(accessChain.base, swizzleBase, indexes); |
4740 | 266 | setPrecision(id, precision); |
4741 | 266 | } else if (isVector(accessChain.base) || isCooperativeVector(accessChain.base)) { |
4742 | 0 | assert(accessChain.indexChain.size() == 1); |
4743 | 0 | id = createVectorExtractDynamic(accessChain.base, resultType, accessChain.indexChain[0]); |
4744 | 18 | } else { |
4745 | 18 | Id lValue = NoResult; |
4746 | 18 | if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) { |
4747 | | // make a new function variable for this r-value, using an initializer, |
4748 | | // and mark it as NonWritable so that downstream it can be detected as a lookup |
4749 | | // table |
4750 | 0 | lValue = createVariable(NoPrecision, StorageClass::Function, getTypeId(accessChain.base), |
4751 | 0 | "indexable", accessChain.base); |
4752 | 0 | addDecoration(lValue, Decoration::NonWritable); |
4753 | 18 | } else { |
4754 | 18 | lValue = createVariable(NoPrecision, StorageClass::Function, getTypeId(accessChain.base), |
4755 | 18 | "indexable"); |
4756 | | // store into it |
4757 | 18 | createStore(accessChain.base, lValue); |
4758 | 18 | } |
4759 | | // move base to the new variable |
4760 | 18 | accessChain.base = lValue; |
4761 | 18 | accessChain.isRValue = false; |
4762 | | |
4763 | | // load through the access chain |
4764 | 18 | id = createLoad(collapseAccessChain(), precision); |
4765 | 18 | } |
4766 | 284 | } else |
4767 | 21.2k | id = accessChain.base; // no precision, it was set when this was defined |
4768 | 21.5k | } else { |
4769 | 18.7k | transferAccessChainSwizzle(true); |
4770 | | |
4771 | | // take LSB of alignment |
4772 | 18.7k | alignment = alignment & ~(alignment & (alignment-1)); |
4773 | 18.7k | if (getStorageClass(accessChain.base) == StorageClass::PhysicalStorageBufferEXT) { |
4774 | 18 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessMask::Aligned); |
4775 | 18 | } |
4776 | | |
4777 | | // load through the access chain |
4778 | 18.7k | id = collapseAccessChain(); |
4779 | | // Apply nonuniform both to the access chain and the loaded value. |
4780 | | // Buffer accesses need the access chain decorated, and this is where |
4781 | | // loaded image types get decorated. TODO: This should maybe move to |
4782 | | // createImageTextureFunctionCall. |
4783 | 18.7k | addDecoration(id, l_nonUniform); |
4784 | 18.7k | id = createLoad(id, precision, memoryAccess, scope, alignment); |
4785 | 18.7k | addDecoration(id, r_nonUniform); |
4786 | 18.7k | } |
4787 | | |
4788 | | // Done, unless there are swizzles to do |
4789 | 40.3k | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
4790 | 39.1k | return id; |
4791 | | |
4792 | | // Do remaining swizzling |
4793 | | |
4794 | | // Do the basic swizzle |
4795 | 1.20k | if (accessChain.swizzle.size() > 0) { |
4796 | 1.20k | Id swizzledType = getScalarTypeId(getTypeId(id)); |
4797 | 1.20k | if (accessChain.swizzle.size() > 1) |
4798 | 1.20k | swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); |
4799 | 1.20k | id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); |
4800 | 1.20k | } |
4801 | | |
4802 | | // Do the dynamic component |
4803 | 1.20k | if (accessChain.component != NoResult) |
4804 | 0 | id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); |
4805 | | |
4806 | 1.20k | addDecoration(id, r_nonUniform); |
4807 | 1.20k | return id; |
4808 | 40.3k | } |
4809 | | |
4810 | | Id Builder::accessChainGetLValue() |
4811 | 3.93k | { |
4812 | 3.93k | assert(accessChain.isRValue == false); |
4813 | | |
4814 | 3.93k | transferAccessChainSwizzle(true); |
4815 | 3.93k | Id lvalue = collapseAccessChain(); |
4816 | | |
4817 | | // If swizzle exists, it is out-of-order or not full, we must load the target vector, |
4818 | | // extract and insert elements to perform writeMask and/or swizzle. This does not |
4819 | | // go with getting a direct l-value pointer. |
4820 | 3.93k | assert(accessChain.swizzle.size() == 0); |
4821 | 3.93k | assert(accessChain.component == NoResult); |
4822 | | |
4823 | 3.93k | return lvalue; |
4824 | 3.93k | } |
4825 | | |
4826 | | // comment in header |
4827 | | Id Builder::accessChainGetInferredType() |
4828 | 40.5k | { |
4829 | | // anything to operate on? |
4830 | | // for untyped pointer, it may be remapped to a descriptor heap. |
4831 | | // for descriptor heap, its base data type will be determined later, |
4832 | | // according to load/store results' types. |
4833 | 40.5k | if (accessChain.base == NoResult || isUntypedPointer(accessChain.base) || |
4834 | 40.5k | isStructureHeapMember(getTypeId(accessChain.base), accessChain.indexChain, 0) != 0) |
4835 | 0 | return NoType; |
4836 | 40.5k | Id type = getTypeId(accessChain.base); |
4837 | | // do initial dereference |
4838 | 40.5k | if (! accessChain.isRValue) |
4839 | 19.0k | type = getContainedTypeId(type); |
4840 | | |
4841 | | // dereference each index |
4842 | 45.0k | for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { |
4843 | 4.46k | if (isStructType(type)) |
4844 | 3.86k | type = getContainedTypeId(type, getConstantScalar(*it)); |
4845 | 591 | else |
4846 | 591 | type = getContainedTypeId(type); |
4847 | 4.46k | } |
4848 | | |
4849 | | // dereference swizzle |
4850 | 40.5k | if (accessChain.swizzle.size() == 1) |
4851 | 1.67k | type = getContainedTypeId(type); |
4852 | 38.8k | else if (accessChain.swizzle.size() > 1) |
4853 | 1.26k | type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); |
4854 | | |
4855 | | // dereference component selection |
4856 | 40.5k | if (accessChain.component) |
4857 | 0 | type = getContainedTypeId(type); |
4858 | | |
4859 | 40.5k | return type; |
4860 | 40.5k | } |
4861 | | |
4862 | | void Builder::dump(std::vector<unsigned int>& out) const |
4863 | 442 | { |
4864 | | // Header, before first instructions: |
4865 | 442 | out.push_back(MagicNumber); |
4866 | 442 | out.push_back(spvVersion); |
4867 | 442 | out.push_back(builderNumber); |
4868 | 442 | out.push_back(uniqueId + 1); |
4869 | 442 | out.push_back(0); |
4870 | | |
4871 | | // Capabilities |
4872 | 1.86k | for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { |
4873 | 1.42k | Instruction capInst(0, 0, Op::OpCapability); |
4874 | 1.42k | capInst.addImmediateOperand(*it); |
4875 | 1.42k | capInst.dump(out); |
4876 | 1.42k | } |
4877 | | |
4878 | 836 | for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { |
4879 | 394 | Instruction extInst(0, 0, Op::OpExtension); |
4880 | 394 | extInst.addStringOperand(it->c_str()); |
4881 | 394 | extInst.dump(out); |
4882 | 394 | } |
4883 | | |
4884 | 442 | dumpInstructions(out, imports); |
4885 | 442 | Instruction memInst(0, 0, Op::OpMemoryModel); |
4886 | 442 | memInst.addImmediateOperand(addressModel); |
4887 | 442 | memInst.addImmediateOperand(memoryModel); |
4888 | 442 | memInst.dump(out); |
4889 | | |
4890 | | // Instructions saved up while building: |
4891 | 442 | dumpInstructions(out, entryPoints); |
4892 | 442 | dumpInstructions(out, executionModes); |
4893 | | |
4894 | | // Debug instructions |
4895 | 442 | dumpInstructions(out, strings); |
4896 | 442 | dumpSourceInstructions(out); |
4897 | 2.49k | for (int e = 0; e < (int)sourceExtensions.size(); ++e) { |
4898 | 2.05k | Instruction sourceExtInst(0, 0, Op::OpSourceExtension); |
4899 | 2.05k | sourceExtInst.addStringOperand(sourceExtensions[e]); |
4900 | 2.05k | sourceExtInst.dump(out); |
4901 | 2.05k | } |
4902 | 442 | dumpInstructions(out, names); |
4903 | 442 | dumpModuleProcesses(out); |
4904 | | |
4905 | | // Annotation instructions |
4906 | 442 | dumpInstructions(out, decorations); |
4907 | | |
4908 | 442 | dumpInstructions(out, constantsTypesGlobals); |
4909 | 442 | dumpInstructions(out, externals); |
4910 | | |
4911 | | // The functions |
4912 | 442 | module.dump(out); |
4913 | 442 | } |
4914 | | |
4915 | | // |
4916 | | // Protected methods. |
4917 | | // |
4918 | | |
4919 | | // Turn the described access chain in 'accessChain' into an instruction(s) |
4920 | | // computing its address. This *cannot* include complex swizzles, which must |
4921 | | // be handled after this is called. |
4922 | | // |
4923 | | // Can generate code. |
4924 | | Id Builder::collapseAccessChain() |
4925 | 32.6k | { |
4926 | 32.6k | assert(accessChain.isRValue == false); |
4927 | | |
4928 | | // did we already emit an access chain for this? |
4929 | 32.6k | if (accessChain.instr != NoResult) |
4930 | 9 | return accessChain.instr; |
4931 | | |
4932 | | // If we have a dynamic component, we can still transfer |
4933 | | // that into a final operand to the access chain. We need to remap the |
4934 | | // dynamic component through the swizzle to get a new dynamic component to |
4935 | | // update. |
4936 | | // |
4937 | | // This was not done in transferAccessChainSwizzle() because it might |
4938 | | // generate code. |
4939 | 32.6k | remapDynamicSwizzle(); |
4940 | 32.6k | if (accessChain.component != NoResult) { |
4941 | | // transfer the dynamic component to the access chain |
4942 | 0 | accessChain.indexChain.push_back(accessChain.component); |
4943 | 0 | accessChain.component = NoResult; |
4944 | 0 | } |
4945 | | |
4946 | | // note that non-trivial swizzling is left pending |
4947 | | |
4948 | | // do we have an access chain? |
4949 | 32.6k | if (accessChain.indexChain.size() == 0) |
4950 | 23.4k | return accessChain.base; |
4951 | | |
4952 | | // emit the access chain |
4953 | 9.23k | StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); |
4954 | | // when descHeap info is set, use another access chain process. |
4955 | 9.23k | if ((isUntypedPointer(accessChain.base) || accessChain.descHeapInfo.structRsrcTyOffsetCount!= 0) && |
4956 | 0 | accessChain.descHeapInfo.descHeapStorageClass != StorageClass::Max) { |
4957 | 0 | accessChain.instr = createDescHeapAccessChain(); |
4958 | 9.23k | } else { |
4959 | 9.23k | accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); |
4960 | 9.23k | } |
4961 | | |
4962 | 9.23k | return accessChain.instr; |
4963 | 32.6k | } |
4964 | | |
4965 | | // For a dynamic component selection of a swizzle. |
4966 | | // |
4967 | | // Turn the swizzle and dynamic component into just a dynamic component. |
4968 | | // |
4969 | | // Generates code. |
4970 | | void Builder::remapDynamicSwizzle() |
4971 | 32.6k | { |
4972 | | // do we have a swizzle to remap a dynamic component through? |
4973 | 32.6k | if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { |
4974 | | // build a vector of the swizzle for the component to map into |
4975 | 0 | std::vector<Id> components; |
4976 | 0 | for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) |
4977 | 0 | components.push_back(makeUintConstant(accessChain.swizzle[c])); |
4978 | 0 | Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); |
4979 | 0 | Id map = makeCompositeConstant(mapType, components); |
4980 | | |
4981 | | // use it |
4982 | 0 | accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); |
4983 | 0 | accessChain.swizzle.clear(); |
4984 | 0 | } |
4985 | 32.6k | } |
4986 | | |
4987 | | // clear out swizzle if it is redundant, that is reselecting the same components |
4988 | | // that would be present without the swizzle. |
4989 | | void Builder::simplifyAccessChainSwizzle() |
4990 | 3.60k | { |
4991 | | // If the swizzle has fewer components than the vector, it is subsetting, and must stay |
4992 | | // to preserve that fact. |
4993 | 3.60k | if (getNumTypeComponents(accessChain.preSwizzleBaseType) > accessChain.swizzle.size()) |
4994 | 3.56k | return; |
4995 | | |
4996 | | // if components are out of order, it is a swizzle |
4997 | 136 | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
4998 | 102 | if (i != accessChain.swizzle[i]) |
4999 | 0 | return; |
5000 | 102 | } |
5001 | | |
5002 | | // otherwise, there is no need to track this swizzle |
5003 | 34 | accessChain.swizzle.clear(); |
5004 | 34 | if (accessChain.component == NoResult) |
5005 | 34 | accessChain.preSwizzleBaseType = NoType; |
5006 | 34 | } |
5007 | | |
5008 | | // To the extent any swizzling can become part of the chain |
5009 | | // of accesses instead of a post operation, make it so. |
5010 | | // If 'dynamic' is true, include transferring the dynamic component, |
5011 | | // otherwise, leave it pending. |
5012 | | // |
5013 | | // Does not generate code. just updates the access chain. |
5014 | | void Builder::transferAccessChainSwizzle(bool dynamic) |
5015 | 53.6k | { |
5016 | | // non existent? |
5017 | 53.6k | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
5018 | 49.7k | return; |
5019 | | |
5020 | | // too complex? |
5021 | | // (this requires either a swizzle, or generating code for a dynamic component) |
5022 | 3.87k | if (accessChain.swizzle.size() > 1) |
5023 | 1.53k | return; |
5024 | | |
5025 | | // single component, either in the swizzle and/or dynamic component |
5026 | 2.34k | if (accessChain.swizzle.size() == 1) { |
5027 | 2.34k | assert(accessChain.component == NoResult); |
5028 | | // handle static component selection |
5029 | 2.34k | accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); |
5030 | 2.34k | accessChain.swizzle.clear(); |
5031 | 2.34k | accessChain.preSwizzleBaseType = NoType; |
5032 | 2.34k | } else if (dynamic && accessChain.component != NoResult) { |
5033 | 0 | assert(accessChain.swizzle.size() == 0); |
5034 | | // handle dynamic component |
5035 | 0 | accessChain.indexChain.push_back(accessChain.component); |
5036 | 0 | accessChain.preSwizzleBaseType = NoType; |
5037 | 0 | accessChain.component = NoResult; |
5038 | 0 | } |
5039 | 2.34k | } |
5040 | | |
5041 | | // Utility method for creating a new block and setting the insert point to |
5042 | | // be in it. This is useful for flow-control operations that need a "dummy" |
5043 | | // block proceeding them (e.g. instructions after a discard, etc). |
5044 | | void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) |
5045 | 933 | { |
5046 | 933 | Block* block = new Block(getUniqueId(), buildPoint->getParent()); |
5047 | 933 | block->setUnreachable(); |
5048 | 933 | buildPoint->getParent().addBlock(block); |
5049 | 933 | setBuildPoint(block); |
5050 | | |
5051 | | // if (name) |
5052 | | // addName(block->getId(), name); |
5053 | 933 | } |
5054 | | |
5055 | | // Comments in header |
5056 | | void Builder::createBranch(bool implicit, Block* block) |
5057 | 3.44k | { |
5058 | 3.44k | Instruction* branch = new Instruction(Op::OpBranch); |
5059 | 3.44k | branch->addIdOperand(block->getId()); |
5060 | 3.44k | if (implicit) { |
5061 | 3.03k | addInstructionNoDebugInfo(std::unique_ptr<Instruction>(branch)); |
5062 | 3.03k | } |
5063 | 411 | else { |
5064 | 411 | addInstruction(std::unique_ptr<Instruction>(branch)); |
5065 | 411 | } |
5066 | 3.44k | block->addPredecessor(buildPoint); |
5067 | 3.44k | } |
5068 | | |
5069 | | // Create OpConstantSizeOfEXT |
5070 | | Id Builder::createConstantSizeOfEXT(Id typeId) |
5071 | 0 | { |
5072 | 0 | Instruction* inst = new Instruction(getUniqueId(), makeIntType(32), Op::OpConstantSizeOfEXT); |
5073 | 0 | inst->addIdOperand(typeId); |
5074 | 0 | constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); |
5075 | 0 | module.mapInstruction(inst); |
5076 | 0 | return inst->getResultId(); |
5077 | 0 | } |
5078 | | |
5079 | | void Builder::createSelectionMerge(Block* mergeBlock, SelectionControlMask control) |
5080 | 1.40k | { |
5081 | 1.40k | Instruction* merge = new Instruction(Op::OpSelectionMerge); |
5082 | 1.40k | merge->reserveOperands(2); |
5083 | 1.40k | merge->addIdOperand(mergeBlock->getId()); |
5084 | 1.40k | merge->addImmediateOperand(control); |
5085 | 1.40k | addInstruction(std::unique_ptr<Instruction>(merge)); |
5086 | 1.40k | } |
5087 | | |
5088 | | void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, LoopControlMask control, |
5089 | | const std::vector<unsigned int>& operands) |
5090 | 353 | { |
5091 | 353 | Instruction* merge = new Instruction(Op::OpLoopMerge); |
5092 | 353 | merge->reserveOperands(operands.size() + 3); |
5093 | 353 | merge->addIdOperand(mergeBlock->getId()); |
5094 | 353 | merge->addIdOperand(continueBlock->getId()); |
5095 | 353 | merge->addImmediateOperand(control); |
5096 | 389 | for (int op = 0; op < (int)operands.size(); ++op) |
5097 | 36 | merge->addImmediateOperand(operands[op]); |
5098 | 353 | addInstruction(std::unique_ptr<Instruction>(merge)); |
5099 | 353 | } |
5100 | | |
5101 | | void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) |
5102 | 1.57k | { |
5103 | 1.57k | Instruction* branch = new Instruction(Op::OpBranchConditional); |
5104 | 1.57k | branch->reserveOperands(3); |
5105 | 1.57k | branch->addIdOperand(condition); |
5106 | 1.57k | branch->addIdOperand(thenBlock->getId()); |
5107 | 1.57k | branch->addIdOperand(elseBlock->getId()); |
5108 | | |
5109 | | // A conditional branch is always attached to a condition expression |
5110 | 1.57k | addInstructionNoDebugInfo(std::unique_ptr<Instruction>(branch)); |
5111 | | |
5112 | 1.57k | thenBlock->addPredecessor(buildPoint); |
5113 | 1.57k | elseBlock->addPredecessor(buildPoint); |
5114 | 1.57k | } |
5115 | | |
5116 | | // OpSource |
5117 | | // [OpSourceContinued] |
5118 | | // ... |
5119 | | void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, |
5120 | | std::vector<unsigned int>& out) const |
5121 | 442 | { |
5122 | 442 | const int maxWordCount = 0xFFFF; |
5123 | 442 | const int opSourceWordCount = 4; |
5124 | 442 | const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; |
5125 | | |
5126 | 442 | if (sourceLang != SourceLanguage::Unknown) { |
5127 | | // OpSource Language Version File Source |
5128 | 442 | Instruction sourceInst(NoResult, NoType, Op::OpSource); |
5129 | 442 | sourceInst.reserveOperands(3); |
5130 | 442 | sourceInst.addImmediateOperand(sourceLang); |
5131 | 442 | sourceInst.addImmediateOperand(sourceVersion); |
5132 | | // File operand |
5133 | 442 | if (fileId != NoResult) { |
5134 | 0 | sourceInst.addIdOperand(fileId); |
5135 | | // Source operand |
5136 | 0 | if (text.size() > 0) { |
5137 | 0 | int nextByte = 0; |
5138 | 0 | std::string subString; |
5139 | 0 | while ((int)text.size() - nextByte > 0) { |
5140 | 0 | subString = text.substr(nextByte, nonNullBytesPerInstruction); |
5141 | 0 | if (nextByte == 0) { |
5142 | | // OpSource |
5143 | 0 | sourceInst.addStringOperand(subString.c_str()); |
5144 | 0 | sourceInst.dump(out); |
5145 | 0 | } else { |
5146 | | // OpSourcContinued |
5147 | 0 | Instruction sourceContinuedInst(Op::OpSourceContinued); |
5148 | 0 | sourceContinuedInst.addStringOperand(subString.c_str()); |
5149 | 0 | sourceContinuedInst.dump(out); |
5150 | 0 | } |
5151 | 0 | nextByte += nonNullBytesPerInstruction; |
5152 | 0 | } |
5153 | 0 | } else |
5154 | 0 | sourceInst.dump(out); |
5155 | 0 | } else |
5156 | 442 | sourceInst.dump(out); |
5157 | 442 | } |
5158 | 442 | } |
5159 | | |
5160 | | // Dump an OpSource[Continued] sequence for the source and every include file |
5161 | | void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const |
5162 | 442 | { |
5163 | 442 | if (emitNonSemanticShaderDebugInfo) return; |
5164 | 442 | dumpSourceInstructions(mainFileId, sourceText, out); |
5165 | 442 | for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) |
5166 | 0 | dumpSourceInstructions(iItr->first, *iItr->second, out); |
5167 | 442 | } |
5168 | | |
5169 | | template <class Range> void Builder::dumpInstructions(std::vector<unsigned int>& out, const Range& instructions) const |
5170 | 3.53k | { |
5171 | 41.2k | for (const auto& inst : instructions) { |
5172 | 41.2k | inst->dump(out); |
5173 | 41.2k | } |
5174 | 3.53k | } void spv::Builder::dumpInstructions<std::__1::vector<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> >, std::__1::allocator<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> > > > >(std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&, std::__1::vector<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> >, std::__1::allocator<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> > > > const&) const Line | Count | Source | 5170 | 3.09k | { | 5171 | 32.7k | for (const auto& inst : instructions) { | 5172 | 32.7k | inst->dump(out); | 5173 | 32.7k | } | 5174 | 3.09k | } |
void spv::Builder::dumpInstructions<std::__1::set<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> >, spv::Builder::DecorationInstructionLessThan, std::__1::allocator<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> > > > >(std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >&, std::__1::set<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> >, spv::Builder::DecorationInstructionLessThan, std::__1::allocator<std::__1::unique_ptr<spv::Instruction, std::__1::default_delete<spv::Instruction> > > > const&) const Line | Count | Source | 5170 | 442 | { | 5171 | 8.53k | for (const auto& inst : instructions) { | 5172 | 8.53k | inst->dump(out); | 5173 | 8.53k | } | 5174 | 442 | } |
|
5175 | | |
5176 | | void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const |
5177 | 442 | { |
5178 | 442 | for (int i = 0; i < (int)moduleProcesses.size(); ++i) { |
5179 | 0 | Instruction moduleProcessed(Op::OpModuleProcessed); |
5180 | 0 | moduleProcessed.addStringOperand(moduleProcesses[i]); |
5181 | 0 | moduleProcessed.dump(out); |
5182 | 0 | } |
5183 | 442 | } |
5184 | | |
5185 | | bool Builder::DecorationInstructionLessThan::operator()(const std::unique_ptr<Instruction>& lhs, |
5186 | | const std::unique_ptr<Instruction>& rhs) const |
5187 | 84.9k | { |
5188 | | // Order by the id to which the decoration applies first. This is more intuitive. |
5189 | 84.9k | assert(lhs->isIdOperand(0) && rhs->isIdOperand(0)); |
5190 | 84.9k | if (lhs->getIdOperand(0) != rhs->getIdOperand(0)) { |
5191 | 69.1k | return lhs->getIdOperand(0) < rhs->getIdOperand(0); |
5192 | 69.1k | } |
5193 | | |
5194 | 15.7k | if (lhs->getOpCode() != rhs->getOpCode()) |
5195 | 702 | return lhs->getOpCode() < rhs->getOpCode(); |
5196 | | |
5197 | | // Now compare the operands. |
5198 | 15.0k | int minSize = std::min(lhs->getNumOperands(), rhs->getNumOperands()); |
5199 | 16.6k | for (int i = 1; i < minSize; ++i) { |
5200 | 15.4k | if (lhs->isIdOperand(i) != rhs->isIdOperand(i)) { |
5201 | 0 | return lhs->isIdOperand(i) < rhs->isIdOperand(i); |
5202 | 0 | } |
5203 | | |
5204 | 15.4k | if (lhs->isIdOperand(i)) { |
5205 | 0 | if (lhs->getIdOperand(i) != rhs->getIdOperand(i)) { |
5206 | 0 | return lhs->getIdOperand(i) < rhs->getIdOperand(i); |
5207 | 0 | } |
5208 | 15.4k | } else { |
5209 | 15.4k | if (lhs->getImmediateOperand(i) != rhs->getImmediateOperand(i)) { |
5210 | 13.8k | return lhs->getImmediateOperand(i) < rhs->getImmediateOperand(i); |
5211 | 13.8k | } |
5212 | 15.4k | } |
5213 | 15.4k | } |
5214 | | |
5215 | 1.23k | if (lhs->getNumOperands() != rhs->getNumOperands()) |
5216 | 0 | return lhs->getNumOperands() < rhs->getNumOperands(); |
5217 | | |
5218 | | // In this case they are equal. |
5219 | 1.23k | return false; |
5220 | 1.23k | } |
5221 | | } // end spv namespace |