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