2 // Copyright (C) 2014-2015 LunarG, Inc.
3 // Copyright (C) 2015-2016 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 // If filename previously seen, use its id, else create a string
118 // and put it in the map.
119 auto sItr = stringIds.find(filename);
120 if (sItr != stringIds.end()) {
121 addLine(sItr->second, currentLine, 0);
123 Instruction* fileString =
124 new Instruction(getUniqueId(), NoType, OpString);
125 fileString->addStringOperand(filename);
126 spv::Id stringId = fileString->getResultId();
127 strings.push_back(std::unique_ptr<Instruction>(fileString));
128 addLine(stringId, currentLine, 0);
129 stringIds[filename] = stringId;
135 void Builder::addLine(Id fileName, int lineNum, int column)
137 Instruction* line = new Instruction(OpLine);
138 line->addIdOperand(fileName);
139 line->addImmediateOperand(lineNum);
140 line->addImmediateOperand(column);
141 buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
144 // For creating new groupedTypes (will return old type if the requested one was already made).
145 Id Builder::makeVoidType()
148 if (groupedTypes[OpTypeVoid].size() == 0) {
149 type = new Instruction(getUniqueId(), NoType, OpTypeVoid);
150 groupedTypes[OpTypeVoid].push_back(type);
151 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
152 module.mapInstruction(type);
154 type = groupedTypes[OpTypeVoid].back();
156 return type->getResultId();
159 Id Builder::makeBoolType()
162 if (groupedTypes[OpTypeBool].size() == 0) {
163 type = new Instruction(getUniqueId(), NoType, OpTypeBool);
164 groupedTypes[OpTypeBool].push_back(type);
165 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
166 module.mapInstruction(type);
168 type = groupedTypes[OpTypeBool].back();
170 return type->getResultId();
173 Id Builder::makeSamplerType()
176 if (groupedTypes[OpTypeSampler].size() == 0) {
177 type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
178 groupedTypes[OpTypeSampler].push_back(type);
179 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
180 module.mapInstruction(type);
182 type = groupedTypes[OpTypeSampler].back();
184 return type->getResultId();
187 Id Builder::makePointer(StorageClass storageClass, Id pointee)
191 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
192 type = groupedTypes[OpTypePointer][t];
193 if (type->getImmediateOperand(0) == (unsigned)storageClass &&
194 type->getIdOperand(1) == pointee)
195 return type->getResultId();
198 // not found, make it
199 type = new Instruction(getUniqueId(), NoType, OpTypePointer);
200 type->addImmediateOperand(storageClass);
201 type->addIdOperand(pointee);
202 groupedTypes[OpTypePointer].push_back(type);
203 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
204 module.mapInstruction(type);
206 return type->getResultId();
209 Id Builder::makeIntegerType(int width, bool hasSign)
213 for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
214 type = groupedTypes[OpTypeInt][t];
215 if (type->getImmediateOperand(0) == (unsigned)width &&
216 type->getImmediateOperand(1) == (hasSign ? 1u : 0u))
217 return type->getResultId();
220 // not found, make it
221 type = new Instruction(getUniqueId(), NoType, OpTypeInt);
222 type->addImmediateOperand(width);
223 type->addImmediateOperand(hasSign ? 1 : 0);
224 groupedTypes[OpTypeInt].push_back(type);
225 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
226 module.mapInstruction(type);
228 // deal with capabilities
232 // these are currently handled by storage-type declarations and post processing
235 addCapability(CapabilityInt64);
241 return type->getResultId();
244 Id Builder::makeFloatType(int width)
248 for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
249 type = groupedTypes[OpTypeFloat][t];
250 if (type->getImmediateOperand(0) == (unsigned)width)
251 return type->getResultId();
254 // not found, make it
255 type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
256 type->addImmediateOperand(width);
257 groupedTypes[OpTypeFloat].push_back(type);
258 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
259 module.mapInstruction(type);
261 // deal with capabilities
264 // currently handled by storage-type declarations and post processing
267 addCapability(CapabilityFloat64);
273 return type->getResultId();
276 // Make a struct without checking for duplication.
277 // See makeStructResultType() for non-decorated structs
278 // needed as the result of some instructions, which does
279 // check for duplicates.
280 Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
282 // Don't look for previous one, because in the general case,
283 // structs can be duplicated except for decorations.
285 // not found, make it
286 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
287 for (int op = 0; op < (int)members.size(); ++op)
288 type->addIdOperand(members[op]);
289 groupedTypes[OpTypeStruct].push_back(type);
290 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
291 module.mapInstruction(type);
292 addName(type->getResultId(), name);
294 return type->getResultId();
297 // Make a struct for the simple results of several instructions,
298 // checking for duplication.
299 Id Builder::makeStructResultType(Id type0, Id type1)
303 for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
304 type = groupedTypes[OpTypeStruct][t];
305 if (type->getNumOperands() != 2)
307 if (type->getIdOperand(0) != type0 ||
308 type->getIdOperand(1) != type1)
310 return type->getResultId();
313 // not found, make it
314 std::vector<spv::Id> members;
315 members.push_back(type0);
316 members.push_back(type1);
318 return makeStructType(members, "ResType");
321 Id Builder::makeVectorType(Id component, int size)
325 for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
326 type = groupedTypes[OpTypeVector][t];
327 if (type->getIdOperand(0) == component &&
328 type->getImmediateOperand(1) == (unsigned)size)
329 return type->getResultId();
332 // not found, make it
333 type = new Instruction(getUniqueId(), NoType, OpTypeVector);
334 type->addIdOperand(component);
335 type->addImmediateOperand(size);
336 groupedTypes[OpTypeVector].push_back(type);
337 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
338 module.mapInstruction(type);
340 return type->getResultId();
343 Id Builder::makeMatrixType(Id component, int cols, int rows)
345 assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
347 Id column = makeVectorType(component, rows);
351 for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
352 type = groupedTypes[OpTypeMatrix][t];
353 if (type->getIdOperand(0) == column &&
354 type->getImmediateOperand(1) == (unsigned)cols)
355 return type->getResultId();
358 // not found, make it
359 type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
360 type->addIdOperand(column);
361 type->addImmediateOperand(cols);
362 groupedTypes[OpTypeMatrix].push_back(type);
363 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
364 module.mapInstruction(type);
366 return type->getResultId();
369 // TODO: performance: track arrays per stride
370 // If a stride is supplied (non-zero) make an array.
371 // If no stride (0), reuse previous array types.
372 // 'size' is an Id of a constant or specialization constant of the array size
373 Id Builder::makeArrayType(Id element, Id sizeId, int stride)
377 // try to find existing type
378 for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
379 type = groupedTypes[OpTypeArray][t];
380 if (type->getIdOperand(0) == element &&
381 type->getIdOperand(1) == sizeId)
382 return type->getResultId();
386 // not found, make it
387 type = new Instruction(getUniqueId(), NoType, OpTypeArray);
388 type->addIdOperand(element);
389 type->addIdOperand(sizeId);
390 groupedTypes[OpTypeArray].push_back(type);
391 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
392 module.mapInstruction(type);
394 return type->getResultId();
397 Id Builder::makeRuntimeArray(Id element)
399 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
400 type->addIdOperand(element);
401 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
402 module.mapInstruction(type);
404 return type->getResultId();
407 Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
411 for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
412 type = groupedTypes[OpTypeFunction][t];
413 if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
415 bool mismatch = false;
416 for (int p = 0; p < (int)paramTypes.size(); ++p) {
417 if (paramTypes[p] != type->getIdOperand(p + 1)) {
423 return type->getResultId();
426 // not found, make it
427 type = new Instruction(getUniqueId(), NoType, OpTypeFunction);
428 type->addIdOperand(returnType);
429 for (int p = 0; p < (int)paramTypes.size(); ++p)
430 type->addIdOperand(paramTypes[p]);
431 groupedTypes[OpTypeFunction].push_back(type);
432 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
433 module.mapInstruction(type);
435 return type->getResultId();
438 Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format)
440 assert(sampled == 1 || sampled == 2);
444 for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
445 type = groupedTypes[OpTypeImage][t];
446 if (type->getIdOperand(0) == sampledType &&
447 type->getImmediateOperand(1) == (unsigned int)dim &&
448 type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
449 type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
450 type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
451 type->getImmediateOperand(5) == sampled &&
452 type->getImmediateOperand(6) == (unsigned int)format)
453 return type->getResultId();
456 // not found, make it
457 type = new Instruction(getUniqueId(), NoType, OpTypeImage);
458 type->addIdOperand(sampledType);
459 type->addImmediateOperand( dim);
460 type->addImmediateOperand( depth ? 1 : 0);
461 type->addImmediateOperand(arrayed ? 1 : 0);
462 type->addImmediateOperand( ms ? 1 : 0);
463 type->addImmediateOperand(sampled);
464 type->addImmediateOperand((unsigned int)format);
466 groupedTypes[OpTypeImage].push_back(type);
467 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
468 module.mapInstruction(type);
470 // deal with capabilities
474 addCapability(CapabilitySampledBuffer);
476 addCapability(CapabilityImageBuffer);
480 addCapability(CapabilitySampled1D);
482 addCapability(CapabilityImage1D);
487 addCapability(CapabilitySampledCubeArray);
489 addCapability(CapabilityImageCubeArray);
494 addCapability(CapabilitySampledRect);
496 addCapability(CapabilityImageRect);
499 addCapability(CapabilityInputAttachment);
507 // Images used with subpass data are not storage
508 // images, so don't require the capability for them.
509 if (dim != Dim::DimSubpassData)
510 addCapability(CapabilityStorageImageMultisample);
512 addCapability(CapabilityImageMSArray);
516 return type->getResultId();
519 Id Builder::makeSampledImageType(Id imageType)
523 for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
524 type = groupedTypes[OpTypeSampledImage][t];
525 if (type->getIdOperand(0) == imageType)
526 return type->getResultId();
529 // not found, make it
530 type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
531 type->addIdOperand(imageType);
533 groupedTypes[OpTypeSampledImage].push_back(type);
534 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
535 module.mapInstruction(type);
537 return type->getResultId();
541 Id Builder::makeAccelerationStructureNVType()
544 if (groupedTypes[OpTypeAccelerationStructureNV].size() == 0) {
545 type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureNV);
546 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
547 module.mapInstruction(type);
549 type = groupedTypes[OpTypeAccelerationStructureNV].back();
552 return type->getResultId();
555 Id Builder::getDerefTypeId(Id resultId) const
557 Id typeId = getTypeId(resultId);
558 assert(isPointerType(typeId));
560 return module.getInstruction(typeId)->getIdOperand(1);
563 Op Builder::getMostBasicTypeClass(Id typeId) const
565 Instruction* instr = module.getInstruction(typeId);
567 Op typeClass = instr->getOpCode();
573 case OpTypeRuntimeArray:
574 return getMostBasicTypeClass(instr->getIdOperand(0));
576 return getMostBasicTypeClass(instr->getIdOperand(1));
582 int Builder::getNumTypeConstituents(Id typeId) const
584 Instruction* instr = module.getInstruction(typeId);
586 switch (instr->getOpCode())
594 return instr->getImmediateOperand(1);
597 Id lengthId = instr->getIdOperand(1);
598 return module.getInstruction(lengthId)->getImmediateOperand(0);
601 return instr->getNumOperands();
608 // Return the lowest-level type of scalar that an homogeneous composite is made out of.
609 // Typically, this is just to find out if something is made out of ints or floats.
610 // However, it includes returning a structure, if say, it is an array of structure.
611 Id Builder::getScalarTypeId(Id typeId) const
613 Instruction* instr = module.getInstruction(typeId);
615 Op typeClass = instr->getOpCode();
623 return instr->getResultId();
627 case OpTypeRuntimeArray:
629 return getScalarTypeId(getContainedTypeId(typeId));
636 // Return the type of 'member' of a composite.
637 Id Builder::getContainedTypeId(Id typeId, int member) const
639 Instruction* instr = module.getInstruction(typeId);
641 Op typeClass = instr->getOpCode();
647 case OpTypeRuntimeArray:
648 return instr->getIdOperand(0);
650 return instr->getIdOperand(1);
652 return instr->getIdOperand(member);
659 // Return the immediately contained type of a given composite type.
660 Id Builder::getContainedTypeId(Id typeId) const
662 return getContainedTypeId(typeId, 0);
665 // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
666 // of width 'width'. The 'width' is only consumed for int and float types.
667 // Returns false otherwise.
668 bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
670 const Instruction& instr = *module.getInstruction(typeId);
672 Op typeClass = instr.getOpCode();
677 return typeClass == typeOp && instr.getImmediateOperand(0) == width;
679 for (int m = 0; m < instr.getNumOperands(); ++m) {
680 if (containsType(instr.getIdOperand(m), typeOp, width))
687 case OpTypeRuntimeArray:
689 return containsType(getContainedTypeId(typeId), typeOp, width);
691 return typeClass == typeOp;
695 // See if a scalar constant of this type has already been created, so it
696 // can be reused rather than duplicated. (Required by the specification).
697 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
699 Instruction* constant;
700 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
701 constant = groupedConstants[typeClass][i];
702 if (constant->getOpCode() == opcode &&
703 constant->getTypeId() == typeId &&
704 constant->getImmediateOperand(0) == value)
705 return constant->getResultId();
711 // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
712 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
714 Instruction* constant;
715 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
716 constant = groupedConstants[typeClass][i];
717 if (constant->getOpCode() == opcode &&
718 constant->getTypeId() == typeId &&
719 constant->getImmediateOperand(0) == v1 &&
720 constant->getImmediateOperand(1) == v2)
721 return constant->getResultId();
727 // Return true if consuming 'opcode' means consuming a constant.
728 // "constant" here means after final transform to executable code,
729 // the value consumed will be a constant, so includes specialization.
730 bool Builder::isConstantOpCode(Op opcode) const
735 case OpConstantFalse:
737 case OpConstantComposite:
738 case OpConstantSampler:
740 case OpSpecConstantTrue:
741 case OpSpecConstantFalse:
743 case OpSpecConstantComposite:
744 case OpSpecConstantOp:
751 // Return true if consuming 'opcode' means consuming a specialization constant.
752 bool Builder::isSpecConstantOpCode(Op opcode) const
755 case OpSpecConstantTrue:
756 case OpSpecConstantFalse:
758 case OpSpecConstantComposite:
759 case OpSpecConstantOp:
766 Id Builder::makeBoolConstant(bool b, bool specConstant)
768 Id typeId = makeBoolType();
769 Instruction* constant;
770 Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
772 // See if we already made it. Applies only to regular constants, because specialization constants
773 // must remain distinct for the purpose of applying a SpecId decoration.
774 if (! specConstant) {
776 for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
777 constant = groupedConstants[OpTypeBool][i];
778 if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
779 existing = constant->getResultId();
787 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
788 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
789 groupedConstants[OpTypeBool].push_back(c);
790 module.mapInstruction(c);
792 return c->getResultId();
795 Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
797 Op opcode = specConstant ? OpSpecConstant : OpConstant;
799 // See if we already made it. Applies only to regular constants, because specialization constants
800 // must remain distinct for the purpose of applying a SpecId decoration.
801 if (! specConstant) {
802 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
807 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
808 c->addImmediateOperand(value);
809 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
810 groupedConstants[OpTypeInt].push_back(c);
811 module.mapInstruction(c);
813 return c->getResultId();
816 Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
818 Op opcode = specConstant ? OpSpecConstant : OpConstant;
820 unsigned op1 = value & 0xFFFFFFFF;
821 unsigned op2 = value >> 32;
823 // See if we already made it. Applies only to regular constants, because specialization constants
824 // must remain distinct for the purpose of applying a SpecId decoration.
825 if (! specConstant) {
826 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
831 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
832 c->addImmediateOperand(op1);
833 c->addImmediateOperand(op2);
834 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
835 groupedConstants[OpTypeInt].push_back(c);
836 module.mapInstruction(c);
838 return c->getResultId();
841 Id Builder::makeFloatConstant(float f, bool specConstant)
843 Op opcode = specConstant ? OpSpecConstant : OpConstant;
844 Id typeId = makeFloatType(32);
845 union { float fl; unsigned int ui; } u;
847 unsigned value = u.ui;
849 // See if we already made it. Applies only to regular constants, because specialization constants
850 // must remain distinct for the purpose of applying a SpecId decoration.
851 if (! specConstant) {
852 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
857 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
858 c->addImmediateOperand(value);
859 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
860 groupedConstants[OpTypeFloat].push_back(c);
861 module.mapInstruction(c);
863 return c->getResultId();
866 Id Builder::makeDoubleConstant(double d, bool specConstant)
868 Op opcode = specConstant ? OpSpecConstant : OpConstant;
869 Id typeId = makeFloatType(64);
870 union { double db; unsigned long long ull; } u;
872 unsigned long long value = u.ull;
873 unsigned op1 = value & 0xFFFFFFFF;
874 unsigned op2 = value >> 32;
876 // See if we already made it. Applies only to regular constants, because specialization constants
877 // must remain distinct for the purpose of applying a SpecId decoration.
878 if (! specConstant) {
879 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
884 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
885 c->addImmediateOperand(op1);
886 c->addImmediateOperand(op2);
887 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
888 groupedConstants[OpTypeFloat].push_back(c);
889 module.mapInstruction(c);
891 return c->getResultId();
894 Id Builder::makeFloat16Constant(float f16, bool specConstant)
896 Op opcode = specConstant ? OpSpecConstant : OpConstant;
897 Id typeId = makeFloatType(16);
899 spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
900 spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
901 fVal.castTo(f16Val, spvutils::kRoundToZero);
903 unsigned value = f16Val.value().getAsFloat().get_value();
905 // See if we already made it. Applies only to regular constants, because specialization constants
906 // must remain distinct for the purpose of applying a SpecId decoration.
908 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
913 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
914 c->addImmediateOperand(value);
915 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
916 groupedConstants[OpTypeFloat].push_back(c);
917 module.mapInstruction(c);
919 return c->getResultId();
922 Id Builder::makeFpConstant(Id type, double d, bool specConstant)
924 assert(isFloatType(type));
926 switch (getScalarTypeWidth(type)) {
928 return makeFloat16Constant((float)d, specConstant);
930 return makeFloatConstant((float)d, specConstant);
932 return makeDoubleConstant(d, specConstant);
941 Id Builder::findCompositeConstant(Op typeClass, const std::vector<Id>& comps)
943 Instruction* constant = 0;
945 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
946 constant = groupedConstants[typeClass][i];
949 if (constant->getNumOperands() != (int)comps.size())
953 bool mismatch = false;
954 for (int op = 0; op < constant->getNumOperands(); ++op) {
955 if (constant->getIdOperand(op) != comps[op]) {
966 return found ? constant->getResultId() : NoResult;
969 Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
971 Instruction* constant = 0;
973 for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
974 constant = groupedStructConstants[typeId][i];
977 bool mismatch = false;
978 for (int op = 0; op < constant->getNumOperands(); ++op) {
979 if (constant->getIdOperand(op) != comps[op]) {
990 return found ? constant->getResultId() : NoResult;
993 // Comments in header
994 Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
996 Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
998 Op typeClass = getTypeClass(typeId);
1000 switch (typeClass) {
1004 if (! specConstant) {
1005 Id existing = findCompositeConstant(typeClass, members);
1011 if (! specConstant) {
1012 Id existing = findStructConstant(typeId, members);
1019 return makeFloatConstant(0.0);
1022 Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
1023 for (int op = 0; op < (int)members.size(); ++op)
1024 c->addIdOperand(members[op]);
1025 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1026 if (typeClass == OpTypeStruct)
1027 groupedStructConstants[typeId].push_back(c);
1029 groupedConstants[typeClass].push_back(c);
1030 module.mapInstruction(c);
1032 return c->getResultId();
1035 Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
1037 Instruction* entryPoint = new Instruction(OpEntryPoint);
1038 entryPoint->addImmediateOperand(model);
1039 entryPoint->addIdOperand(function->getId());
1040 entryPoint->addStringOperand(name);
1042 entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
1047 // Currently relying on the fact that all 'value' of interest are small non-negative values.
1048 void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
1050 Instruction* instr = new Instruction(OpExecutionMode);
1051 instr->addIdOperand(entryPoint->getId());
1052 instr->addImmediateOperand(mode);
1054 instr->addImmediateOperand(value1);
1056 instr->addImmediateOperand(value2);
1058 instr->addImmediateOperand(value3);
1060 executionModes.push_back(std::unique_ptr<Instruction>(instr));
1063 void Builder::addName(Id id, const char* string)
1065 Instruction* name = new Instruction(OpName);
1066 name->addIdOperand(id);
1067 name->addStringOperand(string);
1069 names.push_back(std::unique_ptr<Instruction>(name));
1072 void Builder::addMemberName(Id id, int memberNumber, const char* string)
1074 Instruction* name = new Instruction(OpMemberName);
1075 name->addIdOperand(id);
1076 name->addImmediateOperand(memberNumber);
1077 name->addStringOperand(string);
1079 names.push_back(std::unique_ptr<Instruction>(name));
1082 void Builder::addDecoration(Id id, Decoration decoration, int num)
1084 if (decoration == spv::DecorationMax)
1087 Instruction* dec = new Instruction(OpDecorate);
1088 dec->addIdOperand(id);
1089 dec->addImmediateOperand(decoration);
1091 dec->addImmediateOperand(num);
1093 decorations.push_back(std::unique_ptr<Instruction>(dec));
1096 void Builder::addDecoration(Id id, Decoration decoration, const char* s)
1098 if (decoration == spv::DecorationMax)
1101 Instruction* dec = new Instruction(OpDecorateStringGOOGLE);
1102 dec->addIdOperand(id);
1103 dec->addImmediateOperand(decoration);
1104 dec->addStringOperand(s);
1106 decorations.push_back(std::unique_ptr<Instruction>(dec));
1109 void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
1111 if (decoration == spv::DecorationMax)
1114 Instruction* dec = new Instruction(OpDecorateId);
1115 dec->addIdOperand(id);
1116 dec->addImmediateOperand(decoration);
1117 dec->addIdOperand(idDecoration);
1119 decorations.push_back(std::unique_ptr<Instruction>(dec));
1122 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
1124 if (decoration == spv::DecorationMax)
1127 Instruction* dec = new Instruction(OpMemberDecorate);
1128 dec->addIdOperand(id);
1129 dec->addImmediateOperand(member);
1130 dec->addImmediateOperand(decoration);
1132 dec->addImmediateOperand(num);
1134 decorations.push_back(std::unique_ptr<Instruction>(dec));
1137 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
1139 if (decoration == spv::DecorationMax)
1142 Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
1143 dec->addIdOperand(id);
1144 dec->addImmediateOperand(member);
1145 dec->addImmediateOperand(decoration);
1146 dec->addStringOperand(s);
1148 decorations.push_back(std::unique_ptr<Instruction>(dec));
1151 // Comments in header
1152 Function* Builder::makeEntryPoint(const char* entryPoint)
1154 assert(! entryPointFunction);
1157 std::vector<Id> params;
1158 std::vector<std::vector<Decoration>> decorations;
1160 entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry);
1162 return entryPointFunction;
1165 // Comments in header
1166 Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
1167 const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& decorations, Block **entry)
1169 // Make the function and initial instructions in it
1170 Id typeId = makeFunctionType(returnType, paramTypes);
1171 Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
1172 Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
1174 // Set up the precisions
1175 setPrecision(function->getId(), precision);
1176 for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
1177 for (int d = 0; d < (int)decorations[p].size(); ++d)
1178 addDecoration(firstParamId + p, decorations[p][d]);
1183 *entry = new Block(getUniqueId(), *function);
1184 function->addBlock(*entry);
1185 setBuildPoint(*entry);
1189 addName(function->getId(), name);
1191 functions.push_back(std::unique_ptr<Function>(function));
1196 // Comments in header
1197 void Builder::makeReturn(bool implicit, Id retVal)
1200 Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
1201 inst->addIdOperand(retVal);
1202 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1204 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
1207 createAndSetNoPredecessorBlock("post-return");
1210 // Comments in header
1211 void Builder::leaveFunction()
1213 Block* block = buildPoint;
1214 Function& function = buildPoint->getParent();
1217 // If our function did not contain a return, add a return void now.
1218 if (! block->isTerminated()) {
1219 if (function.getReturnType() == makeVoidType())
1222 makeReturn(true, createUndefined(function.getReturnType()));
1227 // Comments in header
1228 void Builder::makeDiscard()
1230 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill)));
1231 createAndSetNoPredecessorBlock("post-discard");
1234 // Comments in header
1235 Id Builder::createVariable(StorageClass storageClass, Id type, const char* name)
1237 Id pointerType = makePointer(storageClass, type);
1238 Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
1239 inst->addImmediateOperand(storageClass);
1241 switch (storageClass) {
1242 case StorageClassFunction:
1243 // Validation rules require the declaration in the entry block
1244 buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
1248 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
1249 module.mapInstruction(inst);
1254 addName(inst->getResultId(), name);
1256 return inst->getResultId();
1259 // Comments in header
1260 Id Builder::createUndefined(Id type)
1262 Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
1263 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1264 return inst->getResultId();
1267 // Comments in header
1268 void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
1270 Instruction* store = new Instruction(OpStore);
1271 store->addIdOperand(lValue);
1272 store->addIdOperand(rValue);
1274 if (memoryAccess != MemoryAccessMaskNone) {
1275 store->addImmediateOperand(memoryAccess);
1276 if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
1277 store->addIdOperand(makeUintConstant(scope));
1281 buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
1284 // Comments in header
1285 Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
1287 Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
1288 load->addIdOperand(lValue);
1290 if (memoryAccess != MemoryAccessMaskNone) {
1291 load->addImmediateOperand(memoryAccess);
1292 if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
1293 load->addIdOperand(makeUintConstant(scope));
1297 buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
1299 return load->getResultId();
1302 // Comments in header
1303 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
1305 // Figure out the final resulting type.
1306 spv::Id typeId = getTypeId(base);
1307 assert(isPointerType(typeId) && offsets.size() > 0);
1308 typeId = getContainedTypeId(typeId);
1309 for (int i = 0; i < (int)offsets.size(); ++i) {
1310 if (isStructType(typeId)) {
1311 assert(isConstantScalar(offsets[i]));
1312 typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i]));
1314 typeId = getContainedTypeId(typeId, offsets[i]);
1316 typeId = makePointer(storageClass, typeId);
1318 // Make the instruction
1319 Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
1320 chain->addIdOperand(base);
1321 for (int i = 0; i < (int)offsets.size(); ++i)
1322 chain->addIdOperand(offsets[i]);
1323 buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
1325 return chain->getResultId();
1328 Id Builder::createArrayLength(Id base, unsigned int member)
1330 spv::Id intType = makeUintType(32);
1331 Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
1332 length->addIdOperand(base);
1333 length->addImmediateOperand(member);
1334 buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
1336 return length->getResultId();
1339 Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
1341 // Generate code for spec constants if in spec constant operation
1343 if (generatingOpCodeForSpecConst) {
1344 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), std::vector<Id>(1, index));
1346 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1347 extract->addIdOperand(composite);
1348 extract->addImmediateOperand(index);
1349 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1351 return extract->getResultId();
1354 Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
1356 // Generate code for spec constants if in spec constant operation
1358 if (generatingOpCodeForSpecConst) {
1359 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
1361 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
1362 extract->addIdOperand(composite);
1363 for (int i = 0; i < (int)indexes.size(); ++i)
1364 extract->addImmediateOperand(indexes[i]);
1365 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1367 return extract->getResultId();
1370 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
1372 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1373 insert->addIdOperand(object);
1374 insert->addIdOperand(composite);
1375 insert->addImmediateOperand(index);
1376 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1378 return insert->getResultId();
1381 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
1383 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
1384 insert->addIdOperand(object);
1385 insert->addIdOperand(composite);
1386 for (int i = 0; i < (int)indexes.size(); ++i)
1387 insert->addImmediateOperand(indexes[i]);
1388 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1390 return insert->getResultId();
1393 Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
1395 Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
1396 extract->addIdOperand(vector);
1397 extract->addIdOperand(componentIndex);
1398 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
1400 return extract->getResultId();
1403 Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
1405 Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
1406 insert->addIdOperand(vector);
1407 insert->addIdOperand(component);
1408 insert->addIdOperand(componentIndex);
1409 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
1411 return insert->getResultId();
1414 // An opcode that has no operands, no result id, and no type
1415 void Builder::createNoResultOp(Op opCode)
1417 Instruction* op = new Instruction(opCode);
1418 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1421 // An opcode that has one id operand, no result id, and no type
1422 void Builder::createNoResultOp(Op opCode, Id operand)
1424 Instruction* op = new Instruction(opCode);
1425 op->addIdOperand(operand);
1426 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1429 // An opcode that has one or more operands, no result id, and no type
1430 void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
1432 Instruction* op = new Instruction(opCode);
1433 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1434 op->addIdOperand(*it);
1436 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1439 // An opcode that has multiple operands, no result id, and no type
1440 void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
1442 Instruction* op = new Instruction(opCode);
1443 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1445 op->addIdOperand(it->word);
1447 op->addImmediateOperand(it->word);
1449 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1452 void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
1454 Instruction* op = new Instruction(OpControlBarrier);
1455 op->addIdOperand(makeUintConstant(execution));
1456 op->addIdOperand(makeUintConstant(memory));
1457 op->addIdOperand(makeUintConstant(semantics));
1458 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1461 void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
1463 Instruction* op = new Instruction(OpMemoryBarrier);
1464 op->addIdOperand(makeUintConstant(executionScope));
1465 op->addIdOperand(makeUintConstant(memorySemantics));
1466 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1469 // An opcode that has one operands, a result id, and a type
1470 Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
1472 // Generate code for spec constants if in spec constant operation
1474 if (generatingOpCodeForSpecConst) {
1475 return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
1477 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1478 op->addIdOperand(operand);
1479 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1481 return op->getResultId();
1484 Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
1486 // Generate code for spec constants if in spec constant operation
1488 if (generatingOpCodeForSpecConst) {
1489 std::vector<Id> operands(2);
1490 operands[0] = left; operands[1] = right;
1491 return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
1493 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1494 op->addIdOperand(left);
1495 op->addIdOperand(right);
1496 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1498 return op->getResultId();
1501 Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
1503 // Generate code for spec constants if in spec constant operation
1505 if (generatingOpCodeForSpecConst) {
1506 std::vector<Id> operands(3);
1510 return createSpecConstantOp(
1511 opCode, typeId, operands, std::vector<Id>());
1513 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1514 op->addIdOperand(op1);
1515 op->addIdOperand(op2);
1516 op->addIdOperand(op3);
1517 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1519 return op->getResultId();
1522 Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
1524 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1525 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1526 op->addIdOperand(*it);
1527 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1529 return op->getResultId();
1532 Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
1534 Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
1535 for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
1537 op->addIdOperand(it->word);
1539 op->addImmediateOperand(it->word);
1541 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1543 return op->getResultId();
1546 Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& literals)
1548 Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
1549 op->addImmediateOperand((unsigned) opCode);
1550 for (auto it = operands.cbegin(); it != operands.cend(); ++it)
1551 op->addIdOperand(*it);
1552 for (auto it = literals.cbegin(); it != literals.cend(); ++it)
1553 op->addImmediateOperand(*it);
1554 module.mapInstruction(op);
1555 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
1557 return op->getResultId();
1560 Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
1562 Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
1563 op->addIdOperand(function->getId());
1564 for (int a = 0; a < (int)args.size(); ++a)
1565 op->addIdOperand(args[a]);
1566 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
1568 return op->getResultId();
1571 // Comments in header
1572 Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
1574 if (channels.size() == 1)
1575 return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
1577 if (generatingOpCodeForSpecConst) {
1578 std::vector<Id> operands(2);
1579 operands[0] = operands[1] = source;
1580 return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
1582 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1583 assert(isVector(source));
1584 swizzle->addIdOperand(source);
1585 swizzle->addIdOperand(source);
1586 for (int i = 0; i < (int)channels.size(); ++i)
1587 swizzle->addImmediateOperand(channels[i]);
1588 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1590 return setPrecision(swizzle->getResultId(), precision);
1593 // Comments in header
1594 Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
1596 if (channels.size() == 1 && getNumComponents(source) == 1)
1597 return createCompositeInsert(source, target, typeId, channels.front());
1599 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
1601 assert(isVector(target));
1602 swizzle->addIdOperand(target);
1604 assert(getNumComponents(source) == (int)channels.size());
1605 assert(isVector(source));
1606 swizzle->addIdOperand(source);
1608 // Set up an identity shuffle from the base value to the result value
1609 unsigned int components[4];
1610 int numTargetComponents = getNumComponents(target);
1611 for (int i = 0; i < numTargetComponents; ++i)
1614 // Punch in the l-value swizzle
1615 for (int i = 0; i < (int)channels.size(); ++i)
1616 components[channels[i]] = numTargetComponents + i;
1618 // finish the instruction with these components selectors
1619 for (int i = 0; i < numTargetComponents; ++i)
1620 swizzle->addImmediateOperand(components[i]);
1621 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
1623 return swizzle->getResultId();
1626 // Comments in header
1627 void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
1629 int direction = getNumComponents(right) - getNumComponents(left);
1632 left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
1633 else if (direction < 0)
1634 right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
1639 // Comments in header
1640 Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
1642 assert(getNumComponents(scalar) == 1);
1643 assert(getTypeId(scalar) == getScalarTypeId(vectorType));
1645 int numComponents = getNumTypeComponents(vectorType);
1646 if (numComponents == 1)
1649 Instruction* smear = nullptr;
1650 if (generatingOpCodeForSpecConst) {
1651 auto members = std::vector<spv::Id>(numComponents, scalar);
1652 // Sometime even in spec-constant-op mode, the temporary vector created by
1653 // promoting a scalar might not be a spec constant. This should depend on
1656 // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
1657 // In such cases, the temporary vector created from a_front_end_const_scalar
1658 // is not a spec constant vector, even though the binary operation node is marked
1659 // as 'specConstant' and we are in spec-constant-op mode.
1660 auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
1661 smear = module.getInstruction(result_id);
1663 smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
1664 for (int c = 0; c < numComponents; ++c)
1665 smear->addIdOperand(scalar);
1666 buildPoint->addInstruction(std::unique_ptr<Instruction>(smear));
1669 return setPrecision(smear->getResultId(), precision);
1672 // Comments in header
1673 Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
1675 Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
1676 inst->addIdOperand(builtins);
1677 inst->addImmediateOperand(entryPoint);
1678 for (int arg = 0; arg < (int)args.size(); ++arg)
1679 inst->addIdOperand(args[arg]);
1681 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
1683 return inst->getResultId();
1686 // Accept all parameters needed to create a texture instruction.
1687 // Create the correct instruction based on the inputs, and make the call.
1688 Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
1689 bool noImplicitLod, const TextureParameters& parameters)
1691 static const int maxTextureArgs = 10;
1692 Id texArgs[maxTextureArgs] = {};
1695 // Set up the fixed arguments
1698 bool explicitLod = false;
1699 texArgs[numArgs++] = parameters.sampler;
1700 texArgs[numArgs++] = parameters.coords;
1701 if (parameters.Dref != NoResult)
1702 texArgs[numArgs++] = parameters.Dref;
1703 if (parameters.component != NoResult)
1704 texArgs[numArgs++] = parameters.component;
1706 #ifdef NV_EXTENSIONS
1707 if (parameters.granularity != NoResult)
1708 texArgs[numArgs++] = parameters.granularity;
1709 if (parameters.coarse != NoResult)
1710 texArgs[numArgs++] = parameters.coarse;
1714 // Set up the optional arguments
1716 int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
1717 ++numArgs; // speculatively make room for the mask operand
1718 ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
1719 if (parameters.bias) {
1720 mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
1721 texArgs[numArgs++] = parameters.bias;
1723 if (parameters.lod) {
1724 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
1725 texArgs[numArgs++] = parameters.lod;
1727 } else if (parameters.gradX) {
1728 mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
1729 texArgs[numArgs++] = parameters.gradX;
1730 texArgs[numArgs++] = parameters.gradY;
1732 } else if (noImplicitLod && ! fetch && ! gather) {
1733 // have to explicitly use lod of 0 if not allowed to have them be implicit, and
1734 // we would otherwise be about to issue an implicit instruction
1735 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
1736 texArgs[numArgs++] = makeFloatConstant(0.0);
1739 if (parameters.offset) {
1740 if (isConstant(parameters.offset))
1741 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
1743 addCapability(CapabilityImageGatherExtended);
1744 mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
1746 texArgs[numArgs++] = parameters.offset;
1748 if (parameters.offsets) {
1749 addCapability(CapabilityImageGatherExtended);
1750 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
1751 texArgs[numArgs++] = parameters.offsets;
1753 if (parameters.sample) {
1754 mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
1755 texArgs[numArgs++] = parameters.sample;
1757 if (parameters.lodClamp) {
1758 // capability if this bit is used
1759 addCapability(CapabilityMinLod);
1761 mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
1762 texArgs[numArgs++] = parameters.lodClamp;
1764 if (parameters.nonprivate) {
1765 mask = mask | ImageOperandsNonPrivateTexelKHRMask;
1767 if (parameters.volatil) {
1768 mask = mask | ImageOperandsVolatileTexelKHRMask;
1770 if (mask == ImageOperandsMaskNone)
1771 --numArgs; // undo speculative reservation for the mask argument
1773 texArgs[optArgNum] = mask;
1776 // Set up the instruction
1778 Op opCode = OpNop; // All paths below need to set this
1781 opCode = OpImageSparseFetch;
1783 opCode = OpImageFetch;
1784 #ifdef NV_EXTENSIONS
1785 } else if (parameters.granularity && parameters.coarse) {
1786 opCode = OpImageSampleFootprintNV;
1788 } else if (gather) {
1789 if (parameters.Dref)
1791 opCode = OpImageSparseDrefGather;
1793 opCode = OpImageDrefGather;
1796 opCode = OpImageSparseGather;
1798 opCode = OpImageGather;
1799 } else if (explicitLod) {
1800 if (parameters.Dref) {
1803 opCode = OpImageSparseSampleProjDrefExplicitLod;
1805 opCode = OpImageSampleProjDrefExplicitLod;
1808 opCode = OpImageSparseSampleDrefExplicitLod;
1810 opCode = OpImageSampleDrefExplicitLod;
1814 opCode = OpImageSparseSampleProjExplicitLod;
1816 opCode = OpImageSampleProjExplicitLod;
1819 opCode = OpImageSparseSampleExplicitLod;
1821 opCode = OpImageSampleExplicitLod;
1824 if (parameters.Dref) {
1827 opCode = OpImageSparseSampleProjDrefImplicitLod;
1829 opCode = OpImageSampleProjDrefImplicitLod;
1832 opCode = OpImageSparseSampleDrefImplicitLod;
1834 opCode = OpImageSampleDrefImplicitLod;
1838 opCode = OpImageSparseSampleProjImplicitLod;
1840 opCode = OpImageSampleProjImplicitLod;
1843 opCode = OpImageSparseSampleImplicitLod;
1845 opCode = OpImageSampleImplicitLod;
1849 // See if the result type is expecting a smeared result.
1850 // This happens when a legacy shadow*() call is made, which
1851 // gets a vec4 back instead of a float.
1852 Id smearedType = resultType;
1853 if (! isScalarType(resultType)) {
1855 case OpImageSampleDrefImplicitLod:
1856 case OpImageSampleDrefExplicitLod:
1857 case OpImageSampleProjDrefImplicitLod:
1858 case OpImageSampleProjDrefExplicitLod:
1859 resultType = getScalarTypeId(resultType);
1870 typeId0 = resultType;
1871 typeId1 = getDerefTypeId(parameters.texelOut);
1872 resultType = makeStructResultType(typeId0, typeId1);
1875 // Build the SPIR-V instruction
1876 Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
1877 for (int op = 0; op < optArgNum; ++op)
1878 textureInst->addIdOperand(texArgs[op]);
1879 if (optArgNum < numArgs)
1880 textureInst->addImmediateOperand(texArgs[optArgNum]);
1881 for (int op = optArgNum + 1; op < numArgs; ++op)
1882 textureInst->addIdOperand(texArgs[op]);
1883 setPrecision(textureInst->getResultId(), precision);
1884 buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst));
1886 Id resultId = textureInst->getResultId();
1890 addCapability(CapabilitySparseResidency);
1892 // Decode the return type that was a special structure
1893 createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
1894 resultId = createCompositeExtract(resultId, typeId0, 0);
1895 setPrecision(resultId, precision);
1897 // When a smear is needed, do it, as per what was computed
1898 // above when resultType was changed to a scalar type.
1899 if (resultType != smearedType)
1900 resultId = smearScalar(precision, resultId, smearedType);
1906 // Comments in header
1907 Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
1909 // Figure out the result type
1912 case OpImageQuerySize:
1913 case OpImageQuerySizeLod:
1915 int numComponents = 0;
1916 switch (getTypeDimensionality(getImageType(parameters.sampler))) {
1924 case DimSubpassData:
1935 if (isArrayedImageType(getImageType(parameters.sampler)))
1938 Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
1939 if (numComponents == 1)
1940 resultType = intType;
1942 resultType = makeVectorType(intType, numComponents);
1946 case OpImageQueryLod:
1947 #ifdef AMD_EXTENSIONS
1948 resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
1950 resultType = makeVectorType(makeFloatType(32), 2);
1953 case OpImageQueryLevels:
1954 case OpImageQuerySamples:
1955 resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
1962 Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
1963 query->addIdOperand(parameters.sampler);
1964 if (parameters.coords)
1965 query->addIdOperand(parameters.coords);
1967 query->addIdOperand(parameters.lod);
1968 buildPoint->addInstruction(std::unique_ptr<Instruction>(query));
1970 return query->getResultId();
1973 // External comments in header.
1974 // Operates recursively to visit the composite's hierarchy.
1975 Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
1977 Id boolType = makeBoolType();
1978 Id valueType = getTypeId(value1);
1980 Id resultId = NoResult;
1982 int numConstituents = getNumTypeConstituents(valueType);
1984 // Scalars and Vectors
1986 if (isScalarType(valueType) || isVectorType(valueType)) {
1987 assert(valueType == getTypeId(value2));
1988 // These just need a single comparison, just have
1989 // to figure out what it is.
1991 switch (getMostBasicTypeClass(valueType)) {
1993 op = equal ? OpFOrdEqual : OpFOrdNotEqual;
1997 op = equal ? OpIEqual : OpINotEqual;
2000 op = equal ? OpLogicalEqual : OpLogicalNotEqual;
2001 precision = NoPrecision;
2005 if (isScalarType(valueType)) {
2007 resultId = createBinOp(op, boolType, value1, value2);
2010 resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
2011 setPrecision(resultId, precision);
2012 // reduce vector compares...
2013 resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
2016 return setPrecision(resultId, precision);
2019 // Only structs, arrays, and matrices should be left.
2020 // They share in common the reduction operation across their constituents.
2021 assert(isAggregateType(valueType) || isMatrixType(valueType));
2023 // Compare each pair of constituents
2024 for (int constituent = 0; constituent < numConstituents; ++constituent) {
2025 std::vector<unsigned> indexes(1, constituent);
2026 Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
2027 Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
2028 Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
2029 Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
2031 Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
2033 if (constituent == 0)
2034 resultId = subResultId;
2036 resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), precision);
2042 // OpCompositeConstruct
2043 Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
2045 assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size()));
2047 if (generatingOpCodeForSpecConst) {
2048 // Sometime, even in spec-constant-op mode, the constant composite to be
2049 // constructed may not be a specialization constant.
2051 // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
2052 // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
2053 // The second column vector should NOT be spec constant, as it does not contain any spec constants.
2054 // To handle such cases, we check the constituents of the constant vector to determine whether this
2055 // vector should be created as a spec constant.
2056 return makeCompositeConstant(typeId, constituents,
2057 std::any_of(constituents.begin(), constituents.end(),
2058 [&](spv::Id id) { return isSpecConstant(id); }));
2061 Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
2062 for (int c = 0; c < (int)constituents.size(); ++c)
2063 op->addIdOperand(constituents[c]);
2064 buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
2066 return op->getResultId();
2069 // Vector or scalar constructor
2070 Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2072 Id result = NoResult;
2073 unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
2074 unsigned int targetComponent = 0;
2076 // Special case: when calling a vector constructor with a single scalar
2077 // argument, smear the scalar
2078 if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
2079 return smearScalar(precision, sources[0], resultTypeId);
2081 // accumulate the arguments for OpCompositeConstruct
2082 std::vector<Id> constituents;
2083 Id scalarTypeId = getScalarTypeId(resultTypeId);
2085 // lambda to store the result of visiting an argument component
2086 const auto latchResult = [&](Id comp) {
2087 if (numTargetComponents > 1)
2088 constituents.push_back(comp);
2094 // lambda to visit a vector argument's components
2095 const auto accumulateVectorConstituents = [&](Id sourceArg) {
2096 unsigned int sourceSize = getNumComponents(sourceArg);
2097 unsigned int sourcesToUse = sourceSize;
2098 if (sourcesToUse + targetComponent > numTargetComponents)
2099 sourcesToUse = numTargetComponents - targetComponent;
2101 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2102 std::vector<unsigned> swiz;
2104 latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
2108 // lambda to visit a matrix argument's components
2109 const auto accumulateMatrixConstituents = [&](Id sourceArg) {
2110 unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
2111 unsigned int sourcesToUse = sourceSize;
2112 if (sourcesToUse + targetComponent > numTargetComponents)
2113 sourcesToUse = numTargetComponents - targetComponent;
2117 for (unsigned int s = 0; s < sourcesToUse; ++s) {
2118 if (row >= getNumRows(sourceArg)) {
2122 std::vector<Id> indexes;
2123 indexes.push_back(col);
2124 indexes.push_back(row);
2125 latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
2130 // Go through the source arguments, each one could have either
2131 // a single or multiple components to contribute.
2132 for (unsigned int i = 0; i < sources.size(); ++i) {
2133 if (isScalar(sources[i]))
2134 latchResult(sources[i]);
2135 else if (isVector(sources[i]))
2136 accumulateVectorConstituents(sources[i]);
2137 else if (isMatrix(sources[i]))
2138 accumulateMatrixConstituents(sources[i]);
2142 if (targetComponent >= numTargetComponents)
2146 // If the result is a vector, make it from the gathered constituents.
2147 if (constituents.size() > 0)
2148 result = createCompositeConstruct(resultTypeId, constituents);
2150 return setPrecision(result, precision);
2153 // Comments in header
2154 Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
2156 Id componentTypeId = getScalarTypeId(resultTypeId);
2157 int numCols = getTypeNumColumns(resultTypeId);
2158 int numRows = getTypeNumRows(resultTypeId);
2160 Instruction* instr = module.getInstruction(componentTypeId);
2161 unsigned bitCount = instr->getImmediateOperand(0);
2163 // Optimize matrix constructed from a bigger matrix
2164 if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
2165 // To truncate the matrix to a smaller number of rows/columns, we need to:
2166 // 1. For each column, extract the column and truncate it to the required size using shuffle
2167 // 2. Assemble the resulting matrix from all columns
2168 Id matrix = sources[0];
2169 Id columnTypeId = getContainedTypeId(resultTypeId);
2170 Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
2172 std::vector<unsigned> channels;
2173 for (int row = 0; row < numRows; ++row)
2174 channels.push_back(row);
2176 std::vector<Id> matrixColumns;
2177 for (int col = 0; col < numCols; ++col) {
2178 std::vector<unsigned> indexes;
2179 indexes.push_back(col);
2180 Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
2181 setPrecision(colv, precision);
2183 if (numRows != getNumRows(matrix)) {
2184 matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
2186 matrixColumns.push_back(colv);
2190 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2193 // Otherwise, will use a two step process
2194 // 1. make a compile-time 2D array of values
2195 // 2. construct a matrix from that array
2199 // initialize the array to the identity matrix
2200 Id ids[maxMatrixSize][maxMatrixSize];
2201 Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
2202 Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
2203 for (int col = 0; col < 4; ++col) {
2204 for (int row = 0; row < 4; ++row) {
2206 ids[col][row] = one;
2208 ids[col][row] = zero;
2212 // modify components as dictated by the arguments
2213 if (sources.size() == 1 && isScalar(sources[0])) {
2214 // a single scalar; resets the diagonals
2215 for (int col = 0; col < 4; ++col)
2216 ids[col][col] = sources[0];
2217 } else if (isMatrix(sources[0])) {
2218 // constructing from another matrix; copy over the parts that exist in both the argument and constructee
2219 Id matrix = sources[0];
2220 int minCols = std::min(numCols, getNumColumns(matrix));
2221 int minRows = std::min(numRows, getNumRows(matrix));
2222 for (int col = 0; col < minCols; ++col) {
2223 std::vector<unsigned> indexes;
2224 indexes.push_back(col);
2225 for (int row = 0; row < minRows; ++row) {
2226 indexes.push_back(row);
2227 ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
2229 setPrecision(ids[col][row], precision);
2233 // fill in the matrix in column-major order with whatever argument components are available
2237 for (int arg = 0; arg < (int)sources.size(); ++arg) {
2238 Id argComp = sources[arg];
2239 for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
2240 if (getNumComponents(sources[arg]) > 1) {
2241 argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
2242 setPrecision(argComp, precision);
2244 ids[col][row++] = argComp;
2245 if (row == numRows) {
2253 // Step 2: Construct a matrix from that array.
2254 // First make the column vectors, then make the matrix.
2256 // make the column vectors
2257 Id columnTypeId = getContainedTypeId(resultTypeId);
2258 std::vector<Id> matrixColumns;
2259 for (int col = 0; col < numCols; ++col) {
2260 std::vector<Id> vectorComponents;
2261 for (int row = 0; row < numRows; ++row)
2262 vectorComponents.push_back(ids[col][row]);
2263 Id column = createCompositeConstruct(columnTypeId, vectorComponents);
2264 setPrecision(column, precision);
2265 matrixColumns.push_back(column);
2269 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
2272 // Comments in header
2273 Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
2279 function = &builder.getBuildPoint()->getParent();
2281 // make the blocks, but only put the then-block into the function,
2282 // the else-block and merge-block will be added later, in order, after
2283 // earlier code is emitted
2284 thenBlock = new Block(builder.getUniqueId(), *function);
2285 mergeBlock = new Block(builder.getUniqueId(), *function);
2287 // Save the current block, so that we can add in the flow control split when
2288 // makeEndIf is called.
2289 headerBlock = builder.getBuildPoint();
2291 function->addBlock(thenBlock);
2292 builder.setBuildPoint(thenBlock);
2295 // Comments in header
2296 void Builder::If::makeBeginElse()
2298 // Close out the "then" by having it jump to the mergeBlock
2299 builder.createBranch(mergeBlock);
2301 // Make the first else block and add it to the function
2302 elseBlock = new Block(builder.getUniqueId(), *function);
2303 function->addBlock(elseBlock);
2305 // Start building the else block
2306 builder.setBuildPoint(elseBlock);
2309 // Comments in header
2310 void Builder::If::makeEndIf()
2312 // jump to the merge block
2313 builder.createBranch(mergeBlock);
2315 // Go back to the headerBlock and make the flow control split
2316 builder.setBuildPoint(headerBlock);
2317 builder.createSelectionMerge(mergeBlock, control);
2319 builder.createConditionalBranch(condition, thenBlock, elseBlock);
2321 builder.createConditionalBranch(condition, thenBlock, mergeBlock);
2323 // add the merge block to the function
2324 function->addBlock(mergeBlock);
2325 builder.setBuildPoint(mergeBlock);
2328 // Comments in header
2329 void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
2330 const std::vector<int>& valueIndexToSegment, int defaultSegment,
2331 std::vector<Block*>& segmentBlocks)
2333 Function& function = buildPoint->getParent();
2335 // make all the blocks
2336 for (int s = 0; s < numSegments; ++s)
2337 segmentBlocks.push_back(new Block(getUniqueId(), function));
2339 Block* mergeBlock = new Block(getUniqueId(), function);
2341 // make and insert the switch's selection-merge instruction
2342 createSelectionMerge(mergeBlock, control);
2344 // make the switch instruction
2345 Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
2346 switchInst->addIdOperand(selector);
2347 auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
2348 switchInst->addIdOperand(defaultOrMerge->getId());
2349 defaultOrMerge->addPredecessor(buildPoint);
2350 for (int i = 0; i < (int)caseValues.size(); ++i) {
2351 switchInst->addImmediateOperand(caseValues[i]);
2352 switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
2353 segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
2355 buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst));
2357 // push the merge block
2358 switchMerges.push(mergeBlock);
2361 // Comments in header
2362 void Builder::addSwitchBreak()
2364 // branch to the top of the merge block stack
2365 createBranch(switchMerges.top());
2366 createAndSetNoPredecessorBlock("post-switch-break");
2369 // Comments in header
2370 void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
2372 int lastSegment = nextSegment - 1;
2373 if (lastSegment >= 0) {
2374 // Close out previous segment by jumping, if necessary, to next segment
2375 if (! buildPoint->isTerminated())
2376 createBranch(segmentBlock[nextSegment]);
2378 Block* block = segmentBlock[nextSegment];
2379 block->getParent().addBlock(block);
2380 setBuildPoint(block);
2383 // Comments in header
2384 void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
2386 // Close out previous segment by jumping, if necessary, to next segment
2387 if (! buildPoint->isTerminated())
2390 switchMerges.top()->getParent().addBlock(switchMerges.top());
2391 setBuildPoint(switchMerges.top());
2396 Block& Builder::makeNewBlock()
2398 Function& function = buildPoint->getParent();
2399 auto block = new Block(getUniqueId(), function);
2400 function.addBlock(block);
2404 Builder::LoopBlocks& Builder::makeNewLoop()
2406 // This verbosity is needed to simultaneously get the same behavior
2407 // everywhere (id's in the same order), have a syntax that works
2408 // across lots of versions of C++, have no warnings from pedantic
2409 // compilation modes, and leave the rest of the code alone.
2410 Block& head = makeNewBlock();
2411 Block& body = makeNewBlock();
2412 Block& merge = makeNewBlock();
2413 Block& continue_target = makeNewBlock();
2414 LoopBlocks blocks(head, body, merge, continue_target);
2419 void Builder::createLoopContinue()
2421 createBranch(&loops.top().continue_target);
2422 // Set up a block for dead code.
2423 createAndSetNoPredecessorBlock("post-loop-continue");
2426 void Builder::createLoopExit()
2428 createBranch(&loops.top().merge);
2429 // Set up a block for dead code.
2430 createAndSetNoPredecessorBlock("post-loop-break");
2433 void Builder::closeLoop()
2438 void Builder::clearAccessChain()
2440 accessChain.base = NoResult;
2441 accessChain.indexChain.clear();
2442 accessChain.instr = NoResult;
2443 accessChain.swizzle.clear();
2444 accessChain.component = NoResult;
2445 accessChain.preSwizzleBaseType = NoType;
2446 accessChain.isRValue = false;
2447 accessChain.coherentFlags.clear();
2450 // Comments in header
2451 void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType)
2453 // swizzles can be stacked in GLSL, but simplified to a single
2454 // one here; the base type doesn't change
2455 if (accessChain.preSwizzleBaseType == NoType)
2456 accessChain.preSwizzleBaseType = preSwizzleBaseType;
2458 // if needed, propagate the swizzle for the current access chain
2459 if (accessChain.swizzle.size() > 0) {
2460 std::vector<unsigned> oldSwizzle = accessChain.swizzle;
2461 accessChain.swizzle.resize(0);
2462 for (unsigned int i = 0; i < swizzle.size(); ++i) {
2463 assert(swizzle[i] < oldSwizzle.size());
2464 accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
2467 accessChain.swizzle = swizzle;
2469 // determine if we need to track this swizzle anymore
2470 simplifyAccessChainSwizzle();
2473 // Comments in header
2474 void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
2476 assert(accessChain.isRValue == false);
2478 transferAccessChainSwizzle(true);
2479 Id base = collapseAccessChain();
2482 // dynamic component should be gone
2483 assert(accessChain.component == NoResult);
2485 // If swizzle still exists, it is out-of-order or not full, we must load the target vector,
2486 // extract and insert elements to perform writeMask and/or swizzle.
2487 if (accessChain.swizzle.size() > 0) {
2488 Id tempBaseId = createLoad(base);
2489 source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
2492 createStore(source, base, memoryAccess, scope);
2495 // Comments in header
2496 Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, spv::Scope scope)
2500 if (accessChain.isRValue) {
2501 // transfer access chain, but try to stay in registers
2502 transferAccessChainSwizzle(false);
2503 if (accessChain.indexChain.size() > 0) {
2504 Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
2506 // if all the accesses are constants, we can use OpCompositeExtract
2507 std::vector<unsigned> indexes;
2508 bool constant = true;
2509 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
2510 if (isConstantScalar(accessChain.indexChain[i]))
2511 indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
2519 id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
2521 // make a new function variable for this r-value
2522 Id lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable");
2525 createStore(accessChain.base, lValue);
2527 // move base to the new variable
2528 accessChain.base = lValue;
2529 accessChain.isRValue = false;
2531 // load through the access chain
2532 id = createLoad(collapseAccessChain());
2534 setPrecision(id, precision);
2536 id = accessChain.base; // no precision, it was set when this was defined
2538 transferAccessChainSwizzle(true);
2539 // load through the access chain
2540 id = createLoad(collapseAccessChain(), memoryAccess, scope);
2541 setPrecision(id, precision);
2542 addDecoration(id, nonUniform);
2545 // Done, unless there are swizzles to do
2546 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2549 // Do remaining swizzling
2551 // Do the basic swizzle
2552 if (accessChain.swizzle.size() > 0) {
2553 Id swizzledType = getScalarTypeId(getTypeId(id));
2554 if (accessChain.swizzle.size() > 1)
2555 swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
2556 id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
2559 // Do the dynamic component
2560 if (accessChain.component != NoResult)
2561 id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
2563 addDecoration(id, nonUniform);
2567 Id Builder::accessChainGetLValue()
2569 assert(accessChain.isRValue == false);
2571 transferAccessChainSwizzle(true);
2572 Id lvalue = collapseAccessChain();
2574 // If swizzle exists, it is out-of-order or not full, we must load the target vector,
2575 // extract and insert elements to perform writeMask and/or swizzle. This does not
2576 // go with getting a direct l-value pointer.
2577 assert(accessChain.swizzle.size() == 0);
2578 assert(accessChain.component == NoResult);
2583 // comment in header
2584 Id Builder::accessChainGetInferredType()
2586 // anything to operate on?
2587 if (accessChain.base == NoResult)
2589 Id type = getTypeId(accessChain.base);
2591 // do initial dereference
2592 if (! accessChain.isRValue)
2593 type = getContainedTypeId(type);
2595 // dereference each index
2596 for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
2597 if (isStructType(type))
2598 type = getContainedTypeId(type, getConstantScalar(*it));
2600 type = getContainedTypeId(type);
2603 // dereference swizzle
2604 if (accessChain.swizzle.size() == 1)
2605 type = getContainedTypeId(type);
2606 else if (accessChain.swizzle.size() > 1)
2607 type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
2609 // dereference component selection
2610 if (accessChain.component)
2611 type = getContainedTypeId(type);
2616 void Builder::dump(std::vector<unsigned int>& out) const
2618 // Header, before first instructions:
2619 out.push_back(MagicNumber);
2620 out.push_back(spvVersion);
2621 out.push_back(builderNumber);
2622 out.push_back(uniqueId + 1);
2626 for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
2627 Instruction capInst(0, 0, OpCapability);
2628 capInst.addImmediateOperand(*it);
2632 for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
2633 Instruction extInst(0, 0, OpExtension);
2634 extInst.addStringOperand(it->c_str());
2638 dumpInstructions(out, imports);
2639 Instruction memInst(0, 0, OpMemoryModel);
2640 memInst.addImmediateOperand(addressModel);
2641 memInst.addImmediateOperand(memoryModel);
2644 // Instructions saved up while building:
2645 dumpInstructions(out, entryPoints);
2646 dumpInstructions(out, executionModes);
2648 // Debug instructions
2649 dumpInstructions(out, strings);
2650 dumpSourceInstructions(out);
2651 for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
2652 Instruction sourceExtInst(0, 0, OpSourceExtension);
2653 sourceExtInst.addStringOperand(sourceExtensions[e]);
2654 sourceExtInst.dump(out);
2656 dumpInstructions(out, names);
2657 dumpModuleProcesses(out);
2659 // Annotation instructions
2660 dumpInstructions(out, decorations);
2662 dumpInstructions(out, constantsTypesGlobals);
2663 dumpInstructions(out, externals);
2670 // Protected methods.
2673 // Turn the described access chain in 'accessChain' into an instruction(s)
2674 // computing its address. This *cannot* include complex swizzles, which must
2675 // be handled after this is called.
2677 // Can generate code.
2678 Id Builder::collapseAccessChain()
2680 assert(accessChain.isRValue == false);
2682 // did we already emit an access chain for this?
2683 if (accessChain.instr != NoResult)
2684 return accessChain.instr;
2686 // If we have a dynamic component, we can still transfer
2687 // that into a final operand to the access chain. We need to remap the
2688 // dynamic component through the swizzle to get a new dynamic component to
2691 // This was not done in transferAccessChainSwizzle() because it might
2693 remapDynamicSwizzle();
2694 if (accessChain.component != NoResult) {
2695 // transfer the dynamic component to the access chain
2696 accessChain.indexChain.push_back(accessChain.component);
2697 accessChain.component = NoResult;
2700 // note that non-trivial swizzling is left pending
2702 // do we have an access chain?
2703 if (accessChain.indexChain.size() == 0)
2704 return accessChain.base;
2706 // emit the access chain
2707 StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
2708 accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
2710 return accessChain.instr;
2713 // For a dynamic component selection of a swizzle.
2715 // Turn the swizzle and dynamic component into just a dynamic component.
2718 void Builder::remapDynamicSwizzle()
2720 // do we have a swizzle to remap a dynamic component through?
2721 if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
2722 // build a vector of the swizzle for the component to map into
2723 std::vector<Id> components;
2724 for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
2725 components.push_back(makeUintConstant(accessChain.swizzle[c]));
2726 Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
2727 Id map = makeCompositeConstant(mapType, components);
2730 accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
2731 accessChain.swizzle.clear();
2735 // clear out swizzle if it is redundant, that is reselecting the same components
2736 // that would be present without the swizzle.
2737 void Builder::simplifyAccessChainSwizzle()
2739 // If the swizzle has fewer components than the vector, it is subsetting, and must stay
2740 // to preserve that fact.
2741 if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
2744 // if components are out of order, it is a swizzle
2745 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
2746 if (i != accessChain.swizzle[i])
2750 // otherwise, there is no need to track this swizzle
2751 accessChain.swizzle.clear();
2752 if (accessChain.component == NoResult)
2753 accessChain.preSwizzleBaseType = NoType;
2756 // To the extent any swizzling can become part of the chain
2757 // of accesses instead of a post operation, make it so.
2758 // If 'dynamic' is true, include transferring the dynamic component,
2759 // otherwise, leave it pending.
2761 // Does not generate code. just updates the access chain.
2762 void Builder::transferAccessChainSwizzle(bool dynamic)
2765 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
2769 // (this requires either a swizzle, or generating code for a dynamic component)
2770 if (accessChain.swizzle.size() > 1)
2773 // single component, either in the swizzle and/or dynamic component
2774 if (accessChain.swizzle.size() == 1) {
2775 assert(accessChain.component == NoResult);
2776 // handle static component selection
2777 accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
2778 accessChain.swizzle.clear();
2779 accessChain.preSwizzleBaseType = NoType;
2780 } else if (dynamic && accessChain.component != NoResult) {
2781 assert(accessChain.swizzle.size() == 0);
2782 // handle dynamic component
2783 accessChain.indexChain.push_back(accessChain.component);
2784 accessChain.preSwizzleBaseType = NoType;
2785 accessChain.component = NoResult;
2789 // Utility method for creating a new block and setting the insert point to
2790 // be in it. This is useful for flow-control operations that need a "dummy"
2791 // block proceeding them (e.g. instructions after a discard, etc).
2792 void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
2794 Block* block = new Block(getUniqueId(), buildPoint->getParent());
2795 block->setUnreachable();
2796 buildPoint->getParent().addBlock(block);
2797 setBuildPoint(block);
2800 // addName(block->getId(), name);
2803 // Comments in header
2804 void Builder::createBranch(Block* block)
2806 Instruction* branch = new Instruction(OpBranch);
2807 branch->addIdOperand(block->getId());
2808 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
2809 block->addPredecessor(buildPoint);
2812 void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
2814 Instruction* merge = new Instruction(OpSelectionMerge);
2815 merge->addIdOperand(mergeBlock->getId());
2816 merge->addImmediateOperand(control);
2817 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
2820 void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
2821 unsigned int dependencyLength)
2823 Instruction* merge = new Instruction(OpLoopMerge);
2824 merge->addIdOperand(mergeBlock->getId());
2825 merge->addIdOperand(continueBlock->getId());
2826 merge->addImmediateOperand(control);
2827 if ((control & LoopControlDependencyLengthMask) != 0)
2828 merge->addImmediateOperand(dependencyLength);
2829 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
2832 void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
2834 Instruction* branch = new Instruction(OpBranchConditional);
2835 branch->addIdOperand(condition);
2836 branch->addIdOperand(thenBlock->getId());
2837 branch->addIdOperand(elseBlock->getId());
2838 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
2839 thenBlock->addPredecessor(buildPoint);
2840 elseBlock->addPredecessor(buildPoint);
2844 // [OpSourceContinued]
2846 void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
2848 const int maxWordCount = 0xFFFF;
2849 const int opSourceWordCount = 4;
2850 const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
2852 if (source != SourceLanguageUnknown) {
2853 // OpSource Language Version File Source
2854 Instruction sourceInst(NoResult, NoType, OpSource);
2855 sourceInst.addImmediateOperand(source);
2856 sourceInst.addImmediateOperand(sourceVersion);
2858 if (sourceFileStringId != NoResult) {
2859 sourceInst.addIdOperand(sourceFileStringId);
2861 if (sourceText.size() > 0) {
2863 std::string subString;
2864 while ((int)sourceText.size() - nextByte > 0) {
2865 subString = sourceText.substr(nextByte, nonNullBytesPerInstruction);
2866 if (nextByte == 0) {
2868 sourceInst.addStringOperand(subString.c_str());
2869 sourceInst.dump(out);
2872 Instruction sourceContinuedInst(OpSourceContinued);
2873 sourceContinuedInst.addStringOperand(subString.c_str());
2874 sourceContinuedInst.dump(out);
2876 nextByte += nonNullBytesPerInstruction;
2879 sourceInst.dump(out);
2881 sourceInst.dump(out);
2885 void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
2887 for (int i = 0; i < (int)instructions.size(); ++i) {
2888 instructions[i]->dump(out);
2892 void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
2894 for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
2895 Instruction moduleProcessed(OpModuleProcessed);
2896 moduleProcessed.addStringOperand(moduleProcesses[i]);
2897 moduleProcessed.dump(out);
2901 }; // end spv namespace