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.
6 // All rights reserved.
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
12 // Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
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.
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.
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.
38 // Helper for making SPIR-V IR. Generally, this is documented in the header
45 #include <unordered_set>
48 #include "SpvBuilder.h"
51 #include "hex_float.h"
60 Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
61 spvVersion(spvVersion),
62 source(SourceLanguageUnknown),
64 sourceFileStringId(NoResult),
68 addressModel(AddressingModelLogical),
69 memoryModel(MemoryModelGLSL450),
70 builderNumber(magicNumber),
73 entryPointFunction(0),
74 generatingOpCodeForSpecConst(false),
84 Id Builder::import(const char* name)
86 Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
87 import->addStringOperand(name);
88 module.mapInstruction(import);
90 imports.push_back(std::unique_ptr<Instruction>(import));
91 return import->getResultId();
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.
97 void Builder::setLine(int lineNum)
99 if (lineNum != 0 && lineNum != currentLine) {
100 currentLine = lineNum;
102 addLine(sourceFileStringId, currentLine, 0);
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.
109 void Builder::setLine(int lineNum, const char* filename)
111 if (filename == nullptr) {
115 if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
116 strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) {
117 currentLine = lineNum;
118 currentFile = filename;
120 spv::Id strId = getStringId(filename);
121 addLine(strId, currentLine, 0);
126 void Builder::addLine(Id fileName, int lineNum, int column)
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));
135 // For creating new groupedTypes (will return old type if the requested one was already made).
136 Id Builder::makeVoidType()
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);
145 type = groupedTypes[OpTypeVoid].back();
147 return type->getResultId();
150 Id Builder::makeBoolType()
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);
159 type = groupedTypes[OpTypeBool].back();
161 return type->getResultId();
164 Id Builder::makeSamplerType()
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);
173 type = groupedTypes[OpTypeSampler].back();
175 return type->getResultId();
178 Id Builder::makePointer(StorageClass storageClass, Id pointee)
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();
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);
197 return type->getResultId();
200 Id Builder::makeForwardPointer(StorageClass storageClass)
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);
210 return type->getResultId();
213 Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
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();
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);
231 return type->getResultId();
234 Id Builder::makeIntegerType(int width, bool hasSign)
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();
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);
258 // deal with capabilities
262 // these are currently handled by storage-type declarations and post processing
265 addCapability(CapabilityInt64);
271 return type->getResultId();
274 Id Builder::makeFloatType(int width)
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();
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);
296 // deal with capabilities
299 // currently handled by storage-type declarations and post processing
302 addCapability(CapabilityFloat64);
308 return type->getResultId();
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.
315 Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
317 // Don't look for previous one, because in the general case,
318 // structs can be duplicated except for decorations.
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);
329 return type->getResultId();
332 // Make a struct for the simple results of several instructions,
333 // checking for duplication.
334 Id Builder::makeStructResultType(Id type0, Id type1)
338 for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
339 type = groupedTypes[OpTypeStruct][t];
340 if (type->getNumOperands() != 2)
342 if (type->getIdOperand(0) != type0 ||
343 type->getIdOperand(1) != type1)
345 return type->getResultId();
348 // not found, make it
349 std::vector<spv::Id> members;
350 members.push_back(type0);
351 members.push_back(type1);
353 return makeStructType(members, "ResType");
356 Id Builder::makeVectorType(Id component, int size)
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();
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);
375 return type->getResultId();
378 Id Builder::makeMatrixType(Id component, int cols, int rows)
380 assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
382 Id column = makeVectorType(component, rows);
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();
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);
401 return type->getResultId();
404 Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols)
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();
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);
427 return type->getResultId();
430 Id Builder::makeGenericType(spv::Op opcode, std::vector<spv::IdImmediate>& operands)
434 for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) {
435 type = groupedTypes[opcode][t];
436 if (static_cast<size_t>(type->getNumOperands()) != operands.size())
437 continue; // Number mismatch, find next
440 for (int op = 0; match && op < (int)operands.size(); ++op) {
441 match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word;
444 return type->getResultId();
447 // not found, make it
448 type = new Instruction(getUniqueId(), NoType, opcode);
449 for (size_t op = 0; op < operands.size(); ++op) {
450 if (operands[op].isId)
451 type->addIdOperand(operands[op].word);
453 type->addImmediateOperand(operands[op].word);
455 groupedTypes[opcode].push_back(type);
456 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
457 module.mapInstruction(type);
459 return type->getResultId();
462 // TODO: performance: track arrays per stride
463 // If a stride is supplied (non-zero) make an array.
464 // If no stride (0), reuse previous array types.
465 // 'size' is an Id of a constant or specialization constant of the array size
466 Id Builder::makeArrayType(Id element, Id sizeId, int stride)
470 // try to find existing type
471 for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
472 type = groupedTypes[OpTypeArray][t];
473 if (type->getIdOperand(0) == element &&
474 type->getIdOperand(1) == sizeId)
475 return type->getResultId();
479 // not found, make it
480 type = new Instruction(getUniqueId(), NoType, OpTypeArray);
481 type->addIdOperand(element);
482 type->addIdOperand(sizeId);
483 groupedTypes[OpTypeArray].push_back(type);
484 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
485 module.mapInstruction(type);
487 return type->getResultId();
490 Id Builder::makeRuntimeArray(Id element)
492 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
493 type->addIdOperand(element);
494 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
495 module.mapInstruction(type);
497 return type->getResultId();
500 Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
504 for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
505 type = groupedTypes[OpTypeFunction][t];
506 if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
508 bool mismatch = false;
509 for (int p = 0; p < (int)paramTypes.size(); ++p) {
510 if (paramTypes[p] != type->getIdOperand(p + 1)) {
516 return type->getResultId();
519 // not found, make it
520 type = new Instruction(getUniqueId(), NoType, OpTypeFunction);
521 type->addIdOperand(returnType);
522 for (int p = 0; p < (int)paramTypes.size(); ++p)
523 type->addIdOperand(paramTypes[p]);
524 groupedTypes[OpTypeFunction].push_back(type);
525 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
526 module.mapInstruction(type);
528 return type->getResultId();
531 Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled,
534 assert(sampled == 1 || sampled == 2);
538 for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
539 type = groupedTypes[OpTypeImage][t];
540 if (type->getIdOperand(0) == sampledType &&
541 type->getImmediateOperand(1) == (unsigned int)dim &&
542 type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
543 type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
544 type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
545 type->getImmediateOperand(5) == sampled &&
546 type->getImmediateOperand(6) == (unsigned int)format)
547 return type->getResultId();
550 // not found, make it
551 type = new Instruction(getUniqueId(), NoType, OpTypeImage);
552 type->addIdOperand(sampledType);
553 type->addImmediateOperand( dim);
554 type->addImmediateOperand( depth ? 1 : 0);
555 type->addImmediateOperand(arrayed ? 1 : 0);
556 type->addImmediateOperand( ms ? 1 : 0);
557 type->addImmediateOperand(sampled);
558 type->addImmediateOperand((unsigned int)format);
560 groupedTypes[OpTypeImage].push_back(type);
561 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
562 module.mapInstruction(type);
565 // deal with capabilities
569 addCapability(CapabilitySampledBuffer);
571 addCapability(CapabilityImageBuffer);
575 addCapability(CapabilitySampled1D);
577 addCapability(CapabilityImage1D);
582 addCapability(CapabilitySampledCubeArray);
584 addCapability(CapabilityImageCubeArray);
589 addCapability(CapabilitySampledRect);
591 addCapability(CapabilityImageRect);
594 addCapability(CapabilityInputAttachment);
602 // Images used with subpass data are not storage
603 // images, so don't require the capability for them.
604 if (dim != Dim::DimSubpassData)
605 addCapability(CapabilityStorageImageMultisample);
607 addCapability(CapabilityImageMSArray);
612 return type->getResultId();
615 Id Builder::makeSampledImageType(Id imageType)
619 for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
620 type = groupedTypes[OpTypeSampledImage][t];
621 if (type->getIdOperand(0) == imageType)
622 return type->getResultId();
625 // not found, make it
626 type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
627 type->addIdOperand(imageType);
629 groupedTypes[OpTypeSampledImage].push_back(type);
630 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
631 module.mapInstruction(type);
633 return type->getResultId();
637 Id Builder::makeAccelerationStructureType()
640 if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) {
641 type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR);
642 groupedTypes[OpTypeAccelerationStructureKHR].push_back(type);
643 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
644 module.mapInstruction(type);
646 type = groupedTypes[OpTypeAccelerationStructureKHR].back();
649 return type->getResultId();
652 Id Builder::makeRayQueryType()
655 if (groupedTypes[OpTypeRayQueryKHR].size() == 0) {
656 type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR);
657 groupedTypes[OpTypeRayQueryKHR].push_back(type);
658 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
659 module.mapInstruction(type);
661 type = groupedTypes[OpTypeRayQueryKHR].back();
664 return type->getResultId();
668 Id Builder::getDerefTypeId(Id resultId) const
670 Id typeId = getTypeId(resultId);
671 assert(isPointerType(typeId));
673 return module.getInstruction(typeId)->getIdOperand(1);
676 Op Builder::getMostBasicTypeClass(Id typeId) const
678 Instruction* instr = module.getInstruction(typeId);
680 Op typeClass = instr->getOpCode();
686 case OpTypeRuntimeArray:
687 return getMostBasicTypeClass(instr->getIdOperand(0));
689 return getMostBasicTypeClass(instr->getIdOperand(1));
695 int Builder::getNumTypeConstituents(Id typeId) const
697 Instruction* instr = module.getInstruction(typeId);
699 switch (instr->getOpCode())
708 return instr->getImmediateOperand(1);
711 Id lengthId = instr->getIdOperand(1);
712 return module.getInstruction(lengthId)->getImmediateOperand(0);
715 return instr->getNumOperands();
716 case OpTypeCooperativeMatrixNV:
717 // has only one constituent when used with OpCompositeConstruct.
725 // Return the lowest-level type of scalar that an homogeneous composite is made out of.
726 // Typically, this is just to find out if something is made out of ints or floats.
727 // However, it includes returning a structure, if say, it is an array of structure.
728 Id Builder::getScalarTypeId(Id typeId) const
730 Instruction* instr = module.getInstruction(typeId);
732 Op typeClass = instr->getOpCode();
740 return instr->getResultId();
744 case OpTypeRuntimeArray:
746 return getScalarTypeId(getContainedTypeId(typeId));
753 // Return the type of 'member' of a composite.
754 Id Builder::getContainedTypeId(Id typeId, int member) const
756 Instruction* instr = module.getInstruction(typeId);
758 Op typeClass = instr->getOpCode();
764 case OpTypeRuntimeArray:
765 case OpTypeCooperativeMatrixNV:
766 return instr->getIdOperand(0);
768 return instr->getIdOperand(1);
770 return instr->getIdOperand(member);
777 // Figure out the final resulting type of the access chain.
778 Id Builder::getResultingAccessChainType() const
780 assert(accessChain.base != NoResult);
781 Id typeId = getTypeId(accessChain.base);
783 assert(isPointerType(typeId));
784 typeId = getContainedTypeId(typeId);
786 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
787 if (isStructType(typeId)) {
788 assert(isConstantScalar(accessChain.indexChain[i]));
789 typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));
791 typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);
797 // Return the immediately contained type of a given composite type.
798 Id Builder::getContainedTypeId(Id typeId) const
800 return getContainedTypeId(typeId, 0);
803 // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
804 // of width 'width'. The 'width' is only consumed for int and float types.
805 // Returns false otherwise.
806 bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
808 const Instruction& instr = *module.getInstruction(typeId);
810 Op typeClass = instr.getOpCode();
815 return typeClass == typeOp && instr.getImmediateOperand(0) == width;
817 for (int m = 0; m < instr.getNumOperands(); ++m) {
818 if (containsType(instr.getIdOperand(m), typeOp, width))
827 case OpTypeRuntimeArray:
828 return containsType(getContainedTypeId(typeId), typeOp, width);
830 return typeClass == typeOp;
834 // return true if the type is a pointer to PhysicalStorageBufferEXT or an
835 // array of such pointers. These require restrict/aliased decorations.
836 bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
838 const Instruction& instr = *module.getInstruction(typeId);
840 Op typeClass = instr.getOpCode();
844 return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
846 return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));
852 // See if a scalar constant of this type has already been created, so it
853 // can be reused rather than duplicated. (Required by the specification).
854 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
856 Instruction* constant;
857 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
858 constant = groupedConstants[typeClass][i];
859 if (constant->getOpCode() == opcode &&
860 constant->getTypeId() == typeId &&
861 constant->getImmediateOperand(0) == value)
862 return constant->getResultId();
868 // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
869 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
871 Instruction* constant;
872 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
873 constant = groupedConstants[typeClass][i];
874 if (constant->getOpCode() == opcode &&
875 constant->getTypeId() == typeId &&
876 constant->getImmediateOperand(0) == v1 &&
877 constant->getImmediateOperand(1) == v2)
878 return constant->getResultId();
884 // Return true if consuming 'opcode' means consuming a constant.
885 // "constant" here means after final transform to executable code,
886 // the value consumed will be a constant, so includes specialization.
887 bool Builder::isConstantOpCode(Op opcode) const
892 case OpConstantFalse:
894 case OpConstantComposite:
895 case OpConstantSampler:
897 case OpSpecConstantTrue:
898 case OpSpecConstantFalse:
900 case OpSpecConstantComposite:
901 case OpSpecConstantOp:
908 // Return true if consuming 'opcode' means consuming a specialization constant.
909 bool Builder::isSpecConstantOpCode(Op opcode) const
912 case OpSpecConstantTrue:
913 case OpSpecConstantFalse:
915 case OpSpecConstantComposite:
916 case OpSpecConstantOp:
923 Id Builder::makeNullConstant(Id typeId)
925 Instruction* constant;
927 // See if we already made it.
928 Id existing = NoResult;
929 for (int i = 0; i < (int)nullConstants.size(); ++i) {
930 constant = nullConstants[i];
931 if (constant->getTypeId() == typeId)
932 existing = constant->getResultId();
935 if (existing != NoResult)
939 Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull);
940 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
941 nullConstants.push_back(c);
942 module.mapInstruction(c);
944 return c->getResultId();
947 Id Builder::makeBoolConstant(bool b, bool specConstant)
949 Id typeId = makeBoolType();
950 Instruction* constant;
951 Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
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) {
957 for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
958 constant = groupedConstants[OpTypeBool][i];
959 if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
960 existing = constant->getResultId();
968 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
969 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
970 groupedConstants[OpTypeBool].push_back(c);
971 module.mapInstruction(c);
973 return c->getResultId();
976 Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
978 Op opcode = specConstant ? OpSpecConstant : OpConstant;
980 // See if we already made it. Applies only to regular constants, because specialization constants
981 // must remain distinct for the purpose of applying a SpecId decoration.
982 if (! specConstant) {
983 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
988 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
989 c->addImmediateOperand(value);
990 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
991 groupedConstants[OpTypeInt].push_back(c);
992 module.mapInstruction(c);
994 return c->getResultId();
997 Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
999 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1001 unsigned op1 = value & 0xFFFFFFFF;
1002 unsigned op2 = value >> 32;
1004 // See if we already made it. Applies only to regular constants, because specialization constants
1005 // must remain distinct for the purpose of applying a SpecId decoration.
1006 if (! specConstant) {
1007 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
1012 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1013 c->addImmediateOperand(op1);
1014 c->addImmediateOperand(op2);
1015 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1016 groupedConstants[OpTypeInt].push_back(c);
1017 module.mapInstruction(c);
1019 return c->getResultId();
1022 Id Builder::makeFloatConstant(float f, bool specConstant)
1024 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1025 Id typeId = makeFloatType(32);
1026 union { float fl; unsigned int ui; } u;
1028 unsigned value = u.ui;
1030 // See if we already made it. Applies only to regular constants, because specialization constants
1031 // must remain distinct for the purpose of applying a SpecId decoration.
1032 if (! specConstant) {
1033 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
1038 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1039 c->addImmediateOperand(value);
1040 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1041 groupedConstants[OpTypeFloat].push_back(c);
1042 module.mapInstruction(c);
1044 return c->getResultId();
1047 Id Builder::makeDoubleConstant(double d, bool specConstant)
1053 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1054 Id typeId = makeFloatType(64);
1055 union { double db; unsigned long long ull; } u;
1057 unsigned long long value = u.ull;
1058 unsigned op1 = value & 0xFFFFFFFF;
1059 unsigned op2 = value >> 32;
1061 // See if we already made it. Applies only to regular constants, because specialization constants
1062 // must remain distinct for the purpose of applying a SpecId decoration.
1063 if (! specConstant) {
1064 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
1069 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1070 c->addImmediateOperand(op1);
1071 c->addImmediateOperand(op2);
1072 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1073 groupedConstants[OpTypeFloat].push_back(c);
1074 module.mapInstruction(c);
1076 return c->getResultId();
1080 Id Builder::makeFloat16Constant(float f16, bool specConstant)
1086 Op opcode = specConstant ? OpSpecConstant : OpConstant;
1087 Id typeId = makeFloatType(16);
1089 spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
1090 spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
1091 fVal.castTo(f16Val, spvutils::kRoundToZero);
1093 unsigned value = f16Val.value().getAsFloat().get_value();
1095 // See if we already made it. Applies only to regular constants, because specialization constants
1096 // must remain distinct for the purpose of applying a SpecId decoration.
1097 if (!specConstant) {
1098 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
1103 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1104 c->addImmediateOperand(value);
1105 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1106 groupedConstants[OpTypeFloat].push_back(c);
1107 module.mapInstruction(c);
1109 return c->getResultId();
1113 Id Builder::makeFpConstant(Id type, double d, bool specConstant)
1116 const int width = 32;
1117 assert(width == getScalarTypeWidth(type));
1119 const int width = getScalarTypeWidth(type);
1122 assert(isFloatType(type));
1126 return makeFloat16Constant((float)d, specConstant);
1128 return makeFloatConstant((float)d, specConstant);
1130 return makeDoubleConstant(d, specConstant);
1139 Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
1141 Instruction* constant = 0;
1143 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
1144 constant = groupedConstants[typeClass][i];
1146 if (constant->getTypeId() != typeId)
1150 bool mismatch = false;
1151 for (int op = 0; op < constant->getNumOperands(); ++op) {
1152 if (constant->getIdOperand(op) != comps[op]) {
1163 return found ? constant->getResultId() : NoResult;
1166 Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
1168 Instruction* constant = 0;
1170 for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
1171 constant = groupedStructConstants[typeId][i];
1174 bool mismatch = false;
1175 for (int op = 0; op < constant->getNumOperands(); ++op) {
1176 if (constant->getIdOperand(op) != comps[op]) {
1187 return found ? constant->getResultId() : NoResult;
1190 // Comments in header
1191 Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
1193 Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
1195 Op typeClass = getTypeClass(typeId);
1197 switch (typeClass) {
1201 case OpTypeCooperativeMatrixNV:
1202 if (! specConstant) {
1203 Id existing = findCompositeConstant(typeClass, typeId, members);
1209 if (! specConstant) {
1210 Id existing = findStructConstant(typeId, members);
1217 return makeFloatConstant(0.0);
1220 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1221 for (int op = 0; op < (int)members.size(); ++op)
1222 c->addIdOperand(members[op]);
1223 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1224 if (typeClass == OpTypeStruct)
1225 groupedStructConstants[typeId].push_back(c);
1227 groupedConstants[typeClass].push_back(c);
1228 module.mapInstruction(c);
1230 return c->getResultId();
1233 Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
1235 Instruction* entryPoint = new Instruction(OpEntryPoint);
1236 entryPoint->addImmediateOperand(model);
1237 entryPoint->addIdOperand(function->getId());
1238 entryPoint->addStringOperand(name);
1240 entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
1245 // Currently relying on the fact that all 'value' of interest are small non-negative values.
1246 void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
1248 Instruction* instr = new Instruction(OpExecutionMode);
1249 instr->addIdOperand(entryPoint->getId());
1250 instr->addImmediateOperand(mode);
1252 instr->addImmediateOperand(value1);
1254 instr->addImmediateOperand(value2);
1256 instr->addImmediateOperand(value3);
1258 executionModes.push_back(std::unique_ptr<Instruction>(instr));
1261 void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
1263 Instruction* instr = new Instruction(OpExecutionMode);
1264 instr->addIdOperand(entryPoint->getId());
1265 instr->addImmediateOperand(mode);
1266 for (auto literal : literals)
1267 instr->addImmediateOperand(literal);
1269 executionModes.push_back(std::unique_ptr<Instruction>(instr));
1272 void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
1274 Instruction* instr = new Instruction(OpExecutionModeId);
1275 instr->addIdOperand(entryPoint->getId());
1276 instr->addImmediateOperand(mode);
1277 for (auto operandId : operandIds)
1278 instr->addIdOperand(operandId);
1280 executionModes.push_back(std::unique_ptr<Instruction>(instr));
1283 void Builder::addName(Id id, const char* string)
1285 Instruction* name = new Instruction(OpName);
1286 name->addIdOperand(id);
1287 name->addStringOperand(string);
1289 names.push_back(std::unique_ptr<Instruction>(name));
1292 void Builder::addMemberName(Id id, int memberNumber, const char* string)
1294 Instruction* name = new Instruction(OpMemberName);
1295 name->addIdOperand(id);
1296 name->addImmediateOperand(memberNumber);
1297 name->addStringOperand(string);
1299 names.push_back(std::unique_ptr<Instruction>(name));
1302 void Builder::addDecoration(Id id, Decoration decoration, int num)
1304 if (decoration == spv::DecorationMax)
1307 Instruction* dec = new Instruction(OpDecorate);
1308 dec->addIdOperand(id);
1309 dec->addImmediateOperand(decoration);
1311 dec->addImmediateOperand(num);
1313 decorations.push_back(std::unique_ptr<Instruction>(dec));
1316 void Builder::addDecoration(Id id, Decoration decoration, const char* s)
1318 if (decoration == spv::DecorationMax)
1321 Instruction* dec = new Instruction(OpDecorateString);
1322 dec->addIdOperand(id);
1323 dec->addImmediateOperand(decoration);
1324 dec->addStringOperand(s);
1326 decorations.push_back(std::unique_ptr<Instruction>(dec));
1329 void Builder::addDecoration(Id id, Decoration decoration, const std::vector<unsigned>& literals)
1331 if (decoration == spv::DecorationMax)
1334 Instruction* dec = new Instruction(OpDecorate);
1335 dec->addIdOperand(id);
1336 dec->addImmediateOperand(decoration);
1337 for (auto literal : literals)
1338 dec->addImmediateOperand(literal);
1340 decorations.push_back(std::unique_ptr<Instruction>(dec));
1343 void Builder::addDecoration(Id id, Decoration decoration, const std::vector<const char*>& strings)
1345 if (decoration == spv::DecorationMax)
1348 Instruction* dec = new Instruction(OpDecorateString);
1349 dec->addIdOperand(id);
1350 dec->addImmediateOperand(decoration);
1351 for (auto string : strings)
1352 dec->addStringOperand(string);
1354 decorations.push_back(std::unique_ptr<Instruction>(dec));
1357 void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
1359 if (decoration == spv::DecorationMax)
1362 Instruction* dec = new Instruction(OpDecorateId);
1363 dec->addIdOperand(id);
1364 dec->addImmediateOperand(decoration);
1365 dec->addIdOperand(idDecoration);
1367 decorations.push_back(std::unique_ptr<Instruction>(dec));
1370 void Builder::addDecorationId(Id id, Decoration decoration, const std::vector<Id>& operandIds)
1372 if(decoration == spv::DecorationMax)
1375 Instruction* dec = new Instruction(OpDecorateId);
1376 dec->addIdOperand(id);
1377 dec->addImmediateOperand(decoration);
1379 for (auto operandId : operandIds)
1380 dec->addIdOperand(operandId);
1382 decorations.push_back(std::unique_ptr<Instruction>(dec));
1385 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
1387 if (decoration == spv::DecorationMax)
1390 Instruction* dec = new Instruction(OpMemberDecorate);
1391 dec->addIdOperand(id);
1392 dec->addImmediateOperand(member);
1393 dec->addImmediateOperand(decoration);
1395 dec->addImmediateOperand(num);
1397 decorations.push_back(std::unique_ptr<Instruction>(dec));
1400 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
1402 if (decoration == spv::DecorationMax)
1405 Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
1406 dec->addIdOperand(id);
1407 dec->addImmediateOperand(member);
1408 dec->addImmediateOperand(decoration);
1409 dec->addStringOperand(s);
1411 decorations.push_back(std::unique_ptr<Instruction>(dec));
1414 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<unsigned>& literals)
1416 if (decoration == spv::DecorationMax)
1419 Instruction* dec = new Instruction(OpMemberDecorate);
1420 dec->addIdOperand(id);
1421 dec->addImmediateOperand(member);
1422 dec->addImmediateOperand(decoration);
1423 for (auto literal : literals)
1424 dec->addImmediateOperand(literal);
1426 decorations.push_back(std::unique_ptr<Instruction>(dec));
1429 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector<const char*>& strings)
1431 if (decoration == spv::DecorationMax)
1434 Instruction* dec = new Instruction(OpMemberDecorateString);
1435 dec->addIdOperand(id);
1436 dec->addImmediateOperand(member);
1437 dec->addImmediateOperand(decoration);
1438 for (auto string : strings)
1439 dec->addStringOperand(string);
1441 decorations.push_back(std::unique_ptr<Instruction>(dec));
1444 // Comments in header
1445 Function* Builder::makeEntryPoint(const char* entryPoint)
1447 assert(! entryPointFunction);
1450 std::vector<Id> params;
1451 std::vector<std::vector<Decoration>> decorations;
1453 entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry);
1455 return entryPointFunction;
1458 // Comments in header
1459 Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
1460 const std::vector<Id>& paramTypes,
1461 const std::vector<std::vector<Decoration>>& decorations, Block **entry)
1463 // Make the function and initial instructions in it
1464 Id typeId = makeFunctionType(returnType, paramTypes);
1465 Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
1466 Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
1468 // Set up the precisions
1469 setPrecision(function->getId(), precision);
1470 function->setReturnPrecision(precision);
1471 for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
1472 for (int d = 0; d < (int)decorations[p].size(); ++d) {
1473 addDecoration(firstParamId + p, decorations[p][d]);
1474 function->addParamPrecision(p, decorations[p][d]);
1480 *entry = new Block(getUniqueId(), *function);
1481 function->addBlock(*entry);
1482 setBuildPoint(*entry);
1486 addName(function->getId(), name);
1488 functions.push_back(std::unique_ptr<Function>(function));
1493 // Comments in header
1494 void Builder::makeReturn(bool implicit, Id retVal)
1497 Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
1498 inst->addIdOperand(retVal);
1499 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1501 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
1504 createAndSetNoPredecessorBlock("post-return");
1507 // Comments in header
1508 void Builder::leaveFunction()
1510 Block* block = buildPoint;
1511 Function& function = buildPoint->getParent();
1514 // If our function did not contain a return, add a return void now.
1515 if (! block->isTerminated()) {
1516 if (function.getReturnType() == makeVoidType())
1519 makeReturn(true, createUndefined(function.getReturnType()));
1524 // Comments in header
1525 void Builder::makeStatementTerminator(spv::Op opcode, const char *name)
1527 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(opcode)));
1528 createAndSetNoPredecessorBlock(name);
1531 // Comments in header
1532 Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer)
1534 Id pointerType = makePointer(storageClass, type);
1535 Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
1536 inst->addImmediateOperand(storageClass);
1537 if (initializer != NoResult)
1538 inst->addIdOperand(initializer);
1540 switch (storageClass) {
1541 case StorageClassFunction:
1542 // Validation rules require the declaration in the entry block
1543 buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
1547 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1548 module.mapInstruction(inst);
1553 addName(inst->getResultId(), name);
1554 setPrecision(inst->getResultId(), precision);
1556 return inst->getResultId();
1559 // Comments in header
1560 Id Builder::createUndefined(Id type)
1562 Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
1563 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1564 return inst->getResultId();
1567 // av/vis/nonprivate are unnecessary and illegal for some storage classes.
1568 spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc)
1572 case spv::StorageClassUniform:
1573 case spv::StorageClassWorkgroup:
1574 case spv::StorageClassStorageBuffer:
1575 case spv::StorageClassPhysicalStorageBufferEXT:
1578 memoryAccess = spv::MemoryAccessMask(memoryAccess &
1579 ~(spv::MemoryAccessMakePointerAvailableKHRMask |
1580 spv::MemoryAccessMakePointerVisibleKHRMask |
1581 spv::MemoryAccessNonPrivatePointerKHRMask));
1584 return memoryAccess;
1587 // Comments in header
1588 void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope,
1589 unsigned int alignment)
1591 Instruction* store = new Instruction(OpStore);
1592 store->addIdOperand(lValue);
1593 store->addIdOperand(rValue);
1595 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
1597 if (memoryAccess != MemoryAccessMaskNone) {
1598 store->addImmediateOperand(memoryAccess);
1599 if (memoryAccess & spv::MemoryAccessAlignedMask) {
1600 store->addImmediateOperand(alignment);
1602 if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
1603 store->addIdOperand(makeUintConstant(scope));
1607 buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
1610 // Comments in header
1611 Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess,
1612 spv::Scope scope, unsigned int alignment)
1614 Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
1615 load->addIdOperand(lValue);
1617 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
1619 if (memoryAccess != MemoryAccessMaskNone) {
1620 load->addImmediateOperand(memoryAccess);
1621 if (memoryAccess & spv::MemoryAccessAlignedMask) {
1622 load->addImmediateOperand(alignment);
1624 if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
1625 load->addIdOperand(makeUintConstant(scope));
1629 buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
1630 setPrecision(load->getResultId(), precision);
1632 return load->getResultId();
1635 // Comments in header
1636 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
1638 // Figure out the final resulting type.
1639 Id typeId = getResultingAccessChainType();
1640 typeId = makePointer(storageClass, typeId);
1642 // Make the instruction
1643 Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
1644 chain->addIdOperand(base);
1645 for (int i = 0; i < (int)offsets.size(); ++i)
1646 chain->addIdOperand(offsets[i]);
1647 buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
1649 return chain->getResultId();
1652 Id Builder::createArrayLength(Id base, unsigned int member)
1654 spv::Id intType = makeUintType(32);
1655 Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
1656 length->addIdOperand(base);
1657 length->addImmediateOperand(member);
1658 buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
1660 return length->getResultId();
1663 Id Builder::createCooperativeMatrixLength(Id type)
1665 spv::Id intType = makeUintType(32);
1667 // Generate code for spec constants if in spec constant operation
1669 if (generatingOpCodeForSpecConst) {
1670 return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());
1673 Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
1674 length->addIdOperand(type);
1675 buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
1677 return length->getResultId();
1680 Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
1682 // Generate code for spec constants if in spec constant operation
1684 if (generatingOpCodeForSpecConst) {
1685 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite),
1686 std::vector<Id>(1, index));
1688 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1689 extract->addIdOperand(composite);
1690 extract->addImmediateOperand(index);
1691 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1693 return extract->getResultId();
1696 Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
1698 // Generate code for spec constants if in spec constant operation
1700 if (generatingOpCodeForSpecConst) {
1701 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
1703 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1704 extract->addIdOperand(composite);
1705 for (int i = 0; i < (int)indexes.size(); ++i)
1706 extract->addImmediateOperand(indexes[i]);
1707 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1709 return extract->getResultId();
1712 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
1714 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1715 insert->addIdOperand(object);
1716 insert->addIdOperand(composite);
1717 insert->addImmediateOperand(index);
1718 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1720 return insert->getResultId();
1723 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
1725 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1726 insert->addIdOperand(object);
1727 insert->addIdOperand(composite);
1728 for (int i = 0; i < (int)indexes.size(); ++i)
1729 insert->addImmediateOperand(indexes[i]);
1730 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1732 return insert->getResultId();
1735 Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
1737 Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
1738 extract->addIdOperand(vector);
1739 extract->addIdOperand(componentIndex);
1740 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1742 return extract->getResultId();
1745 Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
1747 Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
1748 insert->addIdOperand(vector);
1749 insert->addIdOperand(component);
1750 insert->addIdOperand(componentIndex);
1751 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1753 return insert->getResultId();
1756 // An opcode that has no operands, no result id, and no type
1757 void Builder::createNoResultOp(Op opCode)
1759 Instruction* op = new Instruction(opCode);
1760 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1763 // An opcode that has one id operand, no result id, and no type
1764 void Builder::createNoResultOp(Op opCode, Id operand)
1766 Instruction* op = new Instruction(opCode);
1767 op->addIdOperand(operand);
1768 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1771 // An opcode that has one or more operands, no result id, and no type
1772 void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
1774 Instruction* op = new Instruction(opCode);
1775 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1776 op->addIdOperand(*it);
1778 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1781 // An opcode that has multiple operands, no result id, and no type
1782 void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
1784 Instruction* op = new Instruction(opCode);
1785 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1787 op->addIdOperand(it->word);
1789 op->addImmediateOperand(it->word);
1791 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1794 void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
1796 Instruction* op = new Instruction(OpControlBarrier);
1797 op->addIdOperand(makeUintConstant(execution));
1798 op->addIdOperand(makeUintConstant(memory));
1799 op->addIdOperand(makeUintConstant(semantics));
1800 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1803 void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
1805 Instruction* op = new Instruction(OpMemoryBarrier);
1806 op->addIdOperand(makeUintConstant(executionScope));
1807 op->addIdOperand(makeUintConstant(memorySemantics));
1808 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1811 // An opcode that has one operands, a result id, and a type
1812 Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
1814 // Generate code for spec constants if in spec constant operation
1816 if (generatingOpCodeForSpecConst) {
1817 return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
1819 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1820 op->addIdOperand(operand);
1821 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1823 return op->getResultId();
1826 Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
1828 // Generate code for spec constants if in spec constant operation
1830 if (generatingOpCodeForSpecConst) {
1831 std::vector<Id> operands(2);
1832 operands[0] = left; operands[1] = right;
1833 return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
1835 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1836 op->addIdOperand(left);
1837 op->addIdOperand(right);
1838 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1840 return op->getResultId();
1843 Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
1845 // Generate code for spec constants if in spec constant operation
1847 if (generatingOpCodeForSpecConst) {
1848 std::vector<Id> operands(3);
1852 return createSpecConstantOp(
1853 opCode, typeId, operands, std::vector<Id>());
1855 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1856 op->addIdOperand(op1);
1857 op->addIdOperand(op2);
1858 op->addIdOperand(op3);
1859 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1861 return op->getResultId();
1864 Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
1866 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1867 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1868 op->addIdOperand(*it);
1869 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1871 return op->getResultId();
1874 Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
1876 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1877 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1879 op->addIdOperand(it->word);
1881 op->addImmediateOperand(it->word);
1883 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1885 return op->getResultId();
1888 Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands,
1889 const std::vector<unsigned>& literals)
1891 Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
1892 op->addImmediateOperand((unsigned) opCode);
1893 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1894 op->addIdOperand(*it);
1895 for (auto it = literals.cbegin(); it != literals.cend(); ++it)
1896 op->addImmediateOperand(*it);
1897 module.mapInstruction(op);
1898 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
1900 return op->getResultId();
1903 Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
1905 Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
1906 op->addIdOperand(function->getId());
1907 for (int a = 0; a < (int)args.size(); ++a)
1908 op->addIdOperand(args[a]);
1909 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1911 return op->getResultId();
1914 // Comments in header
1915 Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
1917 if (channels.size() == 1)
1918 return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
1920 if (generatingOpCodeForSpecConst) {
1921 std::vector<Id> operands(2);
1922 operands[0] = operands[1] = source;
1923 return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
1925 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1926 assert(isVector(source));
1927 swizzle->addIdOperand(source);
1928 swizzle->addIdOperand(source);
1929 for (int i = 0; i < (int)channels.size(); ++i)
1930 swizzle->addImmediateOperand(channels[i]);
1931 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1933 return setPrecision(swizzle->getResultId(), precision);
1936 // Comments in header
1937 Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
1939 if (channels.size() == 1 && getNumComponents(source) == 1)
1940 return createCompositeInsert(source, target, typeId, channels.front());
1942 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1944 assert(isVector(target));
1945 swizzle->addIdOperand(target);
1947 assert(getNumComponents(source) == (int)channels.size());
1948 assert(isVector(source));
1949 swizzle->addIdOperand(source);
1951 // Set up an identity shuffle from the base value to the result value
1952 unsigned int components[4];
1953 int numTargetComponents = getNumComponents(target);
1954 for (int i = 0; i < numTargetComponents; ++i)
1957 // Punch in the l-value swizzle
1958 for (int i = 0; i < (int)channels.size(); ++i)
1959 components[channels[i]] = numTargetComponents + i;
1961 // finish the instruction with these components selectors
1962 for (int i = 0; i < numTargetComponents; ++i)
1963 swizzle->addImmediateOperand(components[i]);
1964 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1966 return swizzle->getResultId();
1969 // Comments in header
1970 void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
1972 int direction = getNumComponents(right) - getNumComponents(left);
1975 left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
1976 else if (direction < 0)
1977 right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
1982 // Comments in header
1983 Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
1985 assert(getNumComponents(scalar) == 1);
1986 assert(getTypeId(scalar) == getScalarTypeId(vectorType));
1988 int numComponents = getNumTypeComponents(vectorType);
1989 if (numComponents == 1)
1992 Instruction* smear = nullptr;
1993 if (generatingOpCodeForSpecConst) {
1994 auto members = std::vector<spv::Id>(numComponents, scalar);
1995 // Sometime even in spec-constant-op mode, the temporary vector created by
1996 // promoting a scalar might not be a spec constant. This should depend on
1999 // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
2000 // In such cases, the temporary vector created from a_front_end_const_scalar
2001 // is not a spec constant vector, even though the binary operation node is marked
2002 // as 'specConstant' and we are in spec-constant-op mode.
2003 auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
2004 smear = module.getInstruction(result_id);
2006 smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
2007 for (int c = 0; c < numComponents; ++c)
2008 smear->addIdOperand(scalar);
2009 buildPoint->addInstruction(std::unique_ptr<Instruction>(smear));
2012 return setPrecision(smear->getResultId(), precision);
2015 // Comments in header
2016 Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
2018 Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
2019 inst->addIdOperand(builtins);
2020 inst->addImmediateOperand(entryPoint);
2021 for (int arg = 0; arg < (int)args.size(); ++arg)
2022 inst->addIdOperand(args[arg]);
2024 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
2026 return inst->getResultId();
2029 // Accept all parameters needed to create a texture instruction.
2030 // Create the correct instruction based on the inputs, and make the call.
2031 Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
2032 bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask)
2034 static const int maxTextureArgs = 10;
2035 Id texArgs[maxTextureArgs] = {};
2038 // Set up the fixed arguments
2041 bool explicitLod = false;
2042 texArgs[numArgs++] = parameters.sampler;
2043 texArgs[numArgs++] = parameters.coords;
2044 if (parameters.Dref != NoResult)
2045 texArgs[numArgs++] = parameters.Dref;
2046 if (parameters.component != NoResult)
2047 texArgs[numArgs++] = parameters.component;
2050 if (parameters.granularity != NoResult)
2051 texArgs[numArgs++] = parameters.granularity;
2052 if (parameters.coarse != NoResult)
2053 texArgs[numArgs++] = parameters.coarse;
2057 // Set up the optional arguments
2059 int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
2060 ++numArgs; // speculatively make room for the mask operand
2061 ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
2062 if (parameters.bias) {
2063 mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
2064 texArgs[numArgs++] = parameters.bias;
2066 if (parameters.lod) {
2067 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
2068 texArgs[numArgs++] = parameters.lod;
2070 } else if (parameters.gradX) {
2071 mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
2072 texArgs[numArgs++] = parameters.gradX;
2073 texArgs[numArgs++] = parameters.gradY;
2075 } else if (noImplicitLod && ! fetch && ! gather) {
2076 // have to explicitly use lod of 0 if not allowed to have them be implicit, and
2077 // we would otherwise be about to issue an implicit instruction
2078 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
2079 texArgs[numArgs++] = makeFloatConstant(0.0);
2082 if (parameters.offset) {
2083 if (isConstant(parameters.offset))
2084 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
2086 addCapability(CapabilityImageGatherExtended);
2087 mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
2089 texArgs[numArgs++] = parameters.offset;
2091 if (parameters.offsets) {
2092 addCapability(CapabilityImageGatherExtended);
2093 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
2094 texArgs[numArgs++] = parameters.offsets;
2097 if (parameters.sample) {
2098 mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
2099 texArgs[numArgs++] = parameters.sample;
2101 if (parameters.lodClamp) {
2102 // capability if this bit is used
2103 addCapability(CapabilityMinLod);
2105 mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
2106 texArgs[numArgs++] = parameters.lodClamp;
2108 if (parameters.nonprivate) {
2109 mask = mask | ImageOperandsNonPrivateTexelKHRMask;
2111 if (parameters.volatil) {
2112 mask = mask | ImageOperandsVolatileTexelKHRMask;
2115 mask = mask | signExtensionMask;
2116 if (mask == ImageOperandsMaskNone)
2117 --numArgs; // undo speculative reservation for the mask argument
2119 texArgs[optArgNum] = mask;
2122 // Set up the instruction
2124 Op opCode = OpNop; // All paths below need to set this
2127 opCode = OpImageSparseFetch;
2129 opCode = OpImageFetch;
2131 } else if (parameters.granularity && parameters.coarse) {
2132 opCode = OpImageSampleFootprintNV;
2133 } else if (gather) {
2134 if (parameters.Dref)
2136 opCode = OpImageSparseDrefGather;
2138 opCode = OpImageDrefGather;
2141 opCode = OpImageSparseGather;
2143 opCode = OpImageGather;
2145 } else if (explicitLod) {
2146 if (parameters.Dref) {
2149 opCode = OpImageSparseSampleProjDrefExplicitLod;
2151 opCode = OpImageSampleProjDrefExplicitLod;
2154 opCode = OpImageSparseSampleDrefExplicitLod;
2156 opCode = OpImageSampleDrefExplicitLod;
2160 opCode = OpImageSparseSampleProjExplicitLod;
2162 opCode = OpImageSampleProjExplicitLod;
2165 opCode = OpImageSparseSampleExplicitLod;
2167 opCode = OpImageSampleExplicitLod;
2170 if (parameters.Dref) {
2173 opCode = OpImageSparseSampleProjDrefImplicitLod;
2175 opCode = OpImageSampleProjDrefImplicitLod;
2178 opCode = OpImageSparseSampleDrefImplicitLod;
2180 opCode = OpImageSampleDrefImplicitLod;
2184 opCode = OpImageSparseSampleProjImplicitLod;
2186 opCode = OpImageSampleProjImplicitLod;
2189 opCode = OpImageSparseSampleImplicitLod;
2191 opCode = OpImageSampleImplicitLod;
2195 // See if the result type is expecting a smeared result.
2196 // This happens when a legacy shadow*() call is made, which
2197 // gets a vec4 back instead of a float.
2198 Id smearedType = resultType;
2199 if (! isScalarType(resultType)) {
2201 case OpImageSampleDrefImplicitLod:
2202 case OpImageSampleDrefExplicitLod:
2203 case OpImageSampleProjDrefImplicitLod:
2204 case OpImageSampleProjDrefExplicitLod:
2205 resultType = getScalarTypeId(resultType);
2216 typeId0 = resultType;
2217 typeId1 = getDerefTypeId(parameters.texelOut);
2218 resultType = makeStructResultType(typeId0, typeId1);
2221 // Build the SPIR-V instruction
2222 Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
2223 for (int op = 0; op < optArgNum; ++op)
2224 textureInst->addIdOperand(texArgs[op]);
2225 if (optArgNum < numArgs)
2226 textureInst->addImmediateOperand(texArgs[optArgNum]);
2227 for (int op = optArgNum + 1; op < numArgs; ++op)
2228 textureInst->addIdOperand(texArgs[op]);
2229 setPrecision(textureInst->getResultId(), precision);
2230 buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst));
2232 Id resultId = textureInst->getResultId();
2236 addCapability(CapabilitySparseResidency);
2238 // Decode the return type that was a special structure
2239 createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
2240 resultId = createCompositeExtract(resultId, typeId0, 0);
2241 setPrecision(resultId, precision);
2243 // When a smear is needed, do it, as per what was computed
2244 // above when resultType was changed to a scalar type.
2245 if (resultType != smearedType)
2246 resultId = smearScalar(precision, resultId, smearedType);
2252 // Comments in header
2253 Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
2255 // Figure out the result type
2258 case OpImageQuerySize:
2259 case OpImageQuerySizeLod:
2261 int numComponents = 0;
2262 switch (getTypeDimensionality(getImageType(parameters.sampler))) {
2270 case DimSubpassData:
2281 if (isArrayedImageType(getImageType(parameters.sampler)))
2284 Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
2285 if (numComponents == 1)
2286 resultType = intType;
2288 resultType = makeVectorType(intType, numComponents);
2292 case OpImageQueryLod:
2293 resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
2295 case OpImageQueryLevels:
2296 case OpImageQuerySamples:
2297 resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
2304 Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
2305 query->addIdOperand(parameters.sampler);
2306 if (parameters.coords)
2307 query->addIdOperand(parameters.coords);
2309 query->addIdOperand(parameters.lod);
2310 buildPoint->addInstruction(std::unique_ptr<Instruction>(query));
2311 addCapability(CapabilityImageQuery);
2313 return query->getResultId();
2316 // External comments in header.
2317 // Operates recursively to visit the composite's hierarchy.
2318 Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
2320 Id boolType = makeBoolType();
2321 Id valueType = getTypeId(value1);
2323 Id resultId = NoResult;
2325 int numConstituents = getNumTypeConstituents(valueType);
2327 // Scalars and Vectors
2329 if (isScalarType(valueType) || isVectorType(valueType)) {
2330 assert(valueType == getTypeId(value2));
2331 // These just need a single comparison, just have
2332 // to figure out what it is.
2334 switch (getMostBasicTypeClass(valueType)) {
2336 op = equal ? OpFOrdEqual : OpFUnordNotEqual;
2340 op = equal ? OpIEqual : OpINotEqual;
2343 op = equal ? OpLogicalEqual : OpLogicalNotEqual;
2344 precision = NoPrecision;
2348 if (isScalarType(valueType)) {
2350 resultId = createBinOp(op, boolType, value1, value2);
2353 resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
2354 setPrecision(resultId, precision);
2355 // reduce vector compares...
2356 resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
2359 return setPrecision(resultId, precision);
2362 // Only structs, arrays, and matrices should be left.
2363 // They share in common the reduction operation across their constituents.
2364 assert(isAggregateType(valueType) || isMatrixType(valueType));
2366 // Compare each pair of constituents
2367 for (int constituent = 0; constituent < numConstituents; ++constituent) {
2368 std::vector<unsigned> indexes(1, constituent);
2369 Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
2370 Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
2371 Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
2372 Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
2374 Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
2376 if (constituent == 0)
2377 resultId = subResultId;
2379 resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId),
2386 // OpCompositeConstruct
2387 Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
2389 assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 &&
2390 getNumTypeConstituents(typeId) == (int)constituents.size()));
2392 if (generatingOpCodeForSpecConst) {
2393 // Sometime, even in spec-constant-op mode, the constant composite to be
2394 // constructed may not be a specialization constant.
2396 // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
2397 // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
2398 // The second column vector should NOT be spec constant, as it does not contain any spec constants.
2399 // To handle such cases, we check the constituents of the constant vector to determine whether this
2400 // vector should be created as a spec constant.
2401 return makeCompositeConstant(typeId, constituents,
2402 std::any_of(constituents.begin(), constituents.end(),
2403 [&](spv::Id id) { return isSpecConstant(id); }));
2406 Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
2407 for (int c = 0; c < (int)constituents.size(); ++c)
2408 op->addIdOperand(constituents[c]);
2409 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
2411 return op->getResultId();
2414 // Vector or scalar constructor
2415 Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2417 Id result = NoResult;
2418 unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
2419 unsigned int targetComponent = 0;
2421 // Special case: when calling a vector constructor with a single scalar
2422 // argument, smear the scalar
2423 if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
2424 return smearScalar(precision, sources[0], resultTypeId);
2426 // accumulate the arguments for OpCompositeConstruct
2427 std::vector<Id> constituents;
2428 Id scalarTypeId = getScalarTypeId(resultTypeId);
2430 // lambda to store the result of visiting an argument component
2431 const auto latchResult = [&](Id comp) {
2432 if (numTargetComponents > 1)
2433 constituents.push_back(comp);
2439 // lambda to visit a vector argument's components
2440 const auto accumulateVectorConstituents = [&](Id sourceArg) {
2441 unsigned int sourceSize = getNumComponents(sourceArg);
2442 unsigned int sourcesToUse = sourceSize;
2443 if (sourcesToUse + targetComponent > numTargetComponents)
2444 sourcesToUse = numTargetComponents - targetComponent;
2446 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2447 std::vector<unsigned> swiz;
2449 latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
2453 // lambda to visit a matrix argument's components
2454 const auto accumulateMatrixConstituents = [&](Id sourceArg) {
2455 unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
2456 unsigned int sourcesToUse = sourceSize;
2457 if (sourcesToUse + targetComponent > numTargetComponents)
2458 sourcesToUse = numTargetComponents - targetComponent;
2462 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2463 if (row >= getNumRows(sourceArg)) {
2467 std::vector<Id> indexes;
2468 indexes.push_back(col);
2469 indexes.push_back(row);
2470 latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
2475 // Go through the source arguments, each one could have either
2476 // a single or multiple components to contribute.
2477 for (unsigned int i = 0; i < sources.size(); ++i) {
2479 if (isScalar(sources[i]) || isPointer(sources[i]))
2480 latchResult(sources[i]);
2481 else if (isVector(sources[i]))
2482 accumulateVectorConstituents(sources[i]);
2483 else if (isMatrix(sources[i]))
2484 accumulateMatrixConstituents(sources[i]);
2488 if (targetComponent >= numTargetComponents)
2492 // If the result is a vector, make it from the gathered constituents.
2493 if (constituents.size() > 0)
2494 result = createCompositeConstruct(resultTypeId, constituents);
2496 return setPrecision(result, precision);
2499 // Comments in header
2500 Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2502 Id componentTypeId = getScalarTypeId(resultTypeId);
2503 int numCols = getTypeNumColumns(resultTypeId);
2504 int numRows = getTypeNumRows(resultTypeId);
2506 Instruction* instr = module.getInstruction(componentTypeId);
2508 const unsigned bitCount = 32;
2509 assert(bitCount == instr->getImmediateOperand(0));
2511 const unsigned bitCount = instr->getImmediateOperand(0);
2514 // Optimize matrix constructed from a bigger matrix
2515 if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
2516 // To truncate the matrix to a smaller number of rows/columns, we need to:
2517 // 1. For each column, extract the column and truncate it to the required size using shuffle
2518 // 2. Assemble the resulting matrix from all columns
2519 Id matrix = sources[0];
2520 Id columnTypeId = getContainedTypeId(resultTypeId);
2521 Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
2523 std::vector<unsigned> channels;
2524 for (int row = 0; row < numRows; ++row)
2525 channels.push_back(row);
2527 std::vector<Id> matrixColumns;
2528 for (int col = 0; col < numCols; ++col) {
2529 std::vector<unsigned> indexes;
2530 indexes.push_back(col);
2531 Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
2532 setPrecision(colv, precision);
2534 if (numRows != getNumRows(matrix)) {
2535 matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
2537 matrixColumns.push_back(colv);
2541 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2544 // Otherwise, will use a two step process
2545 // 1. make a compile-time 2D array of values
2546 // 2. construct a matrix from that array
2550 // initialize the array to the identity matrix
2551 Id ids[maxMatrixSize][maxMatrixSize];
2552 Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
2553 Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
2554 for (int col = 0; col < 4; ++col) {
2555 for (int row = 0; row < 4; ++row) {
2557 ids[col][row] = one;
2559 ids[col][row] = zero;
2563 // modify components as dictated by the arguments
2564 if (sources.size() == 1 && isScalar(sources[0])) {
2565 // a single scalar; resets the diagonals
2566 for (int col = 0; col < 4; ++col)
2567 ids[col][col] = sources[0];
2568 } else if (isMatrix(sources[0])) {
2569 // constructing from another matrix; copy over the parts that exist in both the argument and constructee
2570 Id matrix = sources[0];
2571 int minCols = std::min(numCols, getNumColumns(matrix));
2572 int minRows = std::min(numRows, getNumRows(matrix));
2573 for (int col = 0; col < minCols; ++col) {
2574 std::vector<unsigned> indexes;
2575 indexes.push_back(col);
2576 for (int row = 0; row < minRows; ++row) {
2577 indexes.push_back(row);
2578 ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
2580 setPrecision(ids[col][row], precision);
2584 // fill in the matrix in column-major order with whatever argument components are available
2588 for (int arg = 0; arg < (int)sources.size() && col < numCols; ++arg) {
2589 Id argComp = sources[arg];
2590 for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
2591 if (getNumComponents(sources[arg]) > 1) {
2592 argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
2593 setPrecision(argComp, precision);
2595 ids[col][row++] = argComp;
2596 if (row == numRows) {
2600 if (col == numCols) {
2601 // If more components are provided than fit the matrix, discard the rest.
2608 // Step 2: Construct a matrix from that array.
2609 // First make the column vectors, then make the matrix.
2611 // make the column vectors
2612 Id columnTypeId = getContainedTypeId(resultTypeId);
2613 std::vector<Id> matrixColumns;
2614 for (int col = 0; col < numCols; ++col) {
2615 std::vector<Id> vectorComponents;
2616 for (int row = 0; row < numRows; ++row)
2617 vectorComponents.push_back(ids[col][row]);
2618 Id column = createCompositeConstruct(columnTypeId, vectorComponents);
2619 setPrecision(column, precision);
2620 matrixColumns.push_back(column);
2624 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2627 // Comments in header
2628 Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
2634 function = &builder.getBuildPoint()->getParent();
2636 // make the blocks, but only put the then-block into the function,
2637 // the else-block and merge-block will be added later, in order, after
2638 // earlier code is emitted
2639 thenBlock = new Block(builder.getUniqueId(), *function);
2640 mergeBlock = new Block(builder.getUniqueId(), *function);
2642 // Save the current block, so that we can add in the flow control split when
2643 // makeEndIf is called.
2644 headerBlock = builder.getBuildPoint();
2646 function->addBlock(thenBlock);
2647 builder.setBuildPoint(thenBlock);
2650 // Comments in header
2651 void Builder::If::makeBeginElse()
2653 // Close out the "then" by having it jump to the mergeBlock
2654 builder.createBranch(mergeBlock);
2656 // Make the first else block and add it to the function
2657 elseBlock = new Block(builder.getUniqueId(), *function);
2658 function->addBlock(elseBlock);
2660 // Start building the else block
2661 builder.setBuildPoint(elseBlock);
2664 // Comments in header
2665 void Builder::If::makeEndIf()
2667 // jump to the merge block
2668 builder.createBranch(mergeBlock);
2670 // Go back to the headerBlock and make the flow control split
2671 builder.setBuildPoint(headerBlock);
2672 builder.createSelectionMerge(mergeBlock, control);
2674 builder.createConditionalBranch(condition, thenBlock, elseBlock);
2676 builder.createConditionalBranch(condition, thenBlock, mergeBlock);
2678 // add the merge block to the function
2679 function->addBlock(mergeBlock);
2680 builder.setBuildPoint(mergeBlock);
2683 // Comments in header
2684 void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
2685 const std::vector<int>& valueIndexToSegment, int defaultSegment,
2686 std::vector<Block*>& segmentBlocks)
2688 Function& function = buildPoint->getParent();
2690 // make all the blocks
2691 for (int s = 0; s < numSegments; ++s)
2692 segmentBlocks.push_back(new Block(getUniqueId(), function));
2694 Block* mergeBlock = new Block(getUniqueId(), function);
2696 // make and insert the switch's selection-merge instruction
2697 createSelectionMerge(mergeBlock, control);
2699 // make the switch instruction
2700 Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
2701 switchInst->addIdOperand(selector);
2702 auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
2703 switchInst->addIdOperand(defaultOrMerge->getId());
2704 defaultOrMerge->addPredecessor(buildPoint);
2705 for (int i = 0; i < (int)caseValues.size(); ++i) {
2706 switchInst->addImmediateOperand(caseValues[i]);
2707 switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
2708 segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
2710 buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst));
2712 // push the merge block
2713 switchMerges.push(mergeBlock);
2716 // Comments in header
2717 void Builder::addSwitchBreak()
2719 // branch to the top of the merge block stack
2720 createBranch(switchMerges.top());
2721 createAndSetNoPredecessorBlock("post-switch-break");
2724 // Comments in header
2725 void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
2727 int lastSegment = nextSegment - 1;
2728 if (lastSegment >= 0) {
2729 // Close out previous segment by jumping, if necessary, to next segment
2730 if (! buildPoint->isTerminated())
2731 createBranch(segmentBlock[nextSegment]);
2733 Block* block = segmentBlock[nextSegment];
2734 block->getParent().addBlock(block);
2735 setBuildPoint(block);
2738 // Comments in header
2739 void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
2741 // Close out previous segment by jumping, if necessary, to next segment
2742 if (! buildPoint->isTerminated())
2745 switchMerges.top()->getParent().addBlock(switchMerges.top());
2746 setBuildPoint(switchMerges.top());
2751 Block& Builder::makeNewBlock()
2753 Function& function = buildPoint->getParent();
2754 auto block = new Block(getUniqueId(), function);
2755 function.addBlock(block);
2759 Builder::LoopBlocks& Builder::makeNewLoop()
2761 // This verbosity is needed to simultaneously get the same behavior
2762 // everywhere (id's in the same order), have a syntax that works
2763 // across lots of versions of C++, have no warnings from pedantic
2764 // compilation modes, and leave the rest of the code alone.
2765 Block& head = makeNewBlock();
2766 Block& body = makeNewBlock();
2767 Block& merge = makeNewBlock();
2768 Block& continue_target = makeNewBlock();
2769 LoopBlocks blocks(head, body, merge, continue_target);
2774 void Builder::createLoopContinue()
2776 createBranch(&loops.top().continue_target);
2777 // Set up a block for dead code.
2778 createAndSetNoPredecessorBlock("post-loop-continue");
2781 void Builder::createLoopExit()
2783 createBranch(&loops.top().merge);
2784 // Set up a block for dead code.
2785 createAndSetNoPredecessorBlock("post-loop-break");
2788 void Builder::closeLoop()
2793 void Builder::clearAccessChain()
2795 accessChain.base = NoResult;
2796 accessChain.indexChain.clear();
2797 accessChain.instr = NoResult;
2798 accessChain.swizzle.clear();
2799 accessChain.component = NoResult;
2800 accessChain.preSwizzleBaseType = NoType;
2801 accessChain.isRValue = false;
2802 accessChain.coherentFlags.clear();
2803 accessChain.alignment = 0;
2806 // Comments in header
2807 void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType,
2808 AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
2810 accessChain.coherentFlags |= coherentFlags;
2811 accessChain.alignment |= alignment;
2813 // swizzles can be stacked in GLSL, but simplified to a single
2814 // one here; the base type doesn't change
2815 if (accessChain.preSwizzleBaseType == NoType)
2816 accessChain.preSwizzleBaseType = preSwizzleBaseType;
2818 // if needed, propagate the swizzle for the current access chain
2819 if (accessChain.swizzle.size() > 0) {
2820 std::vector<unsigned> oldSwizzle = accessChain.swizzle;
2821 accessChain.swizzle.resize(0);
2822 for (unsigned int i = 0; i < swizzle.size(); ++i) {
2823 assert(swizzle[i] < oldSwizzle.size());
2824 accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
2827 accessChain.swizzle = swizzle;
2829 // determine if we need to track this swizzle anymore
2830 simplifyAccessChainSwizzle();
2833 // Comments in header
2834 void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
2836 assert(accessChain.isRValue == false);
2838 transferAccessChainSwizzle(true);
2840 // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.
2841 if (accessChain.swizzle.size() > 0 &&
2842 getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&
2843 accessChain.component == NoResult) {
2844 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
2845 accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));
2846 accessChain.instr = NoResult;
2848 Id base = collapseAccessChain();
2849 addDecoration(base, nonUniform);
2851 accessChain.indexChain.pop_back();
2852 accessChain.instr = NoResult;
2854 // dynamic component should be gone
2855 assert(accessChain.component == NoResult);
2857 Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);
2859 // take LSB of alignment
2860 alignment = alignment & ~(alignment & (alignment-1));
2861 if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
2862 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2865 createStore(source, base, memoryAccess, scope, alignment);
2869 Id base = collapseAccessChain();
2870 addDecoration(base, nonUniform);
2874 // dynamic component should be gone
2875 assert(accessChain.component == NoResult);
2877 // If swizzle still exists, it may be out-of-order, we must load the target vector,
2878 // extract and insert elements to perform writeMask and/or swizzle.
2879 if (accessChain.swizzle.size() > 0) {
2880 Id tempBaseId = createLoad(base, spv::NoPrecision);
2881 source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
2884 // take LSB of alignment
2885 alignment = alignment & ~(alignment & (alignment-1));
2886 if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
2887 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2890 createStore(source, base, memoryAccess, scope, alignment);
2894 // Comments in header
2895 Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform,
2896 Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess,
2897 spv::Scope scope, unsigned int alignment)
2901 if (accessChain.isRValue) {
2902 // transfer access chain, but try to stay in registers
2903 transferAccessChainSwizzle(false);
2904 if (accessChain.indexChain.size() > 0) {
2905 Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
2907 // if all the accesses are constants, we can use OpCompositeExtract
2908 std::vector<unsigned> indexes;
2909 bool constant = true;
2910 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
2911 if (isConstantScalar(accessChain.indexChain[i]))
2912 indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
2920 id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
2921 setPrecision(id, precision);
2923 Id lValue = NoResult;
2924 if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) {
2925 // make a new function variable for this r-value, using an initializer,
2926 // and mark it as NonWritable so that downstream it can be detected as a lookup
2928 lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
2929 "indexable", accessChain.base);
2930 addDecoration(lValue, DecorationNonWritable);
2932 lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base),
2935 createStore(accessChain.base, lValue);
2937 // move base to the new variable
2938 accessChain.base = lValue;
2939 accessChain.isRValue = false;
2941 // load through the access chain
2942 id = createLoad(collapseAccessChain(), precision);
2945 id = accessChain.base; // no precision, it was set when this was defined
2947 transferAccessChainSwizzle(true);
2949 // take LSB of alignment
2950 alignment = alignment & ~(alignment & (alignment-1));
2951 if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) {
2952 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
2955 // load through the access chain
2956 id = collapseAccessChain();
2957 // Apply nonuniform both to the access chain and the loaded value.
2958 // Buffer accesses need the access chain decorated, and this is where
2959 // loaded image types get decorated. TODO: This should maybe move to
2960 // createImageTextureFunctionCall.
2961 addDecoration(id, l_nonUniform);
2962 id = createLoad(id, precision, memoryAccess, scope, alignment);
2963 addDecoration(id, r_nonUniform);
2966 // Done, unless there are swizzles to do
2967 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2970 // Do remaining swizzling
2972 // Do the basic swizzle
2973 if (accessChain.swizzle.size() > 0) {
2974 Id swizzledType = getScalarTypeId(getTypeId(id));
2975 if (accessChain.swizzle.size() > 1)
2976 swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
2977 id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
2980 // Do the dynamic component
2981 if (accessChain.component != NoResult)
2982 id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
2984 addDecoration(id, r_nonUniform);
2988 Id Builder::accessChainGetLValue()
2990 assert(accessChain.isRValue == false);
2992 transferAccessChainSwizzle(true);
2993 Id lvalue = collapseAccessChain();
2995 // If swizzle exists, it is out-of-order or not full, we must load the target vector,
2996 // extract and insert elements to perform writeMask and/or swizzle. This does not
2997 // go with getting a direct l-value pointer.
2998 assert(accessChain.swizzle.size() == 0);
2999 assert(accessChain.component == NoResult);
3004 // comment in header
3005 Id Builder::accessChainGetInferredType()
3007 // anything to operate on?
3008 if (accessChain.base == NoResult)
3010 Id type = getTypeId(accessChain.base);
3012 // do initial dereference
3013 if (! accessChain.isRValue)
3014 type = getContainedTypeId(type);
3016 // dereference each index
3017 for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
3018 if (isStructType(type))
3019 type = getContainedTypeId(type, getConstantScalar(*it));
3021 type = getContainedTypeId(type);
3024 // dereference swizzle
3025 if (accessChain.swizzle.size() == 1)
3026 type = getContainedTypeId(type);
3027 else if (accessChain.swizzle.size() > 1)
3028 type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
3030 // dereference component selection
3031 if (accessChain.component)
3032 type = getContainedTypeId(type);
3037 void Builder::dump(std::vector<unsigned int>& out) const
3039 // Header, before first instructions:
3040 out.push_back(MagicNumber);
3041 out.push_back(spvVersion);
3042 out.push_back(builderNumber);
3043 out.push_back(uniqueId + 1);
3047 for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
3048 Instruction capInst(0, 0, OpCapability);
3049 capInst.addImmediateOperand(*it);
3053 for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
3054 Instruction extInst(0, 0, OpExtension);
3055 extInst.addStringOperand(it->c_str());
3059 dumpInstructions(out, imports);
3060 Instruction memInst(0, 0, OpMemoryModel);
3061 memInst.addImmediateOperand(addressModel);
3062 memInst.addImmediateOperand(memoryModel);
3065 // Instructions saved up while building:
3066 dumpInstructions(out, entryPoints);
3067 dumpInstructions(out, executionModes);
3069 // Debug instructions
3070 dumpInstructions(out, strings);
3071 dumpSourceInstructions(out);
3072 for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
3073 Instruction sourceExtInst(0, 0, OpSourceExtension);
3074 sourceExtInst.addStringOperand(sourceExtensions[e]);
3075 sourceExtInst.dump(out);
3077 dumpInstructions(out, names);
3078 dumpModuleProcesses(out);
3080 // Annotation instructions
3081 dumpInstructions(out, decorations);
3083 dumpInstructions(out, constantsTypesGlobals);
3084 dumpInstructions(out, externals);
3091 // Protected methods.
3094 // Turn the described access chain in 'accessChain' into an instruction(s)
3095 // computing its address. This *cannot* include complex swizzles, which must
3096 // be handled after this is called.
3098 // Can generate code.
3099 Id Builder::collapseAccessChain()
3101 assert(accessChain.isRValue == false);
3103 // did we already emit an access chain for this?
3104 if (accessChain.instr != NoResult)
3105 return accessChain.instr;
3107 // If we have a dynamic component, we can still transfer
3108 // that into a final operand to the access chain. We need to remap the
3109 // dynamic component through the swizzle to get a new dynamic component to
3112 // This was not done in transferAccessChainSwizzle() because it might
3114 remapDynamicSwizzle();
3115 if (accessChain.component != NoResult) {
3116 // transfer the dynamic component to the access chain
3117 accessChain.indexChain.push_back(accessChain.component);
3118 accessChain.component = NoResult;
3121 // note that non-trivial swizzling is left pending
3123 // do we have an access chain?
3124 if (accessChain.indexChain.size() == 0)
3125 return accessChain.base;
3127 // emit the access chain
3128 StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
3129 accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
3131 return accessChain.instr;
3134 // For a dynamic component selection of a swizzle.
3136 // Turn the swizzle and dynamic component into just a dynamic component.
3139 void Builder::remapDynamicSwizzle()
3141 // do we have a swizzle to remap a dynamic component through?
3142 if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
3143 // build a vector of the swizzle for the component to map into
3144 std::vector<Id> components;
3145 for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
3146 components.push_back(makeUintConstant(accessChain.swizzle[c]));
3147 Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
3148 Id map = makeCompositeConstant(mapType, components);
3151 accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
3152 accessChain.swizzle.clear();
3156 // clear out swizzle if it is redundant, that is reselecting the same components
3157 // that would be present without the swizzle.
3158 void Builder::simplifyAccessChainSwizzle()
3160 // If the swizzle has fewer components than the vector, it is subsetting, and must stay
3161 // to preserve that fact.
3162 if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
3165 // if components are out of order, it is a swizzle
3166 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
3167 if (i != accessChain.swizzle[i])
3171 // otherwise, there is no need to track this swizzle
3172 accessChain.swizzle.clear();
3173 if (accessChain.component == NoResult)
3174 accessChain.preSwizzleBaseType = NoType;
3177 // To the extent any swizzling can become part of the chain
3178 // of accesses instead of a post operation, make it so.
3179 // If 'dynamic' is true, include transferring the dynamic component,
3180 // otherwise, leave it pending.
3182 // Does not generate code. just updates the access chain.
3183 void Builder::transferAccessChainSwizzle(bool dynamic)
3186 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
3190 // (this requires either a swizzle, or generating code for a dynamic component)
3191 if (accessChain.swizzle.size() > 1)
3194 // single component, either in the swizzle and/or dynamic component
3195 if (accessChain.swizzle.size() == 1) {
3196 assert(accessChain.component == NoResult);
3197 // handle static component selection
3198 accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
3199 accessChain.swizzle.clear();
3200 accessChain.preSwizzleBaseType = NoType;
3201 } else if (dynamic && accessChain.component != NoResult) {
3202 assert(accessChain.swizzle.size() == 0);
3203 // handle dynamic component
3204 accessChain.indexChain.push_back(accessChain.component);
3205 accessChain.preSwizzleBaseType = NoType;
3206 accessChain.component = NoResult;
3210 // Utility method for creating a new block and setting the insert point to
3211 // be in it. This is useful for flow-control operations that need a "dummy"
3212 // block proceeding them (e.g. instructions after a discard, etc).
3213 void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
3215 Block* block = new Block(getUniqueId(), buildPoint->getParent());
3216 block->setUnreachable();
3217 buildPoint->getParent().addBlock(block);
3218 setBuildPoint(block);
3221 // addName(block->getId(), name);
3224 // Comments in header
3225 void Builder::createBranch(Block* block)
3227 Instruction* branch = new Instruction(OpBranch);
3228 branch->addIdOperand(block->getId());
3229 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
3230 block->addPredecessor(buildPoint);
3233 void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
3235 Instruction* merge = new Instruction(OpSelectionMerge);
3236 merge->addIdOperand(mergeBlock->getId());
3237 merge->addImmediateOperand(control);
3238 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
3241 void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
3242 const std::vector<unsigned int>& operands)
3244 Instruction* merge = new Instruction(OpLoopMerge);
3245 merge->addIdOperand(mergeBlock->getId());
3246 merge->addIdOperand(continueBlock->getId());
3247 merge->addImmediateOperand(control);
3248 for (int op = 0; op < (int)operands.size(); ++op)
3249 merge->addImmediateOperand(operands[op]);
3250 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
3253 void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
3255 Instruction* branch = new Instruction(OpBranchConditional);
3256 branch->addIdOperand(condition);
3257 branch->addIdOperand(thenBlock->getId());
3258 branch->addIdOperand(elseBlock->getId());
3259 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
3260 thenBlock->addPredecessor(buildPoint);
3261 elseBlock->addPredecessor(buildPoint);
3265 // [OpSourceContinued]
3267 void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
3268 std::vector<unsigned int>& out) const
3270 const int maxWordCount = 0xFFFF;
3271 const int opSourceWordCount = 4;
3272 const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
3274 if (source != SourceLanguageUnknown) {
3275 // OpSource Language Version File Source
3276 Instruction sourceInst(NoResult, NoType, OpSource);
3277 sourceInst.addImmediateOperand(source);
3278 sourceInst.addImmediateOperand(sourceVersion);
3280 if (fileId != NoResult) {
3281 sourceInst.addIdOperand(fileId);
3283 if (text.size() > 0) {
3285 std::string subString;
3286 while ((int)text.size() - nextByte > 0) {
3287 subString = text.substr(nextByte, nonNullBytesPerInstruction);
3288 if (nextByte == 0) {
3290 sourceInst.addStringOperand(subString.c_str());
3291 sourceInst.dump(out);
3294 Instruction sourceContinuedInst(OpSourceContinued);
3295 sourceContinuedInst.addStringOperand(subString.c_str());
3296 sourceContinuedInst.dump(out);
3298 nextByte += nonNullBytesPerInstruction;
3301 sourceInst.dump(out);
3303 sourceInst.dump(out);
3307 // Dump an OpSource[Continued] sequence for the source and every include file
3308 void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
3310 dumpSourceInstructions(sourceFileStringId, sourceText, out);
3311 for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
3312 dumpSourceInstructions(iItr->first, *iItr->second, out);
3315 void Builder::dumpInstructions(std::vector<unsigned int>& out,
3316 const std::vector<std::unique_ptr<Instruction> >& instructions) const
3318 for (int i = 0; i < (int)instructions.size(); ++i) {
3319 instructions[i]->dump(out);
3323 void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
3325 for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
3326 Instruction moduleProcessed(OpModuleProcessed);
3327 moduleProcessed.addStringOperand(moduleProcesses[i]);
3328 moduleProcessed.dump(out);
3332 }; // end spv namespace