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 "hex_float.h" |
50 | |
51 | #ifndef _WIN32 |
52 | #include <cstdio> |
53 | #endif |
54 | |
55 | namespace spv { |
56 | |
57 | Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : |
58 | spvVersion(spvVersion), |
59 | sourceLang(SourceLanguageUnknown), |
60 | sourceVersion(0), |
61 | addressModel(AddressingModelLogical), |
62 | memoryModel(MemoryModelGLSL450), |
63 | builderNumber(magicNumber), |
64 | buildPoint(nullptr), |
65 | uniqueId(0), |
66 | entryPointFunction(nullptr), |
67 | generatingOpCodeForSpecConst(false), |
68 | logger(buildLogger) |
69 | { |
70 | clearAccessChain(); |
71 | } |
72 | |
73 | Builder::~Builder() |
74 | { |
75 | } |
76 | |
77 | Id Builder::import(const char* name) |
78 | { |
79 | Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); |
80 | import->addStringOperand(str: name); |
81 | module.mapInstruction(instruction: import); |
82 | |
83 | imports.push_back(x: std::unique_ptr<Instruction>(import)); |
84 | return import->getResultId(); |
85 | } |
86 | |
87 | // For creating new groupedTypes (will return old type if the requested one was already made). |
88 | Id Builder::makeVoidType() |
89 | { |
90 | Instruction* type; |
91 | if (groupedTypes[OpTypeVoid].size() == 0) { |
92 | Id typeId = getUniqueId(); |
93 | type = new Instruction(typeId, NoType, OpTypeVoid); |
94 | groupedTypes[OpTypeVoid].push_back(x: type); |
95 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
96 | module.mapInstruction(instruction: type); |
97 | // Core OpTypeVoid used for debug void type |
98 | if (emitNonSemanticShaderDebugInfo) |
99 | debugId[typeId] = typeId; |
100 | } else |
101 | type = groupedTypes[OpTypeVoid].back(); |
102 | |
103 | return type->getResultId(); |
104 | } |
105 | |
106 | Id Builder::makeBoolType() |
107 | { |
108 | Instruction* type; |
109 | if (groupedTypes[OpTypeBool].size() == 0) { |
110 | type = new Instruction(getUniqueId(), NoType, OpTypeBool); |
111 | groupedTypes[OpTypeBool].push_back(x: type); |
112 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
113 | module.mapInstruction(instruction: type); |
114 | |
115 | if (emitNonSemanticShaderDebugInfo) { |
116 | auto const debugResultId = makeBoolDebugType(size: 32); |
117 | debugId[type->getResultId()] = debugResultId; |
118 | } |
119 | |
120 | } else |
121 | type = groupedTypes[OpTypeBool].back(); |
122 | |
123 | |
124 | return type->getResultId(); |
125 | } |
126 | |
127 | Id Builder::makeSamplerType() |
128 | { |
129 | Instruction* type; |
130 | if (groupedTypes[OpTypeSampler].size() == 0) { |
131 | type = new Instruction(getUniqueId(), NoType, OpTypeSampler); |
132 | groupedTypes[OpTypeSampler].push_back(x: type); |
133 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
134 | module.mapInstruction(instruction: type); |
135 | } else |
136 | type = groupedTypes[OpTypeSampler].back(); |
137 | |
138 | if (emitNonSemanticShaderDebugInfo) |
139 | { |
140 | auto const debugResultId = makeCompositeDebugType(memberTypes: {}, name: "type.sampler", tag: NonSemanticShaderDebugInfo100Structure, isOpaqueType: true); |
141 | debugId[type->getResultId()] = debugResultId; |
142 | } |
143 | |
144 | return type->getResultId(); |
145 | } |
146 | |
147 | Id Builder::makePointer(StorageClass storageClass, Id pointee) |
148 | { |
149 | // try to find it |
150 | Instruction* type; |
151 | for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { |
152 | type = groupedTypes[OpTypePointer][t]; |
153 | if (type->getImmediateOperand(op: 0) == (unsigned)storageClass && |
154 | type->getIdOperand(op: 1) == pointee) |
155 | return type->getResultId(); |
156 | } |
157 | |
158 | // not found, make it |
159 | type = new Instruction(getUniqueId(), NoType, OpTypePointer); |
160 | type->reserveOperands(count: 2); |
161 | type->addImmediateOperand(immediate: storageClass); |
162 | type->addIdOperand(id: pointee); |
163 | groupedTypes[OpTypePointer].push_back(x: type); |
164 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
165 | module.mapInstruction(instruction: type); |
166 | |
167 | if (emitNonSemanticShaderDebugInfo) { |
168 | const Id debugResultId = makePointerDebugType(storageClass, baseType: pointee); |
169 | debugId[type->getResultId()] = debugResultId; |
170 | } |
171 | |
172 | return type->getResultId(); |
173 | } |
174 | |
175 | Id Builder::makeForwardPointer(StorageClass storageClass) |
176 | { |
177 | // Caching/uniquifying doesn't work here, because we don't know the |
178 | // pointee type and there can be multiple forward pointers of the same |
179 | // storage type. Somebody higher up in the stack must keep track. |
180 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); |
181 | type->addImmediateOperand(immediate: storageClass); |
182 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
183 | module.mapInstruction(instruction: type); |
184 | |
185 | return type->getResultId(); |
186 | } |
187 | |
188 | Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) |
189 | { |
190 | // try to find it |
191 | Instruction* type; |
192 | for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { |
193 | type = groupedTypes[OpTypePointer][t]; |
194 | if (type->getImmediateOperand(op: 0) == (unsigned)storageClass && |
195 | type->getIdOperand(op: 1) == pointee) |
196 | return type->getResultId(); |
197 | } |
198 | |
199 | type = new Instruction(forwardPointerType, NoType, OpTypePointer); |
200 | type->reserveOperands(count: 2); |
201 | type->addImmediateOperand(immediate: storageClass); |
202 | type->addIdOperand(id: pointee); |
203 | groupedTypes[OpTypePointer].push_back(x: type); |
204 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
205 | module.mapInstruction(instruction: type); |
206 | |
207 | return type->getResultId(); |
208 | } |
209 | |
210 | Id Builder::makeIntegerType(int width, bool hasSign) |
211 | { |
212 | // try to find it |
213 | Instruction* type; |
214 | for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { |
215 | type = groupedTypes[OpTypeInt][t]; |
216 | if (type->getImmediateOperand(op: 0) == (unsigned)width && |
217 | type->getImmediateOperand(op: 1) == (hasSign ? 1u : 0u)) |
218 | return type->getResultId(); |
219 | } |
220 | |
221 | // not found, make it |
222 | type = new Instruction(getUniqueId(), NoType, OpTypeInt); |
223 | type->reserveOperands(count: 2); |
224 | type->addImmediateOperand(immediate: width); |
225 | type->addImmediateOperand(immediate: hasSign ? 1 : 0); |
226 | groupedTypes[OpTypeInt].push_back(x: type); |
227 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
228 | module.mapInstruction(instruction: type); |
229 | |
230 | // deal with capabilities |
231 | switch (width) { |
232 | case 8: |
233 | case 16: |
234 | // these are currently handled by storage-type declarations and post processing |
235 | break; |
236 | case 64: |
237 | addCapability(cap: CapabilityInt64); |
238 | break; |
239 | default: |
240 | break; |
241 | } |
242 | |
243 | if (emitNonSemanticShaderDebugInfo) |
244 | { |
245 | auto const debugResultId = makeIntegerDebugType(width, hasSign); |
246 | debugId[type->getResultId()] = debugResultId; |
247 | } |
248 | |
249 | return type->getResultId(); |
250 | } |
251 | |
252 | Id Builder::makeFloatType(int width) |
253 | { |
254 | // try to find it |
255 | Instruction* type; |
256 | for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { |
257 | type = groupedTypes[OpTypeFloat][t]; |
258 | if (type->getImmediateOperand(op: 0) == (unsigned)width) |
259 | return type->getResultId(); |
260 | } |
261 | |
262 | // not found, make it |
263 | type = new Instruction(getUniqueId(), NoType, OpTypeFloat); |
264 | type->addImmediateOperand(immediate: width); |
265 | groupedTypes[OpTypeFloat].push_back(x: type); |
266 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
267 | module.mapInstruction(instruction: type); |
268 | |
269 | // deal with capabilities |
270 | switch (width) { |
271 | case 16: |
272 | // currently handled by storage-type declarations and post processing |
273 | break; |
274 | case 64: |
275 | addCapability(cap: CapabilityFloat64); |
276 | break; |
277 | default: |
278 | break; |
279 | } |
280 | |
281 | if (emitNonSemanticShaderDebugInfo) |
282 | { |
283 | auto const debugResultId = makeFloatDebugType(width); |
284 | debugId[type->getResultId()] = debugResultId; |
285 | } |
286 | |
287 | return type->getResultId(); |
288 | } |
289 | |
290 | // Make a struct without checking for duplication. |
291 | // See makeStructResultType() for non-decorated structs |
292 | // needed as the result of some instructions, which does |
293 | // check for duplicates. |
294 | Id Builder::makeStructType(const std::vector<Id>& members, const char* name, bool const compilerGenerated) |
295 | { |
296 | // Don't look for previous one, because in the general case, |
297 | // structs can be duplicated except for decorations. |
298 | |
299 | // not found, make it |
300 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); |
301 | for (int op = 0; op < (int)members.size(); ++op) |
302 | type->addIdOperand(id: members[op]); |
303 | groupedTypes[OpTypeStruct].push_back(x: type); |
304 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
305 | module.mapInstruction(instruction: type); |
306 | addName(type->getResultId(), name); |
307 | |
308 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
309 | { |
310 | auto const debugResultId = makeCompositeDebugType(memberTypes: members, name, tag: NonSemanticShaderDebugInfo100Structure); |
311 | debugId[type->getResultId()] = debugResultId; |
312 | } |
313 | |
314 | return type->getResultId(); |
315 | } |
316 | |
317 | // Make a struct for the simple results of several instructions, |
318 | // checking for duplication. |
319 | Id Builder::makeStructResultType(Id type0, Id type1) |
320 | { |
321 | // try to find it |
322 | Instruction* type; |
323 | for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { |
324 | type = groupedTypes[OpTypeStruct][t]; |
325 | if (type->getNumOperands() != 2) |
326 | continue; |
327 | if (type->getIdOperand(op: 0) != type0 || |
328 | type->getIdOperand(op: 1) != type1) |
329 | continue; |
330 | return type->getResultId(); |
331 | } |
332 | |
333 | // not found, make it |
334 | std::vector<spv::Id> members; |
335 | members.push_back(x: type0); |
336 | members.push_back(x: type1); |
337 | |
338 | return makeStructType(members, name: "ResType"); |
339 | } |
340 | |
341 | Id Builder::makeVectorType(Id component, int size) |
342 | { |
343 | // try to find it |
344 | Instruction* type; |
345 | for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { |
346 | type = groupedTypes[OpTypeVector][t]; |
347 | if (type->getIdOperand(op: 0) == component && |
348 | type->getImmediateOperand(op: 1) == (unsigned)size) |
349 | return type->getResultId(); |
350 | } |
351 | |
352 | // not found, make it |
353 | type = new Instruction(getUniqueId(), NoType, OpTypeVector); |
354 | type->reserveOperands(count: 2); |
355 | type->addIdOperand(id: component); |
356 | type->addImmediateOperand(immediate: size); |
357 | groupedTypes[OpTypeVector].push_back(x: type); |
358 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
359 | module.mapInstruction(instruction: type); |
360 | |
361 | if (emitNonSemanticShaderDebugInfo) |
362 | { |
363 | auto const debugResultId = makeVectorDebugType(baseType: component, componentCount: size); |
364 | debugId[type->getResultId()] = debugResultId; |
365 | } |
366 | |
367 | return type->getResultId(); |
368 | } |
369 | |
370 | Id Builder::makeMatrixType(Id component, int cols, int rows) |
371 | { |
372 | assert(cols <= maxMatrixSize && rows <= maxMatrixSize); |
373 | |
374 | Id column = makeVectorType(component, size: rows); |
375 | |
376 | // try to find it |
377 | Instruction* type; |
378 | for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { |
379 | type = groupedTypes[OpTypeMatrix][t]; |
380 | if (type->getIdOperand(op: 0) == column && |
381 | type->getImmediateOperand(op: 1) == (unsigned)cols) |
382 | return type->getResultId(); |
383 | } |
384 | |
385 | // not found, make it |
386 | type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); |
387 | type->reserveOperands(count: 2); |
388 | type->addIdOperand(id: column); |
389 | type->addImmediateOperand(immediate: cols); |
390 | groupedTypes[OpTypeMatrix].push_back(x: type); |
391 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
392 | module.mapInstruction(instruction: type); |
393 | |
394 | if (emitNonSemanticShaderDebugInfo) |
395 | { |
396 | auto const debugResultId = makeMatrixDebugType(vectorType: column, vectorCount: cols); |
397 | debugId[type->getResultId()] = debugResultId; |
398 | } |
399 | |
400 | return type->getResultId(); |
401 | } |
402 | |
403 | Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use) |
404 | { |
405 | // try to find it |
406 | Instruction* type; |
407 | for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixKHR].size(); ++t) { |
408 | type = groupedTypes[OpTypeCooperativeMatrixKHR][t]; |
409 | if (type->getIdOperand(op: 0) == component && |
410 | type->getIdOperand(op: 1) == scope && |
411 | type->getIdOperand(op: 2) == rows && |
412 | type->getIdOperand(op: 3) == cols && |
413 | type->getIdOperand(op: 4) == use) |
414 | return type->getResultId(); |
415 | } |
416 | |
417 | // not found, make it |
418 | type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixKHR); |
419 | type->reserveOperands(count: 5); |
420 | type->addIdOperand(id: component); |
421 | type->addIdOperand(id: scope); |
422 | type->addIdOperand(id: rows); |
423 | type->addIdOperand(id: cols); |
424 | type->addIdOperand(id: use); |
425 | groupedTypes[OpTypeCooperativeMatrixKHR].push_back(x: type); |
426 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
427 | module.mapInstruction(instruction: type); |
428 | |
429 | return type->getResultId(); |
430 | } |
431 | |
432 | Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols) |
433 | { |
434 | // try to find it |
435 | Instruction* type; |
436 | for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { |
437 | type = groupedTypes[OpTypeCooperativeMatrixNV][t]; |
438 | if (type->getIdOperand(op: 0) == component && type->getIdOperand(op: 1) == scope && type->getIdOperand(op: 2) == rows && |
439 | type->getIdOperand(op: 3) == cols) |
440 | return type->getResultId(); |
441 | } |
442 | |
443 | // not found, make it |
444 | type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); |
445 | type->reserveOperands(count: 4); |
446 | type->addIdOperand(id: component); |
447 | type->addIdOperand(id: scope); |
448 | type->addIdOperand(id: rows); |
449 | type->addIdOperand(id: cols); |
450 | groupedTypes[OpTypeCooperativeMatrixNV].push_back(x: type); |
451 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
452 | module.mapInstruction(instruction: type); |
453 | |
454 | return type->getResultId(); |
455 | } |
456 | |
457 | Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType) |
458 | { |
459 | Instruction* instr = module.getInstruction(id: otherType); |
460 | if (instr->getOpCode() == OpTypeCooperativeMatrixNV) { |
461 | return makeCooperativeMatrixTypeNV(component, scope: instr->getIdOperand(op: 1), rows: instr->getIdOperand(op: 2), cols: instr->getIdOperand(op: 3)); |
462 | } else { |
463 | assert(instr->getOpCode() == OpTypeCooperativeMatrixKHR); |
464 | return makeCooperativeMatrixTypeKHR(component, scope: instr->getIdOperand(op: 1), rows: instr->getIdOperand(op: 2), cols: instr->getIdOperand(op: 3), use: instr->getIdOperand(op: 4)); |
465 | } |
466 | } |
467 | |
468 | Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands) |
469 | { |
470 | // try to find it |
471 | Instruction* type; |
472 | for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) { |
473 | type = groupedTypes[opcode][t]; |
474 | if (static_cast<size_t>(type->getNumOperands()) != operands.size()) |
475 | continue; // Number mismatch, find next |
476 | |
477 | bool match = true; |
478 | for (int op = 0; match && op < (int)operands.size(); ++op) { |
479 | match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word; |
480 | } |
481 | if (match) |
482 | return type->getResultId(); |
483 | } |
484 | |
485 | // not found, make it |
486 | type = new Instruction(getUniqueId(), NoType, opcode); |
487 | type->reserveOperands(count: operands.size()); |
488 | for (size_t op = 0; op < operands.size(); ++op) { |
489 | if (operands[op].isId) |
490 | type->addIdOperand(id: operands[op].word); |
491 | else |
492 | type->addImmediateOperand(immediate: operands[op].word); |
493 | } |
494 | groupedTypes[opcode].push_back(x: type); |
495 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
496 | module.mapInstruction(instruction: type); |
497 | |
498 | return type->getResultId(); |
499 | } |
500 | |
501 | // TODO: performance: track arrays per stride |
502 | // If a stride is supplied (non-zero) make an array. |
503 | // If no stride (0), reuse previous array types. |
504 | // 'size' is an Id of a constant or specialization constant of the array size |
505 | Id Builder::makeArrayType(Id element, Id sizeId, int stride) |
506 | { |
507 | Instruction* type; |
508 | if (stride == 0) { |
509 | // try to find existing type |
510 | for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { |
511 | type = groupedTypes[OpTypeArray][t]; |
512 | if (type->getIdOperand(op: 0) == element && |
513 | type->getIdOperand(op: 1) == sizeId) |
514 | return type->getResultId(); |
515 | } |
516 | } |
517 | |
518 | // not found, make it |
519 | type = new Instruction(getUniqueId(), NoType, OpTypeArray); |
520 | type->reserveOperands(count: 2); |
521 | type->addIdOperand(id: element); |
522 | type->addIdOperand(id: sizeId); |
523 | groupedTypes[OpTypeArray].push_back(x: type); |
524 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
525 | module.mapInstruction(instruction: type); |
526 | |
527 | if (emitNonSemanticShaderDebugInfo) |
528 | { |
529 | auto const debugResultId = makeArrayDebugType(baseType: element, componentCount: sizeId); |
530 | debugId[type->getResultId()] = debugResultId; |
531 | } |
532 | |
533 | return type->getResultId(); |
534 | } |
535 | |
536 | Id Builder::makeRuntimeArray(Id element) |
537 | { |
538 | Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); |
539 | type->addIdOperand(id: element); |
540 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
541 | module.mapInstruction(instruction: type); |
542 | |
543 | if (emitNonSemanticShaderDebugInfo) |
544 | { |
545 | auto const debugResultId = makeArrayDebugType(baseType: element, componentCount: makeUintConstant(u: 0)); |
546 | debugId[type->getResultId()] = debugResultId; |
547 | } |
548 | |
549 | return type->getResultId(); |
550 | } |
551 | |
552 | Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
553 | { |
554 | // try to find it |
555 | Instruction* type; |
556 | for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { |
557 | type = groupedTypes[OpTypeFunction][t]; |
558 | if (type->getIdOperand(op: 0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) |
559 | continue; |
560 | bool mismatch = false; |
561 | for (int p = 0; p < (int)paramTypes.size(); ++p) { |
562 | if (paramTypes[p] != type->getIdOperand(op: p + 1)) { |
563 | mismatch = true; |
564 | break; |
565 | } |
566 | } |
567 | if (! mismatch) |
568 | { |
569 | // If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void) |
570 | // function type is created for the wrapper function. However, nonsemantic shader debug information is disabled |
571 | // while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create |
572 | // the associated debug function type if it hasn't been created yet. |
573 | if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) { |
574 | assert(sourceLang == spv::SourceLanguageHLSL); |
575 | assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0); |
576 | |
577 | Id debugTypeId = makeDebugFunctionType(returnType, paramTypes: {}); |
578 | debugId[type->getResultId()] = debugTypeId; |
579 | } |
580 | return type->getResultId(); |
581 | } |
582 | } |
583 | |
584 | // not found, make it |
585 | Id typeId = getUniqueId(); |
586 | type = new Instruction(typeId, NoType, OpTypeFunction); |
587 | type->reserveOperands(count: paramTypes.size() + 1); |
588 | type->addIdOperand(id: returnType); |
589 | for (int p = 0; p < (int)paramTypes.size(); ++p) |
590 | type->addIdOperand(id: paramTypes[p]); |
591 | groupedTypes[OpTypeFunction].push_back(x: type); |
592 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
593 | module.mapInstruction(instruction: type); |
594 | |
595 | // make debug type and map it |
596 | if (emitNonSemanticShaderDebugInfo) { |
597 | Id debugTypeId = makeDebugFunctionType(returnType, paramTypes); |
598 | debugId[typeId] = debugTypeId; |
599 | } |
600 | |
601 | return type->getResultId(); |
602 | } |
603 | |
604 | Id Builder::makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes) |
605 | { |
606 | assert(debugId[returnType] != 0); |
607 | |
608 | Id typeId = getUniqueId(); |
609 | auto type = new Instruction(typeId, makeVoidType(), OpExtInst); |
610 | type->reserveOperands(count: paramTypes.size() + 4); |
611 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
612 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeFunction); |
613 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsPublic)); |
614 | type->addIdOperand(id: debugId[returnType]); |
615 | for (auto const paramType : paramTypes) { |
616 | if (isPointerType(typeId: paramType) || isArrayType(typeId: paramType)) { |
617 | type->addIdOperand(id: debugId[getContainedTypeId(typeId: paramType)]); |
618 | } |
619 | else { |
620 | type->addIdOperand(id: debugId[paramType]); |
621 | } |
622 | } |
623 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
624 | module.mapInstruction(instruction: type); |
625 | return typeId; |
626 | } |
627 | |
628 | Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, |
629 | ImageFormat format) |
630 | { |
631 | assert(sampled == 1 || sampled == 2); |
632 | |
633 | // try to find it |
634 | Instruction* type; |
635 | for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { |
636 | type = groupedTypes[OpTypeImage][t]; |
637 | if (type->getIdOperand(op: 0) == sampledType && |
638 | type->getImmediateOperand(op: 1) == (unsigned int)dim && |
639 | type->getImmediateOperand(op: 2) == ( depth ? 1u : 0u) && |
640 | type->getImmediateOperand(op: 3) == (arrayed ? 1u : 0u) && |
641 | type->getImmediateOperand(op: 4) == ( ms ? 1u : 0u) && |
642 | type->getImmediateOperand(op: 5) == sampled && |
643 | type->getImmediateOperand(op: 6) == (unsigned int)format) |
644 | return type->getResultId(); |
645 | } |
646 | |
647 | // not found, make it |
648 | type = new Instruction(getUniqueId(), NoType, OpTypeImage); |
649 | type->reserveOperands(count: 7); |
650 | type->addIdOperand(id: sampledType); |
651 | type->addImmediateOperand( immediate: dim); |
652 | type->addImmediateOperand( immediate: depth ? 1 : 0); |
653 | type->addImmediateOperand(immediate: arrayed ? 1 : 0); |
654 | type->addImmediateOperand( immediate: ms ? 1 : 0); |
655 | type->addImmediateOperand(immediate: sampled); |
656 | type->addImmediateOperand(immediate: (unsigned int)format); |
657 | |
658 | groupedTypes[OpTypeImage].push_back(x: type); |
659 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
660 | module.mapInstruction(instruction: type); |
661 | |
662 | // deal with capabilities |
663 | switch (dim) { |
664 | case DimBuffer: |
665 | if (sampled == 1) |
666 | addCapability(cap: CapabilitySampledBuffer); |
667 | else |
668 | addCapability(cap: CapabilityImageBuffer); |
669 | break; |
670 | case Dim1D: |
671 | if (sampled == 1) |
672 | addCapability(cap: CapabilitySampled1D); |
673 | else |
674 | addCapability(cap: CapabilityImage1D); |
675 | break; |
676 | case DimCube: |
677 | if (arrayed) { |
678 | if (sampled == 1) |
679 | addCapability(cap: CapabilitySampledCubeArray); |
680 | else |
681 | addCapability(cap: CapabilityImageCubeArray); |
682 | } |
683 | break; |
684 | case DimRect: |
685 | if (sampled == 1) |
686 | addCapability(cap: CapabilitySampledRect); |
687 | else |
688 | addCapability(cap: CapabilityImageRect); |
689 | break; |
690 | case DimSubpassData: |
691 | addCapability(cap: CapabilityInputAttachment); |
692 | break; |
693 | default: |
694 | break; |
695 | } |
696 | |
697 | if (ms) { |
698 | if (sampled == 2) { |
699 | // Images used with subpass data are not storage |
700 | // images, so don't require the capability for them. |
701 | if (dim != Dim::DimSubpassData) |
702 | addCapability(cap: CapabilityStorageImageMultisample); |
703 | if (arrayed) |
704 | addCapability(cap: CapabilityImageMSArray); |
705 | } |
706 | } |
707 | |
708 | if (emitNonSemanticShaderDebugInfo) |
709 | { |
710 | auto TypeName = [&dim]() -> char const* { |
711 | switch (dim) { |
712 | case Dim1D: return "type.1d.image"; |
713 | case Dim2D: return "type.2d.image"; |
714 | case Dim3D: return "type.3d.image"; |
715 | case DimCube: return "type.cube.image"; |
716 | default: return "type.image"; |
717 | } |
718 | }; |
719 | |
720 | auto const debugResultId = makeCompositeDebugType(memberTypes: {}, name: TypeName(), tag: NonSemanticShaderDebugInfo100Class, isOpaqueType: true); |
721 | debugId[type->getResultId()] = debugResultId; |
722 | } |
723 | |
724 | return type->getResultId(); |
725 | } |
726 | |
727 | Id Builder::makeSampledImageType(Id imageType) |
728 | { |
729 | // try to find it |
730 | Instruction* type; |
731 | for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { |
732 | type = groupedTypes[OpTypeSampledImage][t]; |
733 | if (type->getIdOperand(op: 0) == imageType) |
734 | return type->getResultId(); |
735 | } |
736 | |
737 | // not found, make it |
738 | type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); |
739 | type->addIdOperand(id: imageType); |
740 | |
741 | groupedTypes[OpTypeSampledImage].push_back(x: type); |
742 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
743 | module.mapInstruction(instruction: type); |
744 | |
745 | if (emitNonSemanticShaderDebugInfo) |
746 | { |
747 | auto const debugResultId = makeCompositeDebugType(memberTypes: {}, name: "type.sampled.image", tag: NonSemanticShaderDebugInfo100Class, isOpaqueType: true); |
748 | debugId[type->getResultId()] = debugResultId; |
749 | } |
750 | |
751 | return type->getResultId(); |
752 | } |
753 | |
754 | Id Builder::makeDebugInfoNone() |
755 | { |
756 | if (debugInfoNone != 0) |
757 | return debugInfoNone; |
758 | |
759 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
760 | inst->reserveOperands(count: 2); |
761 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
762 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugInfoNone); |
763 | |
764 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst)); |
765 | module.mapInstruction(instruction: inst); |
766 | |
767 | debugInfoNone = inst->getResultId(); |
768 | |
769 | return debugInfoNone; |
770 | } |
771 | |
772 | Id Builder::makeBoolDebugType(int const size) |
773 | { |
774 | // try to find it |
775 | Instruction* type; |
776 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
777 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
778 | if (type->getIdOperand(op: 0) == getStringId(str: "bool") && |
779 | type->getIdOperand(op: 1) == static_cast<unsigned int>(size) && |
780 | type->getIdOperand(op: 2) == NonSemanticShaderDebugInfo100Boolean) |
781 | return type->getResultId(); |
782 | } |
783 | |
784 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
785 | type->reserveOperands(count: 6); |
786 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
787 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeBasic); |
788 | |
789 | type->addIdOperand(id: getStringId(str: "bool")); // name id |
790 | type->addIdOperand(id: makeUintConstant(u: size)); // size id |
791 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100Boolean)); // encoding id |
792 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100None)); // flags id |
793 | |
794 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(x: type); |
795 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
796 | module.mapInstruction(instruction: type); |
797 | |
798 | return type->getResultId(); |
799 | } |
800 | |
801 | Id Builder::makeIntegerDebugType(int const width, bool const hasSign) |
802 | { |
803 | const char* typeName = nullptr; |
804 | switch (width) { |
805 | case 8: typeName = hasSign ? "int8_t": "uint8_t"; break; |
806 | case 16: typeName = hasSign ? "int16_t": "uint16_t"; break; |
807 | case 64: typeName = hasSign ? "int64_t": "uint64_t"; break; |
808 | default: typeName = hasSign ? "int": "uint"; |
809 | } |
810 | auto nameId = getStringId(str: typeName); |
811 | // try to find it |
812 | Instruction* type; |
813 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
814 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
815 | if (type->getIdOperand(op: 0) == nameId && |
816 | type->getIdOperand(op: 1) == static_cast<unsigned int>(width) && |
817 | type->getIdOperand(op: 2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned)) |
818 | return type->getResultId(); |
819 | } |
820 | |
821 | // not found, make it |
822 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
823 | type->reserveOperands(count: 6); |
824 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
825 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeBasic); |
826 | type->addIdOperand(id: nameId); // name id |
827 | type->addIdOperand(id: makeUintConstant(u: width)); // size id |
828 | if(hasSign == true) { |
829 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100Signed)); // encoding id |
830 | } else { |
831 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100Unsigned)); // encoding id |
832 | } |
833 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100None)); // flags id |
834 | |
835 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(x: type); |
836 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
837 | module.mapInstruction(instruction: type); |
838 | |
839 | return type->getResultId(); |
840 | } |
841 | |
842 | Id Builder::makeFloatDebugType(int const width) |
843 | { |
844 | const char* typeName = nullptr; |
845 | switch (width) { |
846 | case 16: typeName = "float16_t"; break; |
847 | case 64: typeName = "double"; break; |
848 | default: typeName = "float"; break; |
849 | } |
850 | auto nameId = getStringId(str: typeName); |
851 | // try to find it |
852 | Instruction* type; |
853 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { |
854 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; |
855 | if (type->getIdOperand(op: 0) == nameId && |
856 | type->getIdOperand(op: 1) == static_cast<unsigned int>(width) && |
857 | type->getIdOperand(op: 2) == NonSemanticShaderDebugInfo100Float) |
858 | return type->getResultId(); |
859 | } |
860 | |
861 | // not found, make it |
862 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
863 | type->reserveOperands(count: 6); |
864 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
865 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeBasic); |
866 | type->addIdOperand(id: nameId); // name id |
867 | type->addIdOperand(id: makeUintConstant(u: width)); // size id |
868 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100Float)); // encoding id |
869 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100None)); // flags id |
870 | |
871 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(x: type); |
872 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
873 | module.mapInstruction(instruction: type); |
874 | |
875 | return type->getResultId(); |
876 | } |
877 | |
878 | Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType) |
879 | { |
880 | assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray || |
881 | sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector); |
882 | |
883 | // try to find it |
884 | Instruction* type; |
885 | for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) { |
886 | type = groupedDebugTypes[sequenceType][t]; |
887 | if (type->getIdOperand(op: 0) == baseType && |
888 | type->getIdOperand(op: 1) == makeUintConstant(u: componentCount)) |
889 | return type->getResultId(); |
890 | } |
891 | |
892 | // not found, make it |
893 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
894 | type->reserveOperands(count: 4); |
895 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
896 | type->addImmediateOperand(immediate: sequenceType); |
897 | type->addIdOperand(id: debugId[baseType]); // base type |
898 | type->addIdOperand(id: componentCount); // component count |
899 | |
900 | groupedDebugTypes[sequenceType].push_back(x: type); |
901 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
902 | module.mapInstruction(instruction: type); |
903 | |
904 | return type->getResultId(); |
905 | } |
906 | |
907 | Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount) |
908 | { |
909 | return makeSequentialDebugType(baseType, componentCount, sequenceType: NonSemanticShaderDebugInfo100DebugTypeArray); |
910 | } |
911 | |
912 | Id Builder::makeVectorDebugType(Id const baseType, int const componentCount) |
913 | { |
914 | return makeSequentialDebugType(baseType, componentCount: makeUintConstant(u: componentCount), sequenceType: NonSemanticShaderDebugInfo100DebugTypeVector); |
915 | } |
916 | |
917 | Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor) |
918 | { |
919 | // try to find it |
920 | Instruction* type; |
921 | for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) { |
922 | type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t]; |
923 | if (type->getIdOperand(op: 0) == vectorType && |
924 | type->getIdOperand(op: 1) == makeUintConstant(u: vectorCount)) |
925 | return type->getResultId(); |
926 | } |
927 | |
928 | // not found, make it |
929 | type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
930 | type->reserveOperands(count: 5); |
931 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
932 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeMatrix); |
933 | type->addIdOperand(id: debugId[vectorType]); // vector type id |
934 | type->addIdOperand(id: makeUintConstant(u: vectorCount)); // component count id |
935 | type->addIdOperand(id: makeBoolConstant(b: columnMajor)); // column-major id |
936 | |
937 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(x: type); |
938 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
939 | module.mapInstruction(instruction: type); |
940 | |
941 | return type->getResultId(); |
942 | } |
943 | |
944 | Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc) |
945 | { |
946 | assert(debugId[memberType] != 0); |
947 | |
948 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
949 | type->reserveOperands(count: 10); |
950 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
951 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeMember); |
952 | type->addIdOperand(id: getStringId(str: debugTypeLoc.name)); // name id |
953 | type->addIdOperand(id: debugId[memberType]); // type id |
954 | type->addIdOperand(id: makeDebugSource(fileName: currentFileId)); // source id |
955 | type->addIdOperand(id: makeUintConstant(u: debugTypeLoc.line)); // line id TODO: currentLine is always zero |
956 | type->addIdOperand(id: makeUintConstant(u: debugTypeLoc.column)); // TODO: column id |
957 | type->addIdOperand(id: makeUintConstant(u: 0)); // TODO: offset id |
958 | type->addIdOperand(id: makeUintConstant(u: 0)); // TODO: size id |
959 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id |
960 | |
961 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(x: type); |
962 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
963 | module.mapInstruction(instruction: type); |
964 | |
965 | return type->getResultId(); |
966 | } |
967 | |
968 | // Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be |
969 | // DebugInfoNone, and Name must start with @ to avoid clashes with user defined names. |
970 | Id Builder::makeCompositeDebugType(std::vector<Id> const& memberTypes, char const*const name, |
971 | NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType) |
972 | { |
973 | // Create the debug member types. |
974 | std::vector<Id> memberDebugTypes; |
975 | for(auto const memberType : memberTypes) { |
976 | assert(debugTypeLocs.find(memberType) != debugTypeLocs.end()); |
977 | |
978 | // There _should_ be debug types for all the member types but currently buffer references |
979 | // do not have member debug info generated. |
980 | if (debugId[memberType]) |
981 | memberDebugTypes.emplace_back(args: makeMemberDebugType(memberType, debugTypeLoc: debugTypeLocs[memberType])); |
982 | |
983 | // TODO: Need to rethink this method of passing location information. |
984 | // debugTypeLocs.erase(memberType); |
985 | } |
986 | |
987 | // Create The structure debug type. |
988 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
989 | type->reserveOperands(count: memberDebugTypes.size() + 11); |
990 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
991 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypeComposite); |
992 | type->addIdOperand(id: getStringId(str: name)); // name id |
993 | type->addIdOperand(id: makeUintConstant(u: tag)); // tag id |
994 | type->addIdOperand(id: makeDebugSource(fileName: currentFileId)); // source id |
995 | type->addIdOperand(id: makeUintConstant(u: currentLine)); // line id TODO: currentLine always zero? |
996 | type->addIdOperand(id: makeUintConstant(u: 0)); // TODO: column id |
997 | type->addIdOperand(id: makeDebugCompilationUnit()); // scope id |
998 | if(isOpaqueType == true) { |
999 | // Prepend '@' to opaque types. |
1000 | type->addIdOperand(id: getStringId(str: '@' + std::string(name))); // linkage name id |
1001 | type->addIdOperand(id: makeDebugInfoNone()); // size id |
1002 | } else { |
1003 | type->addIdOperand(id: getStringId(str: name)); // linkage name id |
1004 | type->addIdOperand(id: makeUintConstant(u: 0)); // TODO: size id |
1005 | } |
1006 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id |
1007 | assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty())); |
1008 | for(auto const memberDebugType : memberDebugTypes) { |
1009 | type->addIdOperand(id: memberDebugType); |
1010 | } |
1011 | |
1012 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(x: type); |
1013 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
1014 | module.mapInstruction(instruction: type); |
1015 | |
1016 | return type->getResultId(); |
1017 | } |
1018 | |
1019 | Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType) |
1020 | { |
1021 | const Id debugBaseType = debugId[baseType]; |
1022 | if (!debugBaseType) { |
1023 | return makeDebugInfoNone(); |
1024 | } |
1025 | const Id scID = makeUintConstant(u: storageClass); |
1026 | for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer]) { |
1027 | if (otherType->getIdOperand(op: 2) == debugBaseType && |
1028 | otherType->getIdOperand(op: 3) == scID) { |
1029 | return otherType->getResultId(); |
1030 | } |
1031 | } |
1032 | |
1033 | Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1034 | type->reserveOperands(count: 5); |
1035 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
1036 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugTypePointer); |
1037 | type->addIdOperand(id: debugBaseType); |
1038 | type->addIdOperand(id: scID); |
1039 | type->addIdOperand(id: makeUintConstant(u: 0)); |
1040 | |
1041 | groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(x: type); |
1042 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
1043 | module.mapInstruction(instruction: type); |
1044 | |
1045 | return type->getResultId(); |
1046 | } |
1047 | |
1048 | Id Builder::makeDebugSource(const Id fileName) { |
1049 | if (debugSourceId.find(x: fileName) != debugSourceId.end()) |
1050 | return debugSourceId[fileName]; |
1051 | spv::Id resultId = getUniqueId(); |
1052 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
1053 | sourceInst->reserveOperands(count: 3); |
1054 | sourceInst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1055 | sourceInst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugSource); |
1056 | sourceInst->addIdOperand(id: fileName); |
1057 | if (emitNonSemanticShaderDebugSource) { |
1058 | spv::Id sourceId = 0; |
1059 | if (fileName == mainFileId) { |
1060 | sourceId = getStringId(str: sourceText); |
1061 | } else { |
1062 | auto incItr = includeFiles.find(x: fileName); |
1063 | if (incItr != includeFiles.end()) { |
1064 | sourceId = getStringId(str: *incItr->second); |
1065 | } |
1066 | } |
1067 | |
1068 | // We omit the optional source text item if not available in glslang |
1069 | if (sourceId != 0) { |
1070 | sourceInst->addIdOperand(id: sourceId); |
1071 | } |
1072 | } |
1073 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(sourceInst)); |
1074 | module.mapInstruction(instruction: sourceInst); |
1075 | debugSourceId[fileName] = resultId; |
1076 | return resultId; |
1077 | } |
1078 | |
1079 | Id Builder::makeDebugCompilationUnit() { |
1080 | if (nonSemanticShaderCompilationUnitId != 0) |
1081 | return nonSemanticShaderCompilationUnitId; |
1082 | spv::Id resultId = getUniqueId(); |
1083 | Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
1084 | sourceInst->reserveOperands(count: 6); |
1085 | sourceInst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1086 | sourceInst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugCompilationUnit); |
1087 | sourceInst->addIdOperand(id: makeUintConstant(u: 1)); // TODO(greg-lunarg): Get rid of magic number |
1088 | sourceInst->addIdOperand(id: makeUintConstant(u: 4)); // TODO(greg-lunarg): Get rid of magic number |
1089 | sourceInst->addIdOperand(id: makeDebugSource(fileName: mainFileId)); |
1090 | sourceInst->addIdOperand(id: makeUintConstant(u: sourceLang)); |
1091 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(sourceInst)); |
1092 | module.mapInstruction(instruction: sourceInst); |
1093 | nonSemanticShaderCompilationUnitId = resultId; |
1094 | |
1095 | // We can reasonably assume that makeDebugCompilationUnit will be called before any of |
1096 | // debug-scope stack. Function scopes and lexical scopes will occur afterward. |
1097 | assert(currentDebugScopeId.empty()); |
1098 | currentDebugScopeId.push(x: nonSemanticShaderCompilationUnitId); |
1099 | |
1100 | return resultId; |
1101 | } |
1102 | |
1103 | Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable) |
1104 | { |
1105 | assert(type != 0); |
1106 | |
1107 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1108 | inst->reserveOperands(count: 11); |
1109 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1110 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugGlobalVariable); |
1111 | inst->addIdOperand(id: getStringId(str: name)); // name id |
1112 | inst->addIdOperand(id: type); // type id |
1113 | inst->addIdOperand(id: makeDebugSource(fileName: currentFileId)); // source id |
1114 | inst->addIdOperand(id: makeUintConstant(u: currentLine)); // line id TODO: currentLine always zero? |
1115 | inst->addIdOperand(id: makeUintConstant(u: 0)); // TODO: column id |
1116 | inst->addIdOperand(id: makeDebugCompilationUnit()); // scope id |
1117 | inst->addIdOperand(id: getStringId(str: name)); // linkage name id |
1118 | inst->addIdOperand(id: variable); // variable id |
1119 | inst->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id |
1120 | |
1121 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst)); |
1122 | module.mapInstruction(instruction: inst); |
1123 | |
1124 | return inst->getResultId(); |
1125 | } |
1126 | |
1127 | Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber) |
1128 | { |
1129 | assert(name != nullptr); |
1130 | assert(!currentDebugScopeId.empty()); |
1131 | |
1132 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1133 | inst->reserveOperands(count: 9); |
1134 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1135 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugLocalVariable); |
1136 | inst->addIdOperand(id: getStringId(str: name)); // name id |
1137 | inst->addIdOperand(id: type); // type id |
1138 | inst->addIdOperand(id: makeDebugSource(fileName: currentFileId)); // source id |
1139 | inst->addIdOperand(id: makeUintConstant(u: currentLine)); // line id |
1140 | inst->addIdOperand(id: makeUintConstant(u: 0)); // TODO: column id |
1141 | inst->addIdOperand(id: currentDebugScopeId.top()); // scope id |
1142 | inst->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id |
1143 | if(argNumber != 0) { |
1144 | inst->addIdOperand(id: makeUintConstant(u: argNumber)); |
1145 | } |
1146 | |
1147 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst)); |
1148 | module.mapInstruction(instruction: inst); |
1149 | |
1150 | return inst->getResultId(); |
1151 | } |
1152 | |
1153 | Id Builder::makeDebugExpression() |
1154 | { |
1155 | if (debugExpression != 0) |
1156 | return debugExpression; |
1157 | |
1158 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1159 | inst->reserveOperands(count: 2); |
1160 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1161 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugExpression); |
1162 | |
1163 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst)); |
1164 | module.mapInstruction(instruction: inst); |
1165 | |
1166 | debugExpression = inst->getResultId(); |
1167 | |
1168 | return debugExpression; |
1169 | } |
1170 | |
1171 | Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer) |
1172 | { |
1173 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1174 | inst->reserveOperands(count: 5); |
1175 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1176 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugDeclare); |
1177 | inst->addIdOperand(id: debugLocalVariable); // debug local variable id |
1178 | inst->addIdOperand(id: pointer); // pointer to local variable id |
1179 | inst->addIdOperand(id: makeDebugExpression()); // expression id |
1180 | addInstruction(inst: std::unique_ptr<Instruction>(inst)); |
1181 | |
1182 | return inst->getResultId(); |
1183 | } |
1184 | |
1185 | Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value) |
1186 | { |
1187 | Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); |
1188 | inst->reserveOperands(count: 5); |
1189 | inst->addIdOperand(id: nonSemanticShaderDebugInfo); |
1190 | inst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugValue); |
1191 | inst->addIdOperand(id: debugLocalVariable); // debug local variable id |
1192 | inst->addIdOperand(id: value); // value of local variable id |
1193 | inst->addIdOperand(id: makeDebugExpression()); // expression id |
1194 | addInstruction(inst: std::unique_ptr<Instruction>(inst)); |
1195 | |
1196 | return inst->getResultId(); |
1197 | } |
1198 | |
1199 | Id Builder::makeAccelerationStructureType() |
1200 | { |
1201 | Instruction *type; |
1202 | if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { |
1203 | type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); |
1204 | groupedTypes[OpTypeAccelerationStructureKHR].push_back(x: type); |
1205 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
1206 | module.mapInstruction(instruction: type); |
1207 | if (emitNonSemanticShaderDebugInfo) { |
1208 | spv::Id debugType = makeCompositeDebugType(memberTypes: {}, name: "accelerationStructure", tag: NonSemanticShaderDebugInfo100Structure, isOpaqueType: true); |
1209 | debugId[type->getResultId()] = debugType; |
1210 | } |
1211 | } else { |
1212 | type = groupedTypes[OpTypeAccelerationStructureKHR].back(); |
1213 | } |
1214 | |
1215 | return type->getResultId(); |
1216 | } |
1217 | |
1218 | Id Builder::makeRayQueryType() |
1219 | { |
1220 | Instruction *type; |
1221 | if (groupedTypes[OpTypeRayQueryKHR].size() == 0) { |
1222 | type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR); |
1223 | groupedTypes[OpTypeRayQueryKHR].push_back(x: type); |
1224 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
1225 | module.mapInstruction(instruction: type); |
1226 | if (emitNonSemanticShaderDebugInfo) { |
1227 | spv::Id debugType = makeCompositeDebugType(memberTypes: {}, name: "rayQuery", tag: NonSemanticShaderDebugInfo100Structure, isOpaqueType: true); |
1228 | debugId[type->getResultId()] = debugType; |
1229 | } |
1230 | } else { |
1231 | type = groupedTypes[OpTypeRayQueryKHR].back(); |
1232 | } |
1233 | |
1234 | return type->getResultId(); |
1235 | } |
1236 | |
1237 | Id Builder::makeHitObjectNVType() |
1238 | { |
1239 | Instruction *type; |
1240 | if (groupedTypes[OpTypeHitObjectNV].size() == 0) { |
1241 | type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV); |
1242 | groupedTypes[OpTypeHitObjectNV].push_back(x: type); |
1243 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
1244 | module.mapInstruction(instruction: type); |
1245 | } else { |
1246 | type = groupedTypes[OpTypeHitObjectNV].back(); |
1247 | } |
1248 | |
1249 | return type->getResultId(); |
1250 | } |
1251 | |
1252 | Id Builder::getDerefTypeId(Id resultId) const |
1253 | { |
1254 | Id typeId = getTypeId(resultId); |
1255 | assert(isPointerType(typeId)); |
1256 | |
1257 | return module.getInstruction(id: typeId)->getIdOperand(op: 1); |
1258 | } |
1259 | |
1260 | Op Builder::getMostBasicTypeClass(Id typeId) const |
1261 | { |
1262 | Instruction* instr = module.getInstruction(id: typeId); |
1263 | |
1264 | Op typeClass = instr->getOpCode(); |
1265 | switch (typeClass) |
1266 | { |
1267 | case OpTypeVector: |
1268 | case OpTypeMatrix: |
1269 | case OpTypeArray: |
1270 | case OpTypeRuntimeArray: |
1271 | return getMostBasicTypeClass(typeId: instr->getIdOperand(op: 0)); |
1272 | case OpTypePointer: |
1273 | return getMostBasicTypeClass(typeId: instr->getIdOperand(op: 1)); |
1274 | default: |
1275 | return typeClass; |
1276 | } |
1277 | } |
1278 | |
1279 | int Builder::getNumTypeConstituents(Id typeId) const |
1280 | { |
1281 | Instruction* instr = module.getInstruction(id: typeId); |
1282 | |
1283 | switch (instr->getOpCode()) |
1284 | { |
1285 | case OpTypeBool: |
1286 | case OpTypeInt: |
1287 | case OpTypeFloat: |
1288 | case OpTypePointer: |
1289 | return 1; |
1290 | case OpTypeVector: |
1291 | case OpTypeMatrix: |
1292 | return instr->getImmediateOperand(op: 1); |
1293 | case OpTypeArray: |
1294 | { |
1295 | Id lengthId = instr->getIdOperand(op: 1); |
1296 | return module.getInstruction(id: lengthId)->getImmediateOperand(op: 0); |
1297 | } |
1298 | case OpTypeStruct: |
1299 | return instr->getNumOperands(); |
1300 | case OpTypeCooperativeMatrixKHR: |
1301 | case OpTypeCooperativeMatrixNV: |
1302 | // has only one constituent when used with OpCompositeConstruct. |
1303 | return 1; |
1304 | default: |
1305 | assert(0); |
1306 | return 1; |
1307 | } |
1308 | } |
1309 | |
1310 | // Return the lowest-level type of scalar that an homogeneous composite is made out of. |
1311 | // Typically, this is just to find out if something is made out of ints or floats. |
1312 | // However, it includes returning a structure, if say, it is an array of structure. |
1313 | Id Builder::getScalarTypeId(Id typeId) const |
1314 | { |
1315 | Instruction* instr = module.getInstruction(id: typeId); |
1316 | |
1317 | Op typeClass = instr->getOpCode(); |
1318 | switch (typeClass) |
1319 | { |
1320 | case OpTypeVoid: |
1321 | case OpTypeBool: |
1322 | case OpTypeInt: |
1323 | case OpTypeFloat: |
1324 | case OpTypeStruct: |
1325 | return instr->getResultId(); |
1326 | case OpTypeVector: |
1327 | case OpTypeMatrix: |
1328 | case OpTypeArray: |
1329 | case OpTypeRuntimeArray: |
1330 | case OpTypePointer: |
1331 | return getScalarTypeId(typeId: getContainedTypeId(typeId)); |
1332 | default: |
1333 | assert(0); |
1334 | return NoResult; |
1335 | } |
1336 | } |
1337 | |
1338 | // Return the type of 'member' of a composite. |
1339 | Id Builder::getContainedTypeId(Id typeId, int member) const |
1340 | { |
1341 | Instruction* instr = module.getInstruction(id: typeId); |
1342 | |
1343 | Op typeClass = instr->getOpCode(); |
1344 | switch (typeClass) |
1345 | { |
1346 | case OpTypeVector: |
1347 | case OpTypeMatrix: |
1348 | case OpTypeArray: |
1349 | case OpTypeRuntimeArray: |
1350 | case OpTypeCooperativeMatrixKHR: |
1351 | case OpTypeCooperativeMatrixNV: |
1352 | return instr->getIdOperand(op: 0); |
1353 | case OpTypePointer: |
1354 | return instr->getIdOperand(op: 1); |
1355 | case OpTypeStruct: |
1356 | return instr->getIdOperand(op: member); |
1357 | default: |
1358 | assert(0); |
1359 | return NoResult; |
1360 | } |
1361 | } |
1362 | |
1363 | // Figure out the final resulting type of the access chain. |
1364 | Id Builder::getResultingAccessChainType() const |
1365 | { |
1366 | assert(accessChain.base != NoResult); |
1367 | Id typeId = getTypeId(resultId: accessChain.base); |
1368 | |
1369 | assert(isPointerType(typeId)); |
1370 | typeId = getContainedTypeId(typeId); |
1371 | |
1372 | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
1373 | if (isStructType(typeId)) { |
1374 | assert(isConstantScalar(accessChain.indexChain[i])); |
1375 | typeId = getContainedTypeId(typeId, member: getConstantScalar(resultId: accessChain.indexChain[i])); |
1376 | } else |
1377 | typeId = getContainedTypeId(typeId, member: accessChain.indexChain[i]); |
1378 | } |
1379 | |
1380 | return typeId; |
1381 | } |
1382 | |
1383 | // Return the immediately contained type of a given composite type. |
1384 | Id Builder::getContainedTypeId(Id typeId) const |
1385 | { |
1386 | return getContainedTypeId(typeId, member: 0); |
1387 | } |
1388 | |
1389 | // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' |
1390 | // of width 'width'. The 'width' is only consumed for int and float types. |
1391 | // Returns false otherwise. |
1392 | bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const |
1393 | { |
1394 | const Instruction& instr = *module.getInstruction(id: typeId); |
1395 | |
1396 | Op typeClass = instr.getOpCode(); |
1397 | switch (typeClass) |
1398 | { |
1399 | case OpTypeInt: |
1400 | case OpTypeFloat: |
1401 | return typeClass == typeOp && instr.getImmediateOperand(op: 0) == width; |
1402 | case OpTypeStruct: |
1403 | for (int m = 0; m < instr.getNumOperands(); ++m) { |
1404 | if (containsType(typeId: instr.getIdOperand(op: m), typeOp, width)) |
1405 | return true; |
1406 | } |
1407 | return false; |
1408 | case OpTypePointer: |
1409 | return false; |
1410 | case OpTypeVector: |
1411 | case OpTypeMatrix: |
1412 | case OpTypeArray: |
1413 | case OpTypeRuntimeArray: |
1414 | return containsType(typeId: getContainedTypeId(typeId), typeOp, width); |
1415 | default: |
1416 | return typeClass == typeOp; |
1417 | } |
1418 | } |
1419 | |
1420 | // return true if the type is a pointer to PhysicalStorageBufferEXT or an |
1421 | // contains such a pointer. These require restrict/aliased decorations. |
1422 | bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const |
1423 | { |
1424 | const Instruction& instr = *module.getInstruction(id: typeId); |
1425 | |
1426 | Op typeClass = instr.getOpCode(); |
1427 | switch (typeClass) |
1428 | { |
1429 | case OpTypePointer: |
1430 | return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; |
1431 | case OpTypeArray: |
1432 | return containsPhysicalStorageBufferOrArray(typeId: getContainedTypeId(typeId)); |
1433 | case OpTypeStruct: |
1434 | for (int m = 0; m < instr.getNumOperands(); ++m) { |
1435 | if (containsPhysicalStorageBufferOrArray(typeId: instr.getIdOperand(op: m))) |
1436 | return true; |
1437 | } |
1438 | return false; |
1439 | default: |
1440 | return false; |
1441 | } |
1442 | } |
1443 | |
1444 | // See if a scalar constant of this type has already been created, so it |
1445 | // can be reused rather than duplicated. (Required by the specification). |
1446 | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) |
1447 | { |
1448 | Instruction* constant; |
1449 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1450 | constant = groupedConstants[typeClass][i]; |
1451 | if (constant->getOpCode() == opcode && |
1452 | constant->getTypeId() == typeId && |
1453 | constant->getImmediateOperand(op: 0) == value) |
1454 | return constant->getResultId(); |
1455 | } |
1456 | |
1457 | return 0; |
1458 | } |
1459 | |
1460 | // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). |
1461 | Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) |
1462 | { |
1463 | Instruction* constant; |
1464 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1465 | constant = groupedConstants[typeClass][i]; |
1466 | if (constant->getOpCode() == opcode && |
1467 | constant->getTypeId() == typeId && |
1468 | constant->getImmediateOperand(op: 0) == v1 && |
1469 | constant->getImmediateOperand(op: 1) == v2) |
1470 | return constant->getResultId(); |
1471 | } |
1472 | |
1473 | return 0; |
1474 | } |
1475 | |
1476 | // Return true if consuming 'opcode' means consuming a constant. |
1477 | // "constant" here means after final transform to executable code, |
1478 | // the value consumed will be a constant, so includes specialization. |
1479 | bool Builder::isConstantOpCode(Op opcode) const |
1480 | { |
1481 | switch (opcode) { |
1482 | case OpUndef: |
1483 | case OpConstantTrue: |
1484 | case OpConstantFalse: |
1485 | case OpConstant: |
1486 | case OpConstantComposite: |
1487 | case OpConstantSampler: |
1488 | case OpConstantNull: |
1489 | case OpSpecConstantTrue: |
1490 | case OpSpecConstantFalse: |
1491 | case OpSpecConstant: |
1492 | case OpSpecConstantComposite: |
1493 | case OpSpecConstantOp: |
1494 | return true; |
1495 | default: |
1496 | return false; |
1497 | } |
1498 | } |
1499 | |
1500 | // Return true if consuming 'opcode' means consuming a specialization constant. |
1501 | bool Builder::isSpecConstantOpCode(Op opcode) const |
1502 | { |
1503 | switch (opcode) { |
1504 | case OpSpecConstantTrue: |
1505 | case OpSpecConstantFalse: |
1506 | case OpSpecConstant: |
1507 | case OpSpecConstantComposite: |
1508 | case OpSpecConstantOp: |
1509 | return true; |
1510 | default: |
1511 | return false; |
1512 | } |
1513 | } |
1514 | |
1515 | Id Builder::makeNullConstant(Id typeId) |
1516 | { |
1517 | Instruction* constant; |
1518 | |
1519 | // See if we already made it. |
1520 | Id existing = NoResult; |
1521 | for (int i = 0; i < (int)nullConstants.size(); ++i) { |
1522 | constant = nullConstants[i]; |
1523 | if (constant->getTypeId() == typeId) |
1524 | existing = constant->getResultId(); |
1525 | } |
1526 | |
1527 | if (existing != NoResult) |
1528 | return existing; |
1529 | |
1530 | // Make it |
1531 | Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull); |
1532 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1533 | nullConstants.push_back(x: c); |
1534 | module.mapInstruction(instruction: c); |
1535 | |
1536 | return c->getResultId(); |
1537 | } |
1538 | |
1539 | Id Builder::makeBoolConstant(bool b, bool specConstant) |
1540 | { |
1541 | Id typeId = makeBoolType(); |
1542 | Instruction* constant; |
1543 | Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); |
1544 | |
1545 | // See if we already made it. Applies only to regular constants, because specialization constants |
1546 | // must remain distinct for the purpose of applying a SpecId decoration. |
1547 | if (! specConstant) { |
1548 | Id existing = 0; |
1549 | for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { |
1550 | constant = groupedConstants[OpTypeBool][i]; |
1551 | if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) |
1552 | existing = constant->getResultId(); |
1553 | } |
1554 | |
1555 | if (existing) |
1556 | return existing; |
1557 | } |
1558 | |
1559 | // Make it |
1560 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1561 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1562 | groupedConstants[OpTypeBool].push_back(x: c); |
1563 | module.mapInstruction(instruction: c); |
1564 | |
1565 | return c->getResultId(); |
1566 | } |
1567 | |
1568 | Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) |
1569 | { |
1570 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1571 | |
1572 | // See if we already made it. Applies only to regular constants, because specialization constants |
1573 | // must remain distinct for the purpose of applying a SpecId decoration. |
1574 | if (! specConstant) { |
1575 | Id existing = findScalarConstant(typeClass: OpTypeInt, opcode, typeId, value); |
1576 | if (existing) |
1577 | return existing; |
1578 | } |
1579 | |
1580 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1581 | c->addImmediateOperand(immediate: value); |
1582 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1583 | groupedConstants[OpTypeInt].push_back(x: c); |
1584 | module.mapInstruction(instruction: c); |
1585 | |
1586 | return c->getResultId(); |
1587 | } |
1588 | |
1589 | Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) |
1590 | { |
1591 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1592 | |
1593 | unsigned op1 = value & 0xFFFFFFFF; |
1594 | unsigned op2 = value >> 32; |
1595 | |
1596 | // See if we already made it. Applies only to regular constants, because specialization constants |
1597 | // must remain distinct for the purpose of applying a SpecId decoration. |
1598 | if (! specConstant) { |
1599 | Id existing = findScalarConstant(typeClass: OpTypeInt, opcode, typeId, v1: op1, v2: op2); |
1600 | if (existing) |
1601 | return existing; |
1602 | } |
1603 | |
1604 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1605 | c->reserveOperands(count: 2); |
1606 | c->addImmediateOperand(immediate: op1); |
1607 | c->addImmediateOperand(immediate: op2); |
1608 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1609 | groupedConstants[OpTypeInt].push_back(x: c); |
1610 | module.mapInstruction(instruction: c); |
1611 | |
1612 | return c->getResultId(); |
1613 | } |
1614 | |
1615 | Id Builder::makeFloatConstant(float f, bool specConstant) |
1616 | { |
1617 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1618 | Id typeId = makeFloatType(width: 32); |
1619 | union { float fl; unsigned int ui; } u; |
1620 | u.fl = f; |
1621 | unsigned value = u.ui; |
1622 | |
1623 | // See if we already made it. Applies only to regular constants, because specialization constants |
1624 | // must remain distinct for the purpose of applying a SpecId decoration. |
1625 | if (! specConstant) { |
1626 | Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, value); |
1627 | if (existing) |
1628 | return existing; |
1629 | } |
1630 | |
1631 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1632 | c->addImmediateOperand(immediate: value); |
1633 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1634 | groupedConstants[OpTypeFloat].push_back(x: c); |
1635 | module.mapInstruction(instruction: c); |
1636 | |
1637 | return c->getResultId(); |
1638 | } |
1639 | |
1640 | Id Builder::makeDoubleConstant(double d, bool specConstant) |
1641 | { |
1642 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1643 | Id typeId = makeFloatType(width: 64); |
1644 | union { double db; unsigned long long ull; } u; |
1645 | u.db = d; |
1646 | unsigned long long value = u.ull; |
1647 | unsigned op1 = value & 0xFFFFFFFF; |
1648 | unsigned op2 = value >> 32; |
1649 | |
1650 | // See if we already made it. Applies only to regular constants, because specialization constants |
1651 | // must remain distinct for the purpose of applying a SpecId decoration. |
1652 | if (! specConstant) { |
1653 | Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, v1: op1, v2: op2); |
1654 | if (existing) |
1655 | return existing; |
1656 | } |
1657 | |
1658 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1659 | c->reserveOperands(count: 2); |
1660 | c->addImmediateOperand(immediate: op1); |
1661 | c->addImmediateOperand(immediate: op2); |
1662 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1663 | groupedConstants[OpTypeFloat].push_back(x: c); |
1664 | module.mapInstruction(instruction: c); |
1665 | |
1666 | return c->getResultId(); |
1667 | } |
1668 | |
1669 | Id Builder::makeFloat16Constant(float f16, bool specConstant) |
1670 | { |
1671 | Op opcode = specConstant ? OpSpecConstant : OpConstant; |
1672 | Id typeId = makeFloatType(width: 16); |
1673 | |
1674 | spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16); |
1675 | spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0); |
1676 | fVal.castTo(other&: f16Val, round_dir: spvutils::kRoundToZero); |
1677 | |
1678 | unsigned value = f16Val.value().getAsFloat().get_value(); |
1679 | |
1680 | // See if we already made it. Applies only to regular constants, because specialization constants |
1681 | // must remain distinct for the purpose of applying a SpecId decoration. |
1682 | if (!specConstant) { |
1683 | Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, value); |
1684 | if (existing) |
1685 | return existing; |
1686 | } |
1687 | |
1688 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1689 | c->addImmediateOperand(immediate: value); |
1690 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1691 | groupedConstants[OpTypeFloat].push_back(x: c); |
1692 | module.mapInstruction(instruction: c); |
1693 | |
1694 | return c->getResultId(); |
1695 | } |
1696 | |
1697 | Id Builder::makeFpConstant(Id type, double d, bool specConstant) |
1698 | { |
1699 | const int width = getScalarTypeWidth(typeId: type); |
1700 | |
1701 | assert(isFloatType(type)); |
1702 | |
1703 | switch (width) { |
1704 | case 16: |
1705 | return makeFloat16Constant(f16: (float)d, specConstant); |
1706 | case 32: |
1707 | return makeFloatConstant(f: (float)d, specConstant); |
1708 | case 64: |
1709 | return makeDoubleConstant(d, specConstant); |
1710 | default: |
1711 | break; |
1712 | } |
1713 | |
1714 | assert(false); |
1715 | return NoResult; |
1716 | } |
1717 | |
1718 | Id Builder::importNonSemanticShaderDebugInfoInstructions() |
1719 | { |
1720 | assert(emitNonSemanticShaderDebugInfo == true); |
1721 | |
1722 | if(nonSemanticShaderDebugInfo == 0) |
1723 | { |
1724 | this->addExtension(ext: spv::E_SPV_KHR_non_semantic_info); |
1725 | nonSemanticShaderDebugInfo = this->import(name: "NonSemantic.Shader.DebugInfo.100"); |
1726 | } |
1727 | |
1728 | return nonSemanticShaderDebugInfo; |
1729 | } |
1730 | |
1731 | Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps) |
1732 | { |
1733 | Instruction* constant = nullptr; |
1734 | bool found = false; |
1735 | for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { |
1736 | constant = groupedConstants[typeClass][i]; |
1737 | |
1738 | if (constant->getTypeId() != typeId) |
1739 | continue; |
1740 | |
1741 | // same contents? |
1742 | bool mismatch = false; |
1743 | for (int op = 0; op < constant->getNumOperands(); ++op) { |
1744 | if (constant->getIdOperand(op) != comps[op]) { |
1745 | mismatch = true; |
1746 | break; |
1747 | } |
1748 | } |
1749 | if (! mismatch) { |
1750 | found = true; |
1751 | break; |
1752 | } |
1753 | } |
1754 | |
1755 | return found ? constant->getResultId() : NoResult; |
1756 | } |
1757 | |
1758 | Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps) |
1759 | { |
1760 | Instruction* constant = nullptr; |
1761 | bool found = false; |
1762 | for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { |
1763 | constant = groupedStructConstants[typeId][i]; |
1764 | |
1765 | // same contents? |
1766 | bool mismatch = false; |
1767 | for (int op = 0; op < constant->getNumOperands(); ++op) { |
1768 | if (constant->getIdOperand(op) != comps[op]) { |
1769 | mismatch = true; |
1770 | break; |
1771 | } |
1772 | } |
1773 | if (! mismatch) { |
1774 | found = true; |
1775 | break; |
1776 | } |
1777 | } |
1778 | |
1779 | return found ? constant->getResultId() : NoResult; |
1780 | } |
1781 | |
1782 | // Comments in header |
1783 | Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant) |
1784 | { |
1785 | Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; |
1786 | assert(typeId); |
1787 | Op typeClass = getTypeClass(typeId); |
1788 | |
1789 | switch (typeClass) { |
1790 | case OpTypeVector: |
1791 | case OpTypeArray: |
1792 | case OpTypeMatrix: |
1793 | case OpTypeCooperativeMatrixKHR: |
1794 | case OpTypeCooperativeMatrixNV: |
1795 | if (! specConstant) { |
1796 | Id existing = findCompositeConstant(typeClass, typeId, comps: members); |
1797 | if (existing) |
1798 | return existing; |
1799 | } |
1800 | break; |
1801 | case OpTypeStruct: |
1802 | if (! specConstant) { |
1803 | Id existing = findStructConstant(typeId, comps: members); |
1804 | if (existing) |
1805 | return existing; |
1806 | } |
1807 | break; |
1808 | default: |
1809 | assert(0); |
1810 | return makeFloatConstant(f: 0.0); |
1811 | } |
1812 | |
1813 | Instruction* c = new Instruction(getUniqueId(), typeId, opcode); |
1814 | c->reserveOperands(count: members.size()); |
1815 | for (int op = 0; op < (int)members.size(); ++op) |
1816 | c->addIdOperand(id: members[op]); |
1817 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c)); |
1818 | if (typeClass == OpTypeStruct) |
1819 | groupedStructConstants[typeId].push_back(x: c); |
1820 | else |
1821 | groupedConstants[typeClass].push_back(x: c); |
1822 | module.mapInstruction(instruction: c); |
1823 | |
1824 | return c->getResultId(); |
1825 | } |
1826 | |
1827 | Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) |
1828 | { |
1829 | Instruction* entryPoint = new Instruction(OpEntryPoint); |
1830 | entryPoint->reserveOperands(count: 3); |
1831 | entryPoint->addImmediateOperand(immediate: model); |
1832 | entryPoint->addIdOperand(id: function->getId()); |
1833 | entryPoint->addStringOperand(str: name); |
1834 | |
1835 | entryPoints.push_back(x: std::unique_ptr<Instruction>(entryPoint)); |
1836 | |
1837 | return entryPoint; |
1838 | } |
1839 | |
1840 | // Currently relying on the fact that all 'value' of interest are small non-negative values. |
1841 | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) |
1842 | { |
1843 | // entryPoint can be null if we are in compile-only mode |
1844 | if (!entryPoint) |
1845 | return; |
1846 | |
1847 | Instruction* instr = new Instruction(OpExecutionMode); |
1848 | instr->reserveOperands(count: 3); |
1849 | instr->addIdOperand(id: entryPoint->getId()); |
1850 | instr->addImmediateOperand(immediate: mode); |
1851 | if (value1 >= 0) |
1852 | instr->addImmediateOperand(immediate: value1); |
1853 | if (value2 >= 0) |
1854 | instr->addImmediateOperand(immediate: value2); |
1855 | if (value3 >= 0) |
1856 | instr->addImmediateOperand(immediate: value3); |
1857 | |
1858 | executionModes.push_back(x: std::unique_ptr<Instruction>(instr)); |
1859 | } |
1860 | |
1861 | void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals) |
1862 | { |
1863 | // entryPoint can be null if we are in compile-only mode |
1864 | if (!entryPoint) |
1865 | return; |
1866 | |
1867 | Instruction* instr = new Instruction(OpExecutionMode); |
1868 | instr->reserveOperands(count: literals.size() + 2); |
1869 | instr->addIdOperand(id: entryPoint->getId()); |
1870 | instr->addImmediateOperand(immediate: mode); |
1871 | for (auto literal : literals) |
1872 | instr->addImmediateOperand(immediate: literal); |
1873 | |
1874 | executionModes.push_back(x: std::unique_ptr<Instruction>(instr)); |
1875 | } |
1876 | |
1877 | void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds) |
1878 | { |
1879 | // entryPoint can be null if we are in compile-only mode |
1880 | if (!entryPoint) |
1881 | return; |
1882 | |
1883 | Instruction* instr = new Instruction(OpExecutionModeId); |
1884 | instr->reserveOperands(count: operandIds.size() + 2); |
1885 | instr->addIdOperand(id: entryPoint->getId()); |
1886 | instr->addImmediateOperand(immediate: mode); |
1887 | for (auto operandId : operandIds) |
1888 | instr->addIdOperand(id: operandId); |
1889 | |
1890 | executionModes.push_back(x: std::unique_ptr<Instruction>(instr)); |
1891 | } |
1892 | |
1893 | void Builder::addName(Id id, const char* string) |
1894 | { |
1895 | Instruction* name = new Instruction(OpName); |
1896 | name->reserveOperands(count: 2); |
1897 | name->addIdOperand(id); |
1898 | name->addStringOperand(str: string); |
1899 | |
1900 | names.push_back(x: std::unique_ptr<Instruction>(name)); |
1901 | } |
1902 | |
1903 | void Builder::addMemberName(Id id, int memberNumber, const char* string) |
1904 | { |
1905 | Instruction* name = new Instruction(OpMemberName); |
1906 | name->reserveOperands(count: 3); |
1907 | name->addIdOperand(id); |
1908 | name->addImmediateOperand(immediate: memberNumber); |
1909 | name->addStringOperand(str: string); |
1910 | |
1911 | names.push_back(x: std::unique_ptr<Instruction>(name)); |
1912 | } |
1913 | |
1914 | void Builder::addDecoration(Id id, Decoration decoration, int num) |
1915 | { |
1916 | if (decoration == spv::DecorationMax) |
1917 | return; |
1918 | |
1919 | Instruction* dec = new Instruction(OpDecorate); |
1920 | dec->reserveOperands(count: 2); |
1921 | dec->addIdOperand(id); |
1922 | dec->addImmediateOperand(immediate: decoration); |
1923 | if (num >= 0) |
1924 | dec->addImmediateOperand(immediate: num); |
1925 | |
1926 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1927 | } |
1928 | |
1929 | void Builder::addDecoration(Id id, Decoration decoration, const char* s) |
1930 | { |
1931 | if (decoration == spv::DecorationMax) |
1932 | return; |
1933 | |
1934 | Instruction* dec = new Instruction(OpDecorateString); |
1935 | dec->reserveOperands(count: 3); |
1936 | dec->addIdOperand(id); |
1937 | dec->addImmediateOperand(immediate: decoration); |
1938 | dec->addStringOperand(str: s); |
1939 | |
1940 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1941 | } |
1942 | |
1943 | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals) |
1944 | { |
1945 | if (decoration == spv::DecorationMax) |
1946 | return; |
1947 | |
1948 | Instruction* dec = new Instruction(OpDecorate); |
1949 | dec->reserveOperands(count: literals.size() + 2); |
1950 | dec->addIdOperand(id); |
1951 | dec->addImmediateOperand(immediate: decoration); |
1952 | for (auto literal : literals) |
1953 | dec->addImmediateOperand(immediate: literal); |
1954 | |
1955 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1956 | } |
1957 | |
1958 | void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings) |
1959 | { |
1960 | if (decoration == spv::DecorationMax) |
1961 | return; |
1962 | |
1963 | Instruction* dec = new Instruction(OpDecorateString); |
1964 | dec->reserveOperands(count: strings.size() + 2); |
1965 | dec->addIdOperand(id); |
1966 | dec->addImmediateOperand(immediate: decoration); |
1967 | for (auto string : strings) |
1968 | dec->addStringOperand(str: string); |
1969 | |
1970 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1971 | } |
1972 | |
1973 | void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) { |
1974 | Instruction* dec = new Instruction(OpDecorate); |
1975 | dec->reserveOperands(count: 4); |
1976 | dec->addIdOperand(id); |
1977 | dec->addImmediateOperand(immediate: spv::DecorationLinkageAttributes); |
1978 | dec->addStringOperand(str: name); |
1979 | dec->addImmediateOperand(immediate: linkType); |
1980 | |
1981 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1982 | } |
1983 | |
1984 | void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) |
1985 | { |
1986 | if (decoration == spv::DecorationMax) |
1987 | return; |
1988 | |
1989 | Instruction* dec = new Instruction(OpDecorateId); |
1990 | dec->reserveOperands(count: 3); |
1991 | dec->addIdOperand(id); |
1992 | dec->addImmediateOperand(immediate: decoration); |
1993 | dec->addIdOperand(id: idDecoration); |
1994 | |
1995 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
1996 | } |
1997 | |
1998 | void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds) |
1999 | { |
2000 | if(decoration == spv::DecorationMax) |
2001 | return; |
2002 | |
2003 | Instruction* dec = new Instruction(OpDecorateId); |
2004 | dec->reserveOperands(count: operandIds.size() + 2); |
2005 | dec->addIdOperand(id); |
2006 | dec->addImmediateOperand(immediate: decoration); |
2007 | |
2008 | for (auto operandId : operandIds) |
2009 | dec->addIdOperand(id: operandId); |
2010 | |
2011 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
2012 | } |
2013 | |
2014 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) |
2015 | { |
2016 | if (decoration == spv::DecorationMax) |
2017 | return; |
2018 | |
2019 | Instruction* dec = new Instruction(OpMemberDecorate); |
2020 | dec->reserveOperands(count: 3); |
2021 | dec->addIdOperand(id); |
2022 | dec->addImmediateOperand(immediate: member); |
2023 | dec->addImmediateOperand(immediate: decoration); |
2024 | if (num >= 0) |
2025 | dec->addImmediateOperand(immediate: num); |
2026 | |
2027 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
2028 | } |
2029 | |
2030 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) |
2031 | { |
2032 | if (decoration == spv::DecorationMax) |
2033 | return; |
2034 | |
2035 | Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); |
2036 | dec->reserveOperands(count: 4); |
2037 | dec->addIdOperand(id); |
2038 | dec->addImmediateOperand(immediate: member); |
2039 | dec->addImmediateOperand(immediate: decoration); |
2040 | dec->addStringOperand(str: s); |
2041 | |
2042 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
2043 | } |
2044 | |
2045 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals) |
2046 | { |
2047 | if (decoration == spv::DecorationMax) |
2048 | return; |
2049 | |
2050 | Instruction* dec = new Instruction(OpMemberDecorate); |
2051 | dec->reserveOperands(count: literals.size() + 3); |
2052 | dec->addIdOperand(id); |
2053 | dec->addImmediateOperand(immediate: member); |
2054 | dec->addImmediateOperand(immediate: decoration); |
2055 | for (auto literal : literals) |
2056 | dec->addImmediateOperand(immediate: literal); |
2057 | |
2058 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
2059 | } |
2060 | |
2061 | void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings) |
2062 | { |
2063 | if (decoration == spv::DecorationMax) |
2064 | return; |
2065 | |
2066 | Instruction* dec = new Instruction(OpMemberDecorateString); |
2067 | dec->reserveOperands(count: strings.size() + 3); |
2068 | dec->addIdOperand(id); |
2069 | dec->addImmediateOperand(immediate: member); |
2070 | dec->addImmediateOperand(immediate: decoration); |
2071 | for (auto string : strings) |
2072 | dec->addStringOperand(str: string); |
2073 | |
2074 | decorations.push_back(x: std::unique_ptr<Instruction>(dec)); |
2075 | } |
2076 | |
2077 | void Builder::addInstruction(std::unique_ptr<Instruction> inst) { |
2078 | // Optionally insert OpDebugScope |
2079 | if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) { |
2080 | if (buildPoint->updateDebugScope(scopeId: currentDebugScopeId.top())) { |
2081 | auto scopeInst = std::make_unique<Instruction>(args: getUniqueId(), args: makeVoidType(), args: OpExtInst); |
2082 | scopeInst->reserveOperands(count: 3); |
2083 | scopeInst->addIdOperand(id: nonSemanticShaderDebugInfo); |
2084 | scopeInst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugScope); |
2085 | scopeInst->addIdOperand(id: currentDebugScopeId.top()); |
2086 | buildPoint->addInstruction(inst: std::move(scopeInst)); |
2087 | } |
2088 | |
2089 | dirtyScopeTracker = false; |
2090 | } |
2091 | |
2092 | // Insert OpLine/OpDebugLine if the debug source location has changed |
2093 | if (trackDebugInfo && dirtyLineTracker) { |
2094 | if (buildPoint->updateDebugSourceLocation(line: currentLine, column: 0, fileId: currentFileId)) { |
2095 | if (emitSpirvDebugInfo) { |
2096 | auto lineInst = std::make_unique<Instruction>(args: OpLine); |
2097 | lineInst->reserveOperands(count: 3); |
2098 | lineInst->addIdOperand(id: currentFileId); |
2099 | lineInst->addImmediateOperand(immediate: currentLine); |
2100 | lineInst->addImmediateOperand(immediate: 0); |
2101 | buildPoint->addInstruction(inst: std::move(lineInst)); |
2102 | } |
2103 | if (emitNonSemanticShaderDebugInfo) { |
2104 | auto lineInst = std::make_unique<Instruction>(args: getUniqueId(), args: makeVoidType(), args: OpExtInst); |
2105 | lineInst->reserveOperands(count: 7); |
2106 | lineInst->addIdOperand(id: nonSemanticShaderDebugInfo); |
2107 | lineInst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugLine); |
2108 | lineInst->addIdOperand(id: makeDebugSource(fileName: currentFileId)); |
2109 | lineInst->addIdOperand(id: makeUintConstant(u: currentLine)); |
2110 | lineInst->addIdOperand(id: makeUintConstant(u: currentLine)); |
2111 | lineInst->addIdOperand(id: makeUintConstant(u: 0)); |
2112 | lineInst->addIdOperand(id: makeUintConstant(u: 0)); |
2113 | buildPoint->addInstruction(inst: std::move(lineInst)); |
2114 | } |
2115 | } |
2116 | |
2117 | dirtyLineTracker = false; |
2118 | } |
2119 | |
2120 | buildPoint->addInstruction(inst: std::move(inst)); |
2121 | } |
2122 | |
2123 | // Comments in header |
2124 | Function* Builder::makeEntryPoint(const char* entryPoint) |
2125 | { |
2126 | assert(! entryPointFunction); |
2127 | |
2128 | auto const returnType = makeVoidType(); |
2129 | |
2130 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2131 | if(sourceLang == spv::SourceLanguageHLSL) { |
2132 | emitNonSemanticShaderDebugInfo = false; |
2133 | } |
2134 | |
2135 | Block* entry = nullptr; |
2136 | entryPointFunction = makeFunctionEntry(precision: NoPrecision, returnType, name: entryPoint, linkType: LinkageTypeMax, paramTypes: {}, precisions: {}, entry: &entry); |
2137 | |
2138 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2139 | |
2140 | return entryPointFunction; |
2141 | } |
2142 | |
2143 | // Comments in header |
2144 | Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType, |
2145 | const std::vector<Id>& paramTypes, |
2146 | const std::vector<std::vector<Decoration>>& decorations, Block** entry) |
2147 | { |
2148 | // Make the function and initial instructions in it |
2149 | Id typeId = makeFunctionType(returnType, paramTypes); |
2150 | Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds(numIds: (int)paramTypes.size()); |
2151 | Id funcId = getUniqueId(); |
2152 | Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module); |
2153 | |
2154 | // Set up the precisions |
2155 | setPrecision(id: function->getId(), precision); |
2156 | function->setReturnPrecision(precision); |
2157 | for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { |
2158 | for (int d = 0; d < (int)decorations[p].size(); ++d) { |
2159 | addDecoration(id: firstParamId + p, decoration: decorations[p][d]); |
2160 | function->addParamPrecision(param: p, precision: decorations[p][d]); |
2161 | } |
2162 | } |
2163 | |
2164 | // reset last debug scope |
2165 | if (emitNonSemanticShaderDebugInfo) { |
2166 | dirtyScopeTracker = true; |
2167 | } |
2168 | |
2169 | // CFG |
2170 | assert(entry != nullptr); |
2171 | *entry = new Block(getUniqueId(), *function); |
2172 | function->addBlock(block: *entry); |
2173 | setBuildPoint(*entry); |
2174 | |
2175 | if (name) |
2176 | addName(id: function->getId(), string: name); |
2177 | |
2178 | functions.push_back(x: std::unique_ptr<Function>(function)); |
2179 | |
2180 | return function; |
2181 | } |
2182 | |
2183 | void Builder::setupDebugFunctionEntry(Function* function, const char* name, int line, const std::vector<Id>& paramTypes, |
2184 | const std::vector<char const*>& paramNames) |
2185 | { |
2186 | |
2187 | if (!emitNonSemanticShaderDebugInfo) |
2188 | return; |
2189 | |
2190 | currentLine = line; |
2191 | Id nameId = getStringId(str: unmangleFunctionName(name)); |
2192 | Id funcTypeId = function->getFuncTypeId(); |
2193 | assert(debugId[funcTypeId] != 0); |
2194 | Id funcId = function->getId(); |
2195 | |
2196 | assert(funcId != 0); |
2197 | |
2198 | // Make the debug function instruction |
2199 | Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId); |
2200 | debugId[funcId] = debugFuncId; |
2201 | currentDebugScopeId.push(x: debugFuncId); |
2202 | |
2203 | // DebugScope and DebugLine for parameter DebugDeclares |
2204 | assert(paramTypes.size() == paramNames.size()); |
2205 | if ((int)paramTypes.size() > 0) { |
2206 | Id firstParamId = function->getParamId(p: 0); |
2207 | |
2208 | for (size_t p = 0; p < paramTypes.size(); ++p) { |
2209 | bool passByRef = false; |
2210 | Id paramTypeId = paramTypes[p]; |
2211 | |
2212 | // For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type. |
2213 | if (isPointerType(typeId: paramTypeId) || isArrayType(typeId: paramTypeId)) { |
2214 | passByRef = true; |
2215 | paramTypeId = getContainedTypeId(typeId: paramTypeId); |
2216 | } |
2217 | |
2218 | auto const& paramName = paramNames[p]; |
2219 | auto const debugLocalVariableId = createDebugLocalVariable(type: debugId[paramTypeId], name: paramName, argNumber: p + 1); |
2220 | auto const paramId = static_cast<Id>(firstParamId + p); |
2221 | debugId[paramId] = debugLocalVariableId; |
2222 | |
2223 | if (passByRef) { |
2224 | makeDebugDeclare(debugLocalVariable: debugLocalVariableId, pointer: paramId); |
2225 | } else { |
2226 | makeDebugValue(debugLocalVariable: debugLocalVariableId, value: paramId); |
2227 | } |
2228 | } |
2229 | } |
2230 | |
2231 | // Clear debug scope stack |
2232 | if (emitNonSemanticShaderDebugInfo) |
2233 | currentDebugScopeId.pop(); |
2234 | } |
2235 | |
2236 | Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId) |
2237 | { |
2238 | assert(function != nullptr); |
2239 | assert(nameId != 0); |
2240 | assert(funcTypeId != 0); |
2241 | assert(debugId[funcTypeId] != 0); |
2242 | |
2243 | Id funcId = getUniqueId(); |
2244 | auto type = new Instruction(funcId, makeVoidType(), OpExtInst); |
2245 | type->reserveOperands(count: 11); |
2246 | type->addIdOperand(id: nonSemanticShaderDebugInfo); |
2247 | type->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugFunction); |
2248 | type->addIdOperand(id: nameId); |
2249 | type->addIdOperand(id: debugId[funcTypeId]); |
2250 | type->addIdOperand(id: makeDebugSource(fileName: currentFileId)); // TODO: This points to file of definition instead of declaration |
2251 | type->addIdOperand(id: makeUintConstant(u: currentLine)); // TODO: This points to line of definition instead of declaration |
2252 | type->addIdOperand(id: makeUintConstant(u: 0)); // column |
2253 | type->addIdOperand(id: makeDebugCompilationUnit()); // scope |
2254 | type->addIdOperand(id: nameId); // linkage name |
2255 | type->addIdOperand(id: makeUintConstant(u: NonSemanticShaderDebugInfo100FlagIsPublic)); |
2256 | type->addIdOperand(id: makeUintConstant(u: currentLine)); |
2257 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type)); |
2258 | module.mapInstruction(instruction: type); |
2259 | return funcId; |
2260 | } |
2261 | |
2262 | Id Builder::makeDebugLexicalBlock(uint32_t line) { |
2263 | assert(!currentDebugScopeId.empty()); |
2264 | |
2265 | Id lexId = getUniqueId(); |
2266 | auto lex = new Instruction(lexId, makeVoidType(), OpExtInst); |
2267 | lex->reserveOperands(count: 6); |
2268 | lex->addIdOperand(id: nonSemanticShaderDebugInfo); |
2269 | lex->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugLexicalBlock); |
2270 | lex->addIdOperand(id: makeDebugSource(fileName: currentFileId)); |
2271 | lex->addIdOperand(id: makeUintConstant(u: line)); |
2272 | lex->addIdOperand(id: makeUintConstant(u: 0)); // column |
2273 | lex->addIdOperand(id: currentDebugScopeId.top()); // scope |
2274 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(lex)); |
2275 | module.mapInstruction(instruction: lex); |
2276 | return lexId; |
2277 | } |
2278 | |
2279 | std::string Builder::unmangleFunctionName(std::string const& name) const |
2280 | { |
2281 | assert(name.length() > 0); |
2282 | |
2283 | if(name.rfind(c: '(') != std::string::npos) { |
2284 | return name.substr(pos: 0, n: name.rfind(c: '(')); |
2285 | } else { |
2286 | return name; |
2287 | } |
2288 | } |
2289 | |
2290 | // Comments in header |
2291 | void Builder::makeReturn(bool implicit, Id retVal) |
2292 | { |
2293 | if (retVal) { |
2294 | Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); |
2295 | inst->addIdOperand(id: retVal); |
2296 | addInstruction(inst: std::unique_ptr<Instruction>(inst)); |
2297 | } else |
2298 | addInstruction(inst: std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn))); |
2299 | |
2300 | if (! implicit) |
2301 | createAndSetNoPredecessorBlock("post-return"); |
2302 | } |
2303 | |
2304 | // Comments in header |
2305 | void Builder::enterLexicalBlock(uint32_t line) |
2306 | { |
2307 | // Generate new lexical scope debug instruction |
2308 | Id lexId = makeDebugLexicalBlock(line); |
2309 | currentDebugScopeId.push(x: lexId); |
2310 | dirtyScopeTracker = true; |
2311 | } |
2312 | |
2313 | // Comments in header |
2314 | void Builder::leaveLexicalBlock() |
2315 | { |
2316 | // Pop current scope from stack and clear current scope |
2317 | currentDebugScopeId.pop(); |
2318 | dirtyScopeTracker = true; |
2319 | } |
2320 | |
2321 | // Comments in header |
2322 | void Builder::enterFunction(Function const* function) |
2323 | { |
2324 | // Save and disable debugInfo for HLSL entry point function. It is a wrapper |
2325 | // function with no user code in it. |
2326 | restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; |
2327 | if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) { |
2328 | emitNonSemanticShaderDebugInfo = false; |
2329 | } |
2330 | |
2331 | if (emitNonSemanticShaderDebugInfo) { |
2332 | // Initialize scope state |
2333 | Id funcId = function->getFuncId(); |
2334 | currentDebugScopeId.push(x: debugId[funcId]); |
2335 | // Create DebugFunctionDefinition |
2336 | spv::Id resultId = getUniqueId(); |
2337 | Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst); |
2338 | defInst->reserveOperands(count: 4); |
2339 | defInst->addIdOperand(id: nonSemanticShaderDebugInfo); |
2340 | defInst->addImmediateOperand(immediate: NonSemanticShaderDebugInfo100DebugFunctionDefinition); |
2341 | defInst->addIdOperand(id: debugId[funcId]); |
2342 | defInst->addIdOperand(id: funcId); |
2343 | addInstruction(inst: std::unique_ptr<Instruction>(defInst)); |
2344 | } |
2345 | |
2346 | if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) { |
2347 | Id funcId = function->getFuncId(); |
2348 | addCapability(cap: CapabilityLinkage); |
2349 | addLinkageDecoration(id: funcId, name: function->getExportName(), linkType); |
2350 | } |
2351 | } |
2352 | |
2353 | // Comments in header |
2354 | void Builder::leaveFunction() |
2355 | { |
2356 | Block* block = buildPoint; |
2357 | Function& function = buildPoint->getParent(); |
2358 | assert(block); |
2359 | |
2360 | // If our function did not contain a return, add a return void now. |
2361 | if (! block->isTerminated()) { |
2362 | if (function.getReturnType() == makeVoidType()) |
2363 | makeReturn(implicit: true); |
2364 | else { |
2365 | makeReturn(implicit: true, retVal: createUndefined(type: function.getReturnType())); |
2366 | } |
2367 | } |
2368 | |
2369 | // Clear function scope from debug scope stack |
2370 | if (emitNonSemanticShaderDebugInfo) |
2371 | currentDebugScopeId.pop(); |
2372 | |
2373 | emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; |
2374 | } |
2375 | |
2376 | // Comments in header |
2377 | void Builder::makeStatementTerminator(spv::Op opcode, const char *name) |
2378 | { |
2379 | addInstruction(inst: std::unique_ptr<Instruction>(new Instruction(opcode))); |
2380 | createAndSetNoPredecessorBlock(name); |
2381 | } |
2382 | |
2383 | // Comments in header |
2384 | void Builder::makeStatementTerminator(spv::Op opcode, const std::vector<Id>& operands, const char* name) |
2385 | { |
2386 | // It's assumed that the terminator instruction is always of void return type |
2387 | // However in future if there is a need for non void return type, new helper |
2388 | // methods can be created. |
2389 | createNoResultOp(opcode, operands); |
2390 | createAndSetNoPredecessorBlock(name); |
2391 | } |
2392 | |
2393 | // Comments in header |
2394 | Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer, |
2395 | bool const compilerGenerated) |
2396 | { |
2397 | Id pointerType = makePointer(storageClass, pointee: type); |
2398 | Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); |
2399 | inst->addImmediateOperand(immediate: storageClass); |
2400 | if (initializer != NoResult) |
2401 | inst->addIdOperand(id: initializer); |
2402 | |
2403 | switch (storageClass) { |
2404 | case StorageClassFunction: |
2405 | // Validation rules require the declaration in the entry block |
2406 | buildPoint->getParent().addLocalVariable(inst: std::unique_ptr<Instruction>(inst)); |
2407 | |
2408 | if (emitNonSemanticShaderDebugInfo && !compilerGenerated) |
2409 | { |
2410 | auto const debugLocalVariableId = createDebugLocalVariable(type: debugId[type], name); |
2411 | debugId[inst->getResultId()] = debugLocalVariableId; |
2412 | |
2413 | makeDebugDeclare(debugLocalVariable: debugLocalVariableId, pointer: inst->getResultId()); |
2414 | } |
2415 | |
2416 | break; |
2417 | |
2418 | default: |
2419 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst)); |
2420 | module.mapInstruction(instruction: inst); |
2421 | |
2422 | if (emitNonSemanticShaderDebugInfo) |
2423 | { |
2424 | auto const debugResultId = createDebugGlobalVariable(type: debugId[type], name, variable: inst->getResultId()); |
2425 | debugId[inst->getResultId()] = debugResultId; |
2426 | } |
2427 | break; |
2428 | } |
2429 | |
2430 | if (name) |
2431 | addName(id: inst->getResultId(), string: name); |
2432 | setPrecision(id: inst->getResultId(), precision); |
2433 | |
2434 | return inst->getResultId(); |
2435 | } |
2436 | |
2437 | // Comments in header |
2438 | Id Builder::createUndefined(Id type) |
2439 | { |
2440 | Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); |
2441 | addInstruction(inst: std::unique_ptr<Instruction>(inst)); |
2442 | return inst->getResultId(); |
2443 | } |
2444 | |
2445 | // av/vis/nonprivate are unnecessary and illegal for some storage classes. |
2446 | spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) |
2447 | const |
2448 | { |
2449 | switch (sc) { |
2450 | case spv::StorageClassUniform: |
2451 | case spv::StorageClassWorkgroup: |
2452 | case spv::StorageClassStorageBuffer: |
2453 | case spv::StorageClassPhysicalStorageBufferEXT: |
2454 | break; |
2455 | default: |
2456 | memoryAccess = spv::MemoryAccessMask(memoryAccess & |
2457 | ~(spv::MemoryAccessMakePointerAvailableKHRMask | |
2458 | spv::MemoryAccessMakePointerVisibleKHRMask | |
2459 | spv::MemoryAccessNonPrivatePointerKHRMask)); |
2460 | break; |
2461 | } |
2462 | return memoryAccess; |
2463 | } |
2464 | |
2465 | // Comments in header |
2466 | void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, |
2467 | unsigned int alignment) |
2468 | { |
2469 | Instruction* store = new Instruction(OpStore); |
2470 | store->reserveOperands(count: 2); |
2471 | store->addIdOperand(id: lValue); |
2472 | store->addIdOperand(id: rValue); |
2473 | |
2474 | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, sc: getStorageClass(resultId: lValue)); |
2475 | |
2476 | if (memoryAccess != MemoryAccessMaskNone) { |
2477 | store->addImmediateOperand(immediate: memoryAccess); |
2478 | if (memoryAccess & spv::MemoryAccessAlignedMask) { |
2479 | store->addImmediateOperand(immediate: alignment); |
2480 | } |
2481 | if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { |
2482 | store->addIdOperand(id: makeUintConstant(u: scope)); |
2483 | } |
2484 | } |
2485 | |
2486 | addInstruction(inst: std::unique_ptr<Instruction>(store)); |
2487 | } |
2488 | |
2489 | // Comments in header |
2490 | Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess, |
2491 | spv::Scope scope, unsigned int alignment) |
2492 | { |
2493 | Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(resultId: lValue), OpLoad); |
2494 | load->addIdOperand(id: lValue); |
2495 | |
2496 | memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, sc: getStorageClass(resultId: lValue)); |
2497 | |
2498 | if (memoryAccess != MemoryAccessMaskNone) { |
2499 | load->addImmediateOperand(immediate: memoryAccess); |
2500 | if (memoryAccess & spv::MemoryAccessAlignedMask) { |
2501 | load->addImmediateOperand(immediate: alignment); |
2502 | } |
2503 | if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { |
2504 | load->addIdOperand(id: makeUintConstant(u: scope)); |
2505 | } |
2506 | } |
2507 | |
2508 | addInstruction(inst: std::unique_ptr<Instruction>(load)); |
2509 | setPrecision(id: load->getResultId(), precision); |
2510 | |
2511 | return load->getResultId(); |
2512 | } |
2513 | |
2514 | // Comments in header |
2515 | Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets) |
2516 | { |
2517 | // Figure out the final resulting type. |
2518 | Id typeId = getResultingAccessChainType(); |
2519 | typeId = makePointer(storageClass, pointee: typeId); |
2520 | |
2521 | // Make the instruction |
2522 | Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); |
2523 | chain->reserveOperands(count: offsets.size() + 1); |
2524 | chain->addIdOperand(id: base); |
2525 | for (int i = 0; i < (int)offsets.size(); ++i) |
2526 | chain->addIdOperand(id: offsets[i]); |
2527 | addInstruction(inst: std::unique_ptr<Instruction>(chain)); |
2528 | |
2529 | return chain->getResultId(); |
2530 | } |
2531 | |
2532 | Id Builder::createArrayLength(Id base, unsigned int member) |
2533 | { |
2534 | spv::Id intType = makeUintType(width: 32); |
2535 | Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); |
2536 | length->reserveOperands(count: 2); |
2537 | length->addIdOperand(id: base); |
2538 | length->addImmediateOperand(immediate: member); |
2539 | addInstruction(inst: std::unique_ptr<Instruction>(length)); |
2540 | |
2541 | return length->getResultId(); |
2542 | } |
2543 | |
2544 | Id Builder::createCooperativeMatrixLengthKHR(Id type) |
2545 | { |
2546 | spv::Id intType = makeUintType(width: 32); |
2547 | |
2548 | // Generate code for spec constants if in spec constant operation |
2549 | // generation mode. |
2550 | if (generatingOpCodeForSpecConst) { |
2551 | return createSpecConstantOp(OpCooperativeMatrixLengthKHR, typeId: intType, operands: std::vector<Id>(1, type), literals: std::vector<Id>()); |
2552 | } |
2553 | |
2554 | Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthKHR); |
2555 | length->addIdOperand(id: type); |
2556 | addInstruction(inst: std::unique_ptr<Instruction>(length)); |
2557 | |
2558 | return length->getResultId(); |
2559 | } |
2560 | |
2561 | Id Builder::createCooperativeMatrixLengthNV(Id type) |
2562 | { |
2563 | spv::Id intType = makeUintType(width: 32); |
2564 | |
2565 | // Generate code for spec constants if in spec constant operation |
2566 | // generation mode. |
2567 | if (generatingOpCodeForSpecConst) { |
2568 | return createSpecConstantOp(OpCooperativeMatrixLengthNV, typeId: intType, operands: std::vector<Id>(1, type), literals: std::vector<Id>()); |
2569 | } |
2570 | |
2571 | Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); |
2572 | length->addIdOperand(id: type); |
2573 | addInstruction(inst: std::unique_ptr<Instruction>(length)); |
2574 | |
2575 | return length->getResultId(); |
2576 | } |
2577 | |
2578 | Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) |
2579 | { |
2580 | // Generate code for spec constants if in spec constant operation |
2581 | // generation mode. |
2582 | if (generatingOpCodeForSpecConst) { |
2583 | return createSpecConstantOp(OpCompositeExtract, typeId, operands: std::vector<Id>(1, composite), |
2584 | literals: std::vector<Id>(1, index)); |
2585 | } |
2586 | Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); |
2587 | extract->reserveOperands(count: 2); |
2588 | extract->addIdOperand(id: composite); |
2589 | extract->addImmediateOperand(immediate: index); |
2590 | addInstruction(inst: std::unique_ptr<Instruction>(extract)); |
2591 | |
2592 | return extract->getResultId(); |
2593 | } |
2594 | |
2595 | Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes) |
2596 | { |
2597 | // Generate code for spec constants if in spec constant operation |
2598 | // generation mode. |
2599 | if (generatingOpCodeForSpecConst) { |
2600 | return createSpecConstantOp(OpCompositeExtract, typeId, operands: std::vector<Id>(1, composite), literals: indexes); |
2601 | } |
2602 | Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); |
2603 | extract->reserveOperands(count: indexes.size() + 1); |
2604 | extract->addIdOperand(id: composite); |
2605 | for (int i = 0; i < (int)indexes.size(); ++i) |
2606 | extract->addImmediateOperand(immediate: indexes[i]); |
2607 | addInstruction(inst: std::unique_ptr<Instruction>(extract)); |
2608 | |
2609 | return extract->getResultId(); |
2610 | } |
2611 | |
2612 | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) |
2613 | { |
2614 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); |
2615 | insert->reserveOperands(count: 3); |
2616 | insert->addIdOperand(id: object); |
2617 | insert->addIdOperand(id: composite); |
2618 | insert->addImmediateOperand(immediate: index); |
2619 | addInstruction(inst: std::unique_ptr<Instruction>(insert)); |
2620 | |
2621 | return insert->getResultId(); |
2622 | } |
2623 | |
2624 | Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes) |
2625 | { |
2626 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); |
2627 | insert->reserveOperands(count: indexes.size() + 2); |
2628 | insert->addIdOperand(id: object); |
2629 | insert->addIdOperand(id: composite); |
2630 | for (int i = 0; i < (int)indexes.size(); ++i) |
2631 | insert->addImmediateOperand(immediate: indexes[i]); |
2632 | addInstruction(inst: std::unique_ptr<Instruction>(insert)); |
2633 | |
2634 | return insert->getResultId(); |
2635 | } |
2636 | |
2637 | Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) |
2638 | { |
2639 | Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); |
2640 | extract->reserveOperands(count: 2); |
2641 | extract->addIdOperand(id: vector); |
2642 | extract->addIdOperand(id: componentIndex); |
2643 | addInstruction(inst: std::unique_ptr<Instruction>(extract)); |
2644 | |
2645 | return extract->getResultId(); |
2646 | } |
2647 | |
2648 | Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) |
2649 | { |
2650 | Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); |
2651 | insert->reserveOperands(count: 3); |
2652 | insert->addIdOperand(id: vector); |
2653 | insert->addIdOperand(id: component); |
2654 | insert->addIdOperand(id: componentIndex); |
2655 | addInstruction(inst: std::unique_ptr<Instruction>(insert)); |
2656 | |
2657 | return insert->getResultId(); |
2658 | } |
2659 | |
2660 | // An opcode that has no operands, no result id, and no type |
2661 | void Builder::createNoResultOp(Op opCode) |
2662 | { |
2663 | Instruction* op = new Instruction(opCode); |
2664 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2665 | } |
2666 | |
2667 | // An opcode that has one id operand, no result id, and no type |
2668 | void Builder::createNoResultOp(Op opCode, Id operand) |
2669 | { |
2670 | Instruction* op = new Instruction(opCode); |
2671 | op->addIdOperand(id: operand); |
2672 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2673 | } |
2674 | |
2675 | // An opcode that has one or more operands, no result id, and no type |
2676 | void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands) |
2677 | { |
2678 | Instruction* op = new Instruction(opCode); |
2679 | op->reserveOperands(count: operands.size()); |
2680 | for (auto id : operands) { |
2681 | op->addIdOperand(id); |
2682 | } |
2683 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2684 | } |
2685 | |
2686 | // An opcode that has multiple operands, no result id, and no type |
2687 | void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands) |
2688 | { |
2689 | Instruction* op = new Instruction(opCode); |
2690 | op->reserveOperands(count: operands.size()); |
2691 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
2692 | if (it->isId) |
2693 | op->addIdOperand(id: it->word); |
2694 | else |
2695 | op->addImmediateOperand(immediate: it->word); |
2696 | } |
2697 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2698 | } |
2699 | |
2700 | void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) |
2701 | { |
2702 | Instruction* op = new Instruction(OpControlBarrier); |
2703 | op->reserveOperands(count: 3); |
2704 | op->addIdOperand(id: makeUintConstant(u: execution)); |
2705 | op->addIdOperand(id: makeUintConstant(u: memory)); |
2706 | op->addIdOperand(id: makeUintConstant(u: semantics)); |
2707 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2708 | } |
2709 | |
2710 | void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) |
2711 | { |
2712 | Instruction* op = new Instruction(OpMemoryBarrier); |
2713 | op->reserveOperands(count: 2); |
2714 | op->addIdOperand(id: makeUintConstant(u: executionScope)); |
2715 | op->addIdOperand(id: makeUintConstant(u: memorySemantics)); |
2716 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2717 | } |
2718 | |
2719 | // An opcode that has one operands, a result id, and a type |
2720 | Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) |
2721 | { |
2722 | // Generate code for spec constants if in spec constant operation |
2723 | // generation mode. |
2724 | if (generatingOpCodeForSpecConst) { |
2725 | return createSpecConstantOp(opCode, typeId, operands: std::vector<Id>(1, operand), literals: std::vector<Id>()); |
2726 | } |
2727 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2728 | op->addIdOperand(id: operand); |
2729 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2730 | |
2731 | return op->getResultId(); |
2732 | } |
2733 | |
2734 | Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) |
2735 | { |
2736 | // Generate code for spec constants if in spec constant operation |
2737 | // generation mode. |
2738 | if (generatingOpCodeForSpecConst) { |
2739 | std::vector<Id> operands(2); |
2740 | operands[0] = left; operands[1] = right; |
2741 | return createSpecConstantOp(opCode, typeId, operands, literals: std::vector<Id>()); |
2742 | } |
2743 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2744 | op->reserveOperands(count: 2); |
2745 | op->addIdOperand(id: left); |
2746 | op->addIdOperand(id: right); |
2747 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2748 | |
2749 | return op->getResultId(); |
2750 | } |
2751 | |
2752 | Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) |
2753 | { |
2754 | // Generate code for spec constants if in spec constant operation |
2755 | // generation mode. |
2756 | if (generatingOpCodeForSpecConst) { |
2757 | std::vector<Id> operands(3); |
2758 | operands[0] = op1; |
2759 | operands[1] = op2; |
2760 | operands[2] = op3; |
2761 | return createSpecConstantOp( |
2762 | opCode, typeId, operands, literals: std::vector<Id>()); |
2763 | } |
2764 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2765 | op->reserveOperands(count: 3); |
2766 | op->addIdOperand(id: op1); |
2767 | op->addIdOperand(id: op2); |
2768 | op->addIdOperand(id: op3); |
2769 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2770 | |
2771 | return op->getResultId(); |
2772 | } |
2773 | |
2774 | Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands) |
2775 | { |
2776 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2777 | op->reserveOperands(count: operands.size()); |
2778 | for (auto id : operands) |
2779 | op->addIdOperand(id); |
2780 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2781 | |
2782 | return op->getResultId(); |
2783 | } |
2784 | |
2785 | Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands) |
2786 | { |
2787 | Instruction* op = new Instruction(getUniqueId(), typeId, opCode); |
2788 | op->reserveOperands(count: operands.size()); |
2789 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) { |
2790 | if (it->isId) |
2791 | op->addIdOperand(id: it->word); |
2792 | else |
2793 | op->addImmediateOperand(immediate: it->word); |
2794 | } |
2795 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2796 | |
2797 | return op->getResultId(); |
2798 | } |
2799 | |
2800 | Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, |
2801 | const std::vector<unsigned>& literals) |
2802 | { |
2803 | Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); |
2804 | op->reserveOperands(count: operands.size() + literals.size() + 1); |
2805 | op->addImmediateOperand(immediate: (unsigned) opCode); |
2806 | for (auto it = operands.cbegin(); it != operands.cend(); ++it) |
2807 | op->addIdOperand(id: *it); |
2808 | for (auto it = literals.cbegin(); it != literals.cend(); ++it) |
2809 | op->addImmediateOperand(immediate: *it); |
2810 | module.mapInstruction(instruction: op); |
2811 | constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(op)); |
2812 | |
2813 | // OpSpecConstantOp's using 8 or 16 bit types require the associated capability |
2814 | if (containsType(typeId, typeOp: OpTypeInt, width: 8)) |
2815 | addCapability(cap: CapabilityInt8); |
2816 | if (containsType(typeId, typeOp: OpTypeInt, width: 16)) |
2817 | addCapability(cap: CapabilityInt16); |
2818 | if (containsType(typeId, typeOp: OpTypeFloat, width: 16)) |
2819 | addCapability(cap: CapabilityFloat16); |
2820 | |
2821 | return op->getResultId(); |
2822 | } |
2823 | |
2824 | Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args) |
2825 | { |
2826 | Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); |
2827 | op->reserveOperands(count: args.size() + 1); |
2828 | op->addIdOperand(id: function->getId()); |
2829 | for (int a = 0; a < (int)args.size(); ++a) |
2830 | op->addIdOperand(id: args[a]); |
2831 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
2832 | |
2833 | return op->getResultId(); |
2834 | } |
2835 | |
2836 | // Comments in header |
2837 | Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels) |
2838 | { |
2839 | if (channels.size() == 1) |
2840 | return setPrecision(id: createCompositeExtract(composite: source, typeId, index: channels.front()), precision); |
2841 | |
2842 | if (generatingOpCodeForSpecConst) { |
2843 | std::vector<Id> operands(2); |
2844 | operands[0] = operands[1] = source; |
2845 | return setPrecision(id: createSpecConstantOp(opCode: OpVectorShuffle, typeId, operands, literals: channels), precision); |
2846 | } |
2847 | Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); |
2848 | assert(isVector(source)); |
2849 | swizzle->reserveOperands(count: channels.size() + 2); |
2850 | swizzle->addIdOperand(id: source); |
2851 | swizzle->addIdOperand(id: source); |
2852 | for (int i = 0; i < (int)channels.size(); ++i) |
2853 | swizzle->addImmediateOperand(immediate: channels[i]); |
2854 | addInstruction(inst: std::unique_ptr<Instruction>(swizzle)); |
2855 | |
2856 | return setPrecision(id: swizzle->getResultId(), precision); |
2857 | } |
2858 | |
2859 | // Comments in header |
2860 | Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels) |
2861 | { |
2862 | if (channels.size() == 1 && getNumComponents(resultId: source) == 1) |
2863 | return createCompositeInsert(object: source, composite: target, typeId, index: channels.front()); |
2864 | |
2865 | Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); |
2866 | |
2867 | assert(isVector(target)); |
2868 | swizzle->reserveOperands(count: 2); |
2869 | swizzle->addIdOperand(id: target); |
2870 | |
2871 | assert(getNumComponents(source) == (int)channels.size()); |
2872 | assert(isVector(source)); |
2873 | swizzle->addIdOperand(id: source); |
2874 | |
2875 | // Set up an identity shuffle from the base value to the result value |
2876 | unsigned int components[4]; |
2877 | int numTargetComponents = getNumComponents(resultId: target); |
2878 | for (int i = 0; i < numTargetComponents; ++i) |
2879 | components[i] = i; |
2880 | |
2881 | // Punch in the l-value swizzle |
2882 | for (int i = 0; i < (int)channels.size(); ++i) |
2883 | components[channels[i]] = numTargetComponents + i; |
2884 | |
2885 | // finish the instruction with these components selectors |
2886 | swizzle->reserveOperands(count: numTargetComponents); |
2887 | for (int i = 0; i < numTargetComponents; ++i) |
2888 | swizzle->addImmediateOperand(immediate: components[i]); |
2889 | addInstruction(inst: std::unique_ptr<Instruction>(swizzle)); |
2890 | |
2891 | return swizzle->getResultId(); |
2892 | } |
2893 | |
2894 | // Comments in header |
2895 | void Builder::promoteScalar(Decoration precision, Id& left, Id& right) |
2896 | { |
2897 | int direction = getNumComponents(resultId: right) - getNumComponents(resultId: left); |
2898 | |
2899 | if (direction > 0) |
2900 | left = smearScalar(precision, scalarVal: left, vectorType: makeVectorType(component: getTypeId(resultId: left), size: getNumComponents(resultId: right))); |
2901 | else if (direction < 0) |
2902 | right = smearScalar(precision, scalarVal: right, vectorType: makeVectorType(component: getTypeId(resultId: right), size: getNumComponents(resultId: left))); |
2903 | |
2904 | return; |
2905 | } |
2906 | |
2907 | // Comments in header |
2908 | Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) |
2909 | { |
2910 | assert(getNumComponents(scalar) == 1); |
2911 | assert(getTypeId(scalar) == getScalarTypeId(vectorType)); |
2912 | |
2913 | int numComponents = getNumTypeComponents(typeId: vectorType); |
2914 | if (numComponents == 1) |
2915 | return scalar; |
2916 | |
2917 | Instruction* smear = nullptr; |
2918 | if (generatingOpCodeForSpecConst) { |
2919 | auto members = std::vector<spv::Id>(numComponents, scalar); |
2920 | // Sometime even in spec-constant-op mode, the temporary vector created by |
2921 | // promoting a scalar might not be a spec constant. This should depend on |
2922 | // the scalar. |
2923 | // e.g.: |
2924 | // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; |
2925 | // In such cases, the temporary vector created from a_front_end_const_scalar |
2926 | // is not a spec constant vector, even though the binary operation node is marked |
2927 | // as 'specConstant' and we are in spec-constant-op mode. |
2928 | auto result_id = makeCompositeConstant(typeId: vectorType, members, specConstant: isSpecConstant(resultId: scalar)); |
2929 | smear = module.getInstruction(id: result_id); |
2930 | } else { |
2931 | smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); |
2932 | smear->reserveOperands(count: numComponents); |
2933 | for (int c = 0; c < numComponents; ++c) |
2934 | smear->addIdOperand(id: scalar); |
2935 | addInstruction(inst: std::unique_ptr<Instruction>(smear)); |
2936 | } |
2937 | |
2938 | return setPrecision(id: smear->getResultId(), precision); |
2939 | } |
2940 | |
2941 | // Comments in header |
2942 | Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args) |
2943 | { |
2944 | Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); |
2945 | inst->reserveOperands(count: args.size() + 2); |
2946 | inst->addIdOperand(id: builtins); |
2947 | inst->addImmediateOperand(immediate: entryPoint); |
2948 | for (int arg = 0; arg < (int)args.size(); ++arg) |
2949 | inst->addIdOperand(id: args[arg]); |
2950 | |
2951 | addInstruction(inst: std::unique_ptr<Instruction>(inst)); |
2952 | |
2953 | return inst->getResultId(); |
2954 | } |
2955 | |
2956 | // Accept all parameters needed to create a texture instruction. |
2957 | // Create the correct instruction based on the inputs, and make the call. |
2958 | Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, |
2959 | bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) |
2960 | { |
2961 | std::vector<Id> texArgs; |
2962 | |
2963 | // |
2964 | // Set up the fixed arguments |
2965 | // |
2966 | bool explicitLod = false; |
2967 | texArgs.push_back(x: parameters.sampler); |
2968 | texArgs.push_back(x: parameters.coords); |
2969 | if (parameters.Dref != NoResult) |
2970 | texArgs.push_back(x: parameters.Dref); |
2971 | if (parameters.component != NoResult) |
2972 | texArgs.push_back(x: parameters.component); |
2973 | |
2974 | if (parameters.granularity != NoResult) |
2975 | texArgs.push_back(x: parameters.granularity); |
2976 | if (parameters.coarse != NoResult) |
2977 | texArgs.push_back(x: parameters.coarse); |
2978 | |
2979 | // |
2980 | // Set up the optional arguments |
2981 | // |
2982 | size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any. |
2983 | ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand |
2984 | if (parameters.bias) { |
2985 | mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); |
2986 | texArgs.push_back(x: parameters.bias); |
2987 | } |
2988 | if (parameters.lod) { |
2989 | mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); |
2990 | texArgs.push_back(x: parameters.lod); |
2991 | explicitLod = true; |
2992 | } else if (parameters.gradX) { |
2993 | mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); |
2994 | texArgs.push_back(x: parameters.gradX); |
2995 | texArgs.push_back(x: parameters.gradY); |
2996 | explicitLod = true; |
2997 | } else if (noImplicitLod && ! fetch && ! gather) { |
2998 | // have to explicitly use lod of 0 if not allowed to have them be implicit, and |
2999 | // we would otherwise be about to issue an implicit instruction |
3000 | mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); |
3001 | texArgs.push_back(x: makeFloatConstant(f: 0.0)); |
3002 | explicitLod = true; |
3003 | } |
3004 | if (parameters.offset) { |
3005 | if (isConstant(resultId: parameters.offset)) |
3006 | mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); |
3007 | else { |
3008 | addCapability(cap: CapabilityImageGatherExtended); |
3009 | mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); |
3010 | } |
3011 | texArgs.push_back(x: parameters.offset); |
3012 | } |
3013 | if (parameters.offsets) { |
3014 | addCapability(cap: CapabilityImageGatherExtended); |
3015 | mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); |
3016 | texArgs.push_back(x: parameters.offsets); |
3017 | } |
3018 | if (parameters.sample) { |
3019 | mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); |
3020 | texArgs.push_back(x: parameters.sample); |
3021 | } |
3022 | if (parameters.lodClamp) { |
3023 | // capability if this bit is used |
3024 | addCapability(cap: CapabilityMinLod); |
3025 | |
3026 | mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); |
3027 | texArgs.push_back(x: parameters.lodClamp); |
3028 | } |
3029 | if (parameters.nonprivate) { |
3030 | mask = mask | ImageOperandsNonPrivateTexelKHRMask; |
3031 | } |
3032 | if (parameters.volatil) { |
3033 | mask = mask | ImageOperandsVolatileTexelKHRMask; |
3034 | } |
3035 | mask = mask | signExtensionMask; |
3036 | // insert the operand for the mask, if any bits were set. |
3037 | if (mask != ImageOperandsMaskNone) |
3038 | texArgs.insert(position: texArgs.begin() + optArgNum, x: mask); |
3039 | |
3040 | // |
3041 | // Set up the instruction |
3042 | // |
3043 | Op opCode = OpNop; // All paths below need to set this |
3044 | if (fetch) { |
3045 | if (sparse) |
3046 | opCode = OpImageSparseFetch; |
3047 | else |
3048 | opCode = OpImageFetch; |
3049 | } else if (parameters.granularity && parameters.coarse) { |
3050 | opCode = OpImageSampleFootprintNV; |
3051 | } else if (gather) { |
3052 | if (parameters.Dref) |
3053 | if (sparse) |
3054 | opCode = OpImageSparseDrefGather; |
3055 | else |
3056 | opCode = OpImageDrefGather; |
3057 | else |
3058 | if (sparse) |
3059 | opCode = OpImageSparseGather; |
3060 | else |
3061 | opCode = OpImageGather; |
3062 | } else if (explicitLod) { |
3063 | if (parameters.Dref) { |
3064 | if (proj) |
3065 | if (sparse) |
3066 | opCode = OpImageSparseSampleProjDrefExplicitLod; |
3067 | else |
3068 | opCode = OpImageSampleProjDrefExplicitLod; |
3069 | else |
3070 | if (sparse) |
3071 | opCode = OpImageSparseSampleDrefExplicitLod; |
3072 | else |
3073 | opCode = OpImageSampleDrefExplicitLod; |
3074 | } else { |
3075 | if (proj) |
3076 | if (sparse) |
3077 | opCode = OpImageSparseSampleProjExplicitLod; |
3078 | else |
3079 | opCode = OpImageSampleProjExplicitLod; |
3080 | else |
3081 | if (sparse) |
3082 | opCode = OpImageSparseSampleExplicitLod; |
3083 | else |
3084 | opCode = OpImageSampleExplicitLod; |
3085 | } |
3086 | } else { |
3087 | if (parameters.Dref) { |
3088 | if (proj) |
3089 | if (sparse) |
3090 | opCode = OpImageSparseSampleProjDrefImplicitLod; |
3091 | else |
3092 | opCode = OpImageSampleProjDrefImplicitLod; |
3093 | else |
3094 | if (sparse) |
3095 | opCode = OpImageSparseSampleDrefImplicitLod; |
3096 | else |
3097 | opCode = OpImageSampleDrefImplicitLod; |
3098 | } else { |
3099 | if (proj) |
3100 | if (sparse) |
3101 | opCode = OpImageSparseSampleProjImplicitLod; |
3102 | else |
3103 | opCode = OpImageSampleProjImplicitLod; |
3104 | else |
3105 | if (sparse) |
3106 | opCode = OpImageSparseSampleImplicitLod; |
3107 | else |
3108 | opCode = OpImageSampleImplicitLod; |
3109 | } |
3110 | } |
3111 | |
3112 | // See if the result type is expecting a smeared result. |
3113 | // This happens when a legacy shadow*() call is made, which |
3114 | // gets a vec4 back instead of a float. |
3115 | Id smearedType = resultType; |
3116 | if (! isScalarType(typeId: resultType)) { |
3117 | switch (opCode) { |
3118 | case OpImageSampleDrefImplicitLod: |
3119 | case OpImageSampleDrefExplicitLod: |
3120 | case OpImageSampleProjDrefImplicitLod: |
3121 | case OpImageSampleProjDrefExplicitLod: |
3122 | resultType = getScalarTypeId(typeId: resultType); |
3123 | break; |
3124 | default: |
3125 | break; |
3126 | } |
3127 | } |
3128 | |
3129 | Id typeId0 = 0; |
3130 | Id typeId1 = 0; |
3131 | |
3132 | if (sparse) { |
3133 | typeId0 = resultType; |
3134 | typeId1 = getDerefTypeId(resultId: parameters.texelOut); |
3135 | resultType = makeStructResultType(type0: typeId0, type1: typeId1); |
3136 | } |
3137 | |
3138 | // Build the SPIR-V instruction |
3139 | Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); |
3140 | textureInst->reserveOperands(count: optArgNum + (texArgs.size() - (optArgNum + 1))); |
3141 | for (size_t op = 0; op < optArgNum; ++op) |
3142 | textureInst->addIdOperand(id: texArgs[op]); |
3143 | if (optArgNum < texArgs.size()) |
3144 | textureInst->addImmediateOperand(immediate: texArgs[optArgNum]); |
3145 | for (size_t op = optArgNum + 1; op < texArgs.size(); ++op) |
3146 | textureInst->addIdOperand(id: texArgs[op]); |
3147 | setPrecision(id: textureInst->getResultId(), precision); |
3148 | addInstruction(inst: std::unique_ptr<Instruction>(textureInst)); |
3149 | |
3150 | Id resultId = textureInst->getResultId(); |
3151 | |
3152 | if (sparse) { |
3153 | // set capability |
3154 | addCapability(cap: CapabilitySparseResidency); |
3155 | |
3156 | // Decode the return type that was a special structure |
3157 | createStore(rValue: createCompositeExtract(composite: resultId, typeId: typeId1, index: 1), lValue: parameters.texelOut); |
3158 | resultId = createCompositeExtract(composite: resultId, typeId: typeId0, index: 0); |
3159 | setPrecision(id: resultId, precision); |
3160 | } else { |
3161 | // When a smear is needed, do it, as per what was computed |
3162 | // above when resultType was changed to a scalar type. |
3163 | if (resultType != smearedType) |
3164 | resultId = smearScalar(precision, scalar: resultId, vectorType: smearedType); |
3165 | } |
3166 | |
3167 | return resultId; |
3168 | } |
3169 | |
3170 | // Comments in header |
3171 | Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) |
3172 | { |
3173 | // Figure out the result type |
3174 | Id resultType = 0; |
3175 | switch (opCode) { |
3176 | case OpImageQuerySize: |
3177 | case OpImageQuerySizeLod: |
3178 | { |
3179 | int numComponents = 0; |
3180 | switch (getTypeDimensionality(typeId: getImageType(resultId: parameters.sampler))) { |
3181 | case Dim1D: |
3182 | case DimBuffer: |
3183 | numComponents = 1; |
3184 | break; |
3185 | case Dim2D: |
3186 | case DimCube: |
3187 | case DimRect: |
3188 | case DimSubpassData: |
3189 | numComponents = 2; |
3190 | break; |
3191 | case Dim3D: |
3192 | numComponents = 3; |
3193 | break; |
3194 | |
3195 | default: |
3196 | assert(0); |
3197 | break; |
3198 | } |
3199 | if (isArrayedImageType(typeId: getImageType(resultId: parameters.sampler))) |
3200 | ++numComponents; |
3201 | |
3202 | Id intType = isUnsignedResult ? makeUintType(width: 32) : makeIntType(width: 32); |
3203 | if (numComponents == 1) |
3204 | resultType = intType; |
3205 | else |
3206 | resultType = makeVectorType(component: intType, size: numComponents); |
3207 | |
3208 | break; |
3209 | } |
3210 | case OpImageQueryLod: |
3211 | resultType = makeVectorType(component: getScalarTypeId(typeId: getTypeId(resultId: parameters.coords)), size: 2); |
3212 | break; |
3213 | case OpImageQueryLevels: |
3214 | case OpImageQuerySamples: |
3215 | resultType = isUnsignedResult ? makeUintType(width: 32) : makeIntType(width: 32); |
3216 | break; |
3217 | default: |
3218 | assert(0); |
3219 | break; |
3220 | } |
3221 | |
3222 | Instruction* query = new Instruction(getUniqueId(), resultType, opCode); |
3223 | query->addIdOperand(id: parameters.sampler); |
3224 | if (parameters.coords) |
3225 | query->addIdOperand(id: parameters.coords); |
3226 | if (parameters.lod) |
3227 | query->addIdOperand(id: parameters.lod); |
3228 | addInstruction(inst: std::unique_ptr<Instruction>(query)); |
3229 | addCapability(cap: CapabilityImageQuery); |
3230 | |
3231 | return query->getResultId(); |
3232 | } |
3233 | |
3234 | // External comments in header. |
3235 | // Operates recursively to visit the composite's hierarchy. |
3236 | Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) |
3237 | { |
3238 | Id boolType = makeBoolType(); |
3239 | Id valueType = getTypeId(resultId: value1); |
3240 | |
3241 | Id resultId = NoResult; |
3242 | |
3243 | int numConstituents = getNumTypeConstituents(typeId: valueType); |
3244 | |
3245 | // Scalars and Vectors |
3246 | |
3247 | if (isScalarType(typeId: valueType) || isVectorType(typeId: valueType)) { |
3248 | assert(valueType == getTypeId(value2)); |
3249 | // These just need a single comparison, just have |
3250 | // to figure out what it is. |
3251 | Op op; |
3252 | switch (getMostBasicTypeClass(typeId: valueType)) { |
3253 | case OpTypeFloat: |
3254 | op = equal ? OpFOrdEqual : OpFUnordNotEqual; |
3255 | break; |
3256 | case OpTypeInt: |
3257 | default: |
3258 | op = equal ? OpIEqual : OpINotEqual; |
3259 | break; |
3260 | case OpTypeBool: |
3261 | op = equal ? OpLogicalEqual : OpLogicalNotEqual; |
3262 | precision = NoPrecision; |
3263 | break; |
3264 | } |
3265 | |
3266 | if (isScalarType(typeId: valueType)) { |
3267 | // scalar |
3268 | resultId = createBinOp(opCode: op, typeId: boolType, left: value1, right: value2); |
3269 | } else { |
3270 | // vector |
3271 | resultId = createBinOp(opCode: op, typeId: makeVectorType(component: boolType, size: numConstituents), left: value1, right: value2); |
3272 | setPrecision(id: resultId, precision); |
3273 | // reduce vector compares... |
3274 | resultId = createUnaryOp(opCode: equal ? OpAll : OpAny, typeId: boolType, operand: resultId); |
3275 | } |
3276 | |
3277 | return setPrecision(id: resultId, precision); |
3278 | } |
3279 | |
3280 | // Only structs, arrays, and matrices should be left. |
3281 | // They share in common the reduction operation across their constituents. |
3282 | assert(isAggregateType(valueType) || isMatrixType(valueType)); |
3283 | |
3284 | // Compare each pair of constituents |
3285 | for (int constituent = 0; constituent < numConstituents; ++constituent) { |
3286 | std::vector<unsigned> indexes(1, constituent); |
3287 | Id constituentType1 = getContainedTypeId(typeId: getTypeId(resultId: value1), member: constituent); |
3288 | Id constituentType2 = getContainedTypeId(typeId: getTypeId(resultId: value2), member: constituent); |
3289 | Id constituent1 = createCompositeExtract(composite: value1, typeId: constituentType1, indexes); |
3290 | Id constituent2 = createCompositeExtract(composite: value2, typeId: constituentType2, indexes); |
3291 | |
3292 | Id subResultId = createCompositeCompare(precision, value1: constituent1, value2: constituent2, equal); |
3293 | |
3294 | if (constituent == 0) |
3295 | resultId = subResultId; |
3296 | else |
3297 | resultId = setPrecision(id: createBinOp(opCode: equal ? OpLogicalAnd : OpLogicalOr, typeId: boolType, left: resultId, right: subResultId), |
3298 | precision); |
3299 | } |
3300 | |
3301 | return resultId; |
3302 | } |
3303 | |
3304 | // OpCompositeConstruct |
3305 | Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents) |
3306 | { |
3307 | assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && |
3308 | getNumTypeConstituents(typeId) == (int)constituents.size())); |
3309 | |
3310 | if (generatingOpCodeForSpecConst) { |
3311 | // Sometime, even in spec-constant-op mode, the constant composite to be |
3312 | // constructed may not be a specialization constant. |
3313 | // e.g.: |
3314 | // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); |
3315 | // The first column vector should be a spec constant one, as a_spec_const is a spec constant. |
3316 | // The second column vector should NOT be spec constant, as it does not contain any spec constants. |
3317 | // To handle such cases, we check the constituents of the constant vector to determine whether this |
3318 | // vector should be created as a spec constant. |
3319 | return makeCompositeConstant(typeId, members: constituents, |
3320 | specConstant: std::any_of(first: constituents.begin(), last: constituents.end(), |
3321 | pred: [&](spv::Id id) { return isSpecConstant(resultId: id); })); |
3322 | } |
3323 | |
3324 | Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); |
3325 | op->reserveOperands(count: constituents.size()); |
3326 | for (int c = 0; c < (int)constituents.size(); ++c) |
3327 | op->addIdOperand(id: constituents[c]); |
3328 | addInstruction(inst: std::unique_ptr<Instruction>(op)); |
3329 | |
3330 | return op->getResultId(); |
3331 | } |
3332 | |
3333 | // Vector or scalar constructor |
3334 | Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
3335 | { |
3336 | Id result = NoResult; |
3337 | unsigned int numTargetComponents = getNumTypeComponents(typeId: resultTypeId); |
3338 | unsigned int targetComponent = 0; |
3339 | |
3340 | // Special case: when calling a vector constructor with a single scalar |
3341 | // argument, smear the scalar |
3342 | if (sources.size() == 1 && isScalar(resultId: sources[0]) && numTargetComponents > 1) |
3343 | return smearScalar(precision, scalar: sources[0], vectorType: resultTypeId); |
3344 | |
3345 | // accumulate the arguments for OpCompositeConstruct |
3346 | std::vector<Id> constituents; |
3347 | Id scalarTypeId = getScalarTypeId(typeId: resultTypeId); |
3348 | |
3349 | // lambda to store the result of visiting an argument component |
3350 | const auto latchResult = [&](Id comp) { |
3351 | if (numTargetComponents > 1) |
3352 | constituents.push_back(x: comp); |
3353 | else |
3354 | result = comp; |
3355 | ++targetComponent; |
3356 | }; |
3357 | |
3358 | // lambda to visit a vector argument's components |
3359 | const auto accumulateVectorConstituents = [&](Id sourceArg) { |
3360 | unsigned int sourceSize = getNumComponents(resultId: sourceArg); |
3361 | unsigned int sourcesToUse = sourceSize; |
3362 | if (sourcesToUse + targetComponent > numTargetComponents) |
3363 | sourcesToUse = numTargetComponents - targetComponent; |
3364 | |
3365 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
3366 | std::vector<unsigned> swiz; |
3367 | swiz.push_back(x: s); |
3368 | latchResult(createRvalueSwizzle(precision, typeId: scalarTypeId, source: sourceArg, channels: swiz)); |
3369 | } |
3370 | }; |
3371 | |
3372 | // lambda to visit a matrix argument's components |
3373 | const auto accumulateMatrixConstituents = [&](Id sourceArg) { |
3374 | unsigned int sourceSize = getNumColumns(resultId: sourceArg) * getNumRows(resultId: sourceArg); |
3375 | unsigned int sourcesToUse = sourceSize; |
3376 | if (sourcesToUse + targetComponent > numTargetComponents) |
3377 | sourcesToUse = numTargetComponents - targetComponent; |
3378 | |
3379 | int col = 0; |
3380 | int row = 0; |
3381 | for (unsigned int s = 0; s < sourcesToUse; ++s) { |
3382 | if (row >= getNumRows(resultId: sourceArg)) { |
3383 | row = 0; |
3384 | col++; |
3385 | } |
3386 | std::vector<Id> indexes; |
3387 | indexes.push_back(x: col); |
3388 | indexes.push_back(x: row); |
3389 | latchResult(createCompositeExtract(composite: sourceArg, typeId: scalarTypeId, indexes)); |
3390 | row++; |
3391 | } |
3392 | }; |
3393 | |
3394 | // Go through the source arguments, each one could have either |
3395 | // a single or multiple components to contribute. |
3396 | for (unsigned int i = 0; i < sources.size(); ++i) { |
3397 | |
3398 | if (isScalar(resultId: sources[i]) || isPointer(resultId: sources[i])) |
3399 | latchResult(sources[i]); |
3400 | else if (isVector(resultId: sources[i])) |
3401 | accumulateVectorConstituents(sources[i]); |
3402 | else if (isMatrix(resultId: sources[i])) |
3403 | accumulateMatrixConstituents(sources[i]); |
3404 | else |
3405 | assert(0); |
3406 | |
3407 | if (targetComponent >= numTargetComponents) |
3408 | break; |
3409 | } |
3410 | |
3411 | // If the result is a vector, make it from the gathered constituents. |
3412 | if (constituents.size() > 0) { |
3413 | result = createCompositeConstruct(typeId: resultTypeId, constituents); |
3414 | return setPrecision(id: result, precision); |
3415 | } else { |
3416 | // Precision was set when generating this component. |
3417 | return result; |
3418 | } |
3419 | } |
3420 | |
3421 | // Comments in header |
3422 | Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) |
3423 | { |
3424 | Id componentTypeId = getScalarTypeId(typeId: resultTypeId); |
3425 | int numCols = getTypeNumColumns(typeId: resultTypeId); |
3426 | int numRows = getTypeNumRows(typeId: resultTypeId); |
3427 | |
3428 | Instruction* instr = module.getInstruction(id: componentTypeId); |
3429 | const unsigned bitCount = instr->getImmediateOperand(op: 0); |
3430 | |
3431 | // Optimize matrix constructed from a bigger matrix |
3432 | if (isMatrix(resultId: sources[0]) && getNumColumns(resultId: sources[0]) >= numCols && getNumRows(resultId: sources[0]) >= numRows) { |
3433 | // To truncate the matrix to a smaller number of rows/columns, we need to: |
3434 | // 1. For each column, extract the column and truncate it to the required size using shuffle |
3435 | // 2. Assemble the resulting matrix from all columns |
3436 | Id matrix = sources[0]; |
3437 | Id columnTypeId = getContainedTypeId(typeId: resultTypeId); |
3438 | Id sourceColumnTypeId = getContainedTypeId(typeId: getTypeId(resultId: matrix)); |
3439 | |
3440 | std::vector<unsigned> channels; |
3441 | for (int row = 0; row < numRows; ++row) |
3442 | channels.push_back(x: row); |
3443 | |
3444 | std::vector<Id> matrixColumns; |
3445 | for (int col = 0; col < numCols; ++col) { |
3446 | std::vector<unsigned> indexes; |
3447 | indexes.push_back(x: col); |
3448 | Id colv = createCompositeExtract(composite: matrix, typeId: sourceColumnTypeId, indexes); |
3449 | setPrecision(id: colv, precision); |
3450 | |
3451 | if (numRows != getNumRows(resultId: matrix)) { |
3452 | matrixColumns.push_back(x: createRvalueSwizzle(precision, typeId: columnTypeId, source: colv, channels)); |
3453 | } else { |
3454 | matrixColumns.push_back(x: colv); |
3455 | } |
3456 | } |
3457 | |
3458 | return setPrecision(id: createCompositeConstruct(typeId: resultTypeId, constituents: matrixColumns), precision); |
3459 | } |
3460 | |
3461 | // Otherwise, will use a two step process |
3462 | // 1. make a compile-time 2D array of values |
3463 | // 2. construct a matrix from that array |
3464 | |
3465 | // Step 1. |
3466 | |
3467 | // initialize the array to the identity matrix |
3468 | Id ids[maxMatrixSize][maxMatrixSize]; |
3469 | Id one = (bitCount == 64 ? makeDoubleConstant(d: 1.0) : makeFloatConstant(f: 1.0)); |
3470 | Id zero = (bitCount == 64 ? makeDoubleConstant(d: 0.0) : makeFloatConstant(f: 0.0)); |
3471 | for (int col = 0; col < 4; ++col) { |
3472 | for (int row = 0; row < 4; ++row) { |
3473 | if (col == row) |
3474 | ids[col][row] = one; |
3475 | else |
3476 | ids[col][row] = zero; |
3477 | } |
3478 | } |
3479 | |
3480 | // modify components as dictated by the arguments |
3481 | if (sources.size() == 1 && isScalar(resultId: sources[0])) { |
3482 | // a single scalar; resets the diagonals |
3483 | for (int col = 0; col < 4; ++col) |
3484 | ids[col][col] = sources[0]; |
3485 | } else if (isMatrix(resultId: sources[0])) { |
3486 | // constructing from another matrix; copy over the parts that exist in both the argument and constructee |
3487 | Id matrix = sources[0]; |
3488 | int minCols = std::min(a: numCols, b: getNumColumns(resultId: matrix)); |
3489 | int minRows = std::min(a: numRows, b: getNumRows(resultId: matrix)); |
3490 | for (int col = 0; col < minCols; ++col) { |
3491 | std::vector<unsigned> indexes; |
3492 | indexes.push_back(x: col); |
3493 | for (int row = 0; row < minRows; ++row) { |
3494 | indexes.push_back(x: row); |
3495 | ids[col][row] = createCompositeExtract(composite: matrix, typeId: componentTypeId, indexes); |
3496 | indexes.pop_back(); |
3497 | setPrecision(id: ids[col][row], precision); |
3498 | } |
3499 | } |
3500 | } else { |
3501 | // fill in the matrix in column-major order with whatever argument components are available |
3502 | int row = 0; |
3503 | int col = 0; |
3504 | |
3505 | for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) { |
3506 | Id argComp = sources[arg]; |
3507 | for (int comp = 0; comp < getNumComponents(resultId: sources[arg]); ++comp) { |
3508 | if (getNumComponents(resultId: sources[arg]) > 1) { |
3509 | argComp = createCompositeExtract(composite: sources[arg], typeId: componentTypeId, index: comp); |
3510 | setPrecision(id: argComp, precision); |
3511 | } |
3512 | ids[col][row++] = argComp; |
3513 | if (row == numRows) { |
3514 | row = 0; |
3515 | col++; |
3516 | } |
3517 | if (col == numCols) { |
3518 | // If more components are provided than fit the matrix, discard the rest. |
3519 | break; |
3520 | } |
3521 | } |
3522 | } |
3523 | } |
3524 | |
3525 | // Step 2: Construct a matrix from that array. |
3526 | // First make the column vectors, then make the matrix. |
3527 | |
3528 | // make the column vectors |
3529 | Id columnTypeId = getContainedTypeId(typeId: resultTypeId); |
3530 | std::vector<Id> matrixColumns; |
3531 | for (int col = 0; col < numCols; ++col) { |
3532 | std::vector<Id> vectorComponents; |
3533 | for (int row = 0; row < numRows; ++row) |
3534 | vectorComponents.push_back(x: ids[col][row]); |
3535 | Id column = createCompositeConstruct(typeId: columnTypeId, constituents: vectorComponents); |
3536 | setPrecision(id: column, precision); |
3537 | matrixColumns.push_back(x: column); |
3538 | } |
3539 | |
3540 | // make the matrix |
3541 | return setPrecision(id: createCompositeConstruct(typeId: resultTypeId, constituents: matrixColumns), precision); |
3542 | } |
3543 | |
3544 | // Comments in header |
3545 | Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : |
3546 | builder(gb), |
3547 | condition(cond), |
3548 | control(ctrl), |
3549 | elseBlock(nullptr) |
3550 | { |
3551 | function = &builder.getBuildPoint()->getParent(); |
3552 | |
3553 | // make the blocks, but only put the then-block into the function, |
3554 | // the else-block and merge-block will be added later, in order, after |
3555 | // earlier code is emitted |
3556 | thenBlock = new Block(builder.getUniqueId(), *function); |
3557 | mergeBlock = new Block(builder.getUniqueId(), *function); |
3558 | |
3559 | // Save the current block, so that we can add in the flow control split when |
3560 | // makeEndIf is called. |
3561 | headerBlock = builder.getBuildPoint(); |
3562 | |
3563 | function->addBlock(block: thenBlock); |
3564 | builder.setBuildPoint(thenBlock); |
3565 | } |
3566 | |
3567 | // Comments in header |
3568 | void Builder::If::makeBeginElse() |
3569 | { |
3570 | // Close out the "then" by having it jump to the mergeBlock |
3571 | builder.createBranch(block: mergeBlock); |
3572 | |
3573 | // Make the first else block and add it to the function |
3574 | elseBlock = new Block(builder.getUniqueId(), *function); |
3575 | function->addBlock(block: elseBlock); |
3576 | |
3577 | // Start building the else block |
3578 | builder.setBuildPoint(elseBlock); |
3579 | } |
3580 | |
3581 | // Comments in header |
3582 | void Builder::If::makeEndIf() |
3583 | { |
3584 | // jump to the merge block |
3585 | builder.createBranch(block: mergeBlock); |
3586 | |
3587 | // Go back to the headerBlock and make the flow control split |
3588 | builder.setBuildPoint(headerBlock); |
3589 | builder.createSelectionMerge(mergeBlock, control); |
3590 | if (elseBlock) |
3591 | builder.createConditionalBranch(condition, thenBlock, elseBlock); |
3592 | else |
3593 | builder.createConditionalBranch(condition, thenBlock, elseBlock: mergeBlock); |
3594 | |
3595 | // add the merge block to the function |
3596 | function->addBlock(block: mergeBlock); |
3597 | builder.setBuildPoint(mergeBlock); |
3598 | } |
3599 | |
3600 | // Comments in header |
3601 | void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues, |
3602 | const std::vector<int>& valueIndexToSegment, int defaultSegment, |
3603 | std::vector<Block*>& segmentBlocks) |
3604 | { |
3605 | Function& function = buildPoint->getParent(); |
3606 | |
3607 | // make all the blocks |
3608 | for (int s = 0; s < numSegments; ++s) |
3609 | segmentBlocks.push_back(x: new Block(getUniqueId(), function)); |
3610 | |
3611 | Block* mergeBlock = new Block(getUniqueId(), function); |
3612 | |
3613 | // make and insert the switch's selection-merge instruction |
3614 | createSelectionMerge(mergeBlock, control); |
3615 | |
3616 | // make the switch instruction |
3617 | Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); |
3618 | switchInst->reserveOperands(count: (caseValues.size() * 2) + 2); |
3619 | switchInst->addIdOperand(id: selector); |
3620 | auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; |
3621 | switchInst->addIdOperand(id: defaultOrMerge->getId()); |
3622 | defaultOrMerge->addPredecessor(pred: buildPoint); |
3623 | for (int i = 0; i < (int)caseValues.size(); ++i) { |
3624 | switchInst->addImmediateOperand(immediate: caseValues[i]); |
3625 | switchInst->addIdOperand(id: segmentBlocks[valueIndexToSegment[i]]->getId()); |
3626 | segmentBlocks[valueIndexToSegment[i]]->addPredecessor(pred: buildPoint); |
3627 | } |
3628 | addInstruction(inst: std::unique_ptr<Instruction>(switchInst)); |
3629 | |
3630 | // push the merge block |
3631 | switchMerges.push(x: mergeBlock); |
3632 | } |
3633 | |
3634 | // Comments in header |
3635 | void Builder::addSwitchBreak() |
3636 | { |
3637 | // branch to the top of the merge block stack |
3638 | createBranch(block: switchMerges.top()); |
3639 | createAndSetNoPredecessorBlock("post-switch-break"); |
3640 | } |
3641 | |
3642 | // Comments in header |
3643 | void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment) |
3644 | { |
3645 | int lastSegment = nextSegment - 1; |
3646 | if (lastSegment >= 0) { |
3647 | // Close out previous segment by jumping, if necessary, to next segment |
3648 | if (! buildPoint->isTerminated()) |
3649 | createBranch(block: segmentBlock[nextSegment]); |
3650 | } |
3651 | Block* block = segmentBlock[nextSegment]; |
3652 | block->getParent().addBlock(block); |
3653 | setBuildPoint(block); |
3654 | } |
3655 | |
3656 | // Comments in header |
3657 | void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/) |
3658 | { |
3659 | // Close out previous segment by jumping, if necessary, to next segment |
3660 | if (! buildPoint->isTerminated()) |
3661 | addSwitchBreak(); |
3662 | |
3663 | switchMerges.top()->getParent().addBlock(block: switchMerges.top()); |
3664 | setBuildPoint(switchMerges.top()); |
3665 | |
3666 | switchMerges.pop(); |
3667 | } |
3668 | |
3669 | Block& Builder::makeNewBlock() |
3670 | { |
3671 | Function& function = buildPoint->getParent(); |
3672 | auto block = new Block(getUniqueId(), function); |
3673 | function.addBlock(block); |
3674 | return *block; |
3675 | } |
3676 | |
3677 | Builder::LoopBlocks& Builder::makeNewLoop() |
3678 | { |
3679 | // This verbosity is needed to simultaneously get the same behavior |
3680 | // everywhere (id's in the same order), have a syntax that works |
3681 | // across lots of versions of C++, have no warnings from pedantic |
3682 | // compilation modes, and leave the rest of the code alone. |
3683 | Block& head = makeNewBlock(); |
3684 | Block& body = makeNewBlock(); |
3685 | Block& merge = makeNewBlock(); |
3686 | Block& continue_target = makeNewBlock(); |
3687 | LoopBlocks blocks(head, body, merge, continue_target); |
3688 | loops.push(x: blocks); |
3689 | return loops.top(); |
3690 | } |
3691 | |
3692 | void Builder::createLoopContinue() |
3693 | { |
3694 | createBranch(block: &loops.top().continue_target); |
3695 | // Set up a block for dead code. |
3696 | createAndSetNoPredecessorBlock("post-loop-continue"); |
3697 | } |
3698 | |
3699 | void Builder::createLoopExit() |
3700 | { |
3701 | createBranch(block: &loops.top().merge); |
3702 | // Set up a block for dead code. |
3703 | createAndSetNoPredecessorBlock("post-loop-break"); |
3704 | } |
3705 | |
3706 | void Builder::closeLoop() |
3707 | { |
3708 | loops.pop(); |
3709 | } |
3710 | |
3711 | void Builder::clearAccessChain() |
3712 | { |
3713 | accessChain.base = NoResult; |
3714 | accessChain.indexChain.clear(); |
3715 | accessChain.instr = NoResult; |
3716 | accessChain.swizzle.clear(); |
3717 | accessChain.component = NoResult; |
3718 | accessChain.preSwizzleBaseType = NoType; |
3719 | accessChain.isRValue = false; |
3720 | accessChain.coherentFlags.clear(); |
3721 | accessChain.alignment = 0; |
3722 | } |
3723 | |
3724 | // Comments in header |
3725 | void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, |
3726 | AccessChain::CoherentFlags coherentFlags, unsigned int alignment) |
3727 | { |
3728 | accessChain.coherentFlags |= coherentFlags; |
3729 | accessChain.alignment |= alignment; |
3730 | |
3731 | // swizzles can be stacked in GLSL, but simplified to a single |
3732 | // one here; the base type doesn't change |
3733 | if (accessChain.preSwizzleBaseType == NoType) |
3734 | accessChain.preSwizzleBaseType = preSwizzleBaseType; |
3735 | |
3736 | // if needed, propagate the swizzle for the current access chain |
3737 | if (accessChain.swizzle.size() > 0) { |
3738 | std::vector<unsigned> oldSwizzle = accessChain.swizzle; |
3739 | accessChain.swizzle.resize(new_size: 0); |
3740 | for (unsigned int i = 0; i < swizzle.size(); ++i) { |
3741 | assert(swizzle[i] < oldSwizzle.size()); |
3742 | accessChain.swizzle.push_back(x: oldSwizzle[swizzle[i]]); |
3743 | } |
3744 | } else |
3745 | accessChain.swizzle = swizzle; |
3746 | |
3747 | // determine if we need to track this swizzle anymore |
3748 | simplifyAccessChainSwizzle(); |
3749 | } |
3750 | |
3751 | // Comments in header |
3752 | void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) |
3753 | { |
3754 | assert(accessChain.isRValue == false); |
3755 | |
3756 | transferAccessChainSwizzle(dynamic: true); |
3757 | |
3758 | // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores. |
3759 | if (accessChain.swizzle.size() > 0 && |
3760 | getNumTypeComponents(typeId: getResultingAccessChainType()) != (int)accessChain.swizzle.size() && |
3761 | accessChain.component == NoResult) { |
3762 | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
3763 | accessChain.indexChain.push_back(x: makeUintConstant(u: accessChain.swizzle[i])); |
3764 | accessChain.instr = NoResult; |
3765 | |
3766 | Id base = collapseAccessChain(); |
3767 | addDecoration(id: base, decoration: nonUniform); |
3768 | |
3769 | accessChain.indexChain.pop_back(); |
3770 | accessChain.instr = NoResult; |
3771 | |
3772 | // dynamic component should be gone |
3773 | assert(accessChain.component == NoResult); |
3774 | |
3775 | Id source = createCompositeExtract(composite: rvalue, typeId: getContainedTypeId(typeId: getTypeId(resultId: rvalue)), index: i); |
3776 | |
3777 | // take LSB of alignment |
3778 | alignment = alignment & ~(alignment & (alignment-1)); |
3779 | if (getStorageClass(resultId: base) == StorageClassPhysicalStorageBufferEXT) { |
3780 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3781 | } |
3782 | |
3783 | createStore(rValue: source, lValue: base, memoryAccess, scope, alignment); |
3784 | } |
3785 | } |
3786 | else { |
3787 | Id base = collapseAccessChain(); |
3788 | addDecoration(id: base, decoration: nonUniform); |
3789 | |
3790 | Id source = rvalue; |
3791 | |
3792 | // dynamic component should be gone |
3793 | assert(accessChain.component == NoResult); |
3794 | |
3795 | // If swizzle still exists, it may be out-of-order, we must load the target vector, |
3796 | // extract and insert elements to perform writeMask and/or swizzle. |
3797 | if (accessChain.swizzle.size() > 0) { |
3798 | Id tempBaseId = createLoad(lValue: base, precision: spv::NoPrecision); |
3799 | source = createLvalueSwizzle(typeId: getTypeId(resultId: tempBaseId), target: tempBaseId, source, channels: accessChain.swizzle); |
3800 | } |
3801 | |
3802 | // take LSB of alignment |
3803 | alignment = alignment & ~(alignment & (alignment-1)); |
3804 | if (getStorageClass(resultId: base) == StorageClassPhysicalStorageBufferEXT) { |
3805 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3806 | } |
3807 | |
3808 | createStore(rValue: source, lValue: base, memoryAccess, scope, alignment); |
3809 | } |
3810 | } |
3811 | |
3812 | // Comments in header |
3813 | Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform, |
3814 | Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, |
3815 | spv::Scope scope, unsigned int alignment) |
3816 | { |
3817 | Id id; |
3818 | |
3819 | if (accessChain.isRValue) { |
3820 | // transfer access chain, but try to stay in registers |
3821 | transferAccessChainSwizzle(dynamic: false); |
3822 | if (accessChain.indexChain.size() > 0) { |
3823 | Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; |
3824 | |
3825 | // if all the accesses are constants, we can use OpCompositeExtract |
3826 | std::vector<unsigned> indexes; |
3827 | bool constant = true; |
3828 | for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { |
3829 | if (isConstantScalar(resultId: accessChain.indexChain[i])) |
3830 | indexes.push_back(x: getConstantScalar(resultId: accessChain.indexChain[i])); |
3831 | else { |
3832 | constant = false; |
3833 | break; |
3834 | } |
3835 | } |
3836 | |
3837 | if (constant) { |
3838 | id = createCompositeExtract(composite: accessChain.base, typeId: swizzleBase, indexes); |
3839 | setPrecision(id, precision); |
3840 | } else { |
3841 | Id lValue = NoResult; |
3842 | if (spvVersion >= Spv_1_4 && isValidInitializer(resultId: accessChain.base)) { |
3843 | // make a new function variable for this r-value, using an initializer, |
3844 | // and mark it as NonWritable so that downstream it can be detected as a lookup |
3845 | // table |
3846 | lValue = createVariable(precision: NoPrecision, storageClass: StorageClassFunction, type: getTypeId(resultId: accessChain.base), |
3847 | name: "indexable", initializer: accessChain.base); |
3848 | addDecoration(id: lValue, decoration: DecorationNonWritable); |
3849 | } else { |
3850 | lValue = createVariable(precision: NoPrecision, storageClass: StorageClassFunction, type: getTypeId(resultId: accessChain.base), |
3851 | name: "indexable"); |
3852 | // store into it |
3853 | createStore(rValue: accessChain.base, lValue); |
3854 | } |
3855 | // move base to the new variable |
3856 | accessChain.base = lValue; |
3857 | accessChain.isRValue = false; |
3858 | |
3859 | // load through the access chain |
3860 | id = createLoad(lValue: collapseAccessChain(), precision); |
3861 | } |
3862 | } else |
3863 | id = accessChain.base; // no precision, it was set when this was defined |
3864 | } else { |
3865 | transferAccessChainSwizzle(dynamic: true); |
3866 | |
3867 | // take LSB of alignment |
3868 | alignment = alignment & ~(alignment & (alignment-1)); |
3869 | if (getStorageClass(resultId: accessChain.base) == StorageClassPhysicalStorageBufferEXT) { |
3870 | memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); |
3871 | } |
3872 | |
3873 | // load through the access chain |
3874 | id = collapseAccessChain(); |
3875 | // Apply nonuniform both to the access chain and the loaded value. |
3876 | // Buffer accesses need the access chain decorated, and this is where |
3877 | // loaded image types get decorated. TODO: This should maybe move to |
3878 | // createImageTextureFunctionCall. |
3879 | addDecoration(id, decoration: l_nonUniform); |
3880 | id = createLoad(lValue: id, precision, memoryAccess, scope, alignment); |
3881 | addDecoration(id, decoration: r_nonUniform); |
3882 | } |
3883 | |
3884 | // Done, unless there are swizzles to do |
3885 | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
3886 | return id; |
3887 | |
3888 | // Do remaining swizzling |
3889 | |
3890 | // Do the basic swizzle |
3891 | if (accessChain.swizzle.size() > 0) { |
3892 | Id swizzledType = getScalarTypeId(typeId: getTypeId(resultId: id)); |
3893 | if (accessChain.swizzle.size() > 1) |
3894 | swizzledType = makeVectorType(component: swizzledType, size: (int)accessChain.swizzle.size()); |
3895 | id = createRvalueSwizzle(precision, typeId: swizzledType, source: id, channels: accessChain.swizzle); |
3896 | } |
3897 | |
3898 | // Do the dynamic component |
3899 | if (accessChain.component != NoResult) |
3900 | id = setPrecision(id: createVectorExtractDynamic(vector: id, typeId: resultType, componentIndex: accessChain.component), precision); |
3901 | |
3902 | addDecoration(id, decoration: r_nonUniform); |
3903 | return id; |
3904 | } |
3905 | |
3906 | Id Builder::accessChainGetLValue() |
3907 | { |
3908 | assert(accessChain.isRValue == false); |
3909 | |
3910 | transferAccessChainSwizzle(dynamic: true); |
3911 | Id lvalue = collapseAccessChain(); |
3912 | |
3913 | // If swizzle exists, it is out-of-order or not full, we must load the target vector, |
3914 | // extract and insert elements to perform writeMask and/or swizzle. This does not |
3915 | // go with getting a direct l-value pointer. |
3916 | assert(accessChain.swizzle.size() == 0); |
3917 | assert(accessChain.component == NoResult); |
3918 | |
3919 | return lvalue; |
3920 | } |
3921 | |
3922 | // comment in header |
3923 | Id Builder::accessChainGetInferredType() |
3924 | { |
3925 | // anything to operate on? |
3926 | if (accessChain.base == NoResult) |
3927 | return NoType; |
3928 | Id type = getTypeId(resultId: accessChain.base); |
3929 | |
3930 | // do initial dereference |
3931 | if (! accessChain.isRValue) |
3932 | type = getContainedTypeId(typeId: type); |
3933 | |
3934 | // dereference each index |
3935 | for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { |
3936 | if (isStructType(typeId: type)) |
3937 | type = getContainedTypeId(typeId: type, member: getConstantScalar(resultId: *it)); |
3938 | else |
3939 | type = getContainedTypeId(typeId: type); |
3940 | } |
3941 | |
3942 | // dereference swizzle |
3943 | if (accessChain.swizzle.size() == 1) |
3944 | type = getContainedTypeId(typeId: type); |
3945 | else if (accessChain.swizzle.size() > 1) |
3946 | type = makeVectorType(component: getContainedTypeId(typeId: type), size: (int)accessChain.swizzle.size()); |
3947 | |
3948 | // dereference component selection |
3949 | if (accessChain.component) |
3950 | type = getContainedTypeId(typeId: type); |
3951 | |
3952 | return type; |
3953 | } |
3954 | |
3955 | void Builder::dump(std::vector<unsigned int>& out) const |
3956 | { |
3957 | // Header, before first instructions: |
3958 | out.push_back(x: MagicNumber); |
3959 | out.push_back(x: spvVersion); |
3960 | out.push_back(x: builderNumber); |
3961 | out.push_back(x: uniqueId + 1); |
3962 | out.push_back(x: 0); |
3963 | |
3964 | // Capabilities |
3965 | for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { |
3966 | Instruction capInst(0, 0, OpCapability); |
3967 | capInst.addImmediateOperand(immediate: *it); |
3968 | capInst.dump(out); |
3969 | } |
3970 | |
3971 | for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { |
3972 | Instruction extInst(0, 0, OpExtension); |
3973 | extInst.addStringOperand(str: it->c_str()); |
3974 | extInst.dump(out); |
3975 | } |
3976 | |
3977 | dumpInstructions(out, imports); |
3978 | Instruction memInst(0, 0, OpMemoryModel); |
3979 | memInst.addImmediateOperand(immediate: addressModel); |
3980 | memInst.addImmediateOperand(immediate: memoryModel); |
3981 | memInst.dump(out); |
3982 | |
3983 | // Instructions saved up while building: |
3984 | dumpInstructions(out, entryPoints); |
3985 | dumpInstructions(out, executionModes); |
3986 | |
3987 | // Debug instructions |
3988 | dumpInstructions(out, strings); |
3989 | dumpSourceInstructions(out); |
3990 | for (int e = 0; e < (int)sourceExtensions.size(); ++e) { |
3991 | Instruction sourceExtInst(0, 0, OpSourceExtension); |
3992 | sourceExtInst.addStringOperand(str: sourceExtensions[e]); |
3993 | sourceExtInst.dump(out); |
3994 | } |
3995 | dumpInstructions(out, names); |
3996 | dumpModuleProcesses(out); |
3997 | |
3998 | // Annotation instructions |
3999 | dumpInstructions(out, decorations); |
4000 | |
4001 | dumpInstructions(out, constantsTypesGlobals); |
4002 | dumpInstructions(out, externals); |
4003 | |
4004 | // The functions |
4005 | module.dump(out); |
4006 | } |
4007 | |
4008 | // |
4009 | // Protected methods. |
4010 | // |
4011 | |
4012 | // Turn the described access chain in 'accessChain' into an instruction(s) |
4013 | // computing its address. This *cannot* include complex swizzles, which must |
4014 | // be handled after this is called. |
4015 | // |
4016 | // Can generate code. |
4017 | Id Builder::collapseAccessChain() |
4018 | { |
4019 | assert(accessChain.isRValue == false); |
4020 | |
4021 | // did we already emit an access chain for this? |
4022 | if (accessChain.instr != NoResult) |
4023 | return accessChain.instr; |
4024 | |
4025 | // If we have a dynamic component, we can still transfer |
4026 | // that into a final operand to the access chain. We need to remap the |
4027 | // dynamic component through the swizzle to get a new dynamic component to |
4028 | // update. |
4029 | // |
4030 | // This was not done in transferAccessChainSwizzle() because it might |
4031 | // generate code. |
4032 | remapDynamicSwizzle(); |
4033 | if (accessChain.component != NoResult) { |
4034 | // transfer the dynamic component to the access chain |
4035 | accessChain.indexChain.push_back(x: accessChain.component); |
4036 | accessChain.component = NoResult; |
4037 | } |
4038 | |
4039 | // note that non-trivial swizzling is left pending |
4040 | |
4041 | // do we have an access chain? |
4042 | if (accessChain.indexChain.size() == 0) |
4043 | return accessChain.base; |
4044 | |
4045 | // emit the access chain |
4046 | StorageClass storageClass = (StorageClass)module.getStorageClass(typeId: getTypeId(resultId: accessChain.base)); |
4047 | accessChain.instr = createAccessChain(storageClass, base: accessChain.base, offsets: accessChain.indexChain); |
4048 | |
4049 | return accessChain.instr; |
4050 | } |
4051 | |
4052 | // For a dynamic component selection of a swizzle. |
4053 | // |
4054 | // Turn the swizzle and dynamic component into just a dynamic component. |
4055 | // |
4056 | // Generates code. |
4057 | void Builder::remapDynamicSwizzle() |
4058 | { |
4059 | // do we have a swizzle to remap a dynamic component through? |
4060 | if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { |
4061 | // build a vector of the swizzle for the component to map into |
4062 | std::vector<Id> components; |
4063 | for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) |
4064 | components.push_back(x: makeUintConstant(u: accessChain.swizzle[c])); |
4065 | Id mapType = makeVectorType(component: makeUintType(width: 32), size: (int)accessChain.swizzle.size()); |
4066 | Id map = makeCompositeConstant(typeId: mapType, members: components); |
4067 | |
4068 | // use it |
4069 | accessChain.component = createVectorExtractDynamic(vector: map, typeId: makeUintType(width: 32), componentIndex: accessChain.component); |
4070 | accessChain.swizzle.clear(); |
4071 | } |
4072 | } |
4073 | |
4074 | // clear out swizzle if it is redundant, that is reselecting the same components |
4075 | // that would be present without the swizzle. |
4076 | void Builder::simplifyAccessChainSwizzle() |
4077 | { |
4078 | // If the swizzle has fewer components than the vector, it is subsetting, and must stay |
4079 | // to preserve that fact. |
4080 | if (getNumTypeComponents(typeId: accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) |
4081 | return; |
4082 | |
4083 | // if components are out of order, it is a swizzle |
4084 | for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { |
4085 | if (i != accessChain.swizzle[i]) |
4086 | return; |
4087 | } |
4088 | |
4089 | // otherwise, there is no need to track this swizzle |
4090 | accessChain.swizzle.clear(); |
4091 | if (accessChain.component == NoResult) |
4092 | accessChain.preSwizzleBaseType = NoType; |
4093 | } |
4094 | |
4095 | // To the extent any swizzling can become part of the chain |
4096 | // of accesses instead of a post operation, make it so. |
4097 | // If 'dynamic' is true, include transferring the dynamic component, |
4098 | // otherwise, leave it pending. |
4099 | // |
4100 | // Does not generate code. just updates the access chain. |
4101 | void Builder::transferAccessChainSwizzle(bool dynamic) |
4102 | { |
4103 | // non existent? |
4104 | if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) |
4105 | return; |
4106 | |
4107 | // too complex? |
4108 | // (this requires either a swizzle, or generating code for a dynamic component) |
4109 | if (accessChain.swizzle.size() > 1) |
4110 | return; |
4111 | |
4112 | // single component, either in the swizzle and/or dynamic component |
4113 | if (accessChain.swizzle.size() == 1) { |
4114 | assert(accessChain.component == NoResult); |
4115 | // handle static component selection |
4116 | accessChain.indexChain.push_back(x: makeUintConstant(u: accessChain.swizzle.front())); |
4117 | accessChain.swizzle.clear(); |
4118 | accessChain.preSwizzleBaseType = NoType; |
4119 | } else if (dynamic && accessChain.component != NoResult) { |
4120 | assert(accessChain.swizzle.size() == 0); |
4121 | // handle dynamic component |
4122 | accessChain.indexChain.push_back(x: accessChain.component); |
4123 | accessChain.preSwizzleBaseType = NoType; |
4124 | accessChain.component = NoResult; |
4125 | } |
4126 | } |
4127 | |
4128 | // Utility method for creating a new block and setting the insert point to |
4129 | // be in it. This is useful for flow-control operations that need a "dummy" |
4130 | // block proceeding them (e.g. instructions after a discard, etc). |
4131 | void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) |
4132 | { |
4133 | Block* block = new Block(getUniqueId(), buildPoint->getParent()); |
4134 | block->setUnreachable(); |
4135 | buildPoint->getParent().addBlock(block); |
4136 | setBuildPoint(block); |
4137 | |
4138 | // if (name) |
4139 | // addName(block->getId(), name); |
4140 | } |
4141 | |
4142 | // Comments in header |
4143 | void Builder::createBranch(Block* block) |
4144 | { |
4145 | Instruction* branch = new Instruction(OpBranch); |
4146 | branch->addIdOperand(id: block->getId()); |
4147 | addInstruction(inst: std::unique_ptr<Instruction>(branch)); |
4148 | block->addPredecessor(pred: buildPoint); |
4149 | } |
4150 | |
4151 | void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) |
4152 | { |
4153 | Instruction* merge = new Instruction(OpSelectionMerge); |
4154 | merge->reserveOperands(count: 2); |
4155 | merge->addIdOperand(id: mergeBlock->getId()); |
4156 | merge->addImmediateOperand(immediate: control); |
4157 | addInstruction(inst: std::unique_ptr<Instruction>(merge)); |
4158 | } |
4159 | |
4160 | void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, |
4161 | const std::vector<unsigned int>& operands) |
4162 | { |
4163 | Instruction* merge = new Instruction(OpLoopMerge); |
4164 | merge->reserveOperands(count: operands.size() + 3); |
4165 | merge->addIdOperand(id: mergeBlock->getId()); |
4166 | merge->addIdOperand(id: continueBlock->getId()); |
4167 | merge->addImmediateOperand(immediate: control); |
4168 | for (int op = 0; op < (int)operands.size(); ++op) |
4169 | merge->addImmediateOperand(immediate: operands[op]); |
4170 | addInstruction(inst: std::unique_ptr<Instruction>(merge)); |
4171 | } |
4172 | |
4173 | void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) |
4174 | { |
4175 | Instruction* branch = new Instruction(OpBranchConditional); |
4176 | branch->reserveOperands(count: 3); |
4177 | branch->addIdOperand(id: condition); |
4178 | branch->addIdOperand(id: thenBlock->getId()); |
4179 | branch->addIdOperand(id: elseBlock->getId()); |
4180 | addInstruction(inst: std::unique_ptr<Instruction>(branch)); |
4181 | thenBlock->addPredecessor(pred: buildPoint); |
4182 | elseBlock->addPredecessor(pred: buildPoint); |
4183 | } |
4184 | |
4185 | // OpSource |
4186 | // [OpSourceContinued] |
4187 | // ... |
4188 | void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, |
4189 | std::vector<unsigned int>& out) const |
4190 | { |
4191 | const int maxWordCount = 0xFFFF; |
4192 | const int opSourceWordCount = 4; |
4193 | const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; |
4194 | |
4195 | if (sourceLang != SourceLanguageUnknown) { |
4196 | // OpSource Language Version File Source |
4197 | Instruction sourceInst(NoResult, NoType, OpSource); |
4198 | sourceInst.reserveOperands(count: 3); |
4199 | sourceInst.addImmediateOperand(immediate: sourceLang); |
4200 | sourceInst.addImmediateOperand(immediate: sourceVersion); |
4201 | // File operand |
4202 | if (fileId != NoResult) { |
4203 | sourceInst.addIdOperand(id: fileId); |
4204 | // Source operand |
4205 | if (text.size() > 0) { |
4206 | int nextByte = 0; |
4207 | std::string subString; |
4208 | while ((int)text.size() - nextByte > 0) { |
4209 | subString = text.substr(pos: nextByte, n: nonNullBytesPerInstruction); |
4210 | if (nextByte == 0) { |
4211 | // OpSource |
4212 | sourceInst.addStringOperand(str: subString.c_str()); |
4213 | sourceInst.dump(out); |
4214 | } else { |
4215 | // OpSourcContinued |
4216 | Instruction sourceContinuedInst(OpSourceContinued); |
4217 | sourceContinuedInst.addStringOperand(str: subString.c_str()); |
4218 | sourceContinuedInst.dump(out); |
4219 | } |
4220 | nextByte += nonNullBytesPerInstruction; |
4221 | } |
4222 | } else |
4223 | sourceInst.dump(out); |
4224 | } else |
4225 | sourceInst.dump(out); |
4226 | } |
4227 | } |
4228 | |
4229 | // Dump an OpSource[Continued] sequence for the source and every include file |
4230 | void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const |
4231 | { |
4232 | if (emitNonSemanticShaderDebugInfo) return; |
4233 | dumpSourceInstructions(fileId: mainFileId, text: sourceText, out); |
4234 | for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) |
4235 | dumpSourceInstructions(fileId: iItr->first, text: *iItr->second, out); |
4236 | } |
4237 | |
4238 | void Builder::dumpInstructions(std::vector<unsigned int>& out, |
4239 | const std::vector<std::unique_ptr<Instruction> >& instructions) const |
4240 | { |
4241 | for (int i = 0; i < (int)instructions.size(); ++i) { |
4242 | instructions[i]->dump(out); |
4243 | } |
4244 | } |
4245 | |
4246 | void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const |
4247 | { |
4248 | for (int i = 0; i < (int)moduleProcesses.size(); ++i) { |
4249 | Instruction moduleProcessed(OpModuleProcessed); |
4250 | moduleProcessed.addStringOperand(str: moduleProcesses[i]); |
4251 | moduleProcessed.dump(out); |
4252 | } |
4253 | } |
4254 | |
4255 | } // end spv namespace |
4256 |
Definitions
- Builder
- ~Builder
- import
- makeVoidType
- makeBoolType
- makeSamplerType
- makePointer
- makeForwardPointer
- makePointerFromForwardPointer
- makeIntegerType
- makeFloatType
- makeStructType
- makeStructResultType
- makeVectorType
- makeMatrixType
- makeCooperativeMatrixTypeKHR
- makeCooperativeMatrixTypeNV
- makeCooperativeMatrixTypeWithSameShape
- makeGenericType
- makeArrayType
- makeRuntimeArray
- makeFunctionType
- makeDebugFunctionType
- makeImageType
- makeSampledImageType
- makeDebugInfoNone
- makeBoolDebugType
- makeIntegerDebugType
- makeFloatDebugType
- makeSequentialDebugType
- makeArrayDebugType
- makeVectorDebugType
- makeMatrixDebugType
- makeMemberDebugType
- makeCompositeDebugType
- makePointerDebugType
- makeDebugSource
- makeDebugCompilationUnit
- createDebugGlobalVariable
- createDebugLocalVariable
- makeDebugExpression
- makeDebugDeclare
- makeDebugValue
- makeAccelerationStructureType
- makeRayQueryType
- makeHitObjectNVType
- getDerefTypeId
- getMostBasicTypeClass
- getNumTypeConstituents
- getScalarTypeId
- getContainedTypeId
- getResultingAccessChainType
- getContainedTypeId
- containsType
- containsPhysicalStorageBufferOrArray
- findScalarConstant
- findScalarConstant
- isConstantOpCode
- isSpecConstantOpCode
- makeNullConstant
- makeBoolConstant
- makeIntConstant
- makeInt64Constant
- makeFloatConstant
- makeDoubleConstant
- makeFloat16Constant
- makeFpConstant
- importNonSemanticShaderDebugInfoInstructions
- findCompositeConstant
- findStructConstant
- makeCompositeConstant
- addEntryPoint
- addExecutionMode
- addExecutionMode
- addExecutionModeId
- addName
- addMemberName
- addDecoration
- addDecoration
- addDecoration
- addDecoration
- addLinkageDecoration
- addDecorationId
- addDecorationId
- addMemberDecoration
- addMemberDecoration
- addMemberDecoration
- addMemberDecoration
- addInstruction
- makeEntryPoint
- makeFunctionEntry
- setupDebugFunctionEntry
- makeDebugFunction
- makeDebugLexicalBlock
- unmangleFunctionName
- makeReturn
- enterLexicalBlock
- leaveLexicalBlock
- enterFunction
- leaveFunction
- makeStatementTerminator
- makeStatementTerminator
- createVariable
- createUndefined
- sanitizeMemoryAccessForStorageClass
- createStore
- createLoad
- createAccessChain
- createArrayLength
- createCooperativeMatrixLengthKHR
- createCooperativeMatrixLengthNV
- createCompositeExtract
- createCompositeExtract
- createCompositeInsert
- createCompositeInsert
- createVectorExtractDynamic
- createVectorInsertDynamic
- createNoResultOp
- createNoResultOp
- createNoResultOp
- createNoResultOp
- createControlBarrier
- createMemoryBarrier
- createUnaryOp
- createBinOp
- createTriOp
- createOp
- createOp
- createSpecConstantOp
- createFunctionCall
- createRvalueSwizzle
- createLvalueSwizzle
- promoteScalar
- smearScalar
- createBuiltinCall
- createTextureCall
- createTextureQueryCall
- createCompositeCompare
- createCompositeConstruct
- createConstructor
- createMatrixConstructor
- If
- makeBeginElse
- makeEndIf
- makeSwitch
- addSwitchBreak
- nextSwitchSegment
- endSwitch
- makeNewBlock
- makeNewLoop
- createLoopContinue
- createLoopExit
- closeLoop
- clearAccessChain
- accessChainPushSwizzle
- accessChainStore
- accessChainLoad
- accessChainGetLValue
- accessChainGetInferredType
- dump
- collapseAccessChain
- remapDynamicSwizzle
- simplifyAccessChainSwizzle
- transferAccessChainSwizzle
- createAndSetNoPredecessorBlock
- createBranch
- createSelectionMerge
- createLoopMerge
- createConditionalBranch
- dumpSourceInstructions
- dumpSourceInstructions
- dumpInstructions
Learn Advanced QML with KDAB
Find out more