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
50#ifndef GLSLANG_WEB
51#include "hex_float.h"
52#endif
53
54#ifndef _WIN32
55 #include <cstdio>
56#endif
57
58namespace spv {
59
60Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
61 spvVersion(spvVersion),
62 source(SourceLanguageUnknown),
63 sourceVersion(0),
64 sourceFileStringId(NoResult),
65 currentLine(0),
66 currentFile(nullptr),
67 emitOpLines(false),
68 addressModel(AddressingModelLogical),
69 memoryModel(MemoryModelGLSL450),
70 builderNumber(magicNumber),
71 buildPoint(0),
72 uniqueId(0),
73 entryPointFunction(0),
74 generatingOpCodeForSpecConst(false),
75 logger(buildLogger)
76{
77 clearAccessChain();
78}
79
80Builder::~Builder()
81{
82}
83
84Id Builder::import(const char* name)
85{
86 Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
87 import->addStringOperand(str: name);
88 module.mapInstruction(instruction: import);
89
90 imports.push_back(x: std::unique_ptr<Instruction>(import));
91 return import->getResultId();
92}
93
94// Emit instruction for non-filename-based #line directives (ie. no filename
95// seen yet): emit an OpLine if we've been asked to emit OpLines and the line
96// number has changed since the last time, and is a valid line number.
97void Builder::setLine(int lineNum)
98{
99 if (lineNum != 0 && lineNum != currentLine) {
100 currentLine = lineNum;
101 if (emitOpLines)
102 addLine(fileName: sourceFileStringId, line: currentLine, column: 0);
103 }
104}
105
106// If no filename, do non-filename-based #line emit. Else do filename-based emit.
107// Emit OpLine if we've been asked to emit OpLines and the line number or filename
108// has changed since the last time, and line number is valid.
109void Builder::setLine(int lineNum, const char* filename)
110{
111 if (filename == nullptr) {
112 setLine(lineNum);
113 return;
114 }
115 if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
116 strncmp(s1: filename, s2: currentFile, n: strlen(s: currentFile) + 1) != 0) {
117 currentLine = lineNum;
118 currentFile = filename;
119 if (emitOpLines) {
120 spv::Id strId = getStringId(str: filename);
121 addLine(fileName: strId, line: currentLine, column: 0);
122 }
123 }
124}
125
126void Builder::addLine(Id fileName, int lineNum, int column)
127{
128 Instruction* line = new Instruction(OpLine);
129 line->addIdOperand(id: fileName);
130 line->addImmediateOperand(immediate: lineNum);
131 line->addImmediateOperand(immediate: column);
132 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(line));
133}
134
135// For creating new groupedTypes (will return old type if the requested one was already made).
136Id Builder::makeVoidType()
137{
138 Instruction* type;
139 if (groupedTypes[OpTypeVoid].size() == 0) {
140 type = new Instruction(getUniqueId(), NoType, OpTypeVoid);
141 groupedTypes[OpTypeVoid].push_back(x: type);
142 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
143 module.mapInstruction(instruction: type);
144 } else
145 type = groupedTypes[OpTypeVoid].back();
146
147 return type->getResultId();
148}
149
150Id Builder::makeBoolType()
151{
152 Instruction* type;
153 if (groupedTypes[OpTypeBool].size() == 0) {
154 type = new Instruction(getUniqueId(), NoType, OpTypeBool);
155 groupedTypes[OpTypeBool].push_back(x: type);
156 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
157 module.mapInstruction(instruction: type);
158 } else
159 type = groupedTypes[OpTypeBool].back();
160
161 return type->getResultId();
162}
163
164Id Builder::makeSamplerType()
165{
166 Instruction* type;
167 if (groupedTypes[OpTypeSampler].size() == 0) {
168 type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
169 groupedTypes[OpTypeSampler].push_back(x: type);
170 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
171 module.mapInstruction(instruction: type);
172 } else
173 type = groupedTypes[OpTypeSampler].back();
174
175 return type->getResultId();
176}
177
178Id Builder::makePointer(StorageClass storageClass, Id pointee)
179{
180 // try to find it
181 Instruction* type;
182 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
183 type = groupedTypes[OpTypePointer][t];
184 if (type->getImmediateOperand(op: 0) == (unsigned)storageClass &&
185 type->getIdOperand(op: 1) == pointee)
186 return type->getResultId();
187 }
188
189 // not found, make it
190 type = new Instruction(getUniqueId(), NoType, OpTypePointer);
191 type->addImmediateOperand(immediate: storageClass);
192 type->addIdOperand(id: pointee);
193 groupedTypes[OpTypePointer].push_back(x: type);
194 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
195 module.mapInstruction(instruction: type);
196
197 return type->getResultId();
198}
199
200Id Builder::makeForwardPointer(StorageClass storageClass)
201{
202 // Caching/uniquifying doesn't work here, because we don't know the
203 // pointee type and there can be multiple forward pointers of the same
204 // storage type. Somebody higher up in the stack must keep track.
205 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);
206 type->addImmediateOperand(immediate: storageClass);
207 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
208 module.mapInstruction(instruction: type);
209
210 return type->getResultId();
211}
212
213Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
214{
215 // try to find it
216 Instruction* type;
217 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
218 type = groupedTypes[OpTypePointer][t];
219 if (type->getImmediateOperand(op: 0) == (unsigned)storageClass &&
220 type->getIdOperand(op: 1) == pointee)
221 return type->getResultId();
222 }
223
224 type = new Instruction(forwardPointerType, NoType, OpTypePointer);
225 type->addImmediateOperand(immediate: storageClass);
226 type->addIdOperand(id: pointee);
227 groupedTypes[OpTypePointer].push_back(x: type);
228 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
229 module.mapInstruction(instruction: type);
230
231 return type->getResultId();
232}
233
234Id Builder::makeIntegerType(int width, bool hasSign)
235{
236#ifdef GLSLANG_WEB
237 assert(width == 32);
238 width = 32;
239#endif
240
241 // try to find it
242 Instruction* type;
243 for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
244 type = groupedTypes[OpTypeInt][t];
245 if (type->getImmediateOperand(op: 0) == (unsigned)width &&
246 type->getImmediateOperand(op: 1) == (hasSign ? 1u : 0u))
247 return type->getResultId();
248 }
249
250 // not found, make it
251 type = new Instruction(getUniqueId(), NoType, OpTypeInt);
252 type->addImmediateOperand(immediate: width);
253 type->addImmediateOperand(immediate: hasSign ? 1 : 0);
254 groupedTypes[OpTypeInt].push_back(x: type);
255 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
256 module.mapInstruction(instruction: type);
257
258 // deal with capabilities
259 switch (width) {
260 case 8:
261 case 16:
262 // these are currently handled by storage-type declarations and post processing
263 break;
264 case 64:
265 addCapability(cap: CapabilityInt64);
266 break;
267 default:
268 break;
269 }
270
271 return type->getResultId();
272}
273
274Id Builder::makeFloatType(int width)
275{
276#ifdef GLSLANG_WEB
277 assert(width == 32);
278 width = 32;
279#endif
280
281 // try to find it
282 Instruction* type;
283 for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
284 type = groupedTypes[OpTypeFloat][t];
285 if (type->getImmediateOperand(op: 0) == (unsigned)width)
286 return type->getResultId();
287 }
288
289 // not found, make it
290 type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
291 type->addImmediateOperand(immediate: width);
292 groupedTypes[OpTypeFloat].push_back(x: type);
293 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
294 module.mapInstruction(instruction: type);
295
296 // deal with capabilities
297 switch (width) {
298 case 16:
299 // currently handled by storage-type declarations and post processing
300 break;
301 case 64:
302 addCapability(cap: CapabilityFloat64);
303 break;
304 default:
305 break;
306 }
307
308 return type->getResultId();
309}
310
311// Make a struct without checking for duplication.
312// See makeStructResultType() for non-decorated structs
313// needed as the result of some instructions, which does
314// check for duplicates.
315Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
316{
317 // Don't look for previous one, because in the general case,
318 // structs can be duplicated except for decorations.
319
320 // not found, make it
321 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
322 for (int op = 0; op < (int)members.size(); ++op)
323 type->addIdOperand(id: members[op]);
324 groupedTypes[OpTypeStruct].push_back(x: type);
325 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
326 module.mapInstruction(instruction: type);
327 addName(type->getResultId(), name);
328
329 return type->getResultId();
330}
331
332// Make a struct for the simple results of several instructions,
333// checking for duplication.
334Id Builder::makeStructResultType(Id type0, Id type1)
335{
336 // try to find it
337 Instruction* type;
338 for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
339 type = groupedTypes[OpTypeStruct][t];
340 if (type->getNumOperands() != 2)
341 continue;
342 if (type->getIdOperand(op: 0) != type0 ||
343 type->getIdOperand(op: 1) != type1)
344 continue;
345 return type->getResultId();
346 }
347
348 // not found, make it
349 std::vector<spv::Id> members;
350 members.push_back(x: type0);
351 members.push_back(x: type1);
352
353 return makeStructType(members, name: "ResType");
354}
355
356Id Builder::makeVectorType(Id component, int size)
357{
358 // try to find it
359 Instruction* type;
360 for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
361 type = groupedTypes[OpTypeVector][t];
362 if (type->getIdOperand(op: 0) == component &&
363 type->getImmediateOperand(op: 1) == (unsigned)size)
364 return type->getResultId();
365 }
366
367 // not found, make it
368 type = new Instruction(getUniqueId(), NoType, OpTypeVector);
369 type->addIdOperand(id: component);
370 type->addImmediateOperand(immediate: size);
371 groupedTypes[OpTypeVector].push_back(x: type);
372 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
373 module.mapInstruction(instruction: type);
374
375 return type->getResultId();
376}
377
378Id Builder::makeMatrixType(Id component, int cols, int rows)
379{
380 assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
381
382 Id column = makeVectorType(component, size: rows);
383
384 // try to find it
385 Instruction* type;
386 for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
387 type = groupedTypes[OpTypeMatrix][t];
388 if (type->getIdOperand(op: 0) == column &&
389 type->getImmediateOperand(op: 1) == (unsigned)cols)
390 return type->getResultId();
391 }
392
393 // not found, make it
394 type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
395 type->addIdOperand(id: column);
396 type->addImmediateOperand(immediate: cols);
397 groupedTypes[OpTypeMatrix].push_back(x: type);
398 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
399 module.mapInstruction(instruction: type);
400
401 return type->getResultId();
402}
403
404Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols)
405{
406 // try to find it
407 Instruction* type;
408 for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {
409 type = groupedTypes[OpTypeCooperativeMatrixNV][t];
410 if (type->getIdOperand(op: 0) == component &&
411 type->getIdOperand(op: 1) == scope &&
412 type->getIdOperand(op: 2) == rows &&
413 type->getIdOperand(op: 3) == cols)
414 return type->getResultId();
415 }
416
417 // not found, make it
418 type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);
419 type->addIdOperand(id: component);
420 type->addIdOperand(id: scope);
421 type->addIdOperand(id: rows);
422 type->addIdOperand(id: cols);
423 groupedTypes[OpTypeCooperativeMatrixNV].push_back(x: type);
424 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
425 module.mapInstruction(instruction: type);
426
427 return type->getResultId();
428}
429
430Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)
431{
432 // try to find it
433 Instruction* type;
434 for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {
435 type = groupedTypes[opcode][t];
436 if (static_cast<size_t>(type->getNumOperands()) != operands.size())
437 continue; // Number mismatch, find next
438
439 bool match = true;
440 for (int op = 0; match && op < (int)operands.size(); ++op) {
441 match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;
442 }
443 if (match)
444 return type->getResultId();
445 }
446
447 // not found, make it
448 type = new Instruction(getUniqueId(), NoType, opcode);
449 for (size_t op = 0; op < operands.size(); ++op) {
450 if (operands[op].isId)
451 type->addIdOperand(id: operands[op].word);
452 else
453 type->addImmediateOperand(immediate: operands[op].word);
454 }
455 groupedTypes[opcode].push_back(x: type);
456 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
457 module.mapInstruction(instruction: type);
458
459 return type->getResultId();
460}
461
462// TODO: performance: track arrays per stride
463// If a stride is supplied (non-zero) make an array.
464// If no stride (0), reuse previous array types.
465// 'size' is an Id of a constant or specialization constant of the array size
466Id Builder::makeArrayType(Id element, Id sizeId, int stride)
467{
468 Instruction* type;
469 if (stride == 0) {
470 // try to find existing type
471 for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
472 type = groupedTypes[OpTypeArray][t];
473 if (type->getIdOperand(op: 0) == element &&
474 type->getIdOperand(op: 1) == sizeId)
475 return type->getResultId();
476 }
477 }
478
479 // not found, make it
480 type = new Instruction(getUniqueId(), NoType, OpTypeArray);
481 type->addIdOperand(id: element);
482 type->addIdOperand(id: sizeId);
483 groupedTypes[OpTypeArray].push_back(x: type);
484 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
485 module.mapInstruction(instruction: type);
486
487 return type->getResultId();
488}
489
490Id Builder::makeRuntimeArray(Id element)
491{
492 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
493 type->addIdOperand(id: element);
494 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
495 module.mapInstruction(instruction: type);
496
497 return type->getResultId();
498}
499
500Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
501{
502 // try to find it
503 Instruction* type;
504 for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
505 type = groupedTypes[OpTypeFunction][t];
506 if (type->getIdOperand(op: 0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
507 continue;
508 bool mismatch = false;
509 for (int p = 0; p < (int)paramTypes.size(); ++p) {
510 if (paramTypes[p] != type->getIdOperand(op: p + 1)) {
511 mismatch = true;
512 break;
513 }
514 }
515 if (! mismatch)
516 return type->getResultId();
517 }
518
519 // not found, make it
520 type = new Instruction(getUniqueId(), NoType, OpTypeFunction);
521 type->addIdOperand(id: returnType);
522 for (int p = 0; p < (int)paramTypes.size(); ++p)
523 type->addIdOperand(id: paramTypes[p]);
524 groupedTypes[OpTypeFunction].push_back(x: type);
525 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
526 module.mapInstruction(instruction: type);
527
528 return type->getResultId();
529}
530
531Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,
532 ImageFormat format)
533{
534 assert(sampled == 1 || sampled == 2);
535
536 // try to find it
537 Instruction* type;
538 for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
539 type = groupedTypes[OpTypeImage][t];
540 if (type->getIdOperand(op: 0) == sampledType &&
541 type->getImmediateOperand(op: 1) == (unsigned int)dim &&
542 type->getImmediateOperand(op: 2) == ( depth ? 1u : 0u) &&
543 type->getImmediateOperand(op: 3) == (arrayed ? 1u : 0u) &&
544 type->getImmediateOperand(op: 4) == ( ms ? 1u : 0u) &&
545 type->getImmediateOperand(op: 5) == sampled &&
546 type->getImmediateOperand(op: 6) == (unsigned int)format)
547 return type->getResultId();
548 }
549
550 // not found, make it
551 type = new Instruction(getUniqueId(), NoType, OpTypeImage);
552 type->addIdOperand(id: sampledType);
553 type->addImmediateOperand( immediate: dim);
554 type->addImmediateOperand( immediate: depth ? 1 : 0);
555 type->addImmediateOperand(immediate: arrayed ? 1 : 0);
556 type->addImmediateOperand( immediate: ms ? 1 : 0);
557 type->addImmediateOperand(immediate: sampled);
558 type->addImmediateOperand(immediate: (unsigned int)format);
559
560 groupedTypes[OpTypeImage].push_back(x: type);
561 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
562 module.mapInstruction(instruction: type);
563
564#ifndef GLSLANG_WEB
565 // deal with capabilities
566 switch (dim) {
567 case DimBuffer:
568 if (sampled == 1)
569 addCapability(cap: CapabilitySampledBuffer);
570 else
571 addCapability(cap: CapabilityImageBuffer);
572 break;
573 case Dim1D:
574 if (sampled == 1)
575 addCapability(cap: CapabilitySampled1D);
576 else
577 addCapability(cap: CapabilityImage1D);
578 break;
579 case DimCube:
580 if (arrayed) {
581 if (sampled == 1)
582 addCapability(cap: CapabilitySampledCubeArray);
583 else
584 addCapability(cap: CapabilityImageCubeArray);
585 }
586 break;
587 case DimRect:
588 if (sampled == 1)
589 addCapability(cap: CapabilitySampledRect);
590 else
591 addCapability(cap: CapabilityImageRect);
592 break;
593 case DimSubpassData:
594 addCapability(cap: CapabilityInputAttachment);
595 break;
596 default:
597 break;
598 }
599
600 if (ms) {
601 if (sampled == 2) {
602 // Images used with subpass data are not storage
603 // images, so don't require the capability for them.
604 if (dim != Dim::DimSubpassData)
605 addCapability(cap: CapabilityStorageImageMultisample);
606 if (arrayed)
607 addCapability(cap: CapabilityImageMSArray);
608 }
609 }
610#endif
611
612 return type->getResultId();
613}
614
615Id Builder::makeSampledImageType(Id imageType)
616{
617 // try to find it
618 Instruction* type;
619 for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
620 type = groupedTypes[OpTypeSampledImage][t];
621 if (type->getIdOperand(op: 0) == imageType)
622 return type->getResultId();
623 }
624
625 // not found, make it
626 type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
627 type->addIdOperand(id: imageType);
628
629 groupedTypes[OpTypeSampledImage].push_back(x: type);
630 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
631 module.mapInstruction(instruction: type);
632
633 return type->getResultId();
634}
635
636#ifndef GLSLANG_WEB
637Id Builder::makeAccelerationStructureType()
638{
639 Instruction *type;
640 if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {
641 type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);
642 groupedTypes[OpTypeAccelerationStructureKHR].push_back(x: type);
643 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
644 module.mapInstruction(instruction: type);
645 } else {
646 type = groupedTypes[OpTypeAccelerationStructureKHR].back();
647 }
648
649 return type->getResultId();
650}
651
652Id Builder::makeRayQueryType()
653{
654 Instruction *type;
655 if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {
656 type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);
657 groupedTypes[OpTypeRayQueryKHR].push_back(x: type);
658 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(type));
659 module.mapInstruction(instruction: type);
660 } else {
661 type = groupedTypes[OpTypeRayQueryKHR].back();
662 }
663
664 return type->getResultId();
665}
666#endif
667
668Id Builder::getDerefTypeId(Id resultId) const
669{
670 Id typeId = getTypeId(resultId);
671 assert(isPointerType(typeId));
672
673 return module.getInstruction(id: typeId)->getIdOperand(op: 1);
674}
675
676Op Builder::getMostBasicTypeClass(Id typeId) const
677{
678 Instruction* instr = module.getInstruction(id: typeId);
679
680 Op typeClass = instr->getOpCode();
681 switch (typeClass)
682 {
683 case OpTypeVector:
684 case OpTypeMatrix:
685 case OpTypeArray:
686 case OpTypeRuntimeArray:
687 return getMostBasicTypeClass(typeId: instr->getIdOperand(op: 0));
688 case OpTypePointer:
689 return getMostBasicTypeClass(typeId: instr->getIdOperand(op: 1));
690 default:
691 return typeClass;
692 }
693}
694
695int Builder::getNumTypeConstituents(Id typeId) const
696{
697 Instruction* instr = module.getInstruction(id: typeId);
698
699 switch (instr->getOpCode())
700 {
701 case OpTypeBool:
702 case OpTypeInt:
703 case OpTypeFloat:
704 case OpTypePointer:
705 return 1;
706 case OpTypeVector:
707 case OpTypeMatrix:
708 return instr->getImmediateOperand(op: 1);
709 case OpTypeArray:
710 {
711 Id lengthId = instr->getIdOperand(op: 1);
712 return module.getInstruction(id: lengthId)->getImmediateOperand(op: 0);
713 }
714 case OpTypeStruct:
715 return instr->getNumOperands();
716 case OpTypeCooperativeMatrixNV:
717 // has only one constituent when used with OpCompositeConstruct.
718 return 1;
719 default:
720 assert(0);
721 return 1;
722 }
723}
724
725// Return the lowest-level type of scalar that an homogeneous composite is made out of.
726// Typically, this is just to find out if something is made out of ints or floats.
727// However, it includes returning a structure, if say, it is an array of structure.
728Id Builder::getScalarTypeId(Id typeId) const
729{
730 Instruction* instr = module.getInstruction(id: typeId);
731
732 Op typeClass = instr->getOpCode();
733 switch (typeClass)
734 {
735 case OpTypeVoid:
736 case OpTypeBool:
737 case OpTypeInt:
738 case OpTypeFloat:
739 case OpTypeStruct:
740 return instr->getResultId();
741 case OpTypeVector:
742 case OpTypeMatrix:
743 case OpTypeArray:
744 case OpTypeRuntimeArray:
745 case OpTypePointer:
746 return getScalarTypeId(typeId: getContainedTypeId(typeId));
747 default:
748 assert(0);
749 return NoResult;
750 }
751}
752
753// Return the type of 'member' of a composite.
754Id Builder::getContainedTypeId(Id typeId, int member) const
755{
756 Instruction* instr = module.getInstruction(id: typeId);
757
758 Op typeClass = instr->getOpCode();
759 switch (typeClass)
760 {
761 case OpTypeVector:
762 case OpTypeMatrix:
763 case OpTypeArray:
764 case OpTypeRuntimeArray:
765 case OpTypeCooperativeMatrixNV:
766 return instr->getIdOperand(op: 0);
767 case OpTypePointer:
768 return instr->getIdOperand(op: 1);
769 case OpTypeStruct:
770 return instr->getIdOperand(op: member);
771 default:
772 assert(0);
773 return NoResult;
774 }
775}
776
777// Figure out the final resulting type of the access chain.
778Id Builder::getResultingAccessChainType() const
779{
780 assert(accessChain.base != NoResult);
781 Id typeId = getTypeId(resultId: accessChain.base);
782
783 assert(isPointerType(typeId));
784 typeId = getContainedTypeId(typeId);
785
786 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
787 if (isStructType(typeId)) {
788 assert(isConstantScalar(accessChain.indexChain[i]));
789 typeId = getContainedTypeId(typeId, member: getConstantScalar(resultId: accessChain.indexChain[i]));
790 } else
791 typeId = getContainedTypeId(typeId, member: accessChain.indexChain[i]);
792 }
793
794 return typeId;
795}
796
797// Return the immediately contained type of a given composite type.
798Id Builder::getContainedTypeId(Id typeId) const
799{
800 return getContainedTypeId(typeId, member: 0);
801}
802
803// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
804// of width 'width'. The 'width' is only consumed for int and float types.
805// Returns false otherwise.
806bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
807{
808 const Instruction& instr = *module.getInstruction(id: typeId);
809
810 Op typeClass = instr.getOpCode();
811 switch (typeClass)
812 {
813 case OpTypeInt:
814 case OpTypeFloat:
815 return typeClass == typeOp && instr.getImmediateOperand(op: 0) == width;
816 case OpTypeStruct:
817 for (int m = 0; m < instr.getNumOperands(); ++m) {
818 if (containsType(typeId: instr.getIdOperand(op: m), typeOp, width))
819 return true;
820 }
821 return false;
822 case OpTypePointer:
823 return false;
824 case OpTypeVector:
825 case OpTypeMatrix:
826 case OpTypeArray:
827 case OpTypeRuntimeArray:
828 return containsType(typeId: getContainedTypeId(typeId), typeOp, width);
829 default:
830 return typeClass == typeOp;
831 }
832}
833
834// return true if the type is a pointer to PhysicalStorageBufferEXT or an
835// array of such pointers. These require restrict/aliased decorations.
836bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
837{
838 const Instruction& instr = *module.getInstruction(id: typeId);
839
840 Op typeClass = instr.getOpCode();
841 switch (typeClass)
842 {
843 case OpTypePointer:
844 return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
845 case OpTypeArray:
846 return containsPhysicalStorageBufferOrArray(typeId: getContainedTypeId(typeId));
847 default:
848 return false;
849 }
850}
851
852// See if a scalar constant of this type has already been created, so it
853// can be reused rather than duplicated. (Required by the specification).
854Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
855{
856 Instruction* constant;
857 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
858 constant = groupedConstants[typeClass][i];
859 if (constant->getOpCode() == opcode &&
860 constant->getTypeId() == typeId &&
861 constant->getImmediateOperand(op: 0) == value)
862 return constant->getResultId();
863 }
864
865 return 0;
866}
867
868// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
869Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
870{
871 Instruction* constant;
872 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
873 constant = groupedConstants[typeClass][i];
874 if (constant->getOpCode() == opcode &&
875 constant->getTypeId() == typeId &&
876 constant->getImmediateOperand(op: 0) == v1 &&
877 constant->getImmediateOperand(op: 1) == v2)
878 return constant->getResultId();
879 }
880
881 return 0;
882}
883
884// Return true if consuming 'opcode' means consuming a constant.
885// "constant" here means after final transform to executable code,
886// the value consumed will be a constant, so includes specialization.
887bool Builder::isConstantOpCode(Op opcode) const
888{
889 switch (opcode) {
890 case OpUndef:
891 case OpConstantTrue:
892 case OpConstantFalse:
893 case OpConstant:
894 case OpConstantComposite:
895 case OpConstantSampler:
896 case OpConstantNull:
897 case OpSpecConstantTrue:
898 case OpSpecConstantFalse:
899 case OpSpecConstant:
900 case OpSpecConstantComposite:
901 case OpSpecConstantOp:
902 return true;
903 default:
904 return false;
905 }
906}
907
908// Return true if consuming 'opcode' means consuming a specialization constant.
909bool Builder::isSpecConstantOpCode(Op opcode) const
910{
911 switch (opcode) {
912 case OpSpecConstantTrue:
913 case OpSpecConstantFalse:
914 case OpSpecConstant:
915 case OpSpecConstantComposite:
916 case OpSpecConstantOp:
917 return true;
918 default:
919 return false;
920 }
921}
922
923Id Builder::makeNullConstant(Id typeId)
924{
925 Instruction* constant;
926
927 // See if we already made it.
928 Id existing = NoResult;
929 for (int i = 0; i < (int)nullConstants.size(); ++i) {
930 constant = nullConstants[i];
931 if (constant->getTypeId() == typeId)
932 existing = constant->getResultId();
933 }
934
935 if (existing != NoResult)
936 return existing;
937
938 // Make it
939 Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);
940 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
941 nullConstants.push_back(x: c);
942 module.mapInstruction(instruction: c);
943
944 return c->getResultId();
945}
946
947Id Builder::makeBoolConstant(bool b, bool specConstant)
948{
949 Id typeId = makeBoolType();
950 Instruction* constant;
951 Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
952
953 // See if we already made it. Applies only to regular constants, because specialization constants
954 // must remain distinct for the purpose of applying a SpecId decoration.
955 if (! specConstant) {
956 Id existing = 0;
957 for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
958 constant = groupedConstants[OpTypeBool][i];
959 if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
960 existing = constant->getResultId();
961 }
962
963 if (existing)
964 return existing;
965 }
966
967 // Make it
968 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
969 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
970 groupedConstants[OpTypeBool].push_back(x: c);
971 module.mapInstruction(instruction: c);
972
973 return c->getResultId();
974}
975
976Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
977{
978 Op opcode = specConstant ? OpSpecConstant : OpConstant;
979
980 // See if we already made it. Applies only to regular constants, because specialization constants
981 // must remain distinct for the purpose of applying a SpecId decoration.
982 if (! specConstant) {
983 Id existing = findScalarConstant(typeClass: OpTypeInt, opcode, typeId, value);
984 if (existing)
985 return existing;
986 }
987
988 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
989 c->addImmediateOperand(immediate: value);
990 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
991 groupedConstants[OpTypeInt].push_back(x: c);
992 module.mapInstruction(instruction: c);
993
994 return c->getResultId();
995}
996
997Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
998{
999 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1000
1001 unsigned op1 = value & 0xFFFFFFFF;
1002 unsigned op2 = value >> 32;
1003
1004 // See if we already made it. Applies only to regular constants, because specialization constants
1005 // must remain distinct for the purpose of applying a SpecId decoration.
1006 if (! specConstant) {
1007 Id existing = findScalarConstant(typeClass: OpTypeInt, opcode, typeId, v1: op1, v2: op2);
1008 if (existing)
1009 return existing;
1010 }
1011
1012 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1013 c->addImmediateOperand(immediate: op1);
1014 c->addImmediateOperand(immediate: op2);
1015 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
1016 groupedConstants[OpTypeInt].push_back(x: c);
1017 module.mapInstruction(instruction: c);
1018
1019 return c->getResultId();
1020}
1021
1022Id Builder::makeFloatConstant(float f, bool specConstant)
1023{
1024 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1025 Id typeId = makeFloatType(width: 32);
1026 union { float fl; unsigned int ui; } u;
1027 u.fl = f;
1028 unsigned value = u.ui;
1029
1030 // See if we already made it. Applies only to regular constants, because specialization constants
1031 // must remain distinct for the purpose of applying a SpecId decoration.
1032 if (! specConstant) {
1033 Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, value);
1034 if (existing)
1035 return existing;
1036 }
1037
1038 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1039 c->addImmediateOperand(immediate: value);
1040 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
1041 groupedConstants[OpTypeFloat].push_back(x: c);
1042 module.mapInstruction(instruction: c);
1043
1044 return c->getResultId();
1045}
1046
1047Id Builder::makeDoubleConstant(double d, bool specConstant)
1048{
1049#ifdef GLSLANG_WEB
1050 assert(0);
1051 return NoResult;
1052#else
1053 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1054 Id typeId = makeFloatType(width: 64);
1055 union { double db; unsigned long long ull; } u;
1056 u.db = d;
1057 unsigned long long value = u.ull;
1058 unsigned op1 = value & 0xFFFFFFFF;
1059 unsigned op2 = value >> 32;
1060
1061 // See if we already made it. Applies only to regular constants, because specialization constants
1062 // must remain distinct for the purpose of applying a SpecId decoration.
1063 if (! specConstant) {
1064 Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, v1: op1, v2: op2);
1065 if (existing)
1066 return existing;
1067 }
1068
1069 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1070 c->addImmediateOperand(immediate: op1);
1071 c->addImmediateOperand(immediate: op2);
1072 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
1073 groupedConstants[OpTypeFloat].push_back(x: c);
1074 module.mapInstruction(instruction: c);
1075
1076 return c->getResultId();
1077#endif
1078}
1079
1080Id Builder::makeFloat16Constant(float f16, bool specConstant)
1081{
1082#ifdef GLSLANG_WEB
1083 assert(0);
1084 return NoResult;
1085#else
1086 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1087 Id typeId = makeFloatType(width: 16);
1088
1089 spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
1090 spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
1091 fVal.castTo(other&: f16Val, round_dir: spvutils::kRoundToZero);
1092
1093 unsigned value = f16Val.value().getAsFloat().get_value();
1094
1095 // See if we already made it. Applies only to regular constants, because specialization constants
1096 // must remain distinct for the purpose of applying a SpecId decoration.
1097 if (!specConstant) {
1098 Id existing = findScalarConstant(typeClass: OpTypeFloat, opcode, typeId, value);
1099 if (existing)
1100 return existing;
1101 }
1102
1103 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1104 c->addImmediateOperand(immediate: value);
1105 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
1106 groupedConstants[OpTypeFloat].push_back(x: c);
1107 module.mapInstruction(instruction: c);
1108
1109 return c->getResultId();
1110#endif
1111}
1112
1113Id Builder::makeFpConstant(Id type, double d, bool specConstant)
1114{
1115#ifdef GLSLANG_WEB
1116 const int width = 32;
1117 assert(width == getScalarTypeWidth(type));
1118#else
1119 const int width = getScalarTypeWidth(typeId: type);
1120#endif
1121
1122 assert(isFloatType(type));
1123
1124 switch (width) {
1125 case 16:
1126 return makeFloat16Constant(f16: (float)d, specConstant);
1127 case 32:
1128 return makeFloatConstant(f: (float)d, specConstant);
1129 case 64:
1130 return makeDoubleConstant(d, specConstant);
1131 default:
1132 break;
1133 }
1134
1135 assert(false);
1136 return NoResult;
1137}
1138
1139Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
1140{
1141 Instruction* constant = 0;
1142 bool found = false;
1143 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
1144 constant = groupedConstants[typeClass][i];
1145
1146 if (constant->getTypeId() != typeId)
1147 continue;
1148
1149 // same contents?
1150 bool mismatch = false;
1151 for (int op = 0; op < constant->getNumOperands(); ++op) {
1152 if (constant->getIdOperand(op) != comps[op]) {
1153 mismatch = true;
1154 break;
1155 }
1156 }
1157 if (! mismatch) {
1158 found = true;
1159 break;
1160 }
1161 }
1162
1163 return found ? constant->getResultId() : NoResult;
1164}
1165
1166Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
1167{
1168 Instruction* constant = 0;
1169 bool found = false;
1170 for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
1171 constant = groupedStructConstants[typeId][i];
1172
1173 // same contents?
1174 bool mismatch = false;
1175 for (int op = 0; op < constant->getNumOperands(); ++op) {
1176 if (constant->getIdOperand(op) != comps[op]) {
1177 mismatch = true;
1178 break;
1179 }
1180 }
1181 if (! mismatch) {
1182 found = true;
1183 break;
1184 }
1185 }
1186
1187 return found ? constant->getResultId() : NoResult;
1188}
1189
1190// Comments in header
1191Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
1192{
1193 Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
1194 assert(typeId);
1195 Op typeClass = getTypeClass(typeId);
1196
1197 switch (typeClass) {
1198 case OpTypeVector:
1199 case OpTypeArray:
1200 case OpTypeMatrix:
1201 case OpTypeCooperativeMatrixNV:
1202 if (! specConstant) {
1203 Id existing = findCompositeConstant(typeClass, typeId, comps: members);
1204 if (existing)
1205 return existing;
1206 }
1207 break;
1208 case OpTypeStruct:
1209 if (! specConstant) {
1210 Id existing = findStructConstant(typeId, comps: members);
1211 if (existing)
1212 return existing;
1213 }
1214 break;
1215 default:
1216 assert(0);
1217 return makeFloatConstant(f: 0.0);
1218 }
1219
1220 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1221 for (int op = 0; op < (int)members.size(); ++op)
1222 c->addIdOperand(id: members[op]);
1223 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(c));
1224 if (typeClass == OpTypeStruct)
1225 groupedStructConstants[typeId].push_back(x: c);
1226 else
1227 groupedConstants[typeClass].push_back(x: c);
1228 module.mapInstruction(instruction: c);
1229
1230 return c->getResultId();
1231}
1232
1233Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
1234{
1235 Instruction* entryPoint = new Instruction(OpEntryPoint);
1236 entryPoint->addImmediateOperand(immediate: model);
1237 entryPoint->addIdOperand(id: function->getId());
1238 entryPoint->addStringOperand(str: name);
1239
1240 entryPoints.push_back(x: std::unique_ptr<Instruction>(entryPoint));
1241
1242 return entryPoint;
1243}
1244
1245// Currently relying on the fact that all 'value' of interest are small non-negative values.
1246void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
1247{
1248 Instruction* instr = new Instruction(OpExecutionMode);
1249 instr->addIdOperand(id: entryPoint->getId());
1250 instr->addImmediateOperand(immediate: mode);
1251 if (value1 >= 0)
1252 instr->addImmediateOperand(immediate: value1);
1253 if (value2 >= 0)
1254 instr->addImmediateOperand(immediate: value2);
1255 if (value3 >= 0)
1256 instr->addImmediateOperand(immediate: value3);
1257
1258 executionModes.push_back(x: std::unique_ptr<Instruction>(instr));
1259}
1260
1261void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
1262{
1263 Instruction* instr = new Instruction(OpExecutionMode);
1264 instr->addIdOperand(id: entryPoint->getId());
1265 instr->addImmediateOperand(immediate: mode);
1266 for (auto literal : literals)
1267 instr->addImmediateOperand(immediate: literal);
1268
1269 executionModes.push_back(x: std::unique_ptr<Instruction>(instr));
1270}
1271
1272void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
1273{
1274 Instruction* instr = new Instruction(OpExecutionModeId);
1275 instr->addIdOperand(id: entryPoint->getId());
1276 instr->addImmediateOperand(immediate: mode);
1277 for (auto operandId : operandIds)
1278 instr->addIdOperand(id: operandId);
1279
1280 executionModes.push_back(x: std::unique_ptr<Instruction>(instr));
1281}
1282
1283void Builder::addName(Id id, const char* string)
1284{
1285 Instruction* name = new Instruction(OpName);
1286 name->addIdOperand(id);
1287 name->addStringOperand(str: string);
1288
1289 names.push_back(x: std::unique_ptr<Instruction>(name));
1290}
1291
1292void Builder::addMemberName(Id id, int memberNumber, const char* string)
1293{
1294 Instruction* name = new Instruction(OpMemberName);
1295 name->addIdOperand(id);
1296 name->addImmediateOperand(immediate: memberNumber);
1297 name->addStringOperand(str: string);
1298
1299 names.push_back(x: std::unique_ptr<Instruction>(name));
1300}
1301
1302void Builder::addDecoration(Id id, Decoration decoration, int num)
1303{
1304 if (decoration == spv::DecorationMax)
1305 return;
1306
1307 Instruction* dec = new Instruction(OpDecorate);
1308 dec->addIdOperand(id);
1309 dec->addImmediateOperand(immediate: decoration);
1310 if (num >= 0)
1311 dec->addImmediateOperand(immediate: num);
1312
1313 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1314}
1315
1316void Builder::addDecoration(Id id, Decoration decoration, const char* s)
1317{
1318 if (decoration == spv::DecorationMax)
1319 return;
1320
1321 Instruction* dec = new Instruction(OpDecorateString);
1322 dec->addIdOperand(id);
1323 dec->addImmediateOperand(immediate: decoration);
1324 dec->addStringOperand(str: s);
1325
1326 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1327}
1328
1329void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)
1330{
1331 if (decoration == spv::DecorationMax)
1332 return;
1333
1334 Instruction* dec = new Instruction(OpDecorate);
1335 dec->addIdOperand(id);
1336 dec->addImmediateOperand(immediate: decoration);
1337 for (auto literal : literals)
1338 dec->addImmediateOperand(immediate: literal);
1339
1340 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1341}
1342
1343void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)
1344{
1345 if (decoration == spv::DecorationMax)
1346 return;
1347
1348 Instruction* dec = new Instruction(OpDecorateString);
1349 dec->addIdOperand(id);
1350 dec->addImmediateOperand(immediate: decoration);
1351 for (auto string : strings)
1352 dec->addStringOperand(str: string);
1353
1354 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1355}
1356
1357void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
1358{
1359 if (decoration == spv::DecorationMax)
1360 return;
1361
1362 Instruction* dec = new Instruction(OpDecorateId);
1363 dec->addIdOperand(id);
1364 dec->addImmediateOperand(immediate: decoration);
1365 dec->addIdOperand(id: idDecoration);
1366
1367 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1368}
1369
1370void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)
1371{
1372 if(decoration == spv::DecorationMax)
1373 return;
1374
1375 Instruction* dec = new Instruction(OpDecorateId);
1376 dec->addIdOperand(id);
1377 dec->addImmediateOperand(immediate: decoration);
1378
1379 for (auto operandId : operandIds)
1380 dec->addIdOperand(id: operandId);
1381
1382 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1383}
1384
1385void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
1386{
1387 if (decoration == spv::DecorationMax)
1388 return;
1389
1390 Instruction* dec = new Instruction(OpMemberDecorate);
1391 dec->addIdOperand(id);
1392 dec->addImmediateOperand(immediate: member);
1393 dec->addImmediateOperand(immediate: decoration);
1394 if (num >= 0)
1395 dec->addImmediateOperand(immediate: num);
1396
1397 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1398}
1399
1400void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
1401{
1402 if (decoration == spv::DecorationMax)
1403 return;
1404
1405 Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
1406 dec->addIdOperand(id);
1407 dec->addImmediateOperand(immediate: member);
1408 dec->addImmediateOperand(immediate: decoration);
1409 dec->addStringOperand(str: s);
1410
1411 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1412}
1413
1414void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)
1415{
1416 if (decoration == spv::DecorationMax)
1417 return;
1418
1419 Instruction* dec = new Instruction(OpMemberDecorate);
1420 dec->addIdOperand(id);
1421 dec->addImmediateOperand(immediate: member);
1422 dec->addImmediateOperand(immediate: decoration);
1423 for (auto literal : literals)
1424 dec->addImmediateOperand(immediate: literal);
1425
1426 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1427}
1428
1429void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)
1430{
1431 if (decoration == spv::DecorationMax)
1432 return;
1433
1434 Instruction* dec = new Instruction(OpMemberDecorateString);
1435 dec->addIdOperand(id);
1436 dec->addImmediateOperand(immediate: member);
1437 dec->addImmediateOperand(immediate: decoration);
1438 for (auto string : strings)
1439 dec->addStringOperand(str: string);
1440
1441 decorations.push_back(x: std::unique_ptr<Instruction>(dec));
1442}
1443
1444// Comments in header
1445Function* Builder::makeEntryPoint(const char* entryPoint)
1446{
1447 assert(! entryPointFunction);
1448
1449 Block* entry;
1450 std::vector<Id> params;
1451 std::vector<std::vector<Decoration>> decorations;
1452
1453 entryPointFunction = makeFunctionEntry(precision: NoPrecision, returnType: makeVoidType(), name: entryPoint, paramTypes: params, precisions: decorations, entry: &entry);
1454
1455 return entryPointFunction;
1456}
1457
1458// Comments in header
1459Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
1460 const std::vector<Id>& paramTypes,
1461 const std::vector<std::vector<Decoration>>& decorations, Block **entry)
1462{
1463 // Make the function and initial instructions in it
1464 Id typeId = makeFunctionType(returnType, paramTypes);
1465 Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds(numIds: (int)paramTypes.size());
1466 Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
1467
1468 // Set up the precisions
1469 setPrecision(id: function->getId(), precision);
1470 function->setReturnPrecision(precision);
1471 for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
1472 for (int d = 0; d < (int)decorations[p].size(); ++d) {
1473 addDecoration(id: firstParamId + p, decoration: decorations[p][d]);
1474 function->addParamPrecision(param: p, precision: decorations[p][d]);
1475 }
1476 }
1477
1478 // CFG
1479 if (entry) {
1480 *entry = new Block(getUniqueId(), *function);
1481 function->addBlock(block: *entry);
1482 setBuildPoint(*entry);
1483 }
1484
1485 if (name)
1486 addName(id: function->getId(), string: name);
1487
1488 functions.push_back(x: std::unique_ptr<Function>(function));
1489
1490 return function;
1491}
1492
1493// Comments in header
1494void Builder::makeReturn(bool implicit, Id retVal)
1495{
1496 if (retVal) {
1497 Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
1498 inst->addIdOperand(id: retVal);
1499 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(inst));
1500 } else
1501 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
1502
1503 if (! implicit)
1504 createAndSetNoPredecessorBlock("post-return");
1505}
1506
1507// Comments in header
1508void Builder::leaveFunction()
1509{
1510 Block* block = buildPoint;
1511 Function& function = buildPoint->getParent();
1512 assert(block);
1513
1514 // If our function did not contain a return, add a return void now.
1515 if (! block->isTerminated()) {
1516 if (function.getReturnType() == makeVoidType())
1517 makeReturn(implicit: true);
1518 else {
1519 makeReturn(implicit: true, retVal: createUndefined(type: function.getReturnType()));
1520 }
1521 }
1522}
1523
1524// Comments in header
1525void Builder::makeStatementTerminator(spv::Op opcode, const char *name)
1526{
1527 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(new Instruction(opcode)));
1528 createAndSetNoPredecessorBlock(name);
1529}
1530
1531// Comments in header
1532Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer)
1533{
1534 Id pointerType = makePointer(storageClass, pointee: type);
1535 Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
1536 inst->addImmediateOperand(immediate: storageClass);
1537 if (initializer != NoResult)
1538 inst->addIdOperand(id: initializer);
1539
1540 switch (storageClass) {
1541 case StorageClassFunction:
1542 // Validation rules require the declaration in the entry block
1543 buildPoint->getParent().addLocalVariable(inst: std::unique_ptr<Instruction>(inst));
1544 break;
1545
1546 default:
1547 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(inst));
1548 module.mapInstruction(instruction: inst);
1549 break;
1550 }
1551
1552 if (name)
1553 addName(id: inst->getResultId(), string: name);
1554 setPrecision(id: inst->getResultId(), precision);
1555
1556 return inst->getResultId();
1557}
1558
1559// Comments in header
1560Id Builder::createUndefined(Id type)
1561{
1562 Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
1563 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(inst));
1564 return inst->getResultId();
1565}
1566
1567// av/vis/nonprivate are unnecessary and illegal for some storage classes.
1568spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
1569 const
1570{
1571 switch (sc) {
1572 case spv::StorageClassUniform:
1573 case spv::StorageClassWorkgroup:
1574 case spv::StorageClassStorageBuffer:
1575 case spv::StorageClassPhysicalStorageBufferEXT:
1576 break;
1577 default:
1578 memoryAccess = spv::MemoryAccessMask(memoryAccess &
1579 ~(spv::MemoryAccessMakePointerAvailableKHRMask |
1580 spv::MemoryAccessMakePointerVisibleKHRMask |
1581 spv::MemoryAccessNonPrivatePointerKHRMask));
1582 break;
1583 }
1584 return memoryAccess;
1585}
1586
1587// Comments in header
1588void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,
1589 unsigned int alignment)
1590{
1591 Instruction* store = new Instruction(OpStore);
1592 store->addIdOperand(id: lValue);
1593 store->addIdOperand(id: rValue);
1594
1595 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, sc: getStorageClass(resultId: lValue));
1596
1597 if (memoryAccess != MemoryAccessMaskNone) {
1598 store->addImmediateOperand(immediate: memoryAccess);
1599 if (memoryAccess & spv::MemoryAccessAlignedMask) {
1600 store->addImmediateOperand(immediate: alignment);
1601 }
1602 if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
1603 store->addIdOperand(id: makeUintConstant(u: scope));
1604 }
1605 }
1606
1607 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(store));
1608}
1609
1610// Comments in header
1611Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,
1612 spv::Scope scope, unsigned int alignment)
1613{
1614 Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(resultId: lValue), OpLoad);
1615 load->addIdOperand(id: lValue);
1616
1617 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, sc: getStorageClass(resultId: lValue));
1618
1619 if (memoryAccess != MemoryAccessMaskNone) {
1620 load->addImmediateOperand(immediate: memoryAccess);
1621 if (memoryAccess & spv::MemoryAccessAlignedMask) {
1622 load->addImmediateOperand(immediate: alignment);
1623 }
1624 if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
1625 load->addIdOperand(id: makeUintConstant(u: scope));
1626 }
1627 }
1628
1629 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(load));
1630 setPrecision(id: load->getResultId(), precision);
1631
1632 return load->getResultId();
1633}
1634
1635// Comments in header
1636Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
1637{
1638 // Figure out the final resulting type.
1639 Id typeId = getResultingAccessChainType();
1640 typeId = makePointer(storageClass, pointee: typeId);
1641
1642 // Make the instruction
1643 Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
1644 chain->addIdOperand(id: base);
1645 for (int i = 0; i < (int)offsets.size(); ++i)
1646 chain->addIdOperand(id: offsets[i]);
1647 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(chain));
1648
1649 return chain->getResultId();
1650}
1651
1652Id Builder::createArrayLength(Id base, unsigned int member)
1653{
1654 spv::Id intType = makeUintType(width: 32);
1655 Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
1656 length->addIdOperand(id: base);
1657 length->addImmediateOperand(immediate: member);
1658 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(length));
1659
1660 return length->getResultId();
1661}
1662
1663Id Builder::createCooperativeMatrixLength(Id type)
1664{
1665 spv::Id intType = makeUintType(width: 32);
1666
1667 // Generate code for spec constants if in spec constant operation
1668 // generation mode.
1669 if (generatingOpCodeForSpecConst) {
1670 return createSpecConstantOp(OpCooperativeMatrixLengthNV, typeId: intType, operands: std::vector<Id>(1, type), literals: std::vector<Id>());
1671 }
1672
1673 Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
1674 length->addIdOperand(id: type);
1675 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(length));
1676
1677 return length->getResultId();
1678}
1679
1680Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
1681{
1682 // Generate code for spec constants if in spec constant operation
1683 // generation mode.
1684 if (generatingOpCodeForSpecConst) {
1685 return createSpecConstantOp(OpCompositeExtract, typeId, operands: std::vector<Id>(1, composite),
1686 literals: std::vector<Id>(1, index));
1687 }
1688 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1689 extract->addIdOperand(id: composite);
1690 extract->addImmediateOperand(immediate: index);
1691 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(extract));
1692
1693 return extract->getResultId();
1694}
1695
1696Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
1697{
1698 // Generate code for spec constants if in spec constant operation
1699 // generation mode.
1700 if (generatingOpCodeForSpecConst) {
1701 return createSpecConstantOp(OpCompositeExtract, typeId, operands: std::vector<Id>(1, composite), literals: indexes);
1702 }
1703 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1704 extract->addIdOperand(id: composite);
1705 for (int i = 0; i < (int)indexes.size(); ++i)
1706 extract->addImmediateOperand(immediate: indexes[i]);
1707 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(extract));
1708
1709 return extract->getResultId();
1710}
1711
1712Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
1713{
1714 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1715 insert->addIdOperand(id: object);
1716 insert->addIdOperand(id: composite);
1717 insert->addImmediateOperand(immediate: index);
1718 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(insert));
1719
1720 return insert->getResultId();
1721}
1722
1723Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
1724{
1725 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1726 insert->addIdOperand(id: object);
1727 insert->addIdOperand(id: composite);
1728 for (int i = 0; i < (int)indexes.size(); ++i)
1729 insert->addImmediateOperand(immediate: indexes[i]);
1730 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(insert));
1731
1732 return insert->getResultId();
1733}
1734
1735Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
1736{
1737 Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
1738 extract->addIdOperand(id: vector);
1739 extract->addIdOperand(id: componentIndex);
1740 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(extract));
1741
1742 return extract->getResultId();
1743}
1744
1745Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
1746{
1747 Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
1748 insert->addIdOperand(id: vector);
1749 insert->addIdOperand(id: component);
1750 insert->addIdOperand(id: componentIndex);
1751 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(insert));
1752
1753 return insert->getResultId();
1754}
1755
1756// An opcode that has no operands, no result id, and no type
1757void Builder::createNoResultOp(Op opCode)
1758{
1759 Instruction* op = new Instruction(opCode);
1760 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1761}
1762
1763// An opcode that has one id operand, no result id, and no type
1764void Builder::createNoResultOp(Op opCode, Id operand)
1765{
1766 Instruction* op = new Instruction(opCode);
1767 op->addIdOperand(id: operand);
1768 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1769}
1770
1771// An opcode that has one or more operands, no result id, and no type
1772void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
1773{
1774 Instruction* op = new Instruction(opCode);
1775 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1776 op->addIdOperand(id: *it);
1777 }
1778 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1779}
1780
1781// An opcode that has multiple operands, no result id, and no type
1782void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
1783{
1784 Instruction* op = new Instruction(opCode);
1785 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1786 if (it->isId)
1787 op->addIdOperand(id: it->word);
1788 else
1789 op->addImmediateOperand(immediate: it->word);
1790 }
1791 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1792}
1793
1794void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
1795{
1796 Instruction* op = new Instruction(OpControlBarrier);
1797 op->addIdOperand(id: makeUintConstant(u: execution));
1798 op->addIdOperand(id: makeUintConstant(u: memory));
1799 op->addIdOperand(id: makeUintConstant(u: semantics));
1800 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1801}
1802
1803void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
1804{
1805 Instruction* op = new Instruction(OpMemoryBarrier);
1806 op->addIdOperand(id: makeUintConstant(u: executionScope));
1807 op->addIdOperand(id: makeUintConstant(u: memorySemantics));
1808 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1809}
1810
1811// An opcode that has one operands, a result id, and a type
1812Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
1813{
1814 // Generate code for spec constants if in spec constant operation
1815 // generation mode.
1816 if (generatingOpCodeForSpecConst) {
1817 return createSpecConstantOp(opCode, typeId, operands: std::vector<Id>(1, operand), literals: std::vector<Id>());
1818 }
1819 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1820 op->addIdOperand(id: operand);
1821 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1822
1823 return op->getResultId();
1824}
1825
1826Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
1827{
1828 // Generate code for spec constants if in spec constant operation
1829 // generation mode.
1830 if (generatingOpCodeForSpecConst) {
1831 std::vector<Id> operands(2);
1832 operands[0] = left; operands[1] = right;
1833 return createSpecConstantOp(opCode, typeId, operands, literals: std::vector<Id>());
1834 }
1835 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1836 op->addIdOperand(id: left);
1837 op->addIdOperand(id: right);
1838 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1839
1840 return op->getResultId();
1841}
1842
1843Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
1844{
1845 // Generate code for spec constants if in spec constant operation
1846 // generation mode.
1847 if (generatingOpCodeForSpecConst) {
1848 std::vector<Id> operands(3);
1849 operands[0] = op1;
1850 operands[1] = op2;
1851 operands[2] = op3;
1852 return createSpecConstantOp(
1853 opCode, typeId, operands, literals: std::vector<Id>());
1854 }
1855 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1856 op->addIdOperand(id: op1);
1857 op->addIdOperand(id: op2);
1858 op->addIdOperand(id: op3);
1859 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1860
1861 return op->getResultId();
1862}
1863
1864Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
1865{
1866 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1867 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1868 op->addIdOperand(id: *it);
1869 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1870
1871 return op->getResultId();
1872}
1873
1874Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
1875{
1876 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1877 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1878 if (it->isId)
1879 op->addIdOperand(id: it->word);
1880 else
1881 op->addImmediateOperand(immediate: it->word);
1882 }
1883 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1884
1885 return op->getResultId();
1886}
1887
1888Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,
1889 const std::vector<unsigned>& literals)
1890{
1891 Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
1892 op->addImmediateOperand(immediate: (unsigned) opCode);
1893 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1894 op->addIdOperand(id: *it);
1895 for (auto it = literals.cbegin(); it != literals.cend(); ++it)
1896 op->addImmediateOperand(immediate: *it);
1897 module.mapInstruction(instruction: op);
1898 constantsTypesGlobals.push_back(x: std::unique_ptr<Instruction>(op));
1899
1900 return op->getResultId();
1901}
1902
1903Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
1904{
1905 Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
1906 op->addIdOperand(id: function->getId());
1907 for (int a = 0; a < (int)args.size(); ++a)
1908 op->addIdOperand(id: args[a]);
1909 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
1910
1911 return op->getResultId();
1912}
1913
1914// Comments in header
1915Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
1916{
1917 if (channels.size() == 1)
1918 return setPrecision(id: createCompositeExtract(composite: source, typeId, index: channels.front()), precision);
1919
1920 if (generatingOpCodeForSpecConst) {
1921 std::vector<Id> operands(2);
1922 operands[0] = operands[1] = source;
1923 return setPrecision(id: createSpecConstantOp(opCode: OpVectorShuffle, typeId, operands, literals: channels), precision);
1924 }
1925 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1926 assert(isVector(source));
1927 swizzle->addIdOperand(id: source);
1928 swizzle->addIdOperand(id: source);
1929 for (int i = 0; i < (int)channels.size(); ++i)
1930 swizzle->addImmediateOperand(immediate: channels[i]);
1931 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(swizzle));
1932
1933 return setPrecision(id: swizzle->getResultId(), precision);
1934}
1935
1936// Comments in header
1937Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
1938{
1939 if (channels.size() == 1 && getNumComponents(resultId: source) == 1)
1940 return createCompositeInsert(object: source, composite: target, typeId, index: channels.front());
1941
1942 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1943
1944 assert(isVector(target));
1945 swizzle->addIdOperand(id: target);
1946
1947 assert(getNumComponents(source) == (int)channels.size());
1948 assert(isVector(source));
1949 swizzle->addIdOperand(id: source);
1950
1951 // Set up an identity shuffle from the base value to the result value
1952 unsigned int components[4];
1953 int numTargetComponents = getNumComponents(resultId: target);
1954 for (int i = 0; i < numTargetComponents; ++i)
1955 components[i] = i;
1956
1957 // Punch in the l-value swizzle
1958 for (int i = 0; i < (int)channels.size(); ++i)
1959 components[channels[i]] = numTargetComponents + i;
1960
1961 // finish the instruction with these components selectors
1962 for (int i = 0; i < numTargetComponents; ++i)
1963 swizzle->addImmediateOperand(immediate: components[i]);
1964 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(swizzle));
1965
1966 return swizzle->getResultId();
1967}
1968
1969// Comments in header
1970void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
1971{
1972 int direction = getNumComponents(resultId: right) - getNumComponents(resultId: left);
1973
1974 if (direction > 0)
1975 left = smearScalar(precision, scalarVal: left, vectorType: makeVectorType(component: getTypeId(resultId: left), size: getNumComponents(resultId: right)));
1976 else if (direction < 0)
1977 right = smearScalar(precision, scalarVal: right, vectorType: makeVectorType(component: getTypeId(resultId: right), size: getNumComponents(resultId: left)));
1978
1979 return;
1980}
1981
1982// Comments in header
1983Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
1984{
1985 assert(getNumComponents(scalar) == 1);
1986 assert(getTypeId(scalar) == getScalarTypeId(vectorType));
1987
1988 int numComponents = getNumTypeComponents(typeId: vectorType);
1989 if (numComponents == 1)
1990 return scalar;
1991
1992 Instruction* smear = nullptr;
1993 if (generatingOpCodeForSpecConst) {
1994 auto members = std::vector<spv::Id>(numComponents, scalar);
1995 // Sometime even in spec-constant-op mode, the temporary vector created by
1996 // promoting a scalar might not be a spec constant. This should depend on
1997 // the scalar.
1998 // e.g.:
1999 // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
2000 // In such cases, the temporary vector created from a_front_end_const_scalar
2001 // is not a spec constant vector, even though the binary operation node is marked
2002 // as 'specConstant' and we are in spec-constant-op mode.
2003 auto result_id = makeCompositeConstant(typeId: vectorType, members, specConstant: isSpecConstant(resultId: scalar));
2004 smear = module.getInstruction(id: result_id);
2005 } else {
2006 smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
2007 for (int c = 0; c < numComponents; ++c)
2008 smear->addIdOperand(id: scalar);
2009 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(smear));
2010 }
2011
2012 return setPrecision(id: smear->getResultId(), precision);
2013}
2014
2015// Comments in header
2016Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
2017{
2018 Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
2019 inst->addIdOperand(id: builtins);
2020 inst->addImmediateOperand(immediate: entryPoint);
2021 for (int arg = 0; arg < (int)args.size(); ++arg)
2022 inst->addIdOperand(id: args[arg]);
2023
2024 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(inst));
2025
2026 return inst->getResultId();
2027}
2028
2029// Accept all parameters needed to create a texture instruction.
2030// Create the correct instruction based on the inputs, and make the call.
2031Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
2032 bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)
2033{
2034 static const int maxTextureArgs = 10;
2035 Id texArgs[maxTextureArgs] = {};
2036
2037 //
2038 // Set up the fixed arguments
2039 //
2040 int numArgs = 0;
2041 bool explicitLod = false;
2042 texArgs[numArgs++] = parameters.sampler;
2043 texArgs[numArgs++] = parameters.coords;
2044 if (parameters.Dref != NoResult)
2045 texArgs[numArgs++] = parameters.Dref;
2046 if (parameters.component != NoResult)
2047 texArgs[numArgs++] = parameters.component;
2048
2049#ifndef GLSLANG_WEB
2050 if (parameters.granularity != NoResult)
2051 texArgs[numArgs++] = parameters.granularity;
2052 if (parameters.coarse != NoResult)
2053 texArgs[numArgs++] = parameters.coarse;
2054#endif
2055
2056 //
2057 // Set up the optional arguments
2058 //
2059 int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
2060 ++numArgs; // speculatively make room for the mask operand
2061 ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
2062 if (parameters.bias) {
2063 mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
2064 texArgs[numArgs++] = parameters.bias;
2065 }
2066 if (parameters.lod) {
2067 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
2068 texArgs[numArgs++] = parameters.lod;
2069 explicitLod = true;
2070 } else if (parameters.gradX) {
2071 mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
2072 texArgs[numArgs++] = parameters.gradX;
2073 texArgs[numArgs++] = parameters.gradY;
2074 explicitLod = true;
2075 } else if (noImplicitLod && ! fetch && ! gather) {
2076 // have to explicitly use lod of 0 if not allowed to have them be implicit, and
2077 // we would otherwise be about to issue an implicit instruction
2078 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
2079 texArgs[numArgs++] = makeFloatConstant(f: 0.0);
2080 explicitLod = true;
2081 }
2082 if (parameters.offset) {
2083 if (isConstant(resultId: parameters.offset))
2084 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
2085 else {
2086 addCapability(cap: CapabilityImageGatherExtended);
2087 mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
2088 }
2089 texArgs[numArgs++] = parameters.offset;
2090 }
2091 if (parameters.offsets) {
2092 addCapability(cap: CapabilityImageGatherExtended);
2093 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
2094 texArgs[numArgs++] = parameters.offsets;
2095 }
2096#ifndef GLSLANG_WEB
2097 if (parameters.sample) {
2098 mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
2099 texArgs[numArgs++] = parameters.sample;
2100 }
2101 if (parameters.lodClamp) {
2102 // capability if this bit is used
2103 addCapability(cap: CapabilityMinLod);
2104
2105 mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
2106 texArgs[numArgs++] = parameters.lodClamp;
2107 }
2108 if (parameters.nonprivate) {
2109 mask = mask | ImageOperandsNonPrivateTexelKHRMask;
2110 }
2111 if (parameters.volatil) {
2112 mask = mask | ImageOperandsVolatileTexelKHRMask;
2113 }
2114#endif
2115 mask = mask | signExtensionMask;
2116 if (mask == ImageOperandsMaskNone)
2117 --numArgs; // undo speculative reservation for the mask argument
2118 else
2119 texArgs[optArgNum] = mask;
2120
2121 //
2122 // Set up the instruction
2123 //
2124 Op opCode = OpNop; // All paths below need to set this
2125 if (fetch) {
2126 if (sparse)
2127 opCode = OpImageSparseFetch;
2128 else
2129 opCode = OpImageFetch;
2130#ifndef GLSLANG_WEB
2131 } else if (parameters.granularity && parameters.coarse) {
2132 opCode = OpImageSampleFootprintNV;
2133 } else if (gather) {
2134 if (parameters.Dref)
2135 if (sparse)
2136 opCode = OpImageSparseDrefGather;
2137 else
2138 opCode = OpImageDrefGather;
2139 else
2140 if (sparse)
2141 opCode = OpImageSparseGather;
2142 else
2143 opCode = OpImageGather;
2144#endif
2145 } else if (explicitLod) {
2146 if (parameters.Dref) {
2147 if (proj)
2148 if (sparse)
2149 opCode = OpImageSparseSampleProjDrefExplicitLod;
2150 else
2151 opCode = OpImageSampleProjDrefExplicitLod;
2152 else
2153 if (sparse)
2154 opCode = OpImageSparseSampleDrefExplicitLod;
2155 else
2156 opCode = OpImageSampleDrefExplicitLod;
2157 } else {
2158 if (proj)
2159 if (sparse)
2160 opCode = OpImageSparseSampleProjExplicitLod;
2161 else
2162 opCode = OpImageSampleProjExplicitLod;
2163 else
2164 if (sparse)
2165 opCode = OpImageSparseSampleExplicitLod;
2166 else
2167 opCode = OpImageSampleExplicitLod;
2168 }
2169 } else {
2170 if (parameters.Dref) {
2171 if (proj)
2172 if (sparse)
2173 opCode = OpImageSparseSampleProjDrefImplicitLod;
2174 else
2175 opCode = OpImageSampleProjDrefImplicitLod;
2176 else
2177 if (sparse)
2178 opCode = OpImageSparseSampleDrefImplicitLod;
2179 else
2180 opCode = OpImageSampleDrefImplicitLod;
2181 } else {
2182 if (proj)
2183 if (sparse)
2184 opCode = OpImageSparseSampleProjImplicitLod;
2185 else
2186 opCode = OpImageSampleProjImplicitLod;
2187 else
2188 if (sparse)
2189 opCode = OpImageSparseSampleImplicitLod;
2190 else
2191 opCode = OpImageSampleImplicitLod;
2192 }
2193 }
2194
2195 // See if the result type is expecting a smeared result.
2196 // This happens when a legacy shadow*() call is made, which
2197 // gets a vec4 back instead of a float.
2198 Id smearedType = resultType;
2199 if (! isScalarType(typeId: resultType)) {
2200 switch (opCode) {
2201 case OpImageSampleDrefImplicitLod:
2202 case OpImageSampleDrefExplicitLod:
2203 case OpImageSampleProjDrefImplicitLod:
2204 case OpImageSampleProjDrefExplicitLod:
2205 resultType = getScalarTypeId(typeId: resultType);
2206 break;
2207 default:
2208 break;
2209 }
2210 }
2211
2212 Id typeId0 = 0;
2213 Id typeId1 = 0;
2214
2215 if (sparse) {
2216 typeId0 = resultType;
2217 typeId1 = getDerefTypeId(resultId: parameters.texelOut);
2218 resultType = makeStructResultType(type0: typeId0, type1: typeId1);
2219 }
2220
2221 // Build the SPIR-V instruction
2222 Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
2223 for (int op = 0; op < optArgNum; ++op)
2224 textureInst->addIdOperand(id: texArgs[op]);
2225 if (optArgNum < numArgs)
2226 textureInst->addImmediateOperand(immediate: texArgs[optArgNum]);
2227 for (int op = optArgNum + 1; op < numArgs; ++op)
2228 textureInst->addIdOperand(id: texArgs[op]);
2229 setPrecision(id: textureInst->getResultId(), precision);
2230 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(textureInst));
2231
2232 Id resultId = textureInst->getResultId();
2233
2234 if (sparse) {
2235 // set capability
2236 addCapability(cap: CapabilitySparseResidency);
2237
2238 // Decode the return type that was a special structure
2239 createStore(rValue: createCompositeExtract(composite: resultId, typeId: typeId1, index: 1), lValue: parameters.texelOut);
2240 resultId = createCompositeExtract(composite: resultId, typeId: typeId0, index: 0);
2241 setPrecision(id: resultId, precision);
2242 } else {
2243 // When a smear is needed, do it, as per what was computed
2244 // above when resultType was changed to a scalar type.
2245 if (resultType != smearedType)
2246 resultId = smearScalar(precision, scalar: resultId, vectorType: smearedType);
2247 }
2248
2249 return resultId;
2250}
2251
2252// Comments in header
2253Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
2254{
2255 // Figure out the result type
2256 Id resultType = 0;
2257 switch (opCode) {
2258 case OpImageQuerySize:
2259 case OpImageQuerySizeLod:
2260 {
2261 int numComponents = 0;
2262 switch (getTypeDimensionality(typeId: getImageType(resultId: parameters.sampler))) {
2263 case Dim1D:
2264 case DimBuffer:
2265 numComponents = 1;
2266 break;
2267 case Dim2D:
2268 case DimCube:
2269 case DimRect:
2270 case DimSubpassData:
2271 numComponents = 2;
2272 break;
2273 case Dim3D:
2274 numComponents = 3;
2275 break;
2276
2277 default:
2278 assert(0);
2279 break;
2280 }
2281 if (isArrayedImageType(typeId: getImageType(resultId: parameters.sampler)))
2282 ++numComponents;
2283
2284 Id intType = isUnsignedResult ? makeUintType(width: 32) : makeIntType(width: 32);
2285 if (numComponents == 1)
2286 resultType = intType;
2287 else
2288 resultType = makeVectorType(component: intType, size: numComponents);
2289
2290 break;
2291 }
2292 case OpImageQueryLod:
2293 resultType = makeVectorType(component: getScalarTypeId(typeId: getTypeId(resultId: parameters.coords)), size: 2);
2294 break;
2295 case OpImageQueryLevels:
2296 case OpImageQuerySamples:
2297 resultType = isUnsignedResult ? makeUintType(width: 32) : makeIntType(width: 32);
2298 break;
2299 default:
2300 assert(0);
2301 break;
2302 }
2303
2304 Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
2305 query->addIdOperand(id: parameters.sampler);
2306 if (parameters.coords)
2307 query->addIdOperand(id: parameters.coords);
2308 if (parameters.lod)
2309 query->addIdOperand(id: parameters.lod);
2310 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(query));
2311 addCapability(cap: CapabilityImageQuery);
2312
2313 return query->getResultId();
2314}
2315
2316// External comments in header.
2317// Operates recursively to visit the composite's hierarchy.
2318Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
2319{
2320 Id boolType = makeBoolType();
2321 Id valueType = getTypeId(resultId: value1);
2322
2323 Id resultId = NoResult;
2324
2325 int numConstituents = getNumTypeConstituents(typeId: valueType);
2326
2327 // Scalars and Vectors
2328
2329 if (isScalarType(typeId: valueType) || isVectorType(typeId: valueType)) {
2330 assert(valueType == getTypeId(value2));
2331 // These just need a single comparison, just have
2332 // to figure out what it is.
2333 Op op;
2334 switch (getMostBasicTypeClass(typeId: valueType)) {
2335 case OpTypeFloat:
2336 op = equal ? OpFOrdEqual : OpFUnordNotEqual;
2337 break;
2338 case OpTypeInt:
2339 default:
2340 op = equal ? OpIEqual : OpINotEqual;
2341 break;
2342 case OpTypeBool:
2343 op = equal ? OpLogicalEqual : OpLogicalNotEqual;
2344 precision = NoPrecision;
2345 break;
2346 }
2347
2348 if (isScalarType(typeId: valueType)) {
2349 // scalar
2350 resultId = createBinOp(opCode: op, typeId: boolType, left: value1, right: value2);
2351 } else {
2352 // vector
2353 resultId = createBinOp(opCode: op, typeId: makeVectorType(component: boolType, size: numConstituents), left: value1, right: value2);
2354 setPrecision(id: resultId, precision);
2355 // reduce vector compares...
2356 resultId = createUnaryOp(opCode: equal ? OpAll : OpAny, typeId: boolType, operand: resultId);
2357 }
2358
2359 return setPrecision(id: resultId, precision);
2360 }
2361
2362 // Only structs, arrays, and matrices should be left.
2363 // They share in common the reduction operation across their constituents.
2364 assert(isAggregateType(valueType) || isMatrixType(valueType));
2365
2366 // Compare each pair of constituents
2367 for (int constituent = 0; constituent < numConstituents; ++constituent) {
2368 std::vector<unsigned> indexes(1, constituent);
2369 Id constituentType1 = getContainedTypeId(typeId: getTypeId(resultId: value1), member: constituent);
2370 Id constituentType2 = getContainedTypeId(typeId: getTypeId(resultId: value2), member: constituent);
2371 Id constituent1 = createCompositeExtract(composite: value1, typeId: constituentType1, indexes);
2372 Id constituent2 = createCompositeExtract(composite: value2, typeId: constituentType2, indexes);
2373
2374 Id subResultId = createCompositeCompare(precision, value1: constituent1, value2: constituent2, equal);
2375
2376 if (constituent == 0)
2377 resultId = subResultId;
2378 else
2379 resultId = setPrecision(id: createBinOp(opCode: equal ? OpLogicalAnd : OpLogicalOr, typeId: boolType, left: resultId, right: subResultId),
2380 precision);
2381 }
2382
2383 return resultId;
2384}
2385
2386// OpCompositeConstruct
2387Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
2388{
2389 assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 &&
2390 getNumTypeConstituents(typeId) == (int)constituents.size()));
2391
2392 if (generatingOpCodeForSpecConst) {
2393 // Sometime, even in spec-constant-op mode, the constant composite to be
2394 // constructed may not be a specialization constant.
2395 // e.g.:
2396 // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
2397 // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
2398 // The second column vector should NOT be spec constant, as it does not contain any spec constants.
2399 // To handle such cases, we check the constituents of the constant vector to determine whether this
2400 // vector should be created as a spec constant.
2401 return makeCompositeConstant(typeId, members: constituents,
2402 specConstant: std::any_of(first: constituents.begin(), last: constituents.end(),
2403 pred: [&](spv::Id id) { return isSpecConstant(resultId: id); }));
2404 }
2405
2406 Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
2407 for (int c = 0; c < (int)constituents.size(); ++c)
2408 op->addIdOperand(id: constituents[c]);
2409 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(op));
2410
2411 return op->getResultId();
2412}
2413
2414// Vector or scalar constructor
2415Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2416{
2417 Id result = NoResult;
2418 unsigned int numTargetComponents = getNumTypeComponents(typeId: resultTypeId);
2419 unsigned int targetComponent = 0;
2420
2421 // Special case: when calling a vector constructor with a single scalar
2422 // argument, smear the scalar
2423 if (sources.size() == 1 && isScalar(resultId: sources[0]) && numTargetComponents > 1)
2424 return smearScalar(precision, scalar: sources[0], vectorType: resultTypeId);
2425
2426 // accumulate the arguments for OpCompositeConstruct
2427 std::vector<Id> constituents;
2428 Id scalarTypeId = getScalarTypeId(typeId: resultTypeId);
2429
2430 // lambda to store the result of visiting an argument component
2431 const auto latchResult = [&](Id comp) {
2432 if (numTargetComponents > 1)
2433 constituents.push_back(x: comp);
2434 else
2435 result = comp;
2436 ++targetComponent;
2437 };
2438
2439 // lambda to visit a vector argument's components
2440 const auto accumulateVectorConstituents = [&](Id sourceArg) {
2441 unsigned int sourceSize = getNumComponents(resultId: sourceArg);
2442 unsigned int sourcesToUse = sourceSize;
2443 if (sourcesToUse + targetComponent > numTargetComponents)
2444 sourcesToUse = numTargetComponents - targetComponent;
2445
2446 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2447 std::vector<unsigned> swiz;
2448 swiz.push_back(x: s);
2449 latchResult(createRvalueSwizzle(precision, typeId: scalarTypeId, source: sourceArg, channels: swiz));
2450 }
2451 };
2452
2453 // lambda to visit a matrix argument's components
2454 const auto accumulateMatrixConstituents = [&](Id sourceArg) {
2455 unsigned int sourceSize = getNumColumns(resultId: sourceArg) * getNumRows(resultId: sourceArg);
2456 unsigned int sourcesToUse = sourceSize;
2457 if (sourcesToUse + targetComponent > numTargetComponents)
2458 sourcesToUse = numTargetComponents - targetComponent;
2459
2460 int col = 0;
2461 int row = 0;
2462 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2463 if (row >= getNumRows(resultId: sourceArg)) {
2464 row = 0;
2465 col++;
2466 }
2467 std::vector<Id> indexes;
2468 indexes.push_back(x: col);
2469 indexes.push_back(x: row);
2470 latchResult(createCompositeExtract(composite: sourceArg, typeId: scalarTypeId, indexes));
2471 row++;
2472 }
2473 };
2474
2475 // Go through the source arguments, each one could have either
2476 // a single or multiple components to contribute.
2477 for (unsigned int i = 0; i < sources.size(); ++i) {
2478
2479 if (isScalar(resultId: sources[i]) || isPointer(resultId: sources[i]))
2480 latchResult(sources[i]);
2481 else if (isVector(resultId: sources[i]))
2482 accumulateVectorConstituents(sources[i]);
2483 else if (isMatrix(resultId: sources[i]))
2484 accumulateMatrixConstituents(sources[i]);
2485 else
2486 assert(0);
2487
2488 if (targetComponent >= numTargetComponents)
2489 break;
2490 }
2491
2492 // If the result is a vector, make it from the gathered constituents.
2493 if (constituents.size() > 0)
2494 result = createCompositeConstruct(typeId: resultTypeId, constituents);
2495
2496 return setPrecision(id: result, precision);
2497}
2498
2499// Comments in header
2500Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2501{
2502 Id componentTypeId = getScalarTypeId(typeId: resultTypeId);
2503 int numCols = getTypeNumColumns(typeId: resultTypeId);
2504 int numRows = getTypeNumRows(typeId: resultTypeId);
2505
2506 Instruction* instr = module.getInstruction(id: componentTypeId);
2507#ifdef GLSLANG_WEB
2508 const unsigned bitCount = 32;
2509 assert(bitCount == instr->getImmediateOperand(0));
2510#else
2511 const unsigned bitCount = instr->getImmediateOperand(op: 0);
2512#endif
2513
2514 // Optimize matrix constructed from a bigger matrix
2515 if (isMatrix(resultId: sources[0]) && getNumColumns(resultId: sources[0]) >= numCols && getNumRows(resultId: sources[0]) >= numRows) {
2516 // To truncate the matrix to a smaller number of rows/columns, we need to:
2517 // 1. For each column, extract the column and truncate it to the required size using shuffle
2518 // 2. Assemble the resulting matrix from all columns
2519 Id matrix = sources[0];
2520 Id columnTypeId = getContainedTypeId(typeId: resultTypeId);
2521 Id sourceColumnTypeId = getContainedTypeId(typeId: getTypeId(resultId: matrix));
2522
2523 std::vector<unsigned> channels;
2524 for (int row = 0; row < numRows; ++row)
2525 channels.push_back(x: row);
2526
2527 std::vector<Id> matrixColumns;
2528 for (int col = 0; col < numCols; ++col) {
2529 std::vector<unsigned> indexes;
2530 indexes.push_back(x: col);
2531 Id colv = createCompositeExtract(composite: matrix, typeId: sourceColumnTypeId, indexes);
2532 setPrecision(id: colv, precision);
2533
2534 if (numRows != getNumRows(resultId: matrix)) {
2535 matrixColumns.push_back(x: createRvalueSwizzle(precision, typeId: columnTypeId, source: colv, channels));
2536 } else {
2537 matrixColumns.push_back(x: colv);
2538 }
2539 }
2540
2541 return setPrecision(id: createCompositeConstruct(typeId: resultTypeId, constituents: matrixColumns), precision);
2542 }
2543
2544 // Otherwise, will use a two step process
2545 // 1. make a compile-time 2D array of values
2546 // 2. construct a matrix from that array
2547
2548 // Step 1.
2549
2550 // initialize the array to the identity matrix
2551 Id ids[maxMatrixSize][maxMatrixSize];
2552 Id one = (bitCount == 64 ? makeDoubleConstant(d: 1.0) : makeFloatConstant(f: 1.0));
2553 Id zero = (bitCount == 64 ? makeDoubleConstant(d: 0.0) : makeFloatConstant(f: 0.0));
2554 for (int col = 0; col < 4; ++col) {
2555 for (int row = 0; row < 4; ++row) {
2556 if (col == row)
2557 ids[col][row] = one;
2558 else
2559 ids[col][row] = zero;
2560 }
2561 }
2562
2563 // modify components as dictated by the arguments
2564 if (sources.size() == 1 && isScalar(resultId: sources[0])) {
2565 // a single scalar; resets the diagonals
2566 for (int col = 0; col < 4; ++col)
2567 ids[col][col] = sources[0];
2568 } else if (isMatrix(resultId: sources[0])) {
2569 // constructing from another matrix; copy over the parts that exist in both the argument and constructee
2570 Id matrix = sources[0];
2571 int minCols = std::min(a: numCols, b: getNumColumns(resultId: matrix));
2572 int minRows = std::min(a: numRows, b: getNumRows(resultId: matrix));
2573 for (int col = 0; col < minCols; ++col) {
2574 std::vector<unsigned> indexes;
2575 indexes.push_back(x: col);
2576 for (int row = 0; row < minRows; ++row) {
2577 indexes.push_back(x: row);
2578 ids[col][row] = createCompositeExtract(composite: matrix, typeId: componentTypeId, indexes);
2579 indexes.pop_back();
2580 setPrecision(id: ids[col][row], precision);
2581 }
2582 }
2583 } else {
2584 // fill in the matrix in column-major order with whatever argument components are available
2585 int row = 0;
2586 int col = 0;
2587
2588 for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) {
2589 Id argComp = sources[arg];
2590 for (int comp = 0; comp < getNumComponents(resultId: sources[arg]); ++comp) {
2591 if (getNumComponents(resultId: sources[arg]) > 1) {
2592 argComp = createCompositeExtract(composite: sources[arg], typeId: componentTypeId, index: comp);
2593 setPrecision(id: argComp, precision);
2594 }
2595 ids[col][row++] = argComp;
2596 if (row == numRows) {
2597 row = 0;
2598 col++;
2599 }
2600 if (col == numCols) {
2601 // If more components are provided than fit the matrix, discard the rest.
2602 break;
2603 }
2604 }
2605 }
2606 }
2607
2608 // Step 2: Construct a matrix from that array.
2609 // First make the column vectors, then make the matrix.
2610
2611 // make the column vectors
2612 Id columnTypeId = getContainedTypeId(typeId: resultTypeId);
2613 std::vector<Id> matrixColumns;
2614 for (int col = 0; col < numCols; ++col) {
2615 std::vector<Id> vectorComponents;
2616 for (int row = 0; row < numRows; ++row)
2617 vectorComponents.push_back(x: ids[col][row]);
2618 Id column = createCompositeConstruct(typeId: columnTypeId, constituents: vectorComponents);
2619 setPrecision(id: column, precision);
2620 matrixColumns.push_back(x: column);
2621 }
2622
2623 // make the matrix
2624 return setPrecision(id: createCompositeConstruct(typeId: resultTypeId, constituents: matrixColumns), precision);
2625}
2626
2627// Comments in header
2628Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
2629 builder(gb),
2630 condition(cond),
2631 control(ctrl),
2632 elseBlock(0)
2633{
2634 function = &builder.getBuildPoint()->getParent();
2635
2636 // make the blocks, but only put the then-block into the function,
2637 // the else-block and merge-block will be added later, in order, after
2638 // earlier code is emitted
2639 thenBlock = new Block(builder.getUniqueId(), *function);
2640 mergeBlock = new Block(builder.getUniqueId(), *function);
2641
2642 // Save the current block, so that we can add in the flow control split when
2643 // makeEndIf is called.
2644 headerBlock = builder.getBuildPoint();
2645
2646 function->addBlock(block: thenBlock);
2647 builder.setBuildPoint(thenBlock);
2648}
2649
2650// Comments in header
2651void Builder::If::makeBeginElse()
2652{
2653 // Close out the "then" by having it jump to the mergeBlock
2654 builder.createBranch(block: mergeBlock);
2655
2656 // Make the first else block and add it to the function
2657 elseBlock = new Block(builder.getUniqueId(), *function);
2658 function->addBlock(block: elseBlock);
2659
2660 // Start building the else block
2661 builder.setBuildPoint(elseBlock);
2662}
2663
2664// Comments in header
2665void Builder::If::makeEndIf()
2666{
2667 // jump to the merge block
2668 builder.createBranch(block: mergeBlock);
2669
2670 // Go back to the headerBlock and make the flow control split
2671 builder.setBuildPoint(headerBlock);
2672 builder.createSelectionMerge(mergeBlock, control);
2673 if (elseBlock)
2674 builder.createConditionalBranch(condition, thenBlock, elseBlock);
2675 else
2676 builder.createConditionalBranch(condition, thenBlock, elseBlock: mergeBlock);
2677
2678 // add the merge block to the function
2679 function->addBlock(block: mergeBlock);
2680 builder.setBuildPoint(mergeBlock);
2681}
2682
2683// Comments in header
2684void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
2685 const std::vector<int>& valueIndexToSegment, int defaultSegment,
2686 std::vector<Block*>& segmentBlocks)
2687{
2688 Function& function = buildPoint->getParent();
2689
2690 // make all the blocks
2691 for (int s = 0; s < numSegments; ++s)
2692 segmentBlocks.push_back(x: new Block(getUniqueId(), function));
2693
2694 Block* mergeBlock = new Block(getUniqueId(), function);
2695
2696 // make and insert the switch's selection-merge instruction
2697 createSelectionMerge(mergeBlock, control);
2698
2699 // make the switch instruction
2700 Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
2701 switchInst->addIdOperand(id: selector);
2702 auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
2703 switchInst->addIdOperand(id: defaultOrMerge->getId());
2704 defaultOrMerge->addPredecessor(pred: buildPoint);
2705 for (int i = 0; i < (int)caseValues.size(); ++i) {
2706 switchInst->addImmediateOperand(immediate: caseValues[i]);
2707 switchInst->addIdOperand(id: segmentBlocks[valueIndexToSegment[i]]->getId());
2708 segmentBlocks[valueIndexToSegment[i]]->addPredecessor(pred: buildPoint);
2709 }
2710 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(switchInst));
2711
2712 // push the merge block
2713 switchMerges.push(x: mergeBlock);
2714}
2715
2716// Comments in header
2717void Builder::addSwitchBreak()
2718{
2719 // branch to the top of the merge block stack
2720 createBranch(block: switchMerges.top());
2721 createAndSetNoPredecessorBlock("post-switch-break");
2722}
2723
2724// Comments in header
2725void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
2726{
2727 int lastSegment = nextSegment - 1;
2728 if (lastSegment >= 0) {
2729 // Close out previous segment by jumping, if necessary, to next segment
2730 if (! buildPoint->isTerminated())
2731 createBranch(block: segmentBlock[nextSegment]);
2732 }
2733 Block* block = segmentBlock[nextSegment];
2734 block->getParent().addBlock(block);
2735 setBuildPoint(block);
2736}
2737
2738// Comments in header
2739void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
2740{
2741 // Close out previous segment by jumping, if necessary, to next segment
2742 if (! buildPoint->isTerminated())
2743 addSwitchBreak();
2744
2745 switchMerges.top()->getParent().addBlock(block: switchMerges.top());
2746 setBuildPoint(switchMerges.top());
2747
2748 switchMerges.pop();
2749}
2750
2751Block& Builder::makeNewBlock()
2752{
2753 Function& function = buildPoint->getParent();
2754 auto block = new Block(getUniqueId(), function);
2755 function.addBlock(block);
2756 return *block;
2757}
2758
2759Builder::LoopBlocks& Builder::makeNewLoop()
2760{
2761 // This verbosity is needed to simultaneously get the same behavior
2762 // everywhere (id's in the same order), have a syntax that works
2763 // across lots of versions of C++, have no warnings from pedantic
2764 // compilation modes, and leave the rest of the code alone.
2765 Block& head = makeNewBlock();
2766 Block& body = makeNewBlock();
2767 Block& merge = makeNewBlock();
2768 Block& continue_target = makeNewBlock();
2769 LoopBlocks blocks(head, body, merge, continue_target);
2770 loops.push(x: blocks);
2771 return loops.top();
2772}
2773
2774void Builder::createLoopContinue()
2775{
2776 createBranch(block: &loops.top().continue_target);
2777 // Set up a block for dead code.
2778 createAndSetNoPredecessorBlock("post-loop-continue");
2779}
2780
2781void Builder::createLoopExit()
2782{
2783 createBranch(block: &loops.top().merge);
2784 // Set up a block for dead code.
2785 createAndSetNoPredecessorBlock("post-loop-break");
2786}
2787
2788void Builder::closeLoop()
2789{
2790 loops.pop();
2791}
2792
2793void Builder::clearAccessChain()
2794{
2795 accessChain.base = NoResult;
2796 accessChain.indexChain.clear();
2797 accessChain.instr = NoResult;
2798 accessChain.swizzle.clear();
2799 accessChain.component = NoResult;
2800 accessChain.preSwizzleBaseType = NoType;
2801 accessChain.isRValue = false;
2802 accessChain.coherentFlags.clear();
2803 accessChain.alignment = 0;
2804}
2805
2806// Comments in header
2807void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
2808 AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
2809{
2810 accessChain.coherentFlags |= coherentFlags;
2811 accessChain.alignment |= alignment;
2812
2813 // swizzles can be stacked in GLSL, but simplified to a single
2814 // one here; the base type doesn't change
2815 if (accessChain.preSwizzleBaseType == NoType)
2816 accessChain.preSwizzleBaseType = preSwizzleBaseType;
2817
2818 // if needed, propagate the swizzle for the current access chain
2819 if (accessChain.swizzle.size() > 0) {
2820 std::vector<unsigned> oldSwizzle = accessChain.swizzle;
2821 accessChain.swizzle.resize(new_size: 0);
2822 for (unsigned int i = 0; i < swizzle.size(); ++i) {
2823 assert(swizzle[i] < oldSwizzle.size());
2824 accessChain.swizzle.push_back(x: oldSwizzle[swizzle[i]]);
2825 }
2826 } else
2827 accessChain.swizzle = swizzle;
2828
2829 // determine if we need to track this swizzle anymore
2830 simplifyAccessChainSwizzle();
2831}
2832
2833// Comments in header
2834void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
2835{
2836 assert(accessChain.isRValue == false);
2837
2838 transferAccessChainSwizzle(dynamic: true);
2839
2840 // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.
2841 if (accessChain.swizzle.size() > 0 &&
2842 getNumTypeComponents(typeId: getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&
2843 accessChain.component == NoResult) {
2844 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
2845 accessChain.indexChain.push_back(x: makeUintConstant(u: accessChain.swizzle[i]));
2846 accessChain.instr = NoResult;
2847
2848 Id base = collapseAccessChain();
2849 addDecoration(id: base, decoration: nonUniform);
2850
2851 accessChain.indexChain.pop_back();
2852 accessChain.instr = NoResult;
2853
2854 // dynamic component should be gone
2855 assert(accessChain.component == NoResult);
2856
2857 Id source = createCompositeExtract(composite: rvalue, typeId: getContainedTypeId(typeId: getTypeId(resultId: rvalue)), index: i);
2858
2859 // take LSB of alignment
2860 alignment = alignment & ~(alignment & (alignment-1));
2861 if (getStorageClass(resultId: base) == StorageClassPhysicalStorageBufferEXT) {
2862 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2863 }
2864
2865 createStore(rValue: source, lValue: base, memoryAccess, scope, alignment);
2866 }
2867 }
2868 else {
2869 Id base = collapseAccessChain();
2870 addDecoration(id: base, decoration: nonUniform);
2871
2872 Id source = rvalue;
2873
2874 // dynamic component should be gone
2875 assert(accessChain.component == NoResult);
2876
2877 // If swizzle still exists, it may be out-of-order, we must load the target vector,
2878 // extract and insert elements to perform writeMask and/or swizzle.
2879 if (accessChain.swizzle.size() > 0) {
2880 Id tempBaseId = createLoad(lValue: base, precision: spv::NoPrecision);
2881 source = createLvalueSwizzle(typeId: getTypeId(resultId: tempBaseId), target: tempBaseId, source, channels: accessChain.swizzle);
2882 }
2883
2884 // take LSB of alignment
2885 alignment = alignment & ~(alignment & (alignment-1));
2886 if (getStorageClass(resultId: base) == StorageClassPhysicalStorageBufferEXT) {
2887 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2888 }
2889
2890 createStore(rValue: source, lValue: base, memoryAccess, scope, alignment);
2891 }
2892}
2893
2894// Comments in header
2895Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,
2896 Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,
2897 spv::Scope scope, unsigned int alignment)
2898{
2899 Id id;
2900
2901 if (accessChain.isRValue) {
2902 // transfer access chain, but try to stay in registers
2903 transferAccessChainSwizzle(dynamic: false);
2904 if (accessChain.indexChain.size() > 0) {
2905 Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
2906
2907 // if all the accesses are constants, we can use OpCompositeExtract
2908 std::vector<unsigned> indexes;
2909 bool constant = true;
2910 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
2911 if (isConstantScalar(resultId: accessChain.indexChain[i]))
2912 indexes.push_back(x: getConstantScalar(resultId: accessChain.indexChain[i]));
2913 else {
2914 constant = false;
2915 break;
2916 }
2917 }
2918
2919 if (constant) {
2920 id = createCompositeExtract(composite: accessChain.base, typeId: swizzleBase, indexes);
2921 setPrecision(id, precision);
2922 } else {
2923 Id lValue = NoResult;
2924 if (spvVersion >= Spv_1_4 && isValidInitializer(resultId: accessChain.base)) {
2925 // make a new function variable for this r-value, using an initializer,
2926 // and mark it as NonWritable so that downstream it can be detected as a lookup
2927 // table
2928 lValue = createVariable(precision: NoPrecision, storageClass: StorageClassFunction, type: getTypeId(resultId: accessChain.base),
2929 name: "indexable", initializer: accessChain.base);
2930 addDecoration(id: lValue, decoration: DecorationNonWritable);
2931 } else {
2932 lValue = createVariable(precision: NoPrecision, storageClass: StorageClassFunction, type: getTypeId(resultId: accessChain.base),
2933 name: "indexable");
2934 // store into it
2935 createStore(rValue: accessChain.base, lValue);
2936 }
2937 // move base to the new variable
2938 accessChain.base = lValue;
2939 accessChain.isRValue = false;
2940
2941 // load through the access chain
2942 id = createLoad(lValue: collapseAccessChain(), precision);
2943 }
2944 } else
2945 id = accessChain.base; // no precision, it was set when this was defined
2946 } else {
2947 transferAccessChainSwizzle(dynamic: true);
2948
2949 // take LSB of alignment
2950 alignment = alignment & ~(alignment & (alignment-1));
2951 if (getStorageClass(resultId: accessChain.base) == StorageClassPhysicalStorageBufferEXT) {
2952 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2953 }
2954
2955 // load through the access chain
2956 id = collapseAccessChain();
2957 // Apply nonuniform both to the access chain and the loaded value.
2958 // Buffer accesses need the access chain decorated, and this is where
2959 // loaded image types get decorated. TODO: This should maybe move to
2960 // createImageTextureFunctionCall.
2961 addDecoration(id, decoration: l_nonUniform);
2962 id = createLoad(lValue: id, precision, memoryAccess, scope, alignment);
2963 addDecoration(id, decoration: r_nonUniform);
2964 }
2965
2966 // Done, unless there are swizzles to do
2967 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2968 return id;
2969
2970 // Do remaining swizzling
2971
2972 // Do the basic swizzle
2973 if (accessChain.swizzle.size() > 0) {
2974 Id swizzledType = getScalarTypeId(typeId: getTypeId(resultId: id));
2975 if (accessChain.swizzle.size() > 1)
2976 swizzledType = makeVectorType(component: swizzledType, size: (int)accessChain.swizzle.size());
2977 id = createRvalueSwizzle(precision, typeId: swizzledType, source: id, channels: accessChain.swizzle);
2978 }
2979
2980 // Do the dynamic component
2981 if (accessChain.component != NoResult)
2982 id = setPrecision(id: createVectorExtractDynamic(vector: id, typeId: resultType, componentIndex: accessChain.component), precision);
2983
2984 addDecoration(id, decoration: r_nonUniform);
2985 return id;
2986}
2987
2988Id Builder::accessChainGetLValue()
2989{
2990 assert(accessChain.isRValue == false);
2991
2992 transferAccessChainSwizzle(dynamic: true);
2993 Id lvalue = collapseAccessChain();
2994
2995 // If swizzle exists, it is out-of-order or not full, we must load the target vector,
2996 // extract and insert elements to perform writeMask and/or swizzle. This does not
2997 // go with getting a direct l-value pointer.
2998 assert(accessChain.swizzle.size() == 0);
2999 assert(accessChain.component == NoResult);
3000
3001 return lvalue;
3002}
3003
3004// comment in header
3005Id Builder::accessChainGetInferredType()
3006{
3007 // anything to operate on?
3008 if (accessChain.base == NoResult)
3009 return NoType;
3010 Id type = getTypeId(resultId: accessChain.base);
3011
3012 // do initial dereference
3013 if (! accessChain.isRValue)
3014 type = getContainedTypeId(typeId: type);
3015
3016 // dereference each index
3017 for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
3018 if (isStructType(typeId: type))
3019 type = getContainedTypeId(typeId: type, member: getConstantScalar(resultId: *it));
3020 else
3021 type = getContainedTypeId(typeId: type);
3022 }
3023
3024 // dereference swizzle
3025 if (accessChain.swizzle.size() == 1)
3026 type = getContainedTypeId(typeId: type);
3027 else if (accessChain.swizzle.size() > 1)
3028 type = makeVectorType(component: getContainedTypeId(typeId: type), size: (int)accessChain.swizzle.size());
3029
3030 // dereference component selection
3031 if (accessChain.component)
3032 type = getContainedTypeId(typeId: type);
3033
3034 return type;
3035}
3036
3037void Builder::dump(std::vector<unsigned int>& out) const
3038{
3039 // Header, before first instructions:
3040 out.push_back(x: MagicNumber);
3041 out.push_back(x: spvVersion);
3042 out.push_back(x: builderNumber);
3043 out.push_back(x: uniqueId + 1);
3044 out.push_back(x: 0);
3045
3046 // Capabilities
3047 for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
3048 Instruction capInst(0, 0, OpCapability);
3049 capInst.addImmediateOperand(immediate: *it);
3050 capInst.dump(out);
3051 }
3052
3053 for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
3054 Instruction extInst(0, 0, OpExtension);
3055 extInst.addStringOperand(str: it->c_str());
3056 extInst.dump(out);
3057 }
3058
3059 dumpInstructions(out, imports);
3060 Instruction memInst(0, 0, OpMemoryModel);
3061 memInst.addImmediateOperand(immediate: addressModel);
3062 memInst.addImmediateOperand(immediate: memoryModel);
3063 memInst.dump(out);
3064
3065 // Instructions saved up while building:
3066 dumpInstructions(out, entryPoints);
3067 dumpInstructions(out, executionModes);
3068
3069 // Debug instructions
3070 dumpInstructions(out, strings);
3071 dumpSourceInstructions(out);
3072 for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
3073 Instruction sourceExtInst(0, 0, OpSourceExtension);
3074 sourceExtInst.addStringOperand(str: sourceExtensions[e]);
3075 sourceExtInst.dump(out);
3076 }
3077 dumpInstructions(out, names);
3078 dumpModuleProcesses(out);
3079
3080 // Annotation instructions
3081 dumpInstructions(out, decorations);
3082
3083 dumpInstructions(out, constantsTypesGlobals);
3084 dumpInstructions(out, externals);
3085
3086 // The functions
3087 module.dump(out);
3088}
3089
3090//
3091// Protected methods.
3092//
3093
3094// Turn the described access chain in 'accessChain' into an instruction(s)
3095// computing its address. This *cannot* include complex swizzles, which must
3096// be handled after this is called.
3097//
3098// Can generate code.
3099Id Builder::collapseAccessChain()
3100{
3101 assert(accessChain.isRValue == false);
3102
3103 // did we already emit an access chain for this?
3104 if (accessChain.instr != NoResult)
3105 return accessChain.instr;
3106
3107 // If we have a dynamic component, we can still transfer
3108 // that into a final operand to the access chain. We need to remap the
3109 // dynamic component through the swizzle to get a new dynamic component to
3110 // update.
3111 //
3112 // This was not done in transferAccessChainSwizzle() because it might
3113 // generate code.
3114 remapDynamicSwizzle();
3115 if (accessChain.component != NoResult) {
3116 // transfer the dynamic component to the access chain
3117 accessChain.indexChain.push_back(x: accessChain.component);
3118 accessChain.component = NoResult;
3119 }
3120
3121 // note that non-trivial swizzling is left pending
3122
3123 // do we have an access chain?
3124 if (accessChain.indexChain.size() == 0)
3125 return accessChain.base;
3126
3127 // emit the access chain
3128 StorageClass storageClass = (StorageClass)module.getStorageClass(typeId: getTypeId(resultId: accessChain.base));
3129 accessChain.instr = createAccessChain(storageClass, base: accessChain.base, offsets: accessChain.indexChain);
3130
3131 return accessChain.instr;
3132}
3133
3134// For a dynamic component selection of a swizzle.
3135//
3136// Turn the swizzle and dynamic component into just a dynamic component.
3137//
3138// Generates code.
3139void Builder::remapDynamicSwizzle()
3140{
3141 // do we have a swizzle to remap a dynamic component through?
3142 if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
3143 // build a vector of the swizzle for the component to map into
3144 std::vector<Id> components;
3145 for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
3146 components.push_back(x: makeUintConstant(u: accessChain.swizzle[c]));
3147 Id mapType = makeVectorType(component: makeUintType(width: 32), size: (int)accessChain.swizzle.size());
3148 Id map = makeCompositeConstant(typeId: mapType, members: components);
3149
3150 // use it
3151 accessChain.component = createVectorExtractDynamic(vector: map, typeId: makeUintType(width: 32), componentIndex: accessChain.component);
3152 accessChain.swizzle.clear();
3153 }
3154}
3155
3156// clear out swizzle if it is redundant, that is reselecting the same components
3157// that would be present without the swizzle.
3158void Builder::simplifyAccessChainSwizzle()
3159{
3160 // If the swizzle has fewer components than the vector, it is subsetting, and must stay
3161 // to preserve that fact.
3162 if (getNumTypeComponents(typeId: accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
3163 return;
3164
3165 // if components are out of order, it is a swizzle
3166 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
3167 if (i != accessChain.swizzle[i])
3168 return;
3169 }
3170
3171 // otherwise, there is no need to track this swizzle
3172 accessChain.swizzle.clear();
3173 if (accessChain.component == NoResult)
3174 accessChain.preSwizzleBaseType = NoType;
3175}
3176
3177// To the extent any swizzling can become part of the chain
3178// of accesses instead of a post operation, make it so.
3179// If 'dynamic' is true, include transferring the dynamic component,
3180// otherwise, leave it pending.
3181//
3182// Does not generate code. just updates the access chain.
3183void Builder::transferAccessChainSwizzle(bool dynamic)
3184{
3185 // non existent?
3186 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
3187 return;
3188
3189 // too complex?
3190 // (this requires either a swizzle, or generating code for a dynamic component)
3191 if (accessChain.swizzle.size() > 1)
3192 return;
3193
3194 // single component, either in the swizzle and/or dynamic component
3195 if (accessChain.swizzle.size() == 1) {
3196 assert(accessChain.component == NoResult);
3197 // handle static component selection
3198 accessChain.indexChain.push_back(x: makeUintConstant(u: accessChain.swizzle.front()));
3199 accessChain.swizzle.clear();
3200 accessChain.preSwizzleBaseType = NoType;
3201 } else if (dynamic && accessChain.component != NoResult) {
3202 assert(accessChain.swizzle.size() == 0);
3203 // handle dynamic component
3204 accessChain.indexChain.push_back(x: accessChain.component);
3205 accessChain.preSwizzleBaseType = NoType;
3206 accessChain.component = NoResult;
3207 }
3208}
3209
3210// Utility method for creating a new block and setting the insert point to
3211// be in it. This is useful for flow-control operations that need a "dummy"
3212// block proceeding them (e.g. instructions after a discard, etc).
3213void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
3214{
3215 Block* block = new Block(getUniqueId(), buildPoint->getParent());
3216 block->setUnreachable();
3217 buildPoint->getParent().addBlock(block);
3218 setBuildPoint(block);
3219
3220 // if (name)
3221 // addName(block->getId(), name);
3222}
3223
3224// Comments in header
3225void Builder::createBranch(Block* block)
3226{
3227 Instruction* branch = new Instruction(OpBranch);
3228 branch->addIdOperand(id: block->getId());
3229 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(branch));
3230 block->addPredecessor(pred: buildPoint);
3231}
3232
3233void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
3234{
3235 Instruction* merge = new Instruction(OpSelectionMerge);
3236 merge->addIdOperand(id: mergeBlock->getId());
3237 merge->addImmediateOperand(immediate: control);
3238 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(merge));
3239}
3240
3241void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
3242 const std::vector<unsigned int>& operands)
3243{
3244 Instruction* merge = new Instruction(OpLoopMerge);
3245 merge->addIdOperand(id: mergeBlock->getId());
3246 merge->addIdOperand(id: continueBlock->getId());
3247 merge->addImmediateOperand(immediate: control);
3248 for (int op = 0; op < (int)operands.size(); ++op)
3249 merge->addImmediateOperand(immediate: operands[op]);
3250 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(merge));
3251}
3252
3253void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
3254{
3255 Instruction* branch = new Instruction(OpBranchConditional);
3256 branch->addIdOperand(id: condition);
3257 branch->addIdOperand(id: thenBlock->getId());
3258 branch->addIdOperand(id: elseBlock->getId());
3259 buildPoint->addInstruction(inst: std::unique_ptr<Instruction>(branch));
3260 thenBlock->addPredecessor(pred: buildPoint);
3261 elseBlock->addPredecessor(pred: buildPoint);
3262}
3263
3264// OpSource
3265// [OpSourceContinued]
3266// ...
3267void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
3268 std::vector<unsigned int>& out) const
3269{
3270 const int maxWordCount = 0xFFFF;
3271 const int opSourceWordCount = 4;
3272 const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
3273
3274 if (source != SourceLanguageUnknown) {
3275 // OpSource Language Version File Source
3276 Instruction sourceInst(NoResult, NoType, OpSource);
3277 sourceInst.addImmediateOperand(immediate: source);
3278 sourceInst.addImmediateOperand(immediate: sourceVersion);
3279 // File operand
3280 if (fileId != NoResult) {
3281 sourceInst.addIdOperand(id: fileId);
3282 // Source operand
3283 if (text.size() > 0) {
3284 int nextByte = 0;
3285 std::string subString;
3286 while ((int)text.size() - nextByte > 0) {
3287 subString = text.substr(pos: nextByte, n: nonNullBytesPerInstruction);
3288 if (nextByte == 0) {
3289 // OpSource
3290 sourceInst.addStringOperand(str: subString.c_str());
3291 sourceInst.dump(out);
3292 } else {
3293 // OpSourcContinued
3294 Instruction sourceContinuedInst(OpSourceContinued);
3295 sourceContinuedInst.addStringOperand(str: subString.c_str());
3296 sourceContinuedInst.dump(out);
3297 }
3298 nextByte += nonNullBytesPerInstruction;
3299 }
3300 } else
3301 sourceInst.dump(out);
3302 } else
3303 sourceInst.dump(out);
3304 }
3305}
3306
3307// Dump an OpSource[Continued] sequence for the source and every include file
3308void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
3309{
3310 dumpSourceInstructions(fileId: sourceFileStringId, text: sourceText, out);
3311 for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
3312 dumpSourceInstructions(fileId: iItr->first, text: *iItr->second, out);
3313}
3314
3315void Builder::dumpInstructions(std::vector<unsigned int>& out,
3316 const std::vector<std::unique_ptr<Instruction> >& instructions) const
3317{
3318 for (int i = 0; i < (int)instructions.size(); ++i) {
3319 instructions[i]->dump(out);
3320 }
3321}
3322
3323void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
3324{
3325 for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
3326 Instruction moduleProcessed(OpModuleProcessed);
3327 moduleProcessed.addStringOperand(str: moduleProcesses[i]);
3328 moduleProcessed.dump(out);
3329 }
3330}
3331
3332}; // end spv namespace
3333

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