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
55namespace spv {
56
57Builder::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
73Builder::~Builder()
74{
75}
76
77Id 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).
88Id 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
106Id 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
127Id 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
147Id 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
175Id 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
188Id 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
210Id 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
252Id 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.
294Id 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.
319Id 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
341Id 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
370Id 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
403Id 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
432Id 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
457Id 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
468Id 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
505Id 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
536Id 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
552Id 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
604Id 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
628Id 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
727Id 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
754Id 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
772Id 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
801Id 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
842Id 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
878Id 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
907Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount)
908{
909 return makeSequentialDebugType(baseType, componentCount, sequenceType: NonSemanticShaderDebugInfo100DebugTypeArray);
910}
911
912Id Builder::makeVectorDebugType(Id const baseType, int const componentCount)
913{
914 return makeSequentialDebugType(baseType, componentCount: makeUintConstant(u: componentCount), sequenceType: NonSemanticShaderDebugInfo100DebugTypeVector);
915}
916
917Id 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
944Id 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.
970Id 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
1019Id 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
1048Id 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
1079Id 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
1103Id 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
1127Id 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
1153Id 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
1171Id 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
1185Id 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
1199Id 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
1218Id 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
1237Id 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
1252Id 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
1260Op 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
1279int 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.
1313Id 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.
1339Id 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.
1364Id 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.
1384Id 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.
1392bool 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.
1422bool 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).
1446Id 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').
1461Id 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.
1479bool 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.
1501bool 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
1515Id 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
1539Id 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
1568Id 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
1589Id 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
1615Id 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
1640Id 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
1669Id 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
1697Id 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
1718Id 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
1731Id 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
1758Id 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
1783Id 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
1827Instruction* 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.
1841void 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
1861void 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
1877void 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
1893void 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
1903void 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
1914void 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
1929void 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
1943void 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
1958void 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
1973void 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
1984void 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
1998void 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
2014void 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
2030void 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
2045void 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
2061void 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
2077void 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
2124Function* 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
2144Function* 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
2183void 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
2236Id 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
2262Id 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
2279std::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
2291void 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
2305void 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
2314void 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
2322void 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
2354void 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
2377void 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
2384void 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
2394Id 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
2438Id 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.
2446spv::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
2466void 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
2490Id 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
2515Id 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
2532Id 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
2544Id 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
2561Id 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
2578Id 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
2595Id 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
2612Id 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
2624Id 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
2637Id 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
2648Id 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
2661void 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
2668void 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
2676void 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
2687void 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
2700void 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
2710void 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
2720Id 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
2734Id 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
2752Id 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
2774Id 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
2785Id 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
2800Id 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
2824Id 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
2837Id 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
2860Id 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
2895void 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
2908Id 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
2942Id 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.
2958Id 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
3171Id 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.
3236Id 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
3305Id 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
3334Id 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
3422Id 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
3545Builder::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
3568void 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
3582void 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
3601void 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
3635void 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
3643void 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
3657void 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
3669Block& Builder::makeNewBlock()
3670{
3671 Function& function = buildPoint->getParent();
3672 auto block = new Block(getUniqueId(), function);
3673 function.addBlock(block);
3674 return *block;
3675}
3676
3677Builder::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
3692void 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
3699void Builder::createLoopExit()
3700{
3701 createBranch(block: &loops.top().merge);
3702 // Set up a block for dead code.
3703 createAndSetNoPredecessorBlock("post-loop-break");
3704}
3705
3706void Builder::closeLoop()
3707{
3708 loops.pop();
3709}
3710
3711void 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
3725void 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
3752void 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
3813Id 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
3906Id 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
3923Id 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
3955void 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.
4017Id 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.
4057void 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.
4076void 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.
4101void 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).
4131void 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
4143void 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
4151void 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
4160void 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
4173void 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// ...
4188void 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
4230void 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
4238void 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
4246void 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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtshadertools/src/3rdparty/glslang/SPIRV/SpvBuilder.cpp