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 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
343 args.push_back(intAttr.getValue().getZExtValue());
346 return emitError(loc,
"expected integer attribute for ")
347 << stringifyDecoration(decoration);
348 case spirv::Decoration::BuiltIn:
349 if (
auto strAttr = dyn_cast<StringAttr>(attr)) {
350 auto enumVal = spirv::symbolizeBuiltIn(strAttr.getValue());
352 args.push_back(
static_cast<uint32_t
>(*enumVal));
356 << stringifyDecoration(decoration) <<
" decoration attribute "
357 << strAttr.getValue();
359 return emitError(loc,
"expected string attribute for ")
360 << stringifyDecoration(decoration);
361 case spirv::Decoration::Aliased:
362 case spirv::Decoration::AliasedPointer:
363 case spirv::Decoration::Flat:
364 case spirv::Decoration::NonReadable:
365 case spirv::Decoration::NonWritable:
366 case spirv::Decoration::NoPerspective:
367 case spirv::Decoration::NoSignedWrap:
368 case spirv::Decoration::NoUnsignedWrap:
369 case spirv::Decoration::RelaxedPrecision:
370 case spirv::Decoration::Restrict:
371 case spirv::Decoration::RestrictPointer:
372 case spirv::Decoration::NoContraction:
373 case spirv::Decoration::Constant:
374 case spirv::Decoration::Block:
375 case spirv::Decoration::Invariant:
376 case spirv::Decoration::Patch:
377 case spirv::Decoration::Coherent:
380 if (isa<UnitAttr, DecorationAttr>(attr))
383 "expected unit attribute or decoration attribute for ")
384 << stringifyDecoration(decoration);
385 case spirv::Decoration::CacheControlLoadINTEL:
387 loc, decoration, attr,
"CacheControlLoadINTEL",
388 [&](CacheControlLoadINTELAttr attr) {
389 unsigned cacheLevel = attr.getCacheLevel();
390 LoadCacheControl loadCacheControl = attr.getLoadCacheControl();
391 return emitDecoration(
392 resultID, decoration,
393 {cacheLevel,
static_cast<uint32_t
>(loadCacheControl)});
395 case spirv::Decoration::CacheControlStoreINTEL:
397 loc, decoration, attr,
"CacheControlStoreINTEL",
398 [&](CacheControlStoreINTELAttr attr) {
399 unsigned cacheLevel = attr.getCacheLevel();
400 StoreCacheControl storeCacheControl = attr.getStoreCacheControl();
401 return emitDecoration(
402 resultID, decoration,
403 {cacheLevel,
static_cast<uint32_t
>(storeCacheControl)});
406 return emitError(loc,
"unhandled decoration ")
407 << stringifyDecoration(decoration);
409 return emitDecoration(resultID, decoration, args);
412LogicalResult Serializer::processDecoration(Location loc, uint32_t resultID,
413 NamedAttribute attr) {
414 StringRef attrName = attr.
getName().strref();
416 std::optional<Decoration> decoration =
417 spirv::symbolizeDecoration(decorationName);
420 loc,
"non-argument attributes expected to have snake-case-ified "
421 "decoration name, unhandled attribute with name : ")
424 return processDecorationAttr(loc, resultID, *decoration, attr.
getValue());
427LogicalResult Serializer::processName(uint32_t resultID, StringRef name) {
428 assert(!name.empty() &&
"unexpected empty string for OpName");
429 if (!options.emitSymbolName)
432 SmallVector<uint32_t, 4> nameOperands;
433 nameOperands.push_back(resultID);
440LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
444 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
450LogicalResult Serializer::processTypeDecoration<spirv::RuntimeArrayType>(
454 return emitDecoration(resultID, spirv::Decoration::ArrayStride, {stride});
459LogicalResult Serializer::processMemberDecoration(
464 static_cast<uint32_t
>(memberDecoration.
decoration)});
480bool Serializer::isInterfaceStructPtrType(Type type)
const {
481 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
482 switch (ptrType.getStorageClass()) {
483 case spirv::StorageClass::PhysicalStorageBuffer:
484 case spirv::StorageClass::PushConstant:
485 case spirv::StorageClass::StorageBuffer:
486 case spirv::StorageClass::Uniform:
487 return isa<spirv::StructType>(ptrType.getPointeeType());
495LogicalResult Serializer::processType(Location loc, Type type,
500 return processTypeImpl(loc, type, typeID, serializationCtx);
504Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
516 IntegerType::SignednessSemantics::Signless);
519 typeID = getTypeID(type);
523 typeID = getNextID();
524 SmallVector<uint32_t, 4> operands;
526 operands.push_back(typeID);
527 auto typeEnum = spirv::Opcode::OpTypeVoid;
528 bool deferSerialization =
false;
530 if ((isa<FunctionType>(type) &&
531 succeeded(prepareFunctionType(loc, cast<FunctionType>(type), typeEnum,
533 (isa<GraphType>(type) &&
535 prepareGraphType(loc, cast<GraphType>(type), typeEnum, operands))) ||
536 succeeded(prepareBasicType(loc, type, typeID, typeEnum, operands,
537 deferSerialization, serializationCtx))) {
538 if (deferSerialization)
541 typeIDMap[type] = typeID;
545 if (recursiveStructInfos.count(type) != 0) {
548 for (
auto &ptrInfo : recursiveStructInfos[type]) {
551 SmallVector<uint32_t, 4> ptrOperands;
552 ptrOperands.push_back(ptrInfo.pointerTypeID);
553 ptrOperands.push_back(
static_cast<uint32_t
>(ptrInfo.storageClass));
554 ptrOperands.push_back(typeIDMap[type]);
560 recursiveStructInfos[type].clear();
566 return emitError(loc,
"failed to process type: ") << type;
569LogicalResult Serializer::prepareBasicType(
570 Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
571 SmallVectorImpl<uint32_t> &operands,
bool &deferSerialization,
573 deferSerialization =
false;
575 if (isVoidType(type)) {
576 typeEnum = spirv::Opcode::OpTypeVoid;
580 if (
auto intType = dyn_cast<IntegerType>(type)) {
581 if (intType.getWidth() == 1) {
582 typeEnum = spirv::Opcode::OpTypeBool;
586 typeEnum = spirv::Opcode::OpTypeInt;
587 operands.push_back(intType.getWidth());
592 operands.push_back(intType.isSigned() ? 1 : 0);
596 if (
auto floatType = dyn_cast<FloatType>(type)) {
597 typeEnum = spirv::Opcode::OpTypeFloat;
598 operands.push_back(floatType.getWidth());
599 if (floatType.isBF16()) {
600 operands.push_back(
static_cast<uint32_t
>(spirv::FPEncoding::BFloat16KHR));
605 if (
auto vectorType = dyn_cast<VectorType>(type)) {
606 uint32_t elementTypeID = 0;
607 if (
failed(processTypeImpl(loc, vectorType.getElementType(), elementTypeID,
608 serializationCtx))) {
611 typeEnum = spirv::Opcode::OpTypeVector;
612 operands.push_back(elementTypeID);
613 operands.push_back(vectorType.getNumElements());
617 if (
auto imageType = dyn_cast<spirv::ImageType>(type)) {
618 typeEnum = spirv::Opcode::OpTypeImage;
619 uint32_t sampledTypeID = 0;
620 if (
failed(processType(loc, imageType.getElementType(), sampledTypeID)))
623 llvm::append_values(operands, sampledTypeID,
624 static_cast<uint32_t
>(imageType.getDim()),
625 static_cast<uint32_t
>(imageType.getDepthInfo()),
626 static_cast<uint32_t
>(imageType.getArrayedInfo()),
627 static_cast<uint32_t
>(imageType.getSamplingInfo()),
628 static_cast<uint32_t
>(imageType.getSamplerUseInfo()),
629 static_cast<uint32_t
>(imageType.getImageFormat()));
633 if (
auto arrayType = dyn_cast<spirv::ArrayType>(type)) {
634 typeEnum = spirv::Opcode::OpTypeArray;
635 uint32_t elementTypeID = 0;
636 if (
failed(processTypeImpl(loc, arrayType.getElementType(), elementTypeID,
637 serializationCtx))) {
640 operands.push_back(elementTypeID);
641 if (
auto elementCountID = prepareConstantInt(
642 loc, mlirBuilder.getI32IntegerAttr(arrayType.getNumElements()))) {
643 operands.push_back(elementCountID);
645 return processTypeDecoration(loc, arrayType, resultID);
648 if (
auto ptrType = dyn_cast<spirv::PointerType>(type)) {
649 uint32_t pointeeTypeID = 0;
650 spirv::StructType pointeeStruct =
651 dyn_cast<spirv::StructType>(ptrType.getPointeeType());
654 serializationCtx.count(pointeeStruct.
getIdentifier()) != 0) {
659 SmallVector<uint32_t, 2> forwardPtrOperands;
660 forwardPtrOperands.push_back(resultID);
661 forwardPtrOperands.push_back(
662 static_cast<uint32_t
>(ptrType.getStorageClass()));
665 spirv::Opcode::OpTypeForwardPointer,
677 deferSerialization =
true;
681 recursiveStructInfos[structType].push_back(
682 {resultID, ptrType.getStorageClass()});
684 if (
failed(processTypeImpl(loc, ptrType.getPointeeType(), pointeeTypeID,
689 typeEnum = spirv::Opcode::OpTypePointer;
690 operands.push_back(
static_cast<uint32_t
>(ptrType.getStorageClass()));
691 operands.push_back(pointeeTypeID);
696 if (isInterfaceStructPtrType(ptrType)) {
697 auto structType = cast<spirv::StructType>(ptrType.getPointeeType());
698 if (!structType.hasDecoration(spirv::Decoration::Block))
699 if (
failed(emitDecoration(getTypeID(pointeeStruct),
700 spirv::Decoration::Block)))
701 return emitError(loc,
"cannot decorate ")
702 << pointeeStruct <<
" with Block decoration";
708 if (
auto runtimeArrayType = dyn_cast<spirv::RuntimeArrayType>(type)) {
709 uint32_t elementTypeID = 0;
710 if (
failed(processTypeImpl(loc, runtimeArrayType.getElementType(),
711 elementTypeID, serializationCtx))) {
714 typeEnum = spirv::Opcode::OpTypeRuntimeArray;
715 operands.push_back(elementTypeID);
716 return processTypeDecoration(loc, runtimeArrayType, resultID);
719 if (
auto sampledImageType = dyn_cast<spirv::SampledImageType>(type)) {
720 typeEnum = spirv::Opcode::OpTypeSampledImage;
721 uint32_t imageTypeID = 0;
723 processType(loc, sampledImageType.getImageType(), imageTypeID))) {
726 operands.push_back(imageTypeID);
730 if (
auto structType = dyn_cast<spirv::StructType>(type)) {
731 if (structType.isIdentified()) {
732 if (
failed(processName(resultID, structType.getIdentifier())))
734 serializationCtx.insert(structType.getIdentifier());
737 bool hasOffset = structType.hasOffset();
738 for (
auto elementIndex :
739 llvm::seq<uint32_t>(0, structType.getNumElements())) {
740 uint32_t elementTypeID = 0;
741 if (
failed(processTypeImpl(loc, structType.getElementType(elementIndex),
742 elementTypeID, serializationCtx))) {
745 operands.push_back(elementTypeID);
747 auto intType = IntegerType::get(structType.getContext(), 32);
749 spirv::StructType::MemberDecorationInfo offsetDecoration{
750 elementIndex, spirv::Decoration::Offset,
751 IntegerAttr::get(intType,
752 structType.getMemberOffset(elementIndex))};
753 if (
failed(processMemberDecoration(resultID, offsetDecoration))) {
754 return emitError(loc,
"cannot decorate ")
755 << elementIndex <<
"-th member of " << structType
756 <<
" with its offset";
760 SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
761 structType.getMemberDecorations(memberDecorations);
763 for (
auto &memberDecoration : memberDecorations) {
764 if (
failed(processMemberDecoration(resultID, memberDecoration))) {
765 return emitError(loc,
"cannot decorate ")
766 <<
static_cast<uint32_t
>(memberDecoration.
memberIndex)
767 <<
"-th member of " << structType <<
" with "
768 << stringifyDecoration(memberDecoration.
decoration);
772 SmallVector<spirv::StructType::StructDecorationInfo, 1> structDecorations;
773 structType.getStructDecorations(structDecorations);
775 for (spirv::StructType::StructDecorationInfo &structDecoration :
777 if (
failed(processDecorationAttr(loc, resultID,
778 structDecoration.decoration,
779 structDecoration.decorationValue))) {
780 return emitError(loc,
"cannot decorate struct ")
781 << structType <<
" with "
782 << stringifyDecoration(structDecoration.decoration);
786 typeEnum = spirv::Opcode::OpTypeStruct;
788 if (structType.isIdentified())
789 serializationCtx.remove(structType.getIdentifier());
794 if (
auto cooperativeMatrixType =
795 dyn_cast<spirv::CooperativeMatrixType>(type)) {
796 uint32_t elementTypeID = 0;
797 if (
failed(processTypeImpl(loc, cooperativeMatrixType.getElementType(),
798 elementTypeID, serializationCtx))) {
801 typeEnum = spirv::Opcode::OpTypeCooperativeMatrixKHR;
802 auto getConstantOp = [&](uint32_t id) {
803 auto attr = IntegerAttr::get(IntegerType::get(type.
getContext(), 32),
id);
804 return prepareConstantInt(loc, attr);
807 operands, elementTypeID,
808 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getScope())),
809 getConstantOp(cooperativeMatrixType.getRows()),
810 getConstantOp(cooperativeMatrixType.getColumns()),
811 getConstantOp(
static_cast<uint32_t
>(cooperativeMatrixType.getUse())));
815 if (
auto matrixType = dyn_cast<spirv::MatrixType>(type)) {
816 uint32_t elementTypeID = 0;
817 if (
failed(processTypeImpl(loc, matrixType.getColumnType(), elementTypeID,
818 serializationCtx))) {
821 typeEnum = spirv::Opcode::OpTypeMatrix;
822 llvm::append_values(operands, elementTypeID, matrixType.getNumColumns());
826 if (
auto tensorArmType = dyn_cast<TensorArmType>(type)) {
827 uint32_t elementTypeID = 0;
829 uint32_t shapeID = 0;
831 if (
failed(processTypeImpl(loc, tensorArmType.getElementType(),
832 elementTypeID, serializationCtx))) {
835 if (tensorArmType.hasRank()) {
836 ArrayRef<int64_t> dims = tensorArmType.getShape();
838 rankID = prepareConstantInt(loc, mlirBuilder.getI32IntegerAttr(rank));
843 bool shaped = llvm::all_of(dims, [](
const auto &dim) {
return dim > 0; });
844 if (rank > 0 && shaped) {
845 auto I32Type = IntegerType::get(type.
getContext(), 32);
848 SmallVector<uint64_t, 1> index(rank);
849 shapeID = prepareDenseElementsConstant(
851 mlirBuilder.getI32TensorAttr(SmallVector<int32_t>(dims)), 0,
854 shapeID = prepareArrayConstant(
856 mlirBuilder.getI32ArrayAttr(SmallVector<int32_t>(dims)));
863 typeEnum = spirv::Opcode::OpTypeTensorARM;
864 operands.push_back(elementTypeID);
867 operands.push_back(rankID);
870 operands.push_back(shapeID);
875 return emitError(loc,
"unhandled type in serialization: ") << type;
879Serializer::prepareFunctionType(Location loc, FunctionType type,
880 spirv::Opcode &typeEnum,
881 SmallVectorImpl<uint32_t> &operands) {
882 typeEnum = spirv::Opcode::OpTypeFunction;
883 assert(type.getNumResults() <= 1 &&
884 "serialization supports only a single return value");
885 uint32_t resultID = 0;
887 loc, type.getNumResults() == 1 ? type.getResult(0) : getVoidType(),
891 operands.push_back(resultID);
892 for (
auto &res : type.getInputs()) {
893 uint32_t argTypeID = 0;
894 if (
failed(processType(loc, res, argTypeID))) {
897 operands.push_back(argTypeID);
903Serializer::prepareGraphType(Location loc, GraphType type,
904 spirv::Opcode &typeEnum,
905 SmallVectorImpl<uint32_t> &operands) {
906 typeEnum = spirv::Opcode::OpTypeGraphARM;
907 assert(type.getNumResults() >= 1 &&
908 "serialization requires at least a return value");
910 operands.push_back(type.getNumInputs());
912 for (Type argType : type.getInputs()) {
913 uint32_t argTypeID = 0;
914 if (
failed(processType(loc, argType, argTypeID)))
916 operands.push_back(argTypeID);
919 for (Type resType : type.getResults()) {
920 uint32_t resTypeID = 0;
921 if (
failed(processType(loc, resType, resTypeID)))
923 operands.push_back(resTypeID);
933uint32_t Serializer::prepareConstant(Location loc, Type constType,
934 Attribute valueAttr) {
935 if (
auto id = prepareConstantScalar(loc, valueAttr)) {
942 if (
auto id = getConstantID(valueAttr)) {
947 if (
failed(processType(loc, constType, typeID))) {
951 uint32_t resultID = 0;
952 if (
auto attr = dyn_cast<DenseElementsAttr>(valueAttr)) {
953 int rank = dyn_cast<ShapedType>(attr.getType()).getRank();
954 SmallVector<uint64_t, 4> index(rank);
955 resultID = prepareDenseElementsConstant(loc, constType, attr,
957 }
else if (
auto arrayAttr = dyn_cast<ArrayAttr>(valueAttr)) {
958 resultID = prepareArrayConstant(loc, constType, arrayAttr);
962 emitError(loc,
"cannot serialize attribute: ") << valueAttr;
966 constIDMap[valueAttr] = resultID;
970uint32_t Serializer::prepareArrayConstant(Location loc, Type constType,
973 if (
failed(processType(loc, constType, typeID))) {
977 uint32_t resultID = getNextID();
978 SmallVector<uint32_t, 4> operands = {typeID, resultID};
979 operands.reserve(attr.size() + 2);
980 auto elementType = cast<spirv::ArrayType>(constType).getElementType();
981 for (Attribute elementAttr : attr) {
982 if (
auto elementID = prepareConstant(loc, elementType, elementAttr)) {
983 operands.push_back(elementID);
988 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
997Serializer::prepareDenseElementsConstant(Location loc, Type constType,
998 DenseElementsAttr valueAttr,
int dim,
999 MutableArrayRef<uint64_t> index) {
1000 auto shapedType = dyn_cast<ShapedType>(valueAttr.
getType());
1001 assert(dim <= shapedType.getRank());
1002 if (shapedType.getRank() == dim) {
1003 if (
auto attr = dyn_cast<DenseIntElementsAttr>(valueAttr)) {
1004 return attr.getType().getElementType().isInteger(1)
1005 ? prepareConstantBool(loc, attr.getValues<BoolAttr>()[index])
1006 : prepareConstantInt(loc,
1007 attr.getValues<IntegerAttr>()[index]);
1009 if (
auto attr = dyn_cast<DenseFPElementsAttr>(valueAttr)) {
1010 return prepareConstantFp(loc, attr.getValues<FloatAttr>()[index]);
1015 uint32_t typeID = 0;
1016 if (
failed(processType(loc, constType, typeID))) {
1020 int64_t numberOfConstituents = shapedType.getDimSize(dim);
1021 uint32_t resultID = getNextID();
1022 SmallVector<uint32_t, 4> operands = {typeID, resultID};
1023 auto elementType = cast<spirv::CompositeType>(constType).getElementType(0);
1024 if (
auto tensorArmType = dyn_cast<spirv::TensorArmType>(constType)) {
1025 ArrayRef<int64_t> innerShape = tensorArmType.getShape().drop_front();
1026 if (!innerShape.empty())
1034 if (isa<spirv::CooperativeMatrixType>(constType)) {
1038 "cannot serialize a non-splat value for a cooperative matrix type");
1043 operands.reserve(3);
1046 if (
auto elementID = prepareDenseElementsConstant(
1047 loc, elementType, valueAttr, shapedType.getRank(), index)) {
1048 operands.push_back(elementID);
1052 }
else if (isa<spirv::TensorArmType>(constType) &&
isZeroValue(valueAttr)) {
1054 {typeID, resultID});
1057 operands.reserve(numberOfConstituents + 2);
1058 for (
int i = 0; i < numberOfConstituents; ++i) {
1060 if (
auto elementID = prepareDenseElementsConstant(
1061 loc, elementType, valueAttr, dim + 1, index)) {
1062 operands.push_back(elementID);
1068 spirv::Opcode opcode = spirv::Opcode::OpConstantComposite;
1074uint32_t Serializer::prepareConstantScalar(Location loc, Attribute valueAttr,
1076 if (
auto floatAttr = dyn_cast<FloatAttr>(valueAttr)) {
1077 return prepareConstantFp(loc, floatAttr, isSpec);
1079 if (
auto boolAttr = dyn_cast<BoolAttr>(valueAttr)) {
1080 return prepareConstantBool(loc, boolAttr, isSpec);
1082 if (
auto intAttr = dyn_cast<IntegerAttr>(valueAttr)) {
1083 return prepareConstantInt(loc, intAttr, isSpec);
1089uint32_t Serializer::prepareConstantBool(Location loc, BoolAttr boolAttr,
1093 if (
auto id = getConstantID(boolAttr)) {
1099 uint32_t typeID = 0;
1100 if (
failed(processType(loc, cast<IntegerAttr>(boolAttr).
getType(), typeID))) {
1104 auto resultID = getNextID();
1106 ? (isSpec ? spirv::Opcode::OpSpecConstantTrue
1107 : spirv::Opcode::OpConstantTrue)
1108 : (isSpec ? spirv::Opcode::OpSpecConstantFalse
1109 : spirv::Opcode::OpConstantFalse);
1113 constIDMap[boolAttr] = resultID;
1118uint32_t Serializer::prepareConstantInt(Location loc, IntegerAttr intAttr,
1122 if (
auto id = getConstantID(intAttr)) {
1128 uint32_t typeID = 0;
1129 if (
failed(processType(loc, intAttr.getType(), typeID))) {
1133 auto resultID = getNextID();
1134 APInt value = intAttr.getValue();
1135 unsigned bitwidth = value.getBitWidth();
1136 bool isSigned = intAttr.getType().isSignedInteger();
1138 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1151 word =
static_cast<int32_t
>(value.getSExtValue());
1153 word =
static_cast<uint32_t
>(value.getZExtValue());
1165 words = llvm::bit_cast<DoubleWord>(value.getSExtValue());
1167 words = llvm::bit_cast<DoubleWord>(value.getZExtValue());
1170 {typeID, resultID, words.word1, words.word2});
1173 std::string valueStr;
1174 llvm::raw_string_ostream rss(valueStr);
1175 value.print(rss,
false);
1178 << bitwidth <<
"-bit integer literal: " << valueStr;
1184 constIDMap[intAttr] = resultID;
1189uint32_t Serializer::prepareGraphConstantId(Location loc, Type graphConstType,
1190 IntegerAttr intAttr) {
1192 if (uint32_t
id = getGraphConstantARMId(intAttr)) {
1197 uint32_t typeID = 0;
1198 if (
failed(processType(loc, graphConstType, typeID))) {
1202 uint32_t resultID = getNextID();
1203 APInt value = intAttr.getValue();
1204 unsigned bitwidth = value.getBitWidth();
1205 if (bitwidth > 32) {
1206 emitError(loc,
"Too wide attribute for OpGraphConstantARM: ")
1207 << bitwidth <<
" bits";
1210 bool isSigned = value.isSignedIntN(bitwidth);
1214 word =
static_cast<int32_t
>(value.getSExtValue());
1216 word =
static_cast<uint32_t
>(value.getZExtValue());
1219 {typeID, resultID, word});
1220 graphConstIDMap[intAttr] = resultID;
1224uint32_t Serializer::prepareConstantFp(Location loc, FloatAttr floatAttr,
1228 if (
auto id = getConstantID(floatAttr)) {
1234 uint32_t typeID = 0;
1235 if (
failed(processType(loc, floatAttr.getType(), typeID))) {
1239 auto resultID = getNextID();
1240 APFloat value = floatAttr.getValue();
1241 const llvm::fltSemantics *semantics = &value.getSemantics();
1244 isSpec ? spirv::Opcode::OpSpecConstant : spirv::Opcode::OpConstant;
1246 if (semantics == &APFloat::IEEEsingle()) {
1247 uint32_t word = llvm::bit_cast<uint32_t>(value.convertToFloat());
1249 }
else if (semantics == &APFloat::IEEEdouble()) {
1253 } words = llvm::bit_cast<DoubleWord>(value.convertToDouble());
1255 {typeID, resultID, words.word1, words.word2});
1256 }
else if (semantics == &APFloat::IEEEhalf() ||
1257 semantics == &APFloat::BFloat()) {
1259 static_cast<uint32_t
>(value.bitcastToAPInt().getZExtValue());
1262 std::string valueStr;
1263 llvm::raw_string_ostream rss(valueStr);
1267 << floatAttr.getType() <<
"-typed float literal: " << valueStr;
1272 constIDMap[floatAttr] = resultID;
1281 if (
auto typedAttr = dyn_cast<TypedAttr>(attr)) {
1282 return typedAttr.getType();
1285 if (
auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
1292uint32_t Serializer::prepareConstantCompositeReplicate(
Location loc,
1295 std::pair<Attribute, Type> valueTypePair{valueAttr, resultType};
1296 if (uint32_t
id = getConstantCompositeReplicateID(valueTypePair)) {
1300 uint32_t typeID = 0;
1301 if (
failed(processType(loc, resultType, typeID))) {
1309 auto compositeType = dyn_cast<CompositeType>(resultType);
1312 Type elementType = compositeType.getElementType(0);
1314 uint32_t constandID;
1315 if (elementType == valueType) {
1316 constandID = prepareConstant(loc, elementType, valueAttr);
1318 constandID = prepareConstantCompositeReplicate(loc, elementType, valueAttr);
1321 uint32_t resultID = getNextID();
1322 if (dyn_cast<spirv::TensorArmType>(resultType) &&
isZeroValue(valueAttr)) {
1324 {typeID, resultID});
1327 spirv::Opcode::OpConstantCompositeReplicateEXT,
1328 {typeID, resultID, constandID});
1331 constCompositeReplicateIDMap[valueTypePair] = resultID;
1339uint32_t Serializer::getOrCreateBlockID(
Block *block) {
1340 if (uint32_t
id = getBlockID(block))
1342 return blockIDMap[block] = getNextID();
1346void Serializer::printBlock(
Block *block, raw_ostream &os) {
1347 os <<
"block " << block <<
" (id = ";
1348 if (uint32_t
id = getBlockID(block))
1357Serializer::processBlock(
Block *block,
bool omitLabel,
1359 LLVM_DEBUG(llvm::dbgs() <<
"processing block " << block <<
":\n");
1360 LLVM_DEBUG(block->
print(llvm::dbgs()));
1361 LLVM_DEBUG(llvm::dbgs() <<
'\n');
1363 uint32_t blockID = getOrCreateBlockID(block);
1364 LLVM_DEBUG(printBlock(block, llvm::dbgs()));
1371 if (
failed(emitPhiForBlockArguments(block)))
1381 llvm::IsaPred<spirv::LoopOp, spirv::SelectionOp>)) {
1384 emitMerge =
nullptr;
1387 uint32_t blockID = getNextID();
1393 for (Operation &op : llvm::drop_end(*block)) {
1394 if (
failed(processOperation(&op)))
1402 if (
failed(processOperation(&block->
back())))
1408LogicalResult Serializer::emitPhiForBlockArguments(
Block *block) {
1414 LLVM_DEBUG(llvm::dbgs() <<
"emitting phi instructions..\n");
1421 SmallVector<std::pair<Block *, OperandRange>, 4> predecessors;
1423 auto *terminator = mlirPredecessor->getTerminator();
1424 LLVM_DEBUG(llvm::dbgs() <<
" mlir predecessor ");
1425 LLVM_DEBUG(printBlock(mlirPredecessor, llvm::dbgs()));
1426 LLVM_DEBUG(llvm::dbgs() <<
" terminator: " << *terminator <<
"\n");
1435 LLVM_DEBUG(llvm::dbgs() <<
" spirv predecessor ");
1436 LLVM_DEBUG(printBlock(spirvPredecessor, llvm::dbgs()));
1437 if (
auto branchOp = dyn_cast<spirv::BranchOp>(terminator)) {
1438 predecessors.emplace_back(spirvPredecessor, branchOp.getOperands());
1439 }
else if (
auto branchCondOp =
1440 dyn_cast<spirv::BranchConditionalOp>(terminator)) {
1441 std::optional<OperandRange> blockOperands;
1442 if (branchCondOp.getTrueTarget() == block) {
1443 blockOperands = branchCondOp.getTrueTargetOperands();
1445 assert(branchCondOp.getFalseTarget() == block);
1446 blockOperands = branchCondOp.getFalseTargetOperands();
1448 assert(!blockOperands->empty() &&
1449 "expected non-empty block operand range");
1450 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1451 }
else if (
auto switchOp = dyn_cast<spirv::SwitchOp>(terminator)) {
1452 std::optional<OperandRange> blockOperands;
1453 if (block == switchOp.getDefaultTarget()) {
1454 blockOperands = switchOp.getDefaultOperands();
1456 SuccessorRange targets = switchOp.getTargets();
1457 auto it = llvm::find(targets, block);
1458 assert(it != targets.end());
1459 size_t index = std::distance(targets.begin(), it);
1460 blockOperands = switchOp.getTargetOperands(index);
1462 assert(!blockOperands->empty() &&
1463 "expected non-empty block operand range");
1464 predecessors.emplace_back(spirvPredecessor, *blockOperands);
1466 return terminator->emitError(
"unimplemented terminator for Phi creation");
1469 llvm::dbgs() <<
" block arguments:\n";
1470 for (Value v : predecessors.back().second)
1471 llvm::dbgs() <<
" " << v <<
"\n";
1476 for (
auto argIndex : llvm::seq<unsigned>(0, block->
getNumArguments())) {
1480 uint32_t phiTypeID = 0;
1483 uint32_t phiID = getNextID();
1485 LLVM_DEBUG(llvm::dbgs() <<
"[phi] for block argument #" << argIndex <<
' '
1486 << arg <<
" (id = " << phiID <<
")\n");
1489 SmallVector<uint32_t, 8> phiArgs;
1490 phiArgs.push_back(phiTypeID);
1491 phiArgs.push_back(phiID);
1493 for (
auto predIndex : llvm::seq<unsigned>(0, predecessors.size())) {
1494 Value value = predecessors[predIndex].second[argIndex];
1495 uint32_t predBlockId = getOrCreateBlockID(predecessors[predIndex].first);
1496 LLVM_DEBUG(llvm::dbgs() <<
"[phi] use predecessor (id = " << predBlockId
1497 <<
") value " << value <<
' ');
1499 uint32_t valueId = getValueID(value);
1503 LLVM_DEBUG(llvm::dbgs() <<
"(need to fix)\n");
1504 deferredPhiValues[value].push_back(functionBody.size() + 1 +
1507 LLVM_DEBUG(llvm::dbgs() <<
"(id = " << valueId <<
")\n");
1509 phiArgs.push_back(valueId);
1511 phiArgs.push_back(predBlockId);
1515 valueIDMap[arg] = phiID;
1525LogicalResult Serializer::encodeExtensionInstruction(
1526 Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
1527 ArrayRef<uint32_t> operands) {
1529 auto &setID = extendedInstSetIDMap[extensionSetName];
1531 setID = getNextID();
1532 SmallVector<uint32_t, 16> importOperands;
1533 importOperands.push_back(setID);
1541 if (operands.size() < 2) {
1542 return op->
emitError(
"extended instructions must have a result encoding");
1544 SmallVector<uint32_t, 8> extInstOperands;
1545 extInstOperands.reserve(operands.size() + 2);
1546 extInstOperands.append(operands.begin(), std::next(operands.begin(), 2));
1547 extInstOperands.push_back(setID);
1548 extInstOperands.push_back(extensionOpcode);
1549 extInstOperands.append(std::next(operands.begin(), 2), operands.end());
1555LogicalResult Serializer::processOperation(Operation *opInst) {
1556 LLVM_DEBUG(llvm::dbgs() <<
"[op] '" << opInst->
getName() <<
"'\n");
1561 .Case([&](spirv::AddressOfOp op) {
return processAddressOfOp(op); })
1562 .Case([&](spirv::BranchOp op) {
return processBranchOp(op); })
1563 .Case([&](spirv::BranchConditionalOp op) {
1564 return processBranchConditionalOp(op);
1566 .Case([&](spirv::ConstantOp op) {
return processConstantOp(op); })
1567 .Case([&](spirv::EXTConstantCompositeReplicateOp op) {
1568 return processConstantCompositeReplicateOp(op);
1570 .Case([&](spirv::FuncOp op) {
return processFuncOp(op); })
1571 .Case([&](spirv::GraphARMOp op) {
return processGraphARMOp(op); })
1572 .Case([&](spirv::GraphEntryPointARMOp op) {
1573 return processGraphEntryPointARMOp(op);
1575 .Case([&](spirv::GraphOutputsARMOp op) {
1576 return processGraphOutputsARMOp(op);
1578 .Case([&](spirv::GlobalVariableOp op) {
1579 return processGlobalVariableOp(op);
1581 .Case([&](spirv::GraphConstantARMOp op) {
1582 return processGraphConstantARMOp(op);
1584 .Case([&](spirv::LoopOp op) {
return processLoopOp(op); })
1585 .Case([&](spirv::ReferenceOfOp op) {
return processReferenceOfOp(op); })
1586 .Case([&](spirv::SelectionOp op) {
return processSelectionOp(op); })
1587 .Case([&](spirv::SpecConstantOp op) {
return processSpecConstantOp(op); })
1588 .Case([&](spirv::SpecConstantCompositeOp op) {
1589 return processSpecConstantCompositeOp(op);
1591 .Case([&](spirv::EXTSpecConstantCompositeReplicateOp op) {
1592 return processSpecConstantCompositeReplicateOp(op);
1594 .Case([&](spirv::SpecConstantOperationOp op) {
1595 return processSpecConstantOperationOp(op);
1597 .Case([&](spirv::SwitchOp op) {
return processSwitchOp(op); })
1598 .Case([&](spirv::UndefOp op) {
return processUndefOp(op); })
1599 .Case([&](spirv::VariableOp op) {
return processVariableOp(op); })
1604 [&](Operation *op) {
return dispatchToAutogenSerialization(op); });
1607LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
1608 StringRef extInstSet,
1610 SmallVector<uint32_t, 4> operands;
1611 Location loc = op->
getLoc();
1613 uint32_t resultID = 0;
1615 uint32_t resultTypeID = 0;
1618 operands.push_back(resultTypeID);
1620 resultID = getNextID();
1621 operands.push_back(resultID);
1622 valueIDMap[op->
getResult(0)] = resultID;
1626 operands.push_back(getValueID(operand));
1628 if (
failed(emitDebugLine(functionBody, loc)))
1631 if (extInstSet.empty()) {
1635 if (
failed(encodeExtensionInstruction(op, extInstSet, opcode, operands)))
1641 if (
failed(processDecoration(loc, resultID, attr)))
1649LogicalResult Serializer::emitDecoration(uint32_t
target,
1650 spirv::Decoration decoration,
1651 ArrayRef<uint32_t> params) {
1652 uint32_t wordCount = 3 + params.size();
1653 llvm::append_values(
1656 static_cast<uint32_t
>(decoration));
1657 llvm::append_range(decorations, params);
1661LogicalResult Serializer::emitDebugLine(SmallVectorImpl<uint32_t> &binary,
1663 if (!options.emitDebugInfo)
1666 if (lastProcessedWasMergeInst) {
1667 lastProcessedWasMergeInst =
false;
1671 auto fileLoc = dyn_cast<FileLineColLoc>(loc);
1674 {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