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