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