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