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

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