2 // Copyright (C) 2014-2015 LunarG, Inc.
3 // Copyright (C) 2015-2018 Google, Inc.
5 // All rights reserved.
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
14 // Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following
16 // disclaimer in the documentation and/or other materials provided
17 // with the distribution.
19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20 // contributors may be used to endorse or promote products derived
21 // from this software without specific prior written permission.
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 // POSSIBILITY OF SUCH DAMAGE.
37 // Helper for making SPIR-V IR. Generally, this is documented in the header
44 #include <unordered_set>
47 #include "SpvBuilder.h"
49 #include "hex_float.h"
57 Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
58 spvVersion(spvVersion),
59 source(SourceLanguageUnknown),
61 sourceFileStringId(NoResult),
65 addressModel(AddressingModelLogical),
66 memoryModel(MemoryModelGLSL450),
67 builderNumber(magicNumber),
70 entryPointFunction(0),
71 generatingOpCodeForSpecConst(false),
81 Id Builder::import(const char* name)
83 Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
84 import->addStringOperand(name);
85 module.mapInstruction(import);
87 imports.push_back(std::unique_ptr<Instruction>(import));
88 return import->getResultId();
91 // Emit instruction for non-filename-based #line directives (ie. no filename
92 // seen yet): emit an OpLine if we've been asked to emit OpLines and the line
93 // number has changed since the last time, and is a valid line number.
94 void Builder::setLine(int lineNum)
96 if (lineNum != 0 && lineNum != currentLine) {
97 currentLine = lineNum;
99 addLine(sourceFileStringId, currentLine, 0);
103 // If no filename, do non-filename-based #line emit. Else do filename-based emit.
104 // Emit OpLine if we've been asked to emit OpLines and the line number or filename
105 // has changed since the last time, and line number is valid.
106 void Builder::setLine(int lineNum, const char* filename)
108 if (filename == nullptr) {
112 if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
113 strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) {
114 currentLine = lineNum;
115 currentFile = filename;
117 spv::Id strId = getStringId(filename);
118 addLine(strId, currentLine, 0);
123 void Builder::addLine(Id fileName, int lineNum, int column)
125 Instruction* line = new Instruction(OpLine);
126 line->addIdOperand(fileName);
127 line->addImmediateOperand(lineNum);
128 line->addImmediateOperand(column);
129 buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
132 // For creating new groupedTypes (will return old type if the requested one was already made).
133 Id Builder::makeVoidType()
136 if (groupedTypes[OpTypeVoid].size() == 0) {
137 type = new Instruction(getUniqueId(), NoType, OpTypeVoid);
138 groupedTypes[OpTypeVoid].push_back(type);
139 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
140 module.mapInstruction(type);
142 type = groupedTypes[OpTypeVoid].back();
144 return type->getResultId();
147 Id Builder::makeBoolType()
150 if (groupedTypes[OpTypeBool].size() == 0) {
151 type = new Instruction(getUniqueId(), NoType, OpTypeBool);
152 groupedTypes[OpTypeBool].push_back(type);
153 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
154 module.mapInstruction(type);
156 type = groupedTypes[OpTypeBool].back();
158 return type->getResultId();
161 Id Builder::makeSamplerType()
164 if (groupedTypes[OpTypeSampler].size() == 0) {
165 type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
166 groupedTypes[OpTypeSampler].push_back(type);
167 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
168 module.mapInstruction(type);
170 type = groupedTypes[OpTypeSampler].back();
172 return type->getResultId();
175 Id Builder::makePointer(StorageClass storageClass, Id pointee)
179 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
180 type = groupedTypes[OpTypePointer][t];
181 if (type->getImmediateOperand(0) == (unsigned)storageClass &&
182 type->getIdOperand(1) == pointee)
183 return type->getResultId();
186 // not found, make it
187 type = new Instruction(getUniqueId(), NoType, OpTypePointer);
188 type->addImmediateOperand(storageClass);
189 type->addIdOperand(pointee);
190 groupedTypes[OpTypePointer].push_back(type);
191 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
192 module.mapInstruction(type);
194 return type->getResultId();
197 Id Builder::makeIntegerType(int width, bool hasSign)
201 for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
202 type = groupedTypes[OpTypeInt][t];
203 if (type->getImmediateOperand(0) == (unsigned)width &&
204 type->getImmediateOperand(1) == (hasSign ? 1u : 0u))
205 return type->getResultId();
208 // not found, make it
209 type = new Instruction(getUniqueId(), NoType, OpTypeInt);
210 type->addImmediateOperand(width);
211 type->addImmediateOperand(hasSign ? 1 : 0);
212 groupedTypes[OpTypeInt].push_back(type);
213 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
214 module.mapInstruction(type);
216 // deal with capabilities
220 // these are currently handled by storage-type declarations and post processing
223 addCapability(CapabilityInt64);
229 return type->getResultId();
232 Id Builder::makeFloatType(int width)
236 for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
237 type = groupedTypes[OpTypeFloat][t];
238 if (type->getImmediateOperand(0) == (unsigned)width)
239 return type->getResultId();
242 // not found, make it
243 type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
244 type->addImmediateOperand(width);
245 groupedTypes[OpTypeFloat].push_back(type);
246 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
247 module.mapInstruction(type);
249 // deal with capabilities
252 // currently handled by storage-type declarations and post processing
255 addCapability(CapabilityFloat64);
261 return type->getResultId();
264 // Make a struct without checking for duplication.
265 // See makeStructResultType() for non-decorated structs
266 // needed as the result of some instructions, which does
267 // check for duplicates.
268 Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
270 // Don't look for previous one, because in the general case,
271 // structs can be duplicated except for decorations.
273 // not found, make it
274 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
275 for (int op = 0; op < (int)members.size(); ++op)
276 type->addIdOperand(members[op]);
277 groupedTypes[OpTypeStruct].push_back(type);
278 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
279 module.mapInstruction(type);
280 addName(type->getResultId(), name);
282 return type->getResultId();
285 // Make a struct for the simple results of several instructions,
286 // checking for duplication.
287 Id Builder::makeStructResultType(Id type0, Id type1)
291 for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
292 type = groupedTypes[OpTypeStruct][t];
293 if (type->getNumOperands() != 2)
295 if (type->getIdOperand(0) != type0 ||
296 type->getIdOperand(1) != type1)
298 return type->getResultId();
301 // not found, make it
302 std::vector<spv::Id> members;
303 members.push_back(type0);
304 members.push_back(type1);
306 return makeStructType(members, "ResType");
309 Id Builder::makeVectorType(Id component, int size)
313 for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
314 type = groupedTypes[OpTypeVector][t];
315 if (type->getIdOperand(0) == component &&
316 type->getImmediateOperand(1) == (unsigned)size)
317 return type->getResultId();
320 // not found, make it
321 type = new Instruction(getUniqueId(), NoType, OpTypeVector);
322 type->addIdOperand(component);
323 type->addImmediateOperand(size);
324 groupedTypes[OpTypeVector].push_back(type);
325 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
326 module.mapInstruction(type);
328 return type->getResultId();
331 Id Builder::makeMatrixType(Id component, int cols, int rows)
333 assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
335 Id column = makeVectorType(component, rows);
339 for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
340 type = groupedTypes[OpTypeMatrix][t];
341 if (type->getIdOperand(0) == column &&
342 type->getImmediateOperand(1) == (unsigned)cols)
343 return type->getResultId();
346 // not found, make it
347 type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
348 type->addIdOperand(column);
349 type->addImmediateOperand(cols);
350 groupedTypes[OpTypeMatrix].push_back(type);
351 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
352 module.mapInstruction(type);
354 return type->getResultId();
357 // TODO: performance: track arrays per stride
358 // If a stride is supplied (non-zero) make an array.
359 // If no stride (0), reuse previous array types.
360 // 'size' is an Id of a constant or specialization constant of the array size
361 Id Builder::makeArrayType(Id element, Id sizeId, int stride)
365 // try to find existing type
366 for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
367 type = groupedTypes[OpTypeArray][t];
368 if (type->getIdOperand(0) == element &&
369 type->getIdOperand(1) == sizeId)
370 return type->getResultId();
374 // not found, make it
375 type = new Instruction(getUniqueId(), NoType, OpTypeArray);
376 type->addIdOperand(element);
377 type->addIdOperand(sizeId);
378 groupedTypes[OpTypeArray].push_back(type);
379 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
380 module.mapInstruction(type);
382 return type->getResultId();
385 Id Builder::makeRuntimeArray(Id element)
387 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
388 type->addIdOperand(element);
389 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
390 module.mapInstruction(type);
392 return type->getResultId();
395 Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
399 for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
400 type = groupedTypes[OpTypeFunction][t];
401 if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
403 bool mismatch = false;
404 for (int p = 0; p < (int)paramTypes.size(); ++p) {
405 if (paramTypes[p] != type->getIdOperand(p + 1)) {
411 return type->getResultId();
414 // not found, make it
415 type = new Instruction(getUniqueId(), NoType, OpTypeFunction);
416 type->addIdOperand(returnType);
417 for (int p = 0; p < (int)paramTypes.size(); ++p)
418 type->addIdOperand(paramTypes[p]);
419 groupedTypes[OpTypeFunction].push_back(type);
420 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
421 module.mapInstruction(type);
423 return type->getResultId();
426 Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format)
428 assert(sampled == 1 || sampled == 2);
432 for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
433 type = groupedTypes[OpTypeImage][t];
434 if (type->getIdOperand(0) == sampledType &&
435 type->getImmediateOperand(1) == (unsigned int)dim &&
436 type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
437 type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
438 type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
439 type->getImmediateOperand(5) == sampled &&
440 type->getImmediateOperand(6) == (unsigned int)format)
441 return type->getResultId();
444 // not found, make it
445 type = new Instruction(getUniqueId(), NoType, OpTypeImage);
446 type->addIdOperand(sampledType);
447 type->addImmediateOperand( dim);
448 type->addImmediateOperand( depth ? 1 : 0);
449 type->addImmediateOperand(arrayed ? 1 : 0);
450 type->addImmediateOperand( ms ? 1 : 0);
451 type->addImmediateOperand(sampled);
452 type->addImmediateOperand((unsigned int)format);
454 groupedTypes[OpTypeImage].push_back(type);
455 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
456 module.mapInstruction(type);
458 // deal with capabilities
462 addCapability(CapabilitySampledBuffer);
464 addCapability(CapabilityImageBuffer);
468 addCapability(CapabilitySampled1D);
470 addCapability(CapabilityImage1D);
475 addCapability(CapabilitySampledCubeArray);
477 addCapability(CapabilityImageCubeArray);
482 addCapability(CapabilitySampledRect);
484 addCapability(CapabilityImageRect);
487 addCapability(CapabilityInputAttachment);
495 // Images used with subpass data are not storage
496 // images, so don't require the capability for them.
497 if (dim != Dim::DimSubpassData)
498 addCapability(CapabilityStorageImageMultisample);
500 addCapability(CapabilityImageMSArray);
504 return type->getResultId();
507 Id Builder::makeSampledImageType(Id imageType)
511 for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
512 type = groupedTypes[OpTypeSampledImage][t];
513 if (type->getIdOperand(0) == imageType)
514 return type->getResultId();
517 // not found, make it
518 type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
519 type->addIdOperand(imageType);
521 groupedTypes[OpTypeSampledImage].push_back(type);
522 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
523 module.mapInstruction(type);
525 return type->getResultId();
529 Id Builder::makeAccelerationStructureNVType()
532 if (groupedTypes[OpTypeAccelerationStructureNV].size() == 0) {
533 type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureNV);
534 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
535 module.mapInstruction(type);
537 type = groupedTypes[OpTypeAccelerationStructureNV].back();
540 return type->getResultId();
543 Id Builder::getDerefTypeId(Id resultId) const
545 Id typeId = getTypeId(resultId);
546 assert(isPointerType(typeId));
548 return module.getInstruction(typeId)->getIdOperand(1);
551 Op Builder::getMostBasicTypeClass(Id typeId) const
553 Instruction* instr = module.getInstruction(typeId);
555 Op typeClass = instr->getOpCode();
561 case OpTypeRuntimeArray:
562 return getMostBasicTypeClass(instr->getIdOperand(0));
564 return getMostBasicTypeClass(instr->getIdOperand(1));
570 int Builder::getNumTypeConstituents(Id typeId) const
572 Instruction* instr = module.getInstruction(typeId);
574 switch (instr->getOpCode())
582 return instr->getImmediateOperand(1);
585 Id lengthId = instr->getIdOperand(1);
586 return module.getInstruction(lengthId)->getImmediateOperand(0);
589 return instr->getNumOperands();
596 // Return the lowest-level type of scalar that an homogeneous composite is made out of.
597 // Typically, this is just to find out if something is made out of ints or floats.
598 // However, it includes returning a structure, if say, it is an array of structure.
599 Id Builder::getScalarTypeId(Id typeId) const
601 Instruction* instr = module.getInstruction(typeId);
603 Op typeClass = instr->getOpCode();
611 return instr->getResultId();
615 case OpTypeRuntimeArray:
617 return getScalarTypeId(getContainedTypeId(typeId));
624 // Return the type of 'member' of a composite.
625 Id Builder::getContainedTypeId(Id typeId, int member) const
627 Instruction* instr = module.getInstruction(typeId);
629 Op typeClass = instr->getOpCode();
635 case OpTypeRuntimeArray:
636 return instr->getIdOperand(0);
638 return instr->getIdOperand(1);
640 return instr->getIdOperand(member);
647 // Return the immediately contained type of a given composite type.
648 Id Builder::getContainedTypeId(Id typeId) const
650 return getContainedTypeId(typeId, 0);
653 // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
654 // of width 'width'. The 'width' is only consumed for int and float types.
655 // Returns false otherwise.
656 bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
658 const Instruction& instr = *module.getInstruction(typeId);
660 Op typeClass = instr.getOpCode();
665 return typeClass == typeOp && instr.getImmediateOperand(0) == width;
667 for (int m = 0; m < instr.getNumOperands(); ++m) {
668 if (containsType(instr.getIdOperand(m), typeOp, width))
675 case OpTypeRuntimeArray:
677 return containsType(getContainedTypeId(typeId), typeOp, width);
679 return typeClass == typeOp;
683 // See if a scalar constant of this type has already been created, so it
684 // can be reused rather than duplicated. (Required by the specification).
685 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
687 Instruction* constant;
688 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
689 constant = groupedConstants[typeClass][i];
690 if (constant->getOpCode() == opcode &&
691 constant->getTypeId() == typeId &&
692 constant->getImmediateOperand(0) == value)
693 return constant->getResultId();
699 // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
700 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
702 Instruction* constant;
703 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
704 constant = groupedConstants[typeClass][i];
705 if (constant->getOpCode() == opcode &&
706 constant->getTypeId() == typeId &&
707 constant->getImmediateOperand(0) == v1 &&
708 constant->getImmediateOperand(1) == v2)
709 return constant->getResultId();
715 // Return true if consuming 'opcode' means consuming a constant.
716 // "constant" here means after final transform to executable code,
717 // the value consumed will be a constant, so includes specialization.
718 bool Builder::isConstantOpCode(Op opcode) const
723 case OpConstantFalse:
725 case OpConstantComposite:
726 case OpConstantSampler:
728 case OpSpecConstantTrue:
729 case OpSpecConstantFalse:
731 case OpSpecConstantComposite:
732 case OpSpecConstantOp:
739 // Return true if consuming 'opcode' means consuming a specialization constant.
740 bool Builder::isSpecConstantOpCode(Op opcode) const
743 case OpSpecConstantTrue:
744 case OpSpecConstantFalse:
746 case OpSpecConstantComposite:
747 case OpSpecConstantOp:
754 Id Builder::makeBoolConstant(bool b, bool specConstant)
756 Id typeId = makeBoolType();
757 Instruction* constant;
758 Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
760 // See if we already made it. Applies only to regular constants, because specialization constants
761 // must remain distinct for the purpose of applying a SpecId decoration.
762 if (! specConstant) {
764 for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
765 constant = groupedConstants[OpTypeBool][i];
766 if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
767 existing = constant->getResultId();
775 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
776 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
777 groupedConstants[OpTypeBool].push_back(c);
778 module.mapInstruction(c);
780 return c->getResultId();
783 Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
785 Op opcode = specConstant ? OpSpecConstant : OpConstant;
787 // See if we already made it. Applies only to regular constants, because specialization constants
788 // must remain distinct for the purpose of applying a SpecId decoration.
789 if (! specConstant) {
790 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
795 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
796 c->addImmediateOperand(value);
797 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
798 groupedConstants[OpTypeInt].push_back(c);
799 module.mapInstruction(c);
801 return c->getResultId();
804 Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
806 Op opcode = specConstant ? OpSpecConstant : OpConstant;
808 unsigned op1 = value & 0xFFFFFFFF;
809 unsigned op2 = value >> 32;
811 // See if we already made it. Applies only to regular constants, because specialization constants
812 // must remain distinct for the purpose of applying a SpecId decoration.
813 if (! specConstant) {
814 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
819 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
820 c->addImmediateOperand(op1);
821 c->addImmediateOperand(op2);
822 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
823 groupedConstants[OpTypeInt].push_back(c);
824 module.mapInstruction(c);
826 return c->getResultId();
829 Id Builder::makeFloatConstant(float f, bool specConstant)
831 Op opcode = specConstant ? OpSpecConstant : OpConstant;
832 Id typeId = makeFloatType(32);
833 union { float fl; unsigned int ui; } u;
835 unsigned value = u.ui;
837 // See if we already made it. Applies only to regular constants, because specialization constants
838 // must remain distinct for the purpose of applying a SpecId decoration.
839 if (! specConstant) {
840 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
845 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
846 c->addImmediateOperand(value);
847 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
848 groupedConstants[OpTypeFloat].push_back(c);
849 module.mapInstruction(c);
851 return c->getResultId();
854 Id Builder::makeDoubleConstant(double d, bool specConstant)
856 Op opcode = specConstant ? OpSpecConstant : OpConstant;
857 Id typeId = makeFloatType(64);
858 union { double db; unsigned long long ull; } u;
860 unsigned long long value = u.ull;
861 unsigned op1 = value & 0xFFFFFFFF;
862 unsigned op2 = value >> 32;
864 // See if we already made it. Applies only to regular constants, because specialization constants
865 // must remain distinct for the purpose of applying a SpecId decoration.
866 if (! specConstant) {
867 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
872 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
873 c->addImmediateOperand(op1);
874 c->addImmediateOperand(op2);
875 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
876 groupedConstants[OpTypeFloat].push_back(c);
877 module.mapInstruction(c);
879 return c->getResultId();
882 Id Builder::makeFloat16Constant(float f16, bool specConstant)
884 Op opcode = specConstant ? OpSpecConstant : OpConstant;
885 Id typeId = makeFloatType(16);
887 spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
888 spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
889 fVal.castTo(f16Val, spvutils::kRoundToZero);
891 unsigned value = f16Val.value().getAsFloat().get_value();
893 // See if we already made it. Applies only to regular constants, because specialization constants
894 // must remain distinct for the purpose of applying a SpecId decoration.
896 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
901 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
902 c->addImmediateOperand(value);
903 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
904 groupedConstants[OpTypeFloat].push_back(c);
905 module.mapInstruction(c);
907 return c->getResultId();
910 Id Builder::makeFpConstant(Id type, double d, bool specConstant)
912 assert(isFloatType(type));
914 switch (getScalarTypeWidth(type)) {
916 return makeFloat16Constant((float)d, specConstant);
918 return makeFloatConstant((float)d, specConstant);
920 return makeDoubleConstant(d, specConstant);
929 Id Builder::findCompositeConstant(Op typeClass, const std::vector<Id>& comps)
931 Instruction* constant = 0;
933 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
934 constant = groupedConstants[typeClass][i];
937 if (constant->getNumOperands() != (int)comps.size())
941 bool mismatch = false;
942 for (int op = 0; op < constant->getNumOperands(); ++op) {
943 if (constant->getIdOperand(op) != comps[op]) {
954 return found ? constant->getResultId() : NoResult;
957 Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
959 Instruction* constant = 0;
961 for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
962 constant = groupedStructConstants[typeId][i];
965 bool mismatch = false;
966 for (int op = 0; op < constant->getNumOperands(); ++op) {
967 if (constant->getIdOperand(op) != comps[op]) {
978 return found ? constant->getResultId() : NoResult;
981 // Comments in header
982 Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
984 Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
986 Op typeClass = getTypeClass(typeId);
992 if (! specConstant) {
993 Id existing = findCompositeConstant(typeClass, members);
999 if (! specConstant) {
1000 Id existing = findStructConstant(typeId, members);
1007 return makeFloatConstant(0.0);
1010 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1011 for (int op = 0; op < (int)members.size(); ++op)
1012 c->addIdOperand(members[op]);
1013 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1014 if (typeClass == OpTypeStruct)
1015 groupedStructConstants[typeId].push_back(c);
1017 groupedConstants[typeClass].push_back(c);
1018 module.mapInstruction(c);
1020 return c->getResultId();
1023 Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
1025 Instruction* entryPoint = new Instruction(OpEntryPoint);
1026 entryPoint->addImmediateOperand(model);
1027 entryPoint->addIdOperand(function->getId());
1028 entryPoint->addStringOperand(name);
1030 entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
1035 // Currently relying on the fact that all 'value' of interest are small non-negative values.
1036 void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
1038 Instruction* instr = new Instruction(OpExecutionMode);
1039 instr->addIdOperand(entryPoint->getId());
1040 instr->addImmediateOperand(mode);
1042 instr->addImmediateOperand(value1);
1044 instr->addImmediateOperand(value2);
1046 instr->addImmediateOperand(value3);
1048 executionModes.push_back(std::unique_ptr<Instruction>(instr));
1051 void Builder::addName(Id id, const char* string)
1053 Instruction* name = new Instruction(OpName);
1054 name->addIdOperand(id);
1055 name->addStringOperand(string);
1057 names.push_back(std::unique_ptr<Instruction>(name));
1060 void Builder::addMemberName(Id id, int memberNumber, const char* string)
1062 Instruction* name = new Instruction(OpMemberName);
1063 name->addIdOperand(id);
1064 name->addImmediateOperand(memberNumber);
1065 name->addStringOperand(string);
1067 names.push_back(std::unique_ptr<Instruction>(name));
1070 void Builder::addDecoration(Id id, Decoration decoration, int num)
1072 if (decoration == spv::DecorationMax)
1075 Instruction* dec = new Instruction(OpDecorate);
1076 dec->addIdOperand(id);
1077 dec->addImmediateOperand(decoration);
1079 dec->addImmediateOperand(num);
1081 decorations.push_back(std::unique_ptr<Instruction>(dec));
1084 void Builder::addDecoration(Id id, Decoration decoration, const char* s)
1086 if (decoration == spv::DecorationMax)
1089 Instruction* dec = new Instruction(OpDecorateStringGOOGLE);
1090 dec->addIdOperand(id);
1091 dec->addImmediateOperand(decoration);
1092 dec->addStringOperand(s);
1094 decorations.push_back(std::unique_ptr<Instruction>(dec));
1097 void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
1099 if (decoration == spv::DecorationMax)
1102 Instruction* dec = new Instruction(OpDecorateId);
1103 dec->addIdOperand(id);
1104 dec->addImmediateOperand(decoration);
1105 dec->addIdOperand(idDecoration);
1107 decorations.push_back(std::unique_ptr<Instruction>(dec));
1110 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
1112 if (decoration == spv::DecorationMax)
1115 Instruction* dec = new Instruction(OpMemberDecorate);
1116 dec->addIdOperand(id);
1117 dec->addImmediateOperand(member);
1118 dec->addImmediateOperand(decoration);
1120 dec->addImmediateOperand(num);
1122 decorations.push_back(std::unique_ptr<Instruction>(dec));
1125 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
1127 if (decoration == spv::DecorationMax)
1130 Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
1131 dec->addIdOperand(id);
1132 dec->addImmediateOperand(member);
1133 dec->addImmediateOperand(decoration);
1134 dec->addStringOperand(s);
1136 decorations.push_back(std::unique_ptr<Instruction>(dec));
1139 // Comments in header
1140 Function* Builder::makeEntryPoint(const char* entryPoint)
1142 assert(! entryPointFunction);
1145 std::vector<Id> params;
1146 std::vector<std::vector<Decoration>> decorations;
1148 entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry);
1150 return entryPointFunction;
1153 // Comments in header
1154 Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
1155 const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& decorations, Block **entry)
1157 // Make the function and initial instructions in it
1158 Id typeId = makeFunctionType(returnType, paramTypes);
1159 Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
1160 Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
1162 // Set up the precisions
1163 setPrecision(function->getId(), precision);
1164 for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
1165 for (int d = 0; d < (int)decorations[p].size(); ++d)
1166 addDecoration(firstParamId + p, decorations[p][d]);
1171 *entry = new Block(getUniqueId(), *function);
1172 function->addBlock(*entry);
1173 setBuildPoint(*entry);
1177 addName(function->getId(), name);
1179 functions.push_back(std::unique_ptr<Function>(function));
1184 // Comments in header
1185 void Builder::makeReturn(bool implicit, Id retVal)
1188 Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
1189 inst->addIdOperand(retVal);
1190 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1192 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
1195 createAndSetNoPredecessorBlock("post-return");
1198 // Comments in header
1199 void Builder::leaveFunction()
1201 Block* block = buildPoint;
1202 Function& function = buildPoint->getParent();
1205 // If our function did not contain a return, add a return void now.
1206 if (! block->isTerminated()) {
1207 if (function.getReturnType() == makeVoidType())
1210 makeReturn(true, createUndefined(function.getReturnType()));
1215 // Comments in header
1216 void Builder::makeDiscard()
1218 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill)));
1219 createAndSetNoPredecessorBlock("post-discard");
1222 // Comments in header
1223 Id Builder::createVariable(StorageClass storageClass, Id type, const char* name)
1225 Id pointerType = makePointer(storageClass, type);
1226 Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
1227 inst->addImmediateOperand(storageClass);
1229 switch (storageClass) {
1230 case StorageClassFunction:
1231 // Validation rules require the declaration in the entry block
1232 buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
1236 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1237 module.mapInstruction(inst);
1242 addName(inst->getResultId(), name);
1244 return inst->getResultId();
1247 // Comments in header
1248 Id Builder::createUndefined(Id type)
1250 Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
1251 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1252 return inst->getResultId();
1255 // Comments in header
1256 void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
1258 Instruction* store = new Instruction(OpStore);
1259 store->addIdOperand(lValue);
1260 store->addIdOperand(rValue);
1262 if (memoryAccess != MemoryAccessMaskNone) {
1263 store->addImmediateOperand(memoryAccess);
1264 if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
1265 store->addIdOperand(makeUintConstant(scope));
1269 buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
1272 // Comments in header
1273 Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
1275 Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
1276 load->addIdOperand(lValue);
1278 if (memoryAccess != MemoryAccessMaskNone) {
1279 load->addImmediateOperand(memoryAccess);
1280 if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
1281 load->addIdOperand(makeUintConstant(scope));
1285 buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
1287 return load->getResultId();
1290 // Comments in header
1291 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
1293 // Figure out the final resulting type.
1294 spv::Id typeId = getTypeId(base);
1295 assert(isPointerType(typeId) && offsets.size() > 0);
1296 typeId = getContainedTypeId(typeId);
1297 for (int i = 0; i < (int)offsets.size(); ++i) {
1298 if (isStructType(typeId)) {
1299 assert(isConstantScalar(offsets[i]));
1300 typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i]));
1302 typeId = getContainedTypeId(typeId, offsets[i]);
1304 typeId = makePointer(storageClass, typeId);
1306 // Make the instruction
1307 Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
1308 chain->addIdOperand(base);
1309 for (int i = 0; i < (int)offsets.size(); ++i)
1310 chain->addIdOperand(offsets[i]);
1311 buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
1313 return chain->getResultId();
1316 Id Builder::createArrayLength(Id base, unsigned int member)
1318 spv::Id intType = makeUintType(32);
1319 Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
1320 length->addIdOperand(base);
1321 length->addImmediateOperand(member);
1322 buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
1324 return length->getResultId();
1327 Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
1329 // Generate code for spec constants if in spec constant operation
1331 if (generatingOpCodeForSpecConst) {
1332 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), std::vector<Id>(1, index));
1334 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1335 extract->addIdOperand(composite);
1336 extract->addImmediateOperand(index);
1337 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1339 return extract->getResultId();
1342 Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
1344 // Generate code for spec constants if in spec constant operation
1346 if (generatingOpCodeForSpecConst) {
1347 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
1349 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1350 extract->addIdOperand(composite);
1351 for (int i = 0; i < (int)indexes.size(); ++i)
1352 extract->addImmediateOperand(indexes[i]);
1353 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1355 return extract->getResultId();
1358 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
1360 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1361 insert->addIdOperand(object);
1362 insert->addIdOperand(composite);
1363 insert->addImmediateOperand(index);
1364 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1366 return insert->getResultId();
1369 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
1371 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1372 insert->addIdOperand(object);
1373 insert->addIdOperand(composite);
1374 for (int i = 0; i < (int)indexes.size(); ++i)
1375 insert->addImmediateOperand(indexes[i]);
1376 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1378 return insert->getResultId();
1381 Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
1383 Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
1384 extract->addIdOperand(vector);
1385 extract->addIdOperand(componentIndex);
1386 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1388 return extract->getResultId();
1391 Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
1393 Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
1394 insert->addIdOperand(vector);
1395 insert->addIdOperand(component);
1396 insert->addIdOperand(componentIndex);
1397 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1399 return insert->getResultId();
1402 // An opcode that has no operands, no result id, and no type
1403 void Builder::createNoResultOp(Op opCode)
1405 Instruction* op = new Instruction(opCode);
1406 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1409 // An opcode that has one id operand, no result id, and no type
1410 void Builder::createNoResultOp(Op opCode, Id operand)
1412 Instruction* op = new Instruction(opCode);
1413 op->addIdOperand(operand);
1414 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1417 // An opcode that has one or more operands, no result id, and no type
1418 void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
1420 Instruction* op = new Instruction(opCode);
1421 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1422 op->addIdOperand(*it);
1424 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1427 // An opcode that has multiple operands, no result id, and no type
1428 void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
1430 Instruction* op = new Instruction(opCode);
1431 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1433 op->addIdOperand(it->word);
1435 op->addImmediateOperand(it->word);
1437 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1440 void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
1442 Instruction* op = new Instruction(OpControlBarrier);
1443 op->addIdOperand(makeUintConstant(execution));
1444 op->addIdOperand(makeUintConstant(memory));
1445 op->addIdOperand(makeUintConstant(semantics));
1446 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1449 void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
1451 Instruction* op = new Instruction(OpMemoryBarrier);
1452 op->addIdOperand(makeUintConstant(executionScope));
1453 op->addIdOperand(makeUintConstant(memorySemantics));
1454 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1457 // An opcode that has one operands, a result id, and a type
1458 Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
1460 // Generate code for spec constants if in spec constant operation
1462 if (generatingOpCodeForSpecConst) {
1463 return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
1465 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1466 op->addIdOperand(operand);
1467 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1469 return op->getResultId();
1472 Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
1474 // Generate code for spec constants if in spec constant operation
1476 if (generatingOpCodeForSpecConst) {
1477 std::vector<Id> operands(2);
1478 operands[0] = left; operands[1] = right;
1479 return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
1481 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1482 op->addIdOperand(left);
1483 op->addIdOperand(right);
1484 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1486 return op->getResultId();
1489 Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
1491 // Generate code for spec constants if in spec constant operation
1493 if (generatingOpCodeForSpecConst) {
1494 std::vector<Id> operands(3);
1498 return createSpecConstantOp(
1499 opCode, typeId, operands, std::vector<Id>());
1501 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1502 op->addIdOperand(op1);
1503 op->addIdOperand(op2);
1504 op->addIdOperand(op3);
1505 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1507 return op->getResultId();
1510 Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
1512 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1513 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1514 op->addIdOperand(*it);
1515 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1517 return op->getResultId();
1520 Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
1522 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1523 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1525 op->addIdOperand(it->word);
1527 op->addImmediateOperand(it->word);
1529 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1531 return op->getResultId();
1534 Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& literals)
1536 Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
1537 op->addImmediateOperand((unsigned) opCode);
1538 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1539 op->addIdOperand(*it);
1540 for (auto it = literals.cbegin(); it != literals.cend(); ++it)
1541 op->addImmediateOperand(*it);
1542 module.mapInstruction(op);
1543 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
1545 return op->getResultId();
1548 Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
1550 Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
1551 op->addIdOperand(function->getId());
1552 for (int a = 0; a < (int)args.size(); ++a)
1553 op->addIdOperand(args[a]);
1554 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1556 return op->getResultId();
1559 // Comments in header
1560 Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
1562 if (channels.size() == 1)
1563 return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
1565 if (generatingOpCodeForSpecConst) {
1566 std::vector<Id> operands(2);
1567 operands[0] = operands[1] = source;
1568 return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
1570 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1571 assert(isVector(source));
1572 swizzle->addIdOperand(source);
1573 swizzle->addIdOperand(source);
1574 for (int i = 0; i < (int)channels.size(); ++i)
1575 swizzle->addImmediateOperand(channels[i]);
1576 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1578 return setPrecision(swizzle->getResultId(), precision);
1581 // Comments in header
1582 Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
1584 if (channels.size() == 1 && getNumComponents(source) == 1)
1585 return createCompositeInsert(source, target, typeId, channels.front());
1587 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1589 assert(isVector(target));
1590 swizzle->addIdOperand(target);
1592 assert(getNumComponents(source) == (int)channels.size());
1593 assert(isVector(source));
1594 swizzle->addIdOperand(source);
1596 // Set up an identity shuffle from the base value to the result value
1597 unsigned int components[4];
1598 int numTargetComponents = getNumComponents(target);
1599 for (int i = 0; i < numTargetComponents; ++i)
1602 // Punch in the l-value swizzle
1603 for (int i = 0; i < (int)channels.size(); ++i)
1604 components[channels[i]] = numTargetComponents + i;
1606 // finish the instruction with these components selectors
1607 for (int i = 0; i < numTargetComponents; ++i)
1608 swizzle->addImmediateOperand(components[i]);
1609 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1611 return swizzle->getResultId();
1614 // Comments in header
1615 void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
1617 int direction = getNumComponents(right) - getNumComponents(left);
1620 left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
1621 else if (direction < 0)
1622 right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
1627 // Comments in header
1628 Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
1630 assert(getNumComponents(scalar) == 1);
1631 assert(getTypeId(scalar) == getScalarTypeId(vectorType));
1633 int numComponents = getNumTypeComponents(vectorType);
1634 if (numComponents == 1)
1637 Instruction* smear = nullptr;
1638 if (generatingOpCodeForSpecConst) {
1639 auto members = std::vector<spv::Id>(numComponents, scalar);
1640 // Sometime even in spec-constant-op mode, the temporary vector created by
1641 // promoting a scalar might not be a spec constant. This should depend on
1644 // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
1645 // In such cases, the temporary vector created from a_front_end_const_scalar
1646 // is not a spec constant vector, even though the binary operation node is marked
1647 // as 'specConstant' and we are in spec-constant-op mode.
1648 auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
1649 smear = module.getInstruction(result_id);
1651 smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
1652 for (int c = 0; c < numComponents; ++c)
1653 smear->addIdOperand(scalar);
1654 buildPoint->addInstruction(std::unique_ptr<Instruction>(smear));
1657 return setPrecision(smear->getResultId(), precision);
1660 // Comments in header
1661 Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
1663 Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
1664 inst->addIdOperand(builtins);
1665 inst->addImmediateOperand(entryPoint);
1666 for (int arg = 0; arg < (int)args.size(); ++arg)
1667 inst->addIdOperand(args[arg]);
1669 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1671 return inst->getResultId();
1674 // Accept all parameters needed to create a texture instruction.
1675 // Create the correct instruction based on the inputs, and make the call.
1676 Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
1677 bool noImplicitLod, const TextureParameters& parameters)
1679 static const int maxTextureArgs = 10;
1680 Id texArgs[maxTextureArgs] = {};
1683 // Set up the fixed arguments
1686 bool explicitLod = false;
1687 texArgs[numArgs++] = parameters.sampler;
1688 texArgs[numArgs++] = parameters.coords;
1689 if (parameters.Dref != NoResult)
1690 texArgs[numArgs++] = parameters.Dref;
1691 if (parameters.component != NoResult)
1692 texArgs[numArgs++] = parameters.component;
1694 #ifdef NV_EXTENSIONS
1695 if (parameters.granularity != NoResult)
1696 texArgs[numArgs++] = parameters.granularity;
1697 if (parameters.coarse != NoResult)
1698 texArgs[numArgs++] = parameters.coarse;
1702 // Set up the optional arguments
1704 int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
1705 ++numArgs; // speculatively make room for the mask operand
1706 ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
1707 if (parameters.bias) {
1708 mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
1709 texArgs[numArgs++] = parameters.bias;
1711 if (parameters.lod) {
1712 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
1713 texArgs[numArgs++] = parameters.lod;
1715 } else if (parameters.gradX) {
1716 mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
1717 texArgs[numArgs++] = parameters.gradX;
1718 texArgs[numArgs++] = parameters.gradY;
1720 } else if (noImplicitLod && ! fetch && ! gather) {
1721 // have to explicitly use lod of 0 if not allowed to have them be implicit, and
1722 // we would otherwise be about to issue an implicit instruction
1723 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
1724 texArgs[numArgs++] = makeFloatConstant(0.0);
1727 if (parameters.offset) {
1728 if (isConstant(parameters.offset))
1729 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
1731 addCapability(CapabilityImageGatherExtended);
1732 mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
1734 texArgs[numArgs++] = parameters.offset;
1736 if (parameters.offsets) {
1737 addCapability(CapabilityImageGatherExtended);
1738 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
1739 texArgs[numArgs++] = parameters.offsets;
1741 if (parameters.sample) {
1742 mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
1743 texArgs[numArgs++] = parameters.sample;
1745 if (parameters.lodClamp) {
1746 // capability if this bit is used
1747 addCapability(CapabilityMinLod);
1749 mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
1750 texArgs[numArgs++] = parameters.lodClamp;
1752 if (parameters.nonprivate) {
1753 mask = mask | ImageOperandsNonPrivateTexelKHRMask;
1755 if (parameters.volatil) {
1756 mask = mask | ImageOperandsVolatileTexelKHRMask;
1758 if (mask == ImageOperandsMaskNone)
1759 --numArgs; // undo speculative reservation for the mask argument
1761 texArgs[optArgNum] = mask;
1764 // Set up the instruction
1766 Op opCode = OpNop; // All paths below need to set this
1769 opCode = OpImageSparseFetch;
1771 opCode = OpImageFetch;
1772 #ifdef NV_EXTENSIONS
1773 } else if (parameters.granularity && parameters.coarse) {
1774 opCode = OpImageSampleFootprintNV;
1776 } else if (gather) {
1777 if (parameters.Dref)
1779 opCode = OpImageSparseDrefGather;
1781 opCode = OpImageDrefGather;
1784 opCode = OpImageSparseGather;
1786 opCode = OpImageGather;
1787 } else if (explicitLod) {
1788 if (parameters.Dref) {
1791 opCode = OpImageSparseSampleProjDrefExplicitLod;
1793 opCode = OpImageSampleProjDrefExplicitLod;
1796 opCode = OpImageSparseSampleDrefExplicitLod;
1798 opCode = OpImageSampleDrefExplicitLod;
1802 opCode = OpImageSparseSampleProjExplicitLod;
1804 opCode = OpImageSampleProjExplicitLod;
1807 opCode = OpImageSparseSampleExplicitLod;
1809 opCode = OpImageSampleExplicitLod;
1812 if (parameters.Dref) {
1815 opCode = OpImageSparseSampleProjDrefImplicitLod;
1817 opCode = OpImageSampleProjDrefImplicitLod;
1820 opCode = OpImageSparseSampleDrefImplicitLod;
1822 opCode = OpImageSampleDrefImplicitLod;
1826 opCode = OpImageSparseSampleProjImplicitLod;
1828 opCode = OpImageSampleProjImplicitLod;
1831 opCode = OpImageSparseSampleImplicitLod;
1833 opCode = OpImageSampleImplicitLod;
1837 // See if the result type is expecting a smeared result.
1838 // This happens when a legacy shadow*() call is made, which
1839 // gets a vec4 back instead of a float.
1840 Id smearedType = resultType;
1841 if (! isScalarType(resultType)) {
1843 case OpImageSampleDrefImplicitLod:
1844 case OpImageSampleDrefExplicitLod:
1845 case OpImageSampleProjDrefImplicitLod:
1846 case OpImageSampleProjDrefExplicitLod:
1847 resultType = getScalarTypeId(resultType);
1858 typeId0 = resultType;
1859 typeId1 = getDerefTypeId(parameters.texelOut);
1860 resultType = makeStructResultType(typeId0, typeId1);
1863 // Build the SPIR-V instruction
1864 Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
1865 for (int op = 0; op < optArgNum; ++op)
1866 textureInst->addIdOperand(texArgs[op]);
1867 if (optArgNum < numArgs)
1868 textureInst->addImmediateOperand(texArgs[optArgNum]);
1869 for (int op = optArgNum + 1; op < numArgs; ++op)
1870 textureInst->addIdOperand(texArgs[op]);
1871 setPrecision(textureInst->getResultId(), precision);
1872 buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst));
1874 Id resultId = textureInst->getResultId();
1878 addCapability(CapabilitySparseResidency);
1880 // Decode the return type that was a special structure
1881 createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
1882 resultId = createCompositeExtract(resultId, typeId0, 0);
1883 setPrecision(resultId, precision);
1885 // When a smear is needed, do it, as per what was computed
1886 // above when resultType was changed to a scalar type.
1887 if (resultType != smearedType)
1888 resultId = smearScalar(precision, resultId, smearedType);
1894 // Comments in header
1895 Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
1897 // Figure out the result type
1900 case OpImageQuerySize:
1901 case OpImageQuerySizeLod:
1903 int numComponents = 0;
1904 switch (getTypeDimensionality(getImageType(parameters.sampler))) {
1912 case DimSubpassData:
1923 if (isArrayedImageType(getImageType(parameters.sampler)))
1926 Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
1927 if (numComponents == 1)
1928 resultType = intType;
1930 resultType = makeVectorType(intType, numComponents);
1934 case OpImageQueryLod:
1935 #ifdef AMD_EXTENSIONS
1936 resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
1938 resultType = makeVectorType(makeFloatType(32), 2);
1941 case OpImageQueryLevels:
1942 case OpImageQuerySamples:
1943 resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
1950 Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
1951 query->addIdOperand(parameters.sampler);
1952 if (parameters.coords)
1953 query->addIdOperand(parameters.coords);
1955 query->addIdOperand(parameters.lod);
1956 buildPoint->addInstruction(std::unique_ptr<Instruction>(query));
1958 return query->getResultId();
1961 // External comments in header.
1962 // Operates recursively to visit the composite's hierarchy.
1963 Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
1965 Id boolType = makeBoolType();
1966 Id valueType = getTypeId(value1);
1968 Id resultId = NoResult;
1970 int numConstituents = getNumTypeConstituents(valueType);
1972 // Scalars and Vectors
1974 if (isScalarType(valueType) || isVectorType(valueType)) {
1975 assert(valueType == getTypeId(value2));
1976 // These just need a single comparison, just have
1977 // to figure out what it is.
1979 switch (getMostBasicTypeClass(valueType)) {
1981 op = equal ? OpFOrdEqual : OpFOrdNotEqual;
1985 op = equal ? OpIEqual : OpINotEqual;
1988 op = equal ? OpLogicalEqual : OpLogicalNotEqual;
1989 precision = NoPrecision;
1993 if (isScalarType(valueType)) {
1995 resultId = createBinOp(op, boolType, value1, value2);
1998 resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
1999 setPrecision(resultId, precision);
2000 // reduce vector compares...
2001 resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
2004 return setPrecision(resultId, precision);
2007 // Only structs, arrays, and matrices should be left.
2008 // They share in common the reduction operation across their constituents.
2009 assert(isAggregateType(valueType) || isMatrixType(valueType));
2011 // Compare each pair of constituents
2012 for (int constituent = 0; constituent < numConstituents; ++constituent) {
2013 std::vector<unsigned> indexes(1, constituent);
2014 Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
2015 Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
2016 Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
2017 Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
2019 Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
2021 if (constituent == 0)
2022 resultId = subResultId;
2024 resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), precision);
2030 // OpCompositeConstruct
2031 Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
2033 assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size()));
2035 if (generatingOpCodeForSpecConst) {
2036 // Sometime, even in spec-constant-op mode, the constant composite to be
2037 // constructed may not be a specialization constant.
2039 // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
2040 // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
2041 // The second column vector should NOT be spec constant, as it does not contain any spec constants.
2042 // To handle such cases, we check the constituents of the constant vector to determine whether this
2043 // vector should be created as a spec constant.
2044 return makeCompositeConstant(typeId, constituents,
2045 std::any_of(constituents.begin(), constituents.end(),
2046 [&](spv::Id id) { return isSpecConstant(id); }));
2049 Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
2050 for (int c = 0; c < (int)constituents.size(); ++c)
2051 op->addIdOperand(constituents[c]);
2052 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
2054 return op->getResultId();
2057 // Vector or scalar constructor
2058 Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2060 Id result = NoResult;
2061 unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
2062 unsigned int targetComponent = 0;
2064 // Special case: when calling a vector constructor with a single scalar
2065 // argument, smear the scalar
2066 if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
2067 return smearScalar(precision, sources[0], resultTypeId);
2069 // accumulate the arguments for OpCompositeConstruct
2070 std::vector<Id> constituents;
2071 Id scalarTypeId = getScalarTypeId(resultTypeId);
2073 // lambda to store the result of visiting an argument component
2074 const auto latchResult = [&](Id comp) {
2075 if (numTargetComponents > 1)
2076 constituents.push_back(comp);
2082 // lambda to visit a vector argument's components
2083 const auto accumulateVectorConstituents = [&](Id sourceArg) {
2084 unsigned int sourceSize = getNumComponents(sourceArg);
2085 unsigned int sourcesToUse = sourceSize;
2086 if (sourcesToUse + targetComponent > numTargetComponents)
2087 sourcesToUse = numTargetComponents - targetComponent;
2089 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2090 std::vector<unsigned> swiz;
2092 latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
2096 // lambda to visit a matrix argument's components
2097 const auto accumulateMatrixConstituents = [&](Id sourceArg) {
2098 unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
2099 unsigned int sourcesToUse = sourceSize;
2100 if (sourcesToUse + targetComponent > numTargetComponents)
2101 sourcesToUse = numTargetComponents - targetComponent;
2105 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2106 if (row >= getNumRows(sourceArg)) {
2110 std::vector<Id> indexes;
2111 indexes.push_back(col);
2112 indexes.push_back(row);
2113 latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
2118 // Go through the source arguments, each one could have either
2119 // a single or multiple components to contribute.
2120 for (unsigned int i = 0; i < sources.size(); ++i) {
2121 if (isScalar(sources[i]))
2122 latchResult(sources[i]);
2123 else if (isVector(sources[i]))
2124 accumulateVectorConstituents(sources[i]);
2125 else if (isMatrix(sources[i]))
2126 accumulateMatrixConstituents(sources[i]);
2130 if (targetComponent >= numTargetComponents)
2134 // If the result is a vector, make it from the gathered constituents.
2135 if (constituents.size() > 0)
2136 result = createCompositeConstruct(resultTypeId, constituents);
2138 return setPrecision(result, precision);
2141 // Comments in header
2142 Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2144 Id componentTypeId = getScalarTypeId(resultTypeId);
2145 int numCols = getTypeNumColumns(resultTypeId);
2146 int numRows = getTypeNumRows(resultTypeId);
2148 Instruction* instr = module.getInstruction(componentTypeId);
2149 unsigned bitCount = instr->getImmediateOperand(0);
2151 // Optimize matrix constructed from a bigger matrix
2152 if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
2153 // To truncate the matrix to a smaller number of rows/columns, we need to:
2154 // 1. For each column, extract the column and truncate it to the required size using shuffle
2155 // 2. Assemble the resulting matrix from all columns
2156 Id matrix = sources[0];
2157 Id columnTypeId = getContainedTypeId(resultTypeId);
2158 Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
2160 std::vector<unsigned> channels;
2161 for (int row = 0; row < numRows; ++row)
2162 channels.push_back(row);
2164 std::vector<Id> matrixColumns;
2165 for (int col = 0; col < numCols; ++col) {
2166 std::vector<unsigned> indexes;
2167 indexes.push_back(col);
2168 Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
2169 setPrecision(colv, precision);
2171 if (numRows != getNumRows(matrix)) {
2172 matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
2174 matrixColumns.push_back(colv);
2178 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2181 // Otherwise, will use a two step process
2182 // 1. make a compile-time 2D array of values
2183 // 2. construct a matrix from that array
2187 // initialize the array to the identity matrix
2188 Id ids[maxMatrixSize][maxMatrixSize];
2189 Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
2190 Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
2191 for (int col = 0; col < 4; ++col) {
2192 for (int row = 0; row < 4; ++row) {
2194 ids[col][row] = one;
2196 ids[col][row] = zero;
2200 // modify components as dictated by the arguments
2201 if (sources.size() == 1 && isScalar(sources[0])) {
2202 // a single scalar; resets the diagonals
2203 for (int col = 0; col < 4; ++col)
2204 ids[col][col] = sources[0];
2205 } else if (isMatrix(sources[0])) {
2206 // constructing from another matrix; copy over the parts that exist in both the argument and constructee
2207 Id matrix = sources[0];
2208 int minCols = std::min(numCols, getNumColumns(matrix));
2209 int minRows = std::min(numRows, getNumRows(matrix));
2210 for (int col = 0; col < minCols; ++col) {
2211 std::vector<unsigned> indexes;
2212 indexes.push_back(col);
2213 for (int row = 0; row < minRows; ++row) {
2214 indexes.push_back(row);
2215 ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
2217 setPrecision(ids[col][row], precision);
2221 // fill in the matrix in column-major order with whatever argument components are available
2225 for (int arg = 0; arg < (int)sources.size(); ++arg) {
2226 Id argComp = sources[arg];
2227 for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
2228 if (getNumComponents(sources[arg]) > 1) {
2229 argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
2230 setPrecision(argComp, precision);
2232 ids[col][row++] = argComp;
2233 if (row == numRows) {
2241 // Step 2: Construct a matrix from that array.
2242 // First make the column vectors, then make the matrix.
2244 // make the column vectors
2245 Id columnTypeId = getContainedTypeId(resultTypeId);
2246 std::vector<Id> matrixColumns;
2247 for (int col = 0; col < numCols; ++col) {
2248 std::vector<Id> vectorComponents;
2249 for (int row = 0; row < numRows; ++row)
2250 vectorComponents.push_back(ids[col][row]);
2251 Id column = createCompositeConstruct(columnTypeId, vectorComponents);
2252 setPrecision(column, precision);
2253 matrixColumns.push_back(column);
2257 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2260 // Comments in header
2261 Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
2267 function = &builder.getBuildPoint()->getParent();
2269 // make the blocks, but only put the then-block into the function,
2270 // the else-block and merge-block will be added later, in order, after
2271 // earlier code is emitted
2272 thenBlock = new Block(builder.getUniqueId(), *function);
2273 mergeBlock = new Block(builder.getUniqueId(), *function);
2275 // Save the current block, so that we can add in the flow control split when
2276 // makeEndIf is called.
2277 headerBlock = builder.getBuildPoint();
2279 function->addBlock(thenBlock);
2280 builder.setBuildPoint(thenBlock);
2283 // Comments in header
2284 void Builder::If::makeBeginElse()
2286 // Close out the "then" by having it jump to the mergeBlock
2287 builder.createBranch(mergeBlock);
2289 // Make the first else block and add it to the function
2290 elseBlock = new Block(builder.getUniqueId(), *function);
2291 function->addBlock(elseBlock);
2293 // Start building the else block
2294 builder.setBuildPoint(elseBlock);
2297 // Comments in header
2298 void Builder::If::makeEndIf()
2300 // jump to the merge block
2301 builder.createBranch(mergeBlock);
2303 // Go back to the headerBlock and make the flow control split
2304 builder.setBuildPoint(headerBlock);
2305 builder.createSelectionMerge(mergeBlock, control);
2307 builder.createConditionalBranch(condition, thenBlock, elseBlock);
2309 builder.createConditionalBranch(condition, thenBlock, mergeBlock);
2311 // add the merge block to the function
2312 function->addBlock(mergeBlock);
2313 builder.setBuildPoint(mergeBlock);
2316 // Comments in header
2317 void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
2318 const std::vector<int>& valueIndexToSegment, int defaultSegment,
2319 std::vector<Block*>& segmentBlocks)
2321 Function& function = buildPoint->getParent();
2323 // make all the blocks
2324 for (int s = 0; s < numSegments; ++s)
2325 segmentBlocks.push_back(new Block(getUniqueId(), function));
2327 Block* mergeBlock = new Block(getUniqueId(), function);
2329 // make and insert the switch's selection-merge instruction
2330 createSelectionMerge(mergeBlock, control);
2332 // make the switch instruction
2333 Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
2334 switchInst->addIdOperand(selector);
2335 auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
2336 switchInst->addIdOperand(defaultOrMerge->getId());
2337 defaultOrMerge->addPredecessor(buildPoint);
2338 for (int i = 0; i < (int)caseValues.size(); ++i) {
2339 switchInst->addImmediateOperand(caseValues[i]);
2340 switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
2341 segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
2343 buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst));
2345 // push the merge block
2346 switchMerges.push(mergeBlock);
2349 // Comments in header
2350 void Builder::addSwitchBreak()
2352 // branch to the top of the merge block stack
2353 createBranch(switchMerges.top());
2354 createAndSetNoPredecessorBlock("post-switch-break");
2357 // Comments in header
2358 void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
2360 int lastSegment = nextSegment - 1;
2361 if (lastSegment >= 0) {
2362 // Close out previous segment by jumping, if necessary, to next segment
2363 if (! buildPoint->isTerminated())
2364 createBranch(segmentBlock[nextSegment]);
2366 Block* block = segmentBlock[nextSegment];
2367 block->getParent().addBlock(block);
2368 setBuildPoint(block);
2371 // Comments in header
2372 void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
2374 // Close out previous segment by jumping, if necessary, to next segment
2375 if (! buildPoint->isTerminated())
2378 switchMerges.top()->getParent().addBlock(switchMerges.top());
2379 setBuildPoint(switchMerges.top());
2384 Block& Builder::makeNewBlock()
2386 Function& function = buildPoint->getParent();
2387 auto block = new Block(getUniqueId(), function);
2388 function.addBlock(block);
2392 Builder::LoopBlocks& Builder::makeNewLoop()
2394 // This verbosity is needed to simultaneously get the same behavior
2395 // everywhere (id's in the same order), have a syntax that works
2396 // across lots of versions of C++, have no warnings from pedantic
2397 // compilation modes, and leave the rest of the code alone.
2398 Block& head = makeNewBlock();
2399 Block& body = makeNewBlock();
2400 Block& merge = makeNewBlock();
2401 Block& continue_target = makeNewBlock();
2402 LoopBlocks blocks(head, body, merge, continue_target);
2407 void Builder::createLoopContinue()
2409 createBranch(&loops.top().continue_target);
2410 // Set up a block for dead code.
2411 createAndSetNoPredecessorBlock("post-loop-continue");
2414 void Builder::createLoopExit()
2416 createBranch(&loops.top().merge);
2417 // Set up a block for dead code.
2418 createAndSetNoPredecessorBlock("post-loop-break");
2421 void Builder::closeLoop()
2426 void Builder::clearAccessChain()
2428 accessChain.base = NoResult;
2429 accessChain.indexChain.clear();
2430 accessChain.instr = NoResult;
2431 accessChain.swizzle.clear();
2432 accessChain.component = NoResult;
2433 accessChain.preSwizzleBaseType = NoType;
2434 accessChain.isRValue = false;
2435 accessChain.coherentFlags.clear();
2438 // Comments in header
2439 void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType)
2441 // swizzles can be stacked in GLSL, but simplified to a single
2442 // one here; the base type doesn't change
2443 if (accessChain.preSwizzleBaseType == NoType)
2444 accessChain.preSwizzleBaseType = preSwizzleBaseType;
2446 // if needed, propagate the swizzle for the current access chain
2447 if (accessChain.swizzle.size() > 0) {
2448 std::vector<unsigned> oldSwizzle = accessChain.swizzle;
2449 accessChain.swizzle.resize(0);
2450 for (unsigned int i = 0; i < swizzle.size(); ++i) {
2451 assert(swizzle[i] < oldSwizzle.size());
2452 accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
2455 accessChain.swizzle = swizzle;
2457 // determine if we need to track this swizzle anymore
2458 simplifyAccessChainSwizzle();
2461 // Comments in header
2462 void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
2464 assert(accessChain.isRValue == false);
2466 transferAccessChainSwizzle(true);
2467 Id base = collapseAccessChain();
2470 // dynamic component should be gone
2471 assert(accessChain.component == NoResult);
2473 // If swizzle still exists, it is out-of-order or not full, we must load the target vector,
2474 // extract and insert elements to perform writeMask and/or swizzle.
2475 if (accessChain.swizzle.size() > 0) {
2476 Id tempBaseId = createLoad(base);
2477 source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
2480 createStore(source, base, memoryAccess, scope);
2483 // Comments in header
2484 Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
2488 if (accessChain.isRValue) {
2489 // transfer access chain, but try to stay in registers
2490 transferAccessChainSwizzle(false);
2491 if (accessChain.indexChain.size() > 0) {
2492 Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
2494 // if all the accesses are constants, we can use OpCompositeExtract
2495 std::vector<unsigned> indexes;
2496 bool constant = true;
2497 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
2498 if (isConstantScalar(accessChain.indexChain[i]))
2499 indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
2507 id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
2509 // make a new function variable for this r-value
2510 Id lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable");
2513 createStore(accessChain.base, lValue);
2515 // move base to the new variable
2516 accessChain.base = lValue;
2517 accessChain.isRValue = false;
2519 // load through the access chain
2520 id = createLoad(collapseAccessChain());
2522 setPrecision(id, precision);
2524 id = accessChain.base; // no precision, it was set when this was defined
2526 transferAccessChainSwizzle(true);
2527 // load through the access chain
2528 id = createLoad(collapseAccessChain(), memoryAccess, scope);
2529 setPrecision(id, precision);
2530 addDecoration(id, nonUniform);
2533 // Done, unless there are swizzles to do
2534 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2537 // Do remaining swizzling
2539 // Do the basic swizzle
2540 if (accessChain.swizzle.size() > 0) {
2541 Id swizzledType = getScalarTypeId(getTypeId(id));
2542 if (accessChain.swizzle.size() > 1)
2543 swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
2544 id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
2547 // Do the dynamic component
2548 if (accessChain.component != NoResult)
2549 id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
2551 addDecoration(id, nonUniform);
2555 Id Builder::accessChainGetLValue()
2557 assert(accessChain.isRValue == false);
2559 transferAccessChainSwizzle(true);
2560 Id lvalue = collapseAccessChain();
2562 // If swizzle exists, it is out-of-order or not full, we must load the target vector,
2563 // extract and insert elements to perform writeMask and/or swizzle. This does not
2564 // go with getting a direct l-value pointer.
2565 assert(accessChain.swizzle.size() == 0);
2566 assert(accessChain.component == NoResult);
2571 // comment in header
2572 Id Builder::accessChainGetInferredType()
2574 // anything to operate on?
2575 if (accessChain.base == NoResult)
2577 Id type = getTypeId(accessChain.base);
2579 // do initial dereference
2580 if (! accessChain.isRValue)
2581 type = getContainedTypeId(type);
2583 // dereference each index
2584 for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
2585 if (isStructType(type))
2586 type = getContainedTypeId(type, getConstantScalar(*it));
2588 type = getContainedTypeId(type);
2591 // dereference swizzle
2592 if (accessChain.swizzle.size() == 1)
2593 type = getContainedTypeId(type);
2594 else if (accessChain.swizzle.size() > 1)
2595 type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
2597 // dereference component selection
2598 if (accessChain.component)
2599 type = getContainedTypeId(type);
2604 void Builder::dump(std::vector<unsigned int>& out) const
2606 // Header, before first instructions:
2607 out.push_back(MagicNumber);
2608 out.push_back(spvVersion);
2609 out.push_back(builderNumber);
2610 out.push_back(uniqueId + 1);
2614 for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
2615 Instruction capInst(0, 0, OpCapability);
2616 capInst.addImmediateOperand(*it);
2620 for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
2621 Instruction extInst(0, 0, OpExtension);
2622 extInst.addStringOperand(it->c_str());
2626 dumpInstructions(out, imports);
2627 Instruction memInst(0, 0, OpMemoryModel);
2628 memInst.addImmediateOperand(addressModel);
2629 memInst.addImmediateOperand(memoryModel);
2632 // Instructions saved up while building:
2633 dumpInstructions(out, entryPoints);
2634 dumpInstructions(out, executionModes);
2636 // Debug instructions
2637 dumpInstructions(out, strings);
2638 dumpSourceInstructions(out);
2639 for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
2640 Instruction sourceExtInst(0, 0, OpSourceExtension);
2641 sourceExtInst.addStringOperand(sourceExtensions[e]);
2642 sourceExtInst.dump(out);
2644 dumpInstructions(out, names);
2645 dumpModuleProcesses(out);
2647 // Annotation instructions
2648 dumpInstructions(out, decorations);
2650 dumpInstructions(out, constantsTypesGlobals);
2651 dumpInstructions(out, externals);
2658 // Protected methods.
2661 // Turn the described access chain in 'accessChain' into an instruction(s)
2662 // computing its address. This *cannot* include complex swizzles, which must
2663 // be handled after this is called.
2665 // Can generate code.
2666 Id Builder::collapseAccessChain()
2668 assert(accessChain.isRValue == false);
2670 // did we already emit an access chain for this?
2671 if (accessChain.instr != NoResult)
2672 return accessChain.instr;
2674 // If we have a dynamic component, we can still transfer
2675 // that into a final operand to the access chain. We need to remap the
2676 // dynamic component through the swizzle to get a new dynamic component to
2679 // This was not done in transferAccessChainSwizzle() because it might
2681 remapDynamicSwizzle();
2682 if (accessChain.component != NoResult) {
2683 // transfer the dynamic component to the access chain
2684 accessChain.indexChain.push_back(accessChain.component);
2685 accessChain.component = NoResult;
2688 // note that non-trivial swizzling is left pending
2690 // do we have an access chain?
2691 if (accessChain.indexChain.size() == 0)
2692 return accessChain.base;
2694 // emit the access chain
2695 StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
2696 accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
2698 return accessChain.instr;
2701 // For a dynamic component selection of a swizzle.
2703 // Turn the swizzle and dynamic component into just a dynamic component.
2706 void Builder::remapDynamicSwizzle()
2708 // do we have a swizzle to remap a dynamic component through?
2709 if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
2710 // build a vector of the swizzle for the component to map into
2711 std::vector<Id> components;
2712 for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
2713 components.push_back(makeUintConstant(accessChain.swizzle[c]));
2714 Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
2715 Id map = makeCompositeConstant(mapType, components);
2718 accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
2719 accessChain.swizzle.clear();
2723 // clear out swizzle if it is redundant, that is reselecting the same components
2724 // that would be present without the swizzle.
2725 void Builder::simplifyAccessChainSwizzle()
2727 // If the swizzle has fewer components than the vector, it is subsetting, and must stay
2728 // to preserve that fact.
2729 if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
2732 // if components are out of order, it is a swizzle
2733 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
2734 if (i != accessChain.swizzle[i])
2738 // otherwise, there is no need to track this swizzle
2739 accessChain.swizzle.clear();
2740 if (accessChain.component == NoResult)
2741 accessChain.preSwizzleBaseType = NoType;
2744 // To the extent any swizzling can become part of the chain
2745 // of accesses instead of a post operation, make it so.
2746 // If 'dynamic' is true, include transferring the dynamic component,
2747 // otherwise, leave it pending.
2749 // Does not generate code. just updates the access chain.
2750 void Builder::transferAccessChainSwizzle(bool dynamic)
2753 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2757 // (this requires either a swizzle, or generating code for a dynamic component)
2758 if (accessChain.swizzle.size() > 1)
2761 // single component, either in the swizzle and/or dynamic component
2762 if (accessChain.swizzle.size() == 1) {
2763 assert(accessChain.component == NoResult);
2764 // handle static component selection
2765 accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
2766 accessChain.swizzle.clear();
2767 accessChain.preSwizzleBaseType = NoType;
2768 } else if (dynamic && accessChain.component != NoResult) {
2769 assert(accessChain.swizzle.size() == 0);
2770 // handle dynamic component
2771 accessChain.indexChain.push_back(accessChain.component);
2772 accessChain.preSwizzleBaseType = NoType;
2773 accessChain.component = NoResult;
2777 // Utility method for creating a new block and setting the insert point to
2778 // be in it. This is useful for flow-control operations that need a "dummy"
2779 // block proceeding them (e.g. instructions after a discard, etc).
2780 void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
2782 Block* block = new Block(getUniqueId(), buildPoint->getParent());
2783 block->setUnreachable();
2784 buildPoint->getParent().addBlock(block);
2785 setBuildPoint(block);
2788 // addName(block->getId(), name);
2791 // Comments in header
2792 void Builder::createBranch(Block* block)
2794 Instruction* branch = new Instruction(OpBranch);
2795 branch->addIdOperand(block->getId());
2796 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
2797 block->addPredecessor(buildPoint);
2800 void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
2802 Instruction* merge = new Instruction(OpSelectionMerge);
2803 merge->addIdOperand(mergeBlock->getId());
2804 merge->addImmediateOperand(control);
2805 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
2808 void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
2809 unsigned int dependencyLength)
2811 Instruction* merge = new Instruction(OpLoopMerge);
2812 merge->addIdOperand(mergeBlock->getId());
2813 merge->addIdOperand(continueBlock->getId());
2814 merge->addImmediateOperand(control);
2815 if ((control & LoopControlDependencyLengthMask) != 0)
2816 merge->addImmediateOperand(dependencyLength);
2817 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
2820 void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
2822 Instruction* branch = new Instruction(OpBranchConditional);
2823 branch->addIdOperand(condition);
2824 branch->addIdOperand(thenBlock->getId());
2825 branch->addIdOperand(elseBlock->getId());
2826 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
2827 thenBlock->addPredecessor(buildPoint);
2828 elseBlock->addPredecessor(buildPoint);
2832 // [OpSourceContinued]
2834 void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
2835 std::vector<unsigned int>& out) const
2837 const int maxWordCount = 0xFFFF;
2838 const int opSourceWordCount = 4;
2839 const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
2841 if (source != SourceLanguageUnknown) {
2842 // OpSource Language Version File Source
2843 Instruction sourceInst(NoResult, NoType, OpSource);
2844 sourceInst.addImmediateOperand(source);
2845 sourceInst.addImmediateOperand(sourceVersion);
2847 if (fileId != NoResult) {
2848 sourceInst.addIdOperand(fileId);
2850 if (text.size() > 0) {
2852 std::string subString;
2853 while ((int)text.size() - nextByte > 0) {
2854 subString = text.substr(nextByte, nonNullBytesPerInstruction);
2855 if (nextByte == 0) {
2857 sourceInst.addStringOperand(subString.c_str());
2858 sourceInst.dump(out);
2861 Instruction sourceContinuedInst(OpSourceContinued);
2862 sourceContinuedInst.addStringOperand(subString.c_str());
2863 sourceContinuedInst.dump(out);
2865 nextByte += nonNullBytesPerInstruction;
2868 sourceInst.dump(out);
2870 sourceInst.dump(out);
2874 // Dump an OpSource[Continued] sequence for the source and every include file
2875 void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
2877 dumpSourceInstructions(sourceFileStringId, sourceText, out);
2878 for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
2879 dumpSourceInstructions(iItr->first, *iItr->second, out);
2882 void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
2884 for (int i = 0; i < (int)instructions.size(); ++i) {
2885 instructions[i]->dump(out);
2889 void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
2891 for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
2892 Instruction moduleProcessed(OpModuleProcessed);
2893 moduleProcessed.addStringOperand(moduleProcesses[i]);
2894 moduleProcessed.dump(out);
2898 }; // end spv namespace