21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/Sequence.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/ADT/bit.h"
26#include "llvm/Support/Debug.h"
30#define DEBUG_TYPE "spirv-serialization"
37 if (
auto selectionOp = dyn_cast<spirv::SelectionOp>(op))
38 return selectionOp.getMergeBlock();
39 if (
auto loopOp = dyn_cast<spirv::LoopOp>(op))
40 return loopOp.getMergeBlock();
51 if (
auto loopOp = dyn_cast<spirv::LoopOp>(block->
getParentOp())) {
55 while ((op = op->getPrevNode()) !=
nullptr)
74 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
75 return floatAttr.getValue().isZero();
77 if (
auto boolAttr = dyn_cast<BoolAttr>(attr)) {
78 return !boolAttr.getValue();
80 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
81 return intAttr.getValue().isZero();
83 if (
auto splatElemAttr = dyn_cast<SplatElementsAttr>(attr)) {
86 if (
auto denseElemAttr = dyn_cast<DenseElementsAttr>(attr)) {
102 for (
Operation &op : llvm::drop_begin(ops))
103 if (
auto funcOp = dyn_cast<spirv::FuncOp>(op))
104 if (funcOp.getBody().empty())
115 uint32_t wordCount = 1 + operands.size();
117 binary.append(operands.begin(), operands.end());
122 : module(module), mlirBuilder(module.
getContext()), options(options) {}
125 LLVM_DEBUG(llvm::dbgs() <<
"+++ starting serialization +++\n");
127 if (failed(module.verifyInvariants()))
132 if (failed(processExtension())) {
135 processMemoryModel();
142 for (
auto &op : *module.getBody()) {
143 if (failed(processOperation(&op))) {
148 LLVM_DEBUG(llvm::dbgs() <<
"+++ completed serialization +++\n");
154 extensions.size() + extendedSets.size() +
155 memoryModel.size() + entryPoints.size() +
156 executionModes.size() + decorations.size() +
157 typesGlobalValues.size() + functions.size() + graphs.size();
160 binary.reserve(moduleSize);
164 binary.append(capabilities.begin(), capabilities.end());
165 binary.append(extensions.begin(), extensions.end());
166 binary.append(extendedSets.begin(), extendedSets.end());
167 binary.append(memoryModel.begin(), memoryModel.end());
168 binary.append(entryPoints.begin(), entryPoints.end());
169 binary.append(executionModes.begin(), executionModes.end());
170 binary.append(debug.begin(), debug.end());
171 binary.append(names.begin(), names.end());
172 binary.append(decorations.begin(), decorations.end());
173 binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
174 binary.append(functions.begin(), functions.end());
175 binary.append(graphs.begin(), graphs.end());
180 os <<
"\n= Value <id> Map =\n\n";
181 for (
auto valueIDPair : valueIDMap) {
182 Value val = valueIDPair.first;
183 os <<
" " << val <<
" "
184 <<
"id = " << valueIDPair.second <<
' ';
186 os <<
"from op '" << op->getName() <<
"'";
187 }
else if (
auto arg = dyn_cast<BlockArgument>(val)) {
188 Block *block = arg.getOwner();
189 os <<
"from argument of block " << block <<
' ';
201uint32_t Serializer::getOrCreateFunctionID(StringRef fnName) {
202 auto funcID = funcIDMap.lookup(fnName);
204 funcID = getNextID();
205 funcIDMap[fnName] = funcID;
210void Serializer::processCapability() {
211 for (
auto cap : module.getVceTriple()->getCapabilities())
213 {
static_cast<uint32_t
>(cap)});
216void Serializer::processDebugInfo() {
217 if (!options.emitDebugInfo)
219 auto fileLoc = dyn_cast<FileLineColLoc>(module.getLoc());
220 auto fileName = fileLoc ? fileLoc.getFilename().strref() :
"<unknown>";
221 fileID = getNextID();
222 SmallVector<uint32_t, 16> operands;
223 operands.push_back(fileID);
229LogicalResult Serializer::processExtension() {
230 llvm::SmallVector<uint32_t, 16> extName;
231 llvm::SmallSet<Extension, 4> deducedExts(
232 llvm::from_range, module.getVceTriple()->getExtensions());
233 auto nonSemanticInfoExt = spirv::Extension::SPV_KHR_non_semantic_info;
234 if (options.emitDebugInfo && !deducedExts.contains(nonSemanticInfoExt)) {
236 if (!is_contained(targetEnvAttr.getExtensions(), nonSemanticInfoExt))
237 return module.emitError(
238 "SPV_KHR_non_semantic_info extension not available");
239 deducedExts.insert(nonSemanticInfoExt);
241 for (spirv::Extension ext : deducedExts) {
249void Serializer::processMemoryModel() {
250 StringAttr memoryModelName =
module.getMemoryModelAttrName();
251 auto mm =
static_cast<uint32_t
>(
252 module->getAttrOfType<spirv::MemoryModelAttr>(memoryModelName)
255 StringAttr addressingModelName =
module.getAddressingModelAttrName();
256 auto am =
static_cast<uint32_t
>(
257 module->getAttrOfType<spirv::AddressingModelAttr>(addressingModelName)
266 if (attrName ==
"fp_fast_math_mode")
267 return "FPFastMathMode";
269 if (attrName ==
"fp_rounding_mode")
270 return "FPRoundingMode";
272 if (attrName ==
"cache_control_load_intel")
273 return "CacheControlLoadINTEL";
274 if (attrName ==
"cache_control_store_intel")
275 return "CacheControlStoreINTEL";
277 return llvm::convertToCamelFromSnakeCase(attrName,
true);
280template <
typename AttrTy,
typename EmitF>
283 StringRef attrName, EmitF emitter) {
284 auto arrayAttr = dyn_cast<ArrayAttr>(attrList);
286 return emitError(loc,
"expecting array attribute of ")
287 << attrName <<
" for " << stringifyDecoration(decoration);
289 if (arrayAttr.empty()) {
290 return emitError(loc,
"expecting non-empty array attribute of ")
291 << attrName <<
" for " << stringifyDecoration(decoration);
293 for (
Attribute attr : arrayAttr.getValue()) {
294 auto cacheControlAttr = dyn_cast<AttrTy>(attr);
295 if (!cacheControlAttr) {
296 return emitError(loc,
"expecting array attribute of ")
297 << attrName <<
" for " << stringifyDecoration(decoration);
301 if (failed(emitter(cacheControlAttr)))
307LogicalResult Serializer::processDecorationAttr(
Location loc, uint32_t resultID,
308 Decoration decoration,
311 switch (decoration) {
312 case spirv::Decoration::LinkageAttributes: {
315 auto linkageAttr = dyn_cast<spirv::LinkageAttributesAttr>(attr);
316 auto linkageName = linkageAttr.getLinkageName();
317 auto linkageType = linkageAttr.getLinkageType().getValue();
321 args.push_back(
static_cast<uint32_t
>(linkageType));
324 case spirv::Decoration::FPFastMathMode:
325 if (
auto intAttr = dyn_cast<FPFastMathModeAttr>(attr)) {
326 args.push_back(
static_cast<uint32_t
>(intAttr.getValue()));
329 return emitError(loc,
"expected FPFastMathModeAttr attribute for ")
330 << stringifyDecoration(decoration);
331 case spirv::Decoration::FPRoundingMode:
332 if (
auto intAttr = dyn_cast<FPRoundingModeAttr>(attr)) {
333 args.push_back(
static_cast<uint32_t
>(intAttr.getValue()));
336 return emitError(loc,
"expected FPRoundingModeAttr attribute for ")
337 << stringifyDecoration(decoration);
338 case spirv::Decoration::Binding:
339 case spirv::Decoration::DescriptorSet:
340 case spirv::Decoration::Location:
341 case spirv::Decoration::Index:
342 case spirv::Decoration::Offset:
343 case spirv::Decoration::XfbBuffer:
344 case spirv::Decoration::XfbStride:
345 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
346 args.push_back(intAttr.getValue().getZExtValue());
349 return emitError(loc,
"expected integer attribute for ")
350 << stringifyDecoration(decoration);
351 case spirv::Decoration::BuiltIn:
352 if (
auto strAttr = dyn_cast<StringAttr>(attr)) {
353 auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
355 args.push_back(
static_cast<uint32_t
>(*enumVal));
359 << stringifyDecoration(decoration) <<
" decoration attribute "
360 << strAttr.getValue();
362 return emitError(loc,
"expected string attribute for ")
363 << stringifyDecoration(decoration);
364 case spirv::Decoration::Aliased:
365 case spirv::Decoration::AliasedPointer:
366 case spirv::Decoration::Flat:
367 case spirv::Decoration::NonReadable:
368 case spirv::Decoration::NonWritable:
369 case spirv::Decoration::NoPerspective:
370 case spirv::Decoration::NoSignedWrap:
371 case spirv::Decoration::NoUnsignedWrap:
372 case spirv::Decoration::RelaxedPrecision:
373 case spirv::Decoration::Restrict:
374 case spirv::Decoration::RestrictPointer:
375 case spirv::Decoration::NoContraction:
376 case spirv::Decoration::Constant:
377 case spirv::Decoration::Block:
378 case spirv::Decoration::Invariant:
379 case spirv::Decoration::Patch:
380 case spirv::Decoration::Coherent:
383 if (isa<UnitAttr, DecorationAttr>(attr))
386 "expected unit attribute or decoration attribute for ")
387 << stringifyDecoration(decoration);
388 case spirv::Decoration::CacheControlLoadINTEL:
390 loc, decoration, attr,
"CacheControlLoadINTEL",
391 [&](CacheControlLoadINTELAttr attr) {
392 unsigned cacheLevel = attr.getCacheLevel();
393 LoadCacheControl loadCacheControl = attr.getLoadCacheControl();
394 return emitDecoration(
395 resultID, decoration,
396 {cacheLevel,
static_cast<uint32_t
>(loadCacheControl)});
398 case spirv::Decoration::CacheControlStoreINTEL:
400 loc, decoration, attr,
"CacheControlStoreINTEL",
401 [&](CacheControlStoreINTELAttr attr) {
402 unsigned cacheLevel = attr.getCacheLevel();
403 StoreCacheControl storeCacheControl = attr.getStoreCacheControl();
404 return emitDecoration(
405 resultID, decoration,
406 {cacheLevel,
static_cast<uint32_t
>(storeCacheControl)});
409 return emitError(loc,
"unhandled decoration ")
410 << stringifyDecoration(decoration);
412 return emitDecoration(resultID, decoration, args);
415LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
416 NamedAttribute attr) {
417 StringRef attrName = attr.
getName().strref();
419 std::optional<Decoration> decoration =
420 spirv::symbolizeDecoration(decorationName);
423 loc,
"non-argument attributes expected to have snake-case-ified "
424 "decoration name, unhandled attribute with name : ")
427 return processDecorationAttr(loc, resultID, *decoration, attr.
getValue());
430LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
431 assert(!name.empty() &&
"unexpected empty string for OpName");
432 if (!options.emitSymbolName)
435 SmallVector<uint32_t, 4> nameOperands;
436 nameOperands.push_back(resultID);
443LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
447 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
453LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
457 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
462LogicalResult Serializer::processMemberDecoration(
467 static_cast<uint32_t
>(memberDecoration.
decoration)});
483bool Serializer::isInterfaceStructPtrType(Type type)
const {
484 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
485 switch (ptrType.getStorageClass()) {
486 case spirv::StorageClass::PhysicalStorageBuffer:
487 case spirv::StorageClass::PushConstant:
488 case spirv::StorageClass::StorageBuffer:
489 case spirv::StorageClass::Uniform:
490 return isa<spirv::StructType>(ptrType.getPointeeType());
498LogicalResult Serializer::processType(Location loc, Type type,
503 return processTypeImpl(loc, type, typeID, serializationCtx);
507Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
519 IntegerType::SignednessSemantics::Signless);
522 typeID = getTypeID(type);
526 typeID = getNextID();
527 SmallVector<uint32_t, 4> operands;
529 operands.push_back(typeID);
530 auto typeEnum = spirv::Opcode::OpTypeVoid;
531 bool deferSerialization =
false;
533 if ((isa<FunctionType>(type) &&
534 succeeded(prepareFunctionType(loc, cast<FunctionType>(type), typeEnum,
536 (isa<GraphType>(type) &&
538 prepareGraphType(loc, cast<GraphType>(type), typeEnum, operands))) ||
539 succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
540 deferSerialization, serializationCtx))) {
541 if (deferSerialization)
544 typeIDMap[type] = typeID;
548 if (recursiveStructInfos.count(type) != 0) {
551 for (
auto &ptrInfo : recursiveStructInfos[type]) {
554 SmallVector<uint32_t, 4> ptrOperands;
555 ptrOperands.push_back(ptrInfo.pointerTypeID);
556 ptrOperands.push_back(
static_cast<uint32_t
>(ptrInfo.storageClass));
557 ptrOperands.push_back(typeIDMap[type]);
563 recursiveStructInfos[type].clear();
569 return emitError(loc,
"failed to process type: ") << type;
572LogicalResult Serializer::prepareBasicType(
573 Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
574 SmallVectorImpl<uint32_t> &operands,
bool &deferSerialization,
576 deferSerialization =
false;
578 if (isVoidType(type)) {
579 typeEnum = spirv::Opcode::OpTypeVoid;
583 if (
auto intType = dyn_cast<IntegerType>(type)) {
584 if (intType.getWidth() == 1) {
585 typeEnum = spirv::Opcode::OpTypeBool;
589 typeEnum = spirv::Opcode::OpTypeInt;
590 operands.push_back(intType.getWidth());
595 operands.push_back(intType.isSigned() ? 1 : 0);
599 if (
auto floatType = dyn_cast<FloatType>(type)) {
600 typeEnum = spirv::Opcode::OpTypeFloat;
601 operands.push_back(floatType.getWidth());
602 if (floatType.isBF16()) {
603 operands.push_back(
static_cast<uint32_t
>(spirv::FPEncoding::BFloat16KHR));
605 if (floatType.isF8E4M3FN()) {
607 static_cast<uint32_t
>(spirv::FPEncoding::Float8E4M3EXT));
609 if (floatType.isF8E5M2()) {
611 static_cast<uint32_t
>(spirv::FPEncoding::Float8E5M2EXT));
617 if (
auto vectorType = dyn_cast<VectorType>(type)) {
618 uint32_t elementTypeID = 0;
619 if (
failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
620 serializationCtx))) {
623 typeEnum = spirv::Opcode::OpTypeVector;
624 operands.push_back(elementTypeID);
625 operands.push_back(vectorType.getNumElements());
629 if (
auto imageType = dyn_cast<spirv::ImageType>(type)) {
630 typeEnum = spirv::Opcode::OpTypeImage;
631 uint32_t sampledTypeID = 0;
632 if (
failed(processType(loc, imageType.getElementType(), sampledTypeID)))
635 llvm::append_values(operands, sampledTypeID,
636 static_cast<uint32_t
>(imageType.getDim()),
637 static_cast<uint32_t
>(imageType.getDepthInfo()),
638 static_cast<uint32_t
>(imageType.getArrayedInfo()),
639 static_cast<uint32_t
>(imageType.getSamplingInfo()),
640 static_cast<uint32_t
>(imageType.getSamplerUseInfo()),
641 static_cast<uint32_t
>(imageType.getImageFormat()));
645 if (
auto arrayType = dyn_cast<spirv::ArrayType>(type)) {
646 typeEnum = spirv::Opcode::OpTypeArray;
647 uint32_t elementTypeID = 0;
648 if (
failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
649 serializationCtx))) {
652 operands.push_back(elementTypeID);
653 if (
auto elementCountID = prepareConstantInt(
654 loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
655 operands.push_back(elementCountID);
657 return processTypeDecoration(loc, arrayType, resultID);
660 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
661 uint32_t pointeeTypeID = 0;
662 spirv::StructType pointeeStruct =
663 dyn_cast<spirv::StructType>(ptrType.getPointeeType());
666 serializationCtx.count(pointeeStruct.
getIdentifier()) != 0) {
671 SmallVector<uint32_t, 2> forwardPtrOperands;
672 forwardPtrOperands.push_back(resultID);
673 forwardPtrOperands.push_back(
674 static_cast<uint32_t
>(ptrType.getStorageClass()));
677 spirv::Opcode::OpTypeForwardPointer,
689 deferSerialization =
true;
693 recursiveStructInfos[structType].push_back(
694 {resultID, ptrType.getStorageClass()});
696 if (
failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
701 typeEnum = spirv::Opcode::OpTypePointer;
702 operands.push_back(
static_cast<uint32_t
>(ptrType.getStorageClass()));
703 operands.push_back(pointeeTypeID);
708 if (isInterfaceStructPtrType(ptrType)) {
709 auto structType = cast<spirv::StructType>(ptrType.getPointeeType());
710 if (!structType.hasDecoration(spirv::Decoration::Block))
711 if (
failed(emitDecoration(getTypeID(pointeeStruct),
712 spirv::Decoration::Block)))
713 return emitError(loc,
"cannot decorate ")
714 << pointeeStruct <<
" with Block decoration";
720 if (
auto runtimeArrayType = dyn_cast<spirv::RuntimeArrayType>(type)) {
721 uint32_t elementTypeID = 0;
722 if (
failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
723 elementTypeID, serializationCtx))) {
726 typeEnum = spirv::Opcode::OpTypeRuntimeArray;
727 operands.push_back(elementTypeID);
728 return processTypeDecoration(loc, runtimeArrayType, resultID);
731 if (isa<spirv::SamplerType>(type)) {
732 typeEnum = spirv::Opcode::OpTypeSampler;
736 if (
auto sampledImageType = dyn_cast<spirv::SampledImageType>(type)) {
737 typeEnum = spirv::Opcode::OpTypeSampledImage;
738 uint32_t imageTypeID = 0;
740 processType(loc, sampledImageType.getImageType(), imageTypeID))) {
743 operands.push_back(imageTypeID);
747 if (
auto structType = dyn_cast<spirv::StructType>(type)) {
748 if (structType.isIdentified()) {
749 if (
failed(processName(resultID, structType.getIdentifier())))
751 serializationCtx.insert(structType.getIdentifier());
754 bool hasOffset = structType.hasOffset();
755 for (
auto elementIndex :
756 llvm::seq<uint32_t>(0, structType.getNumElements())) {
757 uint32_t elementTypeID = 0;
758 if (
failed(processTypeImpl(loc, structType.getElementType(elementIndex),
759 elementTypeID, serializationCtx))) {
762 operands.push_back(elementTypeID);
764 auto intType = IntegerType::get(structType.getContext(), 32);
766 spirv::StructType::MemberDecorationInfo offsetDecoration{
767 elementIndex, spirv::Decoration::Offset,
768 IntegerAttr::get(intType,
769 structType.getMemberOffset(elementIndex))};
770 if (
failed(processMemberDecoration(resultID, offsetDecoration))) {
771 return emitError(loc,
"cannot decorate ")
772 << elementIndex <<
"-th member of " << structType
773 <<
" with its offset";
777 SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
778 structType.getMemberDecorations(memberDecorations);
780 for (
auto &memberDecoration : memberDecorations) {
781 if (
failed(processMemberDecoration(resultID, memberDecoration))) {
782 return emitError(loc,
"cannot decorate ")
783 <<
static_cast<uint32_t
>(memberDecoration.
memberIndex)
784 <<
"-th member of " << structType <<
" with "
785 << stringifyDecoration(memberDecoration.
decoration);
789 SmallVector<spirv::StructType::StructDecorationInfo, 1> structDecorations;
790 structType.getStructDecorations(structDecorations);
792 for (spirv::StructType::StructDecorationInfo &structDecoration :
794 if (
failed(processDecorationAttr(loc, resultID,
795 structDecoration.decoration,
796 structDecoration.decorationValue))) {
797 return emitError(loc,
"cannot decorate struct ")
798 << structType <<
" with "
799 << stringifyDecoration(structDecoration.decoration);
803 typeEnum = spirv::Opcode::OpTypeStruct;
805 if (structType.isIdentified())
806 serializationCtx.remove(structType.getIdentifier());
811 if (
auto cooperativeMatrixType =
812 dyn_cast<spirv::CooperativeMatrixType>(type)) {
813 uint32_t elementTypeID = 0;
814 if (
failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
815 elementTypeID, serializationCtx))) {
818 typeEnum = spirv::Opcode::OpTypeCooperativeMatrixKHR;
819 auto getConstantOp = [&](uint32_t id) {
820 auto attr = IntegerAttr::get(IntegerType::get(type.
getContext(), 32),
id);
821 return prepareConstantInt(loc, attr);
824 operands, elementTypeID,
825 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getScope())),
826 getConstantOp(cooperativeMatrixType.getRows()),
827 getConstantOp(cooperativeMatrixType.getColumns()),
828 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getUse())));
832 if (
auto matrixType = dyn_cast<spirv::MatrixType>(type)) {
833 uint32_t elementTypeID = 0;
834 if (
failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
835 serializationCtx))) {
838 typeEnum = spirv::Opcode::OpTypeMatrix;
839 llvm::append_values(operands, elementTypeID, matrixType.getNumColumns());
843 if (
auto tensorArmType = dyn_cast<TensorArmType>(type)) {
844 uint32_t elementTypeID = 0;
846 uint32_t shapeID = 0;
848 if (
failed(processTypeImpl(loc, tensorArmType.getElementType(),
849 elementTypeID, serializationCtx))) {
852 if (tensorArmType.hasRank()) {
853 ArrayRef<int64_t> dims = tensorArmType.getShape();
855 rankID = prepareConstantInt(loc, mlirBuilder.getI32IntegerAttr(rank));
860 bool shaped = llvm::all_of(dims, [](
const auto &dim) {
return dim > 0; });
861 if (rank > 0 && shaped) {
862 auto I32Type = IntegerType::get(type.
getContext(), 32);
865 SmallVector<uint64_t, 1> index(rank);
866 shapeID = prepareDenseElementsConstant(
868 mlirBuilder.getI32TensorAttr(SmallVector<int32_t>(dims)), 0,
871 shapeID = prepareArrayConstant(
873 mlirBuilder.getI32ArrayAttr(SmallVector<int32_t>(dims)));
880 typeEnum = spirv::Opcode::OpTypeTensorARM;
881 operands.push_back(elementTypeID);
884 operands.push_back(rankID);
887 operands.push_back(shapeID);
892 return emitError(loc,
"unhandled type in serialization: ") << type;
896Serializer::prepareFunctionType(Location loc, FunctionType type,
897 spirv::Opcode &typeEnum,
898 SmallVectorImpl<uint32_t> &operands) {
899 typeEnum = spirv::Opcode::OpTypeFunction;
900 assert(type.getNumResults() <= 1 &&
901 "serialization supports only a single return value");
902 uint32_t resultID = 0;
904 loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
908 operands.push_back(resultID);
909 for (
auto &res : type.getInputs()) {
910 uint32_t argTypeID = 0;
911 if (
failed(processType(loc, res, argTypeID))) {
914 operands.push_back(argTypeID);
920Serializer::prepareGraphType(Location loc, GraphType type,
921 spirv::Opcode &typeEnum,
922 SmallVectorImpl<uint32_t> &operands) {
923 typeEnum = spirv::Opcode::OpTypeGraphARM;
924 assert(type.getNumResults() >= 1 &&
925 "serialization requires at least a return value");
927 operands.push_back(type.getNumInputs());
929 for (Type argType : type.getInputs()) {
930 uint32_t argTypeID = 0;
931 if (
failed(processType(loc, argType, argTypeID)))
933 operands.push_back(argTypeID);
936 for (Type resType : type.getResults()) {
937 uint32_t resTypeID = 0;
938 if (
failed(processType(loc, resType, resTypeID)))
940 operands.push_back(resTypeID);
950uint32_t Serializer::prepareConstant(Location loc, Type constType,
951 Attribute valueAttr) {
952 if (
auto id = prepareConstantScalar(loc, valueAttr)) {
959 if (
auto id = getConstantID(valueAttr)) {
964 if (
failed(processType(loc, constType, typeID))) {
968 uint32_t resultID = 0;
969 if (
auto attr = dyn_cast<DenseElementsAttr>(valueAttr)) {
970 int rank = dyn_cast<ShapedType>(attr.getType()).getRank();
971 SmallVector<uint64_t, 4> index(rank);
972 resultID = prepareDenseElementsConstant(loc, constType, attr,
974 }
else if (
auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
975 resultID = prepareArrayConstant(loc, constType, arrayAttr);
979 emitError(loc,
"cannot serialize attribute: ") << valueAttr;
983 constIDMap[valueAttr] = resultID;
987uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
990 if (
failed(processType(loc, constType, typeID))) {
994 uint32_t resultID = getNextID();
995 SmallVector<uint32_t, 4> operands = {typeID, resultID};
996 operands.reserve(attr.size() + 2);
997 auto elementType = cast<spirv::ArrayType>(constType).getElementType();
998 for (Attribute elementAttr : attr) {
999 if (
auto elementID = prepareConstant(loc, elementType, elementAttr)) {
1000 operands.push_back(elementID);
1005 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1014Serializer::prepareDenseElementsConstant(Location loc, Type constType,
1015 DenseElementsAttr valueAttr,
int dim,
1016 MutableArrayRef<uint64_t> index) {
1017 auto shapedType = dyn_cast<ShapedType>(valueAttr.
getType());
1018 assert(dim <= shapedType.getRank());
1019 if (shapedType.getRank() == dim) {
1020 if (
auto attr = dyn_cast<DenseIntElementsAttr>(valueAttr)) {
1021 return attr.getType().getElementType().isInteger(1)
1022 ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
1023 : prepareConstantInt(loc,
1024 attr.getValues<IntegerAttr>()[index]);
1026 if (
auto attr = dyn_cast<DenseFPElementsAttr>(valueAttr)) {
1027 return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
1032 uint32_t typeID = 0;
1033 if (
failed(processType(loc, constType, typeID))) {
1037 int64_t numberOfConstituents = shapedType.getDimSize(dim);
1038 uint32_t resultID = getNextID();
1039 SmallVector<uint32_t, 4> operands = {typeID, resultID};
1040 auto elementType = cast<spirv::CompositeType>(constType).getElementType(0);
1041 if (
auto tensorArmType = dyn_cast<spirv::TensorArmType>(constType)) {
1042 ArrayRef<int64_t> innerShape = tensorArmType.getShape().drop_front();
1043 if (!innerShape.empty())
1051 if (isa<spirv::CooperativeMatrixType>(constType)) {
1055 "cannot serialize a non-splat value for a cooperative matrix type");
1060 operands.reserve(3);
1063 if (
auto elementID = prepareDenseElementsConstant(
1064 loc, elementType, valueAttr, shapedType.getRank(), index)) {
1065 operands.push_back(elementID);
1069 }
else if (isa<spirv::TensorArmType>(constType) &&
isZeroValue(valueAttr)) {
1071 {typeID, resultID});
1074 operands.reserve(numberOfConstituents + 2);
1075 for (
int i = 0; i < numberOfConstituents; ++i) {
1077 if (
auto elementID = prepareDenseElementsConstant(
1078 loc, elementType, valueAttr, dim + 1, index)) {
1079 operands.push_back(elementID);
1085 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1091uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
1093 if (
auto floatAttr = dyn_cast<FloatAttr>(valueAttr)) {
1094 return prepareConstantFp(loc, floatAttr, isSpec);
1096 if (
auto boolAttr = dyn_cast<BoolAttr>(valueAttr)) {
1097 return prepareConstantBool(loc, boolAttr, isSpec);
1099 if (
auto intAttr = dyn_cast<IntegerAttr>(valueAttr)) {
1100 return prepareConstantInt(loc, intAttr, isSpec);
1106uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
1110 if (
auto id = getConstantID(boolAttr)) {
1116 uint32_t typeID = 0;
1117 if (
failed(processType(loc, cast<IntegerAttr>(boolAttr).
getType(), typeID))) {
1121 auto resultID = getNextID();
1123 ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
1124 : spirv::Opcode::OpConstantTrue)
1125 : (isSpec ? spirv::Opcode::OpSpecConstantFalse
1126 : spirv::Opcode::OpConstantFalse);
1130 constIDMap[boolAttr] = resultID;
1135uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
1139 if (
auto id = getConstantID(intAttr)) {
1145 uint32_t typeID = 0;
1146 if (
failed(processType(loc, intAttr.getType(), typeID))) {
1150 auto resultID = getNextID();
1151 APInt value = intAttr.getValue();
1152 unsigned bitwidth = value.getBitWidth();
1153 bool isSigned = intAttr.getType().isSignedInteger();
1155 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1168 word =
static_cast<int32_t
>(value.getSExtValue());
1170 word =
static_cast<uint32_t
>(value.getZExtValue());
1182 words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
1184 words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
1187 {typeID, resultID, words.word1, words.word2});
1190 std::string valueStr;
1191 llvm::raw_string_ostream rss(valueStr);
1192 value.print(rss,
false);
1195 << bitwidth <<
"-bit integer literal: " << valueStr;
1201 constIDMap[intAttr] = resultID;
1206uint32_t Serializer::prepareGraphConstantId(Location loc, Type graphConstType,
1207 IntegerAttr intAttr) {
1209 if (uint32_t
id = getGraphConstantARMId(intAttr)) {
1214 uint32_t typeID = 0;
1215 if (
failed(processType(loc, graphConstType, typeID))) {
1219 uint32_t resultID = getNextID();
1220 APInt value = intAttr.getValue();
1221 unsigned bitwidth = value.getBitWidth();
1222 if (bitwidth > 32) {
1223 emitError(loc,
"Too wide attribute for OpGraphConstantARM: ")
1224 << bitwidth <<
" bits";
1227 bool isSigned = value.isSignedIntN(bitwidth);
1231 word =
static_cast<int32_t
>(value.getSExtValue());
1233 word =
static_cast<uint32_t
>(value.getZExtValue());
1236 {typeID, resultID, word});
1237 graphConstIDMap[intAttr] = resultID;
1241uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
1245 if (
auto id = getConstantID(floatAttr)) {
1251 uint32_t typeID = 0;
1252 if (
failed(processType(loc, floatAttr.getType(), typeID))) {
1256 auto resultID = getNextID();
1257 APFloat value = floatAttr.getValue();
1258 const llvm::fltSemantics *semantics = &value.getSemantics();
1261 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1263 if (semantics == &APFloat::IEEEsingle()) {
1264 uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
1266 }
else if (semantics == &APFloat::IEEEdouble()) {
1270 } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
1272 {typeID, resultID, words.word1, words.word2});
1273 }
else if (llvm::is_contained({&APFloat::IEEEhalf(), &APFloat::BFloat(),
1274 &APFloat::Float8E4M3FN(),
1275 &APFloat::Float8E5M2()},
1278 static_cast<uint32_t
>(value.bitcastToAPInt().getZExtValue());
1281 std::string valueStr;
1282 llvm::raw_string_ostream rss(valueStr);
1286 << floatAttr.getType() <<
"-typed float literal: " << valueStr;
1291 constIDMap[floatAttr] = resultID;
1300 if (
auto typedAttr = dyn_cast<TypedAttr>(attr)) {
1301 return typedAttr.getType();
1304 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
1311uint32_t Serializer::prepareConstantCompositeReplicate(
Location loc,
1314 std::pair<Attribute, Type> valueTypePair{valueAttr, resultType};
1315 if (uint32_t
id = getConstantCompositeReplicateID(valueTypePair)) {
1319 uint32_t typeID = 0;
1320 if (
failed(processType(loc, resultType, typeID))) {
1328 auto compositeType = dyn_cast<CompositeType>(resultType);
1331 Type elementType = compositeType.getElementType(0);
1333 uint32_t constandID;
1334 if (elementType == valueType) {
1335 constandID = prepareConstant(loc, elementType, valueAttr);
1337 constandID = prepareConstantCompositeReplicate(loc, elementType, valueAttr);
1340 uint32_t resultID = getNextID();
1341 if (dyn_cast<spirv::TensorArmType>(resultType) &&
isZeroValue(valueAttr)) {
1343 {typeID, resultID});
1346 spirv::Opcode::OpConstantCompositeReplicateEXT,
1347 {typeID, resultID, constandID});
1350 constCompositeReplicateIDMap[valueTypePair] = resultID;
1358uint32_t Serializer::getOrCreateBlockID(
Block *block) {
1359 if (uint32_t
id = getBlockID(block))
1361 return blockIDMap[block] = getNextID();
1365void Serializer::printBlock(
Block *block, raw_ostream &os) {
1366 os <<
"block " << block <<
" (id = ";
1367 if (uint32_t
id = getBlockID(block))
1376Serializer::processBlock(
Block *block,
bool omitLabel,
1378 LLVM_DEBUG(llvm::dbgs() <<
"processing block " << block <<
":\n");
1379 LLVM_DEBUG(block->
print(llvm::dbgs()));
1380 LLVM_DEBUG(llvm::dbgs() <<
'\n');
1382 uint32_t blockID = getOrCreateBlockID(block);
1383 LLVM_DEBUG(printBlock(block, llvm::dbgs()));
1390 if (
failed(emitPhiForBlockArguments(block)))
1400 llvm::IsaPred<spirv::LoopOp, spirv::SelectionOp>)) {
1403 emitMerge =
nullptr;
1406 uint32_t blockID = getNextID();
1412 for (Operation &op : llvm::drop_end(*block)) {
1413 if (
failed(processOperation(&op)))
1421 if (
failed(processOperation(&block->
back())))
1427LogicalResult Serializer::emitPhiForBlockArguments(
Block *block) {
1433 LLVM_DEBUG(llvm::dbgs() <<
"emitting phi instructions..\n");
1440 SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
1442 auto *terminator = mlirPredecessor->getTerminator();
1443 LLVM_DEBUG(llvm::dbgs() <<
" mlir predecessor ");
1444 LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1445 LLVM_DEBUG(llvm::dbgs() <<
" terminator: " << *terminator <<
"\n");
1454 LLVM_DEBUG(llvm::dbgs() <<
" spirv predecessor ");
1455 LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1456 if (
auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1457 predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1458 }
else if (
auto branchCondOp =
1459 dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1460 std::optional<OperandRange> blockOperands;
1461 if (branchCondOp.getTrueTarget() == block) {
1462 blockOperands = branchCondOp.getTrueTargetOperands();
1464 assert(branchCondOp.getFalseTarget() == block);
1465 blockOperands = branchCondOp.getFalseTargetOperands();
1467 assert(!blockOperands->empty() &&
1468 "expected non-empty block operand range");
1469 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1470 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(terminator)) {
1471 std::optional<OperandRange> blockOperands;
1472 if (block == switchOp.getDefaultTarget()) {
1473 blockOperands = switchOp.getDefaultOperands();
1475 SuccessorRange targets = switchOp.getTargets();
1476 auto it = llvm::find(targets, block);
1477 assert(it != targets.end());
1478 size_t index = std::distance(targets.begin(), it);
1479 blockOperands = switchOp.getTargetOperands(index);
1481 assert(!blockOperands->empty() &&
1482 "expected non-empty block operand range");
1483 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1485 return terminator->emitError(
"unimplemented terminator for Phi creation");
1488 llvm::dbgs() <<
" block arguments:\n";
1489 for (Value v : predecessors.back().second)
1490 llvm::dbgs() <<
" " << v <<
"\n";
1495 for (
auto argIndex : llvm::seq<unsigned>(0, block->
getNumArguments())) {
1499 uint32_t phiTypeID = 0;
1502 uint32_t phiID = getNextID();
1504 LLVM_DEBUG(llvm::dbgs() <<
"[phi] for block argument #" << argIndex <<
' '
1505 << arg <<
" (id = " << phiID <<
")\n");
1508 SmallVector<uint32_t, 8> phiArgs;
1509 phiArgs.push_back(phiTypeID);
1510 phiArgs.push_back(phiID);
1512 for (
auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1513 Value value = predecessors[predIndex].second[argIndex];
1514 uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1515 LLVM_DEBUG(llvm::dbgs() <<
"[phi] use predecessor (id = " << predBlockId
1516 <<
") value " << value <<
' ');
1518 uint32_t valueId = getValueID(value);
1522 LLVM_DEBUG(llvm::dbgs() <<
"(need to fix)\n");
1523 deferredPhiValues[value].push_back(functionBody.size() + 1 +
1526 LLVM_DEBUG(llvm::dbgs() <<
"(id = " << valueId <<
")\n");
1528 phiArgs.push_back(valueId);
1530 phiArgs.push_back(predBlockId);
1534 valueIDMap[arg] = phiID;
1544LogicalResult Serializer::encodeExtensionInstruction(
1545 Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1546 ArrayRef<uint32_t> operands) {
1548 auto &setID = extendedInstSetIDMap[extensionSetName];
1550 setID = getNextID();
1551 SmallVector<uint32_t, 16> importOperands;
1552 importOperands.push_back(setID);
1560 if (operands.size() < 2) {
1561 return op->
emitError(
"extended instructions must have a result encoding");
1563 SmallVector<uint32_t, 8> extInstOperands;
1564 extInstOperands.reserve(operands.size() + 2);
1565 extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1566 extInstOperands.push_back(setID);
1567 extInstOperands.push_back(extensionOpcode);
1568 extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1574LogicalResult Serializer::processOperation(Operation *opInst) {
1575 LLVM_DEBUG(llvm::dbgs() <<
"[op] '" << opInst->
getName() <<
"'\n");
1580 .Case([&](spirv::AddressOfOp op) {
return processAddressOfOp(op); })
1581 .Case([&](spirv::BranchOp op) {
return processBranchOp(op); })
1582 .Case([&](spirv::BranchConditionalOp op) {
1583 return processBranchConditionalOp(op);
1585 .Case([&](spirv::ConstantOp op) {
return processConstantOp(op); })
1586 .Case([&](spirv::EXTConstantCompositeReplicateOp op) {
1587 return processConstantCompositeReplicateOp(op);
1589 .Case([&](spirv::FuncOp op) {
return processFuncOp(op); })
1590 .Case([&](spirv::GraphARMOp op) {
return processGraphARMOp(op); })
1591 .Case([&](spirv::GraphEntryPointARMOp op) {
1592 return processGraphEntryPointARMOp(op);
1594 .Case([&](spirv::GraphOutputsARMOp op) {
1595 return processGraphOutputsARMOp(op);
1597 .Case([&](spirv::GlobalVariableOp op) {
1598 return processGlobalVariableOp(op);
1600 .Case([&](spirv::GraphConstantARMOp op) {
1601 return processGraphConstantARMOp(op);
1603 .Case([&](spirv::LoopOp op) {
return processLoopOp(op); })
1604 .Case([&](spirv::ReferenceOfOp op) {
return processReferenceOfOp(op); })
1605 .Case([&](spirv::SelectionOp op) {
return processSelectionOp(op); })
1606 .Case([&](spirv::SpecConstantOp op) {
return processSpecConstantOp(op); })
1607 .Case([&](spirv::SpecConstantCompositeOp op) {
1608 return processSpecConstantCompositeOp(op);
1610 .Case([&](spirv::EXTSpecConstantCompositeReplicateOp op) {
1611 return processSpecConstantCompositeReplicateOp(op);
1613 .Case([&](spirv::SpecConstantOperationOp op) {
1614 return processSpecConstantOperationOp(op);
1616 .Case([&](spirv::SwitchOp op) {
return processSwitchOp(op); })
1617 .Case([&](spirv::UndefOp op) {
return processUndefOp(op); })
1618 .Case([&](spirv::VariableOp op) {
return processVariableOp(op); })
1623 [&](Operation *op) {
return dispatchToAutogenSerialization(op); });
1626LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1627 StringRef extInstSet,
1629 SmallVector<uint32_t, 4> operands;
1630 Location loc = op->
getLoc();
1632 uint32_t resultID = 0;
1634 uint32_t resultTypeID = 0;
1637 operands.push_back(resultTypeID);
1639 resultID = getNextID();
1640 operands.push_back(resultID);
1641 valueIDMap[op->
getResult(0)] = resultID;
1645 operands.push_back(getValueID(operand));
1647 if (
failed(emitDebugLine(functionBody, loc)))
1650 if (extInstSet.empty()) {
1654 if (
failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1660 if (
failed(processDecoration(loc, resultID, attr)))
1668LogicalResult Serializer::emitDecoration(uint32_t
target,
1669 spirv::Decoration decoration,
1670 ArrayRef<uint32_t> params) {
1671 uint32_t wordCount = 3 + params.size();
1672 llvm::append_values(
1675 static_cast<uint32_t
>(decoration));
1676 llvm::append_range(decorations, params);
1680LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1682 if (!options.emitDebugInfo)
1685 if (lastProcessedWasMergeInst) {
1686 lastProcessedWasMergeInst =
false;
1690 auto fileLoc = dyn_cast<FileLineColLoc>(loc);
1693 {fileID, fileLoc.getLine(), fileLoc.getColumn()});
static Block * getStructuredControlFlowOpMergeBlock(Operation *op)
Returns the merge block if the given op is a structured control flow op.
static Block * getPhiIncomingBlock(Block *block)
Given a predecessor block for a block with arguments, returns the block that should be used as the pa...
static bool isZeroValue(Attribute attr)
static void moveFuncDeclarationsToTop(spirv::ModuleOp moduleOp)
Move all functions declaration before functions definitions.
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
Location getLoc() const
Return the location for this argument.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< pred_iterator > getPredecessors()
OpListType & getOperations()
void print(raw_ostream &os)
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
llvm::iplist< Operation > OpListType
This is the list of operations in the block.
bool getValue() const
Return the boolean value of this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Operation is the basic unit of execution within MLIR.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_range getOperands()
Returns an iterator on the underlying Value's.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
unsigned getNumResults()
Return the number of results held by this operation.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
unsigned getArrayStride() const
Returns the array stride in bytes.
static ArrayType get(Type elementType, unsigned elementCount)
unsigned getArrayStride() const
Returns the array stride in bytes.
void printValueIDMap(raw_ostream &os)
(For debugging) prints each value and its corresponding result <id>.
Serializer(spirv::ModuleOp module, const SerializationOptions &options)
Creates a serializer for the given SPIR-V module.
LogicalResult serialize()
Serializes the remembered SPIR-V module.
void collect(SmallVectorImpl< uint32_t > &binary)
Collects the final SPIR-V binary.
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
bool isIdentified() const
Returns true if the StructType is identified.
StringRef getIdentifier() const
For literal structs, return an empty string.
static TensorArmType get(ArrayRef< int64_t > shape, Type elementType)
static Type getValueType(Attribute attr)
void encodeStringLiteralInto(SmallVectorImpl< uint32_t > &binary, StringRef literal)
Encodes an SPIR-V literal string into the given binary vector.
TargetEnvAttr lookupTargetEnvOrDefault(Operation *op)
Queries the target environment recursively from enclosing symbol table ops containing the given op or...
uint32_t getPrefixedOpcode(uint32_t wordCount, spirv::Opcode opcode)
Returns the word-count-prefixed opcode for an SPIR-V instruction.
void encodeInstructionInto(SmallVectorImpl< uint32_t > &binary, spirv::Opcode op, ArrayRef< uint32_t > operands)
Encodes an SPIR-V instruction with the given opcode and operands into the given binary vector.
void appendModuleHeader(SmallVectorImpl< uint32_t > &header, spirv::Version version, uint32_t idBound)
Appends a SPRI-V module header to header with the given version and idBound.
constexpr unsigned kHeaderWordCount
SPIR-V binary header word count.
static LogicalResult processDecorationList(Location loc, Decoration decoration, Attribute attrList, StringRef attrName, EmitF emitter)
static std::string getDecorationName(StringRef attrName)
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::SetVector< T, Vector, Set, N > SetVector
llvm::TypeSwitch< T, ResultT > TypeSwitch
llvm::function_ref< Fn > function_ref
Attribute decorationValue