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