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