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 = llvm::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 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
342 args.push_back(intAttr.getValue().getZExtValue());
345 return emitError(loc,
"expected integer attribute for ")
346 << stringifyDecoration(decoration);
347 case spirv::Decoration::BuiltIn:
348 if (
auto strAttr = dyn_cast<StringAttr>(attr)) {
349 auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
351 args.push_back(
static_cast<uint32_t
>(*enumVal));
355 << stringifyDecoration(decoration) <<
" decoration attribute "
356 << strAttr.getValue();
358 return emitError(loc,
"expected string attribute for ")
359 << stringifyDecoration(decoration);
360 case spirv::Decoration::Aliased:
361 case spirv::Decoration::AliasedPointer:
362 case spirv::Decoration::Flat:
363 case spirv::Decoration::NonReadable:
364 case spirv::Decoration::NonWritable:
365 case spirv::Decoration::NoPerspective:
366 case spirv::Decoration::NoSignedWrap:
367 case spirv::Decoration::NoUnsignedWrap:
368 case spirv::Decoration::RelaxedPrecision:
369 case spirv::Decoration::Restrict:
370 case spirv::Decoration::RestrictPointer:
371 case spirv::Decoration::NoContraction:
372 case spirv::Decoration::Constant:
373 case spirv::Decoration::Block:
374 case spirv::Decoration::Invariant:
375 case spirv::Decoration::Patch:
376 case spirv::Decoration::Coherent:
379 if (isa<UnitAttr, DecorationAttr>(attr))
382 "expected unit attribute or decoration attribute for ")
383 << stringifyDecoration(decoration);
384 case spirv::Decoration::CacheControlLoadINTEL:
386 loc, decoration, attr,
"CacheControlLoadINTEL",
387 [&](CacheControlLoadINTELAttr attr) {
388 unsigned cacheLevel = attr.getCacheLevel();
389 LoadCacheControl loadCacheControl = attr.getLoadCacheControl();
390 return emitDecoration(
391 resultID, decoration,
392 {cacheLevel,
static_cast<uint32_t
>(loadCacheControl)});
394 case spirv::Decoration::CacheControlStoreINTEL:
396 loc, decoration, attr,
"CacheControlStoreINTEL",
397 [&](CacheControlStoreINTELAttr attr) {
398 unsigned cacheLevel = attr.getCacheLevel();
399 StoreCacheControl storeCacheControl = attr.getStoreCacheControl();
400 return emitDecoration(
401 resultID, decoration,
402 {cacheLevel,
static_cast<uint32_t
>(storeCacheControl)});
405 return emitError(loc,
"unhandled decoration ")
406 << stringifyDecoration(decoration);
408 return emitDecoration(resultID, decoration, args);
411LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
412 NamedAttribute attr) {
413 StringRef attrName = attr.
getName().strref();
415 std::optional<Decoration> decoration =
416 spirv::symbolizeDecoration(decorationName);
419 loc,
"non-argument attributes expected to have snake-case-ified "
420 "decoration name, unhandled attribute with name : ")
423 return processDecorationAttr(loc, resultID, *decoration, attr.
getValue());
426LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
427 assert(!name.empty() &&
"unexpected empty string for OpName");
428 if (!options.emitSymbolName)
431 SmallVector<uint32_t, 4> nameOperands;
432 nameOperands.push_back(resultID);
439LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
443 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
449LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
453 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
458LogicalResult Serializer::processMemberDecoration(
463 static_cast<uint32_t
>(memberDecoration.
decoration)});
479bool Serializer::isInterfaceStructPtrType(Type type)
const {
480 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
481 switch (ptrType.getStorageClass()) {
482 case spirv::StorageClass::PhysicalStorageBuffer:
483 case spirv::StorageClass::PushConstant:
484 case spirv::StorageClass::StorageBuffer:
485 case spirv::StorageClass::Uniform:
486 return isa<spirv::StructType>(ptrType.getPointeeType());
494LogicalResult Serializer::processType(Location loc, Type type,
499 return processTypeImpl(loc, type, typeID, serializationCtx);
503Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
515 IntegerType::SignednessSemantics::Signless);
518 typeID = getTypeID(type);
522 typeID = getNextID();
523 SmallVector<uint32_t, 4> operands;
525 operands.push_back(typeID);
526 auto typeEnum = spirv::Opcode::OpTypeVoid;
527 bool deferSerialization =
false;
529 if ((isa<FunctionType>(type) &&
530 succeeded(prepareFunctionType(loc, cast<FunctionType>(type), typeEnum,
532 (isa<GraphType>(type) &&
534 prepareGraphType(loc, cast<GraphType>(type), typeEnum, operands))) ||
535 succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
536 deferSerialization, serializationCtx))) {
537 if (deferSerialization)
540 typeIDMap[type] = typeID;
544 if (recursiveStructInfos.count(type) != 0) {
547 for (
auto &ptrInfo : recursiveStructInfos[type]) {
550 SmallVector<uint32_t, 4> ptrOperands;
551 ptrOperands.push_back(ptrInfo.pointerTypeID);
552 ptrOperands.push_back(
static_cast<uint32_t
>(ptrInfo.storageClass));
553 ptrOperands.push_back(typeIDMap[type]);
559 recursiveStructInfos[type].clear();
565 return emitError(loc,
"failed to process type: ") << type;
568LogicalResult Serializer::prepareBasicType(
569 Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
570 SmallVectorImpl<uint32_t> &operands,
bool &deferSerialization,
572 deferSerialization =
false;
574 if (isVoidType(type)) {
575 typeEnum = spirv::Opcode::OpTypeVoid;
579 if (
auto intType = dyn_cast<IntegerType>(type)) {
580 if (intType.getWidth() == 1) {
581 typeEnum = spirv::Opcode::OpTypeBool;
585 typeEnum = spirv::Opcode::OpTypeInt;
586 operands.push_back(intType.getWidth());
591 operands.push_back(intType.isSigned() ? 1 : 0);
595 if (
auto floatType = dyn_cast<FloatType>(type)) {
596 typeEnum = spirv::Opcode::OpTypeFloat;
597 operands.push_back(floatType.getWidth());
598 if (floatType.isBF16()) {
599 operands.push_back(
static_cast<uint32_t
>(spirv::FPEncoding::BFloat16KHR));
604 if (
auto vectorType = dyn_cast<VectorType>(type)) {
605 uint32_t elementTypeID = 0;
606 if (
failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
607 serializationCtx))) {
610 typeEnum = spirv::Opcode::OpTypeVector;
611 operands.push_back(elementTypeID);
612 operands.push_back(vectorType.getNumElements());
616 if (
auto imageType = dyn_cast<spirv::ImageType>(type)) {
617 typeEnum = spirv::Opcode::OpTypeImage;
618 uint32_t sampledTypeID = 0;
619 if (
failed(processType(loc, imageType.getElementType(), sampledTypeID)))
622 llvm::append_values(operands, sampledTypeID,
623 static_cast<uint32_t
>(imageType.getDim()),
624 static_cast<uint32_t
>(imageType.getDepthInfo()),
625 static_cast<uint32_t
>(imageType.getArrayedInfo()),
626 static_cast<uint32_t
>(imageType.getSamplingInfo()),
627 static_cast<uint32_t
>(imageType.getSamplerUseInfo()),
628 static_cast<uint32_t
>(imageType.getImageFormat()));
632 if (
auto arrayType = dyn_cast<spirv::ArrayType>(type)) {
633 typeEnum = spirv::Opcode::OpTypeArray;
634 uint32_t elementTypeID = 0;
635 if (
failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
636 serializationCtx))) {
639 operands.push_back(elementTypeID);
640 if (
auto elementCountID = prepareConstantInt(
641 loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
642 operands.push_back(elementCountID);
644 return processTypeDecoration(loc, arrayType, resultID);
647 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
648 uint32_t pointeeTypeID = 0;
649 spirv::StructType pointeeStruct =
650 dyn_cast<spirv::StructType>(ptrType.getPointeeType());
653 serializationCtx.count(pointeeStruct.
getIdentifier()) != 0) {
658 SmallVector<uint32_t, 2> forwardPtrOperands;
659 forwardPtrOperands.push_back(resultID);
660 forwardPtrOperands.push_back(
661 static_cast<uint32_t
>(ptrType.getStorageClass()));
664 spirv::Opcode::OpTypeForwardPointer,
676 deferSerialization =
true;
680 recursiveStructInfos[structType].push_back(
681 {resultID, ptrType.getStorageClass()});
683 if (
failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
688 typeEnum = spirv::Opcode::OpTypePointer;
689 operands.push_back(
static_cast<uint32_t
>(ptrType.getStorageClass()));
690 operands.push_back(pointeeTypeID);
695 if (isInterfaceStructPtrType(ptrType)) {
696 auto structType = cast<spirv::StructType>(ptrType.getPointeeType());
697 if (!structType.hasDecoration(spirv::Decoration::Block))
698 if (
failed(emitDecoration(getTypeID(pointeeStruct),
699 spirv::Decoration::Block)))
700 return emitError(loc,
"cannot decorate ")
701 << pointeeStruct <<
" with Block decoration";
707 if (
auto runtimeArrayType = dyn_cast<spirv::RuntimeArrayType>(type)) {
708 uint32_t elementTypeID = 0;
709 if (
failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
710 elementTypeID, serializationCtx))) {
713 typeEnum = spirv::Opcode::OpTypeRuntimeArray;
714 operands.push_back(elementTypeID);
715 return processTypeDecoration(loc, runtimeArrayType, resultID);
718 if (
auto sampledImageType = dyn_cast<spirv::SampledImageType>(type)) {
719 typeEnum = spirv::Opcode::OpTypeSampledImage;
720 uint32_t imageTypeID = 0;
722 processType(loc, sampledImageType.getImageType(), imageTypeID))) {
725 operands.push_back(imageTypeID);
729 if (
auto structType = dyn_cast<spirv::StructType>(type)) {
730 if (structType.isIdentified()) {
731 if (
failed(processName(resultID, structType.getIdentifier())))
733 serializationCtx.insert(structType.getIdentifier());
736 bool hasOffset = structType.hasOffset();
737 for (
auto elementIndex :
738 llvm::seq<uint32_t>(0, structType.getNumElements())) {
739 uint32_t elementTypeID = 0;
740 if (
failed(processTypeImpl(loc, structType.getElementType(elementIndex),
741 elementTypeID, serializationCtx))) {
744 operands.push_back(elementTypeID);
746 auto intType = IntegerType::get(structType.getContext(), 32);
748 spirv::StructType::MemberDecorationInfo offsetDecoration{
749 elementIndex, spirv::Decoration::Offset,
750 IntegerAttr::get(intType,
751 structType.getMemberOffset(elementIndex))};
752 if (
failed(processMemberDecoration(resultID, offsetDecoration))) {
753 return emitError(loc,
"cannot decorate ")
754 << elementIndex <<
"-th member of " << structType
755 <<
" with its offset";
759 SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
760 structType.getMemberDecorations(memberDecorations);
762 for (
auto &memberDecoration : memberDecorations) {
763 if (
failed(processMemberDecoration(resultID, memberDecoration))) {
764 return emitError(loc,
"cannot decorate ")
765 <<
static_cast<uint32_t
>(memberDecoration.
memberIndex)
766 <<
"-th member of " << structType <<
" with "
767 << stringifyDecoration(memberDecoration.
decoration);
771 SmallVector<spirv::StructType::StructDecorationInfo, 1> structDecorations;
772 structType.getStructDecorations(structDecorations);
774 for (spirv::StructType::StructDecorationInfo &structDecoration :
776 if (
failed(processDecorationAttr(loc, resultID,
777 structDecoration.decoration,
778 structDecoration.decorationValue))) {
779 return emitError(loc,
"cannot decorate struct ")
780 << structType <<
" with "
781 << stringifyDecoration(structDecoration.decoration);
785 typeEnum = spirv::Opcode::OpTypeStruct;
787 if (structType.isIdentified())
788 serializationCtx.remove(structType.getIdentifier());
793 if (
auto cooperativeMatrixType =
794 dyn_cast<spirv::CooperativeMatrixType>(type)) {
795 uint32_t elementTypeID = 0;
796 if (
failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
797 elementTypeID, serializationCtx))) {
800 typeEnum = spirv::Opcode::OpTypeCooperativeMatrixKHR;
801 auto getConstantOp = [&](uint32_t id) {
802 auto attr = IntegerAttr::get(IntegerType::get(type.
getContext(), 32),
id);
803 return prepareConstantInt(loc, attr);
806 operands, elementTypeID,
807 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getScope())),
808 getConstantOp(cooperativeMatrixType.getRows()),
809 getConstantOp(cooperativeMatrixType.getColumns()),
810 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getUse())));
814 if (
auto matrixType = dyn_cast<spirv::MatrixType>(type)) {
815 uint32_t elementTypeID = 0;
816 if (
failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
817 serializationCtx))) {
820 typeEnum = spirv::Opcode::OpTypeMatrix;
821 llvm::append_values(operands, elementTypeID, matrixType.getNumColumns());
825 if (
auto tensorArmType = llvm::dyn_cast<TensorArmType>(type)) {
826 uint32_t elementTypeID = 0;
828 uint32_t shapeID = 0;
830 if (
failed(processTypeImpl(loc, tensorArmType.getElementType(),
831 elementTypeID, serializationCtx))) {
834 if (tensorArmType.hasRank()) {
835 ArrayRef<int64_t> dims = tensorArmType.getShape();
837 rankID = prepareConstantInt(loc, mlirBuilder.getI32IntegerAttr(rank));
842 bool shaped = llvm::all_of(dims, [](
const auto &dim) {
return dim > 0; });
843 if (rank > 0 && shaped) {
844 auto I32Type = IntegerType::get(type.
getContext(), 32);
847 SmallVector<uint64_t, 1> index(rank);
848 shapeID = prepareDenseElementsConstant(
850 mlirBuilder.getI32TensorAttr(SmallVector<int32_t>(dims)), 0,
853 shapeID = prepareArrayConstant(
855 mlirBuilder.getI32ArrayAttr(SmallVector<int32_t>(dims)));
862 typeEnum = spirv::Opcode::OpTypeTensorARM;
863 operands.push_back(elementTypeID);
866 operands.push_back(rankID);
869 operands.push_back(shapeID);
874 return emitError(loc,
"unhandled type in serialization: ") << type;
878Serializer::prepareFunctionType(Location loc, FunctionType type,
879 spirv::Opcode &typeEnum,
880 SmallVectorImpl<uint32_t> &operands) {
881 typeEnum = spirv::Opcode::OpTypeFunction;
882 assert(type.getNumResults() <= 1 &&
883 "serialization supports only a single return value");
884 uint32_t resultID = 0;
886 loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
890 operands.push_back(resultID);
891 for (
auto &res : type.getInputs()) {
892 uint32_t argTypeID = 0;
893 if (
failed(processType(loc, res, argTypeID))) {
896 operands.push_back(argTypeID);
902Serializer::prepareGraphType(Location loc, GraphType type,
903 spirv::Opcode &typeEnum,
904 SmallVectorImpl<uint32_t> &operands) {
905 typeEnum = spirv::Opcode::OpTypeGraphARM;
906 assert(type.getNumResults() >= 1 &&
907 "serialization requires at least a return value");
909 operands.push_back(type.getNumInputs());
911 for (Type argType : type.getInputs()) {
912 uint32_t argTypeID = 0;
913 if (
failed(processType(loc, argType, argTypeID)))
915 operands.push_back(argTypeID);
918 for (Type resType : type.getResults()) {
919 uint32_t resTypeID = 0;
920 if (
failed(processType(loc, resType, resTypeID)))
922 operands.push_back(resTypeID);
932uint32_t Serializer::prepareConstant(Location loc, Type constType,
933 Attribute valueAttr) {
934 if (
auto id = prepareConstantScalar(loc, valueAttr)) {
941 if (
auto id = getConstantID(valueAttr)) {
946 if (
failed(processType(loc, constType, typeID))) {
950 uint32_t resultID = 0;
951 if (
auto attr = dyn_cast<DenseElementsAttr>(valueAttr)) {
952 int rank = dyn_cast<ShapedType>(attr.getType()).getRank();
953 SmallVector<uint64_t, 4> index(rank);
954 resultID = prepareDenseElementsConstant(loc, constType, attr,
956 }
else if (
auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
957 resultID = prepareArrayConstant(loc, constType, arrayAttr);
961 emitError(loc,
"cannot serialize attribute: ") << valueAttr;
965 constIDMap[valueAttr] = resultID;
969uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
972 if (
failed(processType(loc, constType, typeID))) {
976 uint32_t resultID = getNextID();
977 SmallVector<uint32_t, 4> operands = {typeID, resultID};
978 operands.reserve(attr.size() + 2);
979 auto elementType = cast<spirv::ArrayType>(constType).getElementType();
980 for (Attribute elementAttr : attr) {
981 if (
auto elementID = prepareConstant(loc, elementType, elementAttr)) {
982 operands.push_back(elementID);
987 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
996Serializer::prepareDenseElementsConstant(Location loc, Type constType,
997 DenseElementsAttr valueAttr,
int dim,
998 MutableArrayRef<uint64_t> index) {
999 auto shapedType = dyn_cast<ShapedType>(valueAttr.
getType());
1000 assert(dim <= shapedType.getRank());
1001 if (shapedType.getRank() == dim) {
1002 if (
auto attr = dyn_cast<DenseIntElementsAttr>(valueAttr)) {
1003 return attr.getType().getElementType().isInteger(1)
1004 ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
1005 : prepareConstantInt(loc,
1006 attr.getValues<IntegerAttr>()[index]);
1008 if (
auto attr = dyn_cast<DenseFPElementsAttr>(valueAttr)) {
1009 return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
1014 uint32_t typeID = 0;
1015 if (
failed(processType(loc, constType, typeID))) {
1019 int64_t numberOfConstituents = shapedType.getDimSize(dim);
1020 uint32_t resultID = getNextID();
1021 SmallVector<uint32_t, 4> operands = {typeID, resultID};
1022 auto elementType = cast<spirv::CompositeType>(constType).getElementType(0);
1023 if (
auto tensorArmType = dyn_cast<spirv::TensorArmType>(constType)) {
1024 ArrayRef<int64_t> innerShape = tensorArmType.getShape().drop_front();
1025 if (!innerShape.empty())
1033 if (isa<spirv::CooperativeMatrixType>(constType)) {
1037 "cannot serialize a non-splat value for a cooperative matrix type");
1042 operands.reserve(3);
1045 if (
auto elementID = prepareDenseElementsConstant(
1046 loc, elementType, valueAttr, shapedType.getRank(), index)) {
1047 operands.push_back(elementID);
1051 }
else if (isa<spirv::TensorArmType>(constType) &&
isZeroValue(valueAttr)) {
1053 {typeID, resultID});
1056 operands.reserve(numberOfConstituents + 2);
1057 for (
int i = 0; i < numberOfConstituents; ++i) {
1059 if (
auto elementID = prepareDenseElementsConstant(
1060 loc, elementType, valueAttr, dim + 1, index)) {
1061 operands.push_back(elementID);
1067 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1073uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
1075 if (
auto floatAttr = dyn_cast<FloatAttr>(valueAttr)) {
1076 return prepareConstantFp(loc, floatAttr, isSpec);
1078 if (
auto boolAttr = dyn_cast<BoolAttr>(valueAttr)) {
1079 return prepareConstantBool(loc, boolAttr, isSpec);
1081 if (
auto intAttr = dyn_cast<IntegerAttr>(valueAttr)) {
1082 return prepareConstantInt(loc, intAttr, isSpec);
1088uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
1092 if (
auto id = getConstantID(boolAttr)) {
1098 uint32_t typeID = 0;
1099 if (
failed(processType(loc, cast<IntegerAttr>(boolAttr).
getType(), typeID))) {
1103 auto resultID = getNextID();
1105 ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
1106 : spirv::Opcode::OpConstantTrue)
1107 : (isSpec ? spirv::Opcode::OpSpecConstantFalse
1108 : spirv::Opcode::OpConstantFalse);
1112 constIDMap[boolAttr] = resultID;
1117uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
1121 if (
auto id = getConstantID(intAttr)) {
1127 uint32_t typeID = 0;
1128 if (
failed(processType(loc, intAttr.getType(), typeID))) {
1132 auto resultID = getNextID();
1133 APInt value = intAttr.getValue();
1134 unsigned bitwidth = value.getBitWidth();
1135 bool isSigned = intAttr.getType().isSignedInteger();
1137 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1150 word =
static_cast<int32_t
>(value.getSExtValue());
1152 word =
static_cast<uint32_t
>(value.getZExtValue());
1164 words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
1166 words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
1169 {typeID, resultID, words.word1, words.word2});
1172 std::string valueStr;
1173 llvm::raw_string_ostream rss(valueStr);
1174 value.print(rss,
false);
1177 << bitwidth <<
"-bit integer literal: " << valueStr;
1183 constIDMap[intAttr] = resultID;
1188uint32_t Serializer::prepareGraphConstantId(Location loc, Type graphConstType,
1189 IntegerAttr intAttr) {
1191 if (uint32_t
id = getGraphConstantARMId(intAttr)) {
1196 uint32_t typeID = 0;
1197 if (
failed(processType(loc, graphConstType, typeID))) {
1201 uint32_t resultID = getNextID();
1202 APInt value = intAttr.getValue();
1203 unsigned bitwidth = value.getBitWidth();
1204 if (bitwidth > 32) {
1205 emitError(loc,
"Too wide attribute for OpGraphConstantARM: ")
1206 << bitwidth <<
" bits";
1209 bool isSigned = value.isSignedIntN(bitwidth);
1213 word =
static_cast<int32_t
>(value.getSExtValue());
1215 word =
static_cast<uint32_t
>(value.getZExtValue());
1218 {typeID, resultID, word});
1219 graphConstIDMap[intAttr] = resultID;
1223uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
1227 if (
auto id = getConstantID(floatAttr)) {
1233 uint32_t typeID = 0;
1234 if (
failed(processType(loc, floatAttr.getType(), typeID))) {
1238 auto resultID = getNextID();
1239 APFloat value = floatAttr.getValue();
1240 const llvm::fltSemantics *semantics = &value.getSemantics();
1243 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1245 if (semantics == &APFloat::IEEEsingle()) {
1246 uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
1248 }
else if (semantics == &APFloat::IEEEdouble()) {
1252 } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
1254 {typeID, resultID, words.word1, words.word2});
1255 }
else if (semantics == &APFloat::IEEEhalf() ||
1256 semantics == &APFloat::BFloat()) {
1258 static_cast<uint32_t
>(value.bitcastToAPInt().getZExtValue());
1261 std::string valueStr;
1262 llvm::raw_string_ostream rss(valueStr);
1266 << floatAttr.getType() <<
"-typed float literal: " << valueStr;
1271 constIDMap[floatAttr] = resultID;
1280 if (
auto typedAttr = dyn_cast<TypedAttr>(attr)) {
1281 return typedAttr.getType();
1284 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
1291uint32_t Serializer::prepareConstantCompositeReplicate(
Location loc,
1294 std::pair<Attribute, Type> valueTypePair{valueAttr, resultType};
1295 if (uint32_t
id = getConstantCompositeReplicateID(valueTypePair)) {
1299 uint32_t typeID = 0;
1300 if (
failed(processType(loc, resultType, typeID))) {
1308 auto compositeType = dyn_cast<CompositeType>(resultType);
1311 Type elementType = compositeType.getElementType(0);
1313 uint32_t constandID;
1314 if (elementType == valueType) {
1315 constandID = prepareConstant(loc, elementType, valueAttr);
1317 constandID = prepareConstantCompositeReplicate(loc, elementType, valueAttr);
1320 uint32_t resultID = getNextID();
1321 if (dyn_cast<spirv::TensorArmType>(resultType) &&
isZeroValue(valueAttr)) {
1323 {typeID, resultID});
1326 spirv::Opcode::OpConstantCompositeReplicateEXT,
1327 {typeID, resultID, constandID});
1330 constCompositeReplicateIDMap[valueTypePair] = resultID;
1338uint32_t Serializer::getOrCreateBlockID(
Block *block) {
1339 if (uint32_t
id = getBlockID(block))
1341 return blockIDMap[block] = getNextID();
1345void Serializer::printBlock(
Block *block, raw_ostream &os) {
1346 os <<
"block " << block <<
" (id = ";
1347 if (uint32_t
id = getBlockID(block))
1356Serializer::processBlock(
Block *block,
bool omitLabel,
1358 LLVM_DEBUG(llvm::dbgs() <<
"processing block " << block <<
":\n");
1359 LLVM_DEBUG(block->
print(llvm::dbgs()));
1360 LLVM_DEBUG(llvm::dbgs() <<
'\n');
1362 uint32_t blockID = getOrCreateBlockID(block);
1363 LLVM_DEBUG(printBlock(block, llvm::dbgs()));
1370 if (
failed(emitPhiForBlockArguments(block)))
1380 llvm::IsaPred<spirv::LoopOp, spirv::SelectionOp>)) {
1383 emitMerge =
nullptr;
1386 uint32_t blockID = getNextID();
1392 for (Operation &op : llvm::drop_end(*block)) {
1393 if (
failed(processOperation(&op)))
1401 if (
failed(processOperation(&block->
back())))
1407LogicalResult Serializer::emitPhiForBlockArguments(
Block *block) {
1413 LLVM_DEBUG(llvm::dbgs() <<
"emitting phi instructions..\n");
1420 SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
1422 auto *terminator = mlirPredecessor->getTerminator();
1423 LLVM_DEBUG(llvm::dbgs() <<
" mlir predecessor ");
1424 LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1425 LLVM_DEBUG(llvm::dbgs() <<
" terminator: " << *terminator <<
"\n");
1434 LLVM_DEBUG(llvm::dbgs() <<
" spirv predecessor ");
1435 LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1436 if (
auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1437 predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1438 }
else if (
auto branchCondOp =
1439 dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1440 std::optional<OperandRange> blockOperands;
1441 if (branchCondOp.getTrueTarget() == block) {
1442 blockOperands = branchCondOp.getTrueTargetOperands();
1444 assert(branchCondOp.getFalseTarget() == block);
1445 blockOperands = branchCondOp.getFalseTargetOperands();
1447 assert(!blockOperands->empty() &&
1448 "expected non-empty block operand range");
1449 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1450 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(terminator)) {
1451 std::optional<OperandRange> blockOperands;
1452 if (block == switchOp.getDefaultTarget()) {
1453 blockOperands = switchOp.getDefaultOperands();
1455 SuccessorRange targets = switchOp.getTargets();
1456 auto it = llvm::find(targets, block);
1457 assert(it != targets.end());
1458 size_t index = std::distance(targets.begin(), it);
1459 blockOperands = switchOp.getTargetOperands(index);
1461 assert(!blockOperands->empty() &&
1462 "expected non-empty block operand range");
1463 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1465 return terminator->emitError(
"unimplemented terminator for Phi creation");
1468 llvm::dbgs() <<
" block arguments:\n";
1469 for (Value v : predecessors.back().second)
1470 llvm::dbgs() <<
" " << v <<
"\n";
1475 for (
auto argIndex : llvm::seq<unsigned>(0, block->
getNumArguments())) {
1479 uint32_t phiTypeID = 0;
1482 uint32_t phiID = getNextID();
1484 LLVM_DEBUG(llvm::dbgs() <<
"[phi] for block argument #" << argIndex <<
' '
1485 << arg <<
" (id = " << phiID <<
")\n");
1488 SmallVector<uint32_t, 8> phiArgs;
1489 phiArgs.push_back(phiTypeID);
1490 phiArgs.push_back(phiID);
1492 for (
auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1493 Value value = predecessors[predIndex].second[argIndex];
1494 uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1495 LLVM_DEBUG(llvm::dbgs() <<
"[phi] use predecessor (id = " << predBlockId
1496 <<
") value " << value <<
' ');
1498 uint32_t valueId = getValueID(value);
1502 LLVM_DEBUG(llvm::dbgs() <<
"(need to fix)\n");
1503 deferredPhiValues[value].push_back(functionBody.size() + 1 +
1506 LLVM_DEBUG(llvm::dbgs() <<
"(id = " << valueId <<
")\n");
1508 phiArgs.push_back(valueId);
1510 phiArgs.push_back(predBlockId);
1514 valueIDMap[arg] = phiID;
1524LogicalResult Serializer::encodeExtensionInstruction(
1525 Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1526 ArrayRef<uint32_t> operands) {
1528 auto &setID = extendedInstSetIDMap[extensionSetName];
1530 setID = getNextID();
1531 SmallVector<uint32_t, 16> importOperands;
1532 importOperands.push_back(setID);
1540 if (operands.size() < 2) {
1541 return op->
emitError(
"extended instructions must have a result encoding");
1543 SmallVector<uint32_t, 8> extInstOperands;
1544 extInstOperands.reserve(operands.size() + 2);
1545 extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1546 extInstOperands.push_back(setID);
1547 extInstOperands.push_back(extensionOpcode);
1548 extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1554LogicalResult Serializer::processOperation(Operation *opInst) {
1555 LLVM_DEBUG(llvm::dbgs() <<
"[op] '" << opInst->
getName() <<
"'\n");
1560 .Case([&](spirv::AddressOfOp op) {
return processAddressOfOp(op); })
1561 .Case([&](spirv::BranchOp op) {
return processBranchOp(op); })
1562 .Case([&](spirv::BranchConditionalOp op) {
1563 return processBranchConditionalOp(op);
1565 .Case([&](spirv::ConstantOp op) {
return processConstantOp(op); })
1566 .Case([&](spirv::EXTConstantCompositeReplicateOp op) {
1567 return processConstantCompositeReplicateOp(op);
1569 .Case([&](spirv::FuncOp op) {
return processFuncOp(op); })
1570 .Case([&](spirv::GraphARMOp op) {
return processGraphARMOp(op); })
1571 .Case([&](spirv::GraphEntryPointARMOp op) {
1572 return processGraphEntryPointARMOp(op);
1574 .Case([&](spirv::GraphOutputsARMOp op) {
1575 return processGraphOutputsARMOp(op);
1577 .Case([&](spirv::GlobalVariableOp op) {
1578 return processGlobalVariableOp(op);
1580 .Case([&](spirv::GraphConstantARMOp op) {
1581 return processGraphConstantARMOp(op);
1583 .Case([&](spirv::LoopOp op) {
return processLoopOp(op); })
1584 .Case([&](spirv::ReferenceOfOp op) {
return processReferenceOfOp(op); })
1585 .Case([&](spirv::SelectionOp op) {
return processSelectionOp(op); })
1586 .Case([&](spirv::SpecConstantOp op) {
return processSpecConstantOp(op); })
1587 .Case([&](spirv::SpecConstantCompositeOp op) {
1588 return processSpecConstantCompositeOp(op);
1590 .Case([&](spirv::EXTSpecConstantCompositeReplicateOp op) {
1591 return processSpecConstantCompositeReplicateOp(op);
1593 .Case([&](spirv::SpecConstantOperationOp op) {
1594 return processSpecConstantOperationOp(op);
1596 .Case([&](spirv::SwitchOp op) {
return processSwitchOp(op); })
1597 .Case([&](spirv::UndefOp op) {
return processUndefOp(op); })
1598 .Case([&](spirv::VariableOp op) {
return processVariableOp(op); })
1603 [&](Operation *op) {
return dispatchToAutogenSerialization(op); });
1606LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1607 StringRef extInstSet,
1609 SmallVector<uint32_t, 4> operands;
1610 Location loc = op->
getLoc();
1612 uint32_t resultID = 0;
1614 uint32_t resultTypeID = 0;
1617 operands.push_back(resultTypeID);
1619 resultID = getNextID();
1620 operands.push_back(resultID);
1621 valueIDMap[op->
getResult(0)] = resultID;
1625 operands.push_back(getValueID(operand));
1627 if (
failed(emitDebugLine(functionBody, loc)))
1630 if (extInstSet.empty()) {
1634 if (
failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1640 if (
failed(processDecoration(loc, resultID, attr)))
1648LogicalResult Serializer::emitDecoration(uint32_t
target,
1649 spirv::Decoration decoration,
1650 ArrayRef<uint32_t> params) {
1651 uint32_t wordCount = 3 + params.size();
1652 llvm::append_values(
1655 static_cast<uint32_t
>(decoration));
1656 llvm::append_range(decorations, params);
1660LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1662 if (!options.emitDebugInfo)
1665 if (lastProcessedWasMergeInst) {
1666 lastProcessedWasMergeInst =
false;
1670 auto fileLoc = dyn_cast<FileLineColLoc>(loc);
1673 {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.
static bool isZeroValue(Value val)
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