31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/ADT/StringExtras.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/MathExtras.h"
39#define DEBUG_TYPE "mlir-spirv-conversion"
49static std::optional<SmallVector<int64_t>>
getTargetShape(VectorType vecType) {
50 LLVM_DEBUG(llvm::dbgs() <<
"Get target shape\n");
51 if (vecType.isScalable()) {
52 LLVM_DEBUG(llvm::dbgs()
53 <<
"--scalable vectors are not supported -> BAIL\n");
56 if (vecType.getRank() == 0) {
57 LLVM_DEBUG(llvm::dbgs() <<
"--0-D vectors are not supported -> BAIL\n");
64 LLVM_DEBUG(llvm::dbgs() <<
"--no unrolling target shape defined\n");
68 if (!maybeShapeRatio) {
69 LLVM_DEBUG(llvm::dbgs()
70 <<
"--could not compute integral shape ratio -> BAIL\n");
73 if (llvm::all_of(*maybeShapeRatio, [](
int64_t v) {
return v == 1; })) {
74 LLVM_DEBUG(llvm::dbgs() <<
"--no unrolling needed -> SKIP\n");
77 LLVM_DEBUG(llvm::dbgs()
78 <<
"--found an integral shape ratio to unroll to -> SUCCESS\n");
88template <
typename LabelT>
89static LogicalResult checkExtensionRequirements(
92 for (
const auto &ors : candidates) {
98 for (spirv::Extension ext : ors)
99 extStrings.push_back(spirv::stringifyExtension(ext));
101 llvm::dbgs() << label <<
" illegal: requires at least one extension in ["
102 << llvm::join(extStrings,
", ")
103 <<
"] but none allowed in target environment\n";
116template <
typename LabelT>
117static LogicalResult checkCapabilityRequirements(
120 for (
const auto &ors : candidates) {
121 if (targetEnv.
allows(ors))
126 for (spirv::Capability cap : ors)
127 capStrings.push_back(spirv::stringifyCapability(cap));
129 llvm::dbgs() << label <<
" illegal: requires at least one capability in ["
130 << llvm::join(capStrings,
", ")
131 <<
"] but none allowed in target environment\n";
140static bool needsExplicitLayout(spirv::StorageClass storageClass) {
141 switch (storageClass) {
142 case spirv::StorageClass::PhysicalStorageBuffer:
143 case spirv::StorageClass::PushConstant:
144 case spirv::StorageClass::StorageBuffer:
145 case spirv::StorageClass::Uniform:
155wrapInStructAndGetPointer(
Type elementType, spirv::StorageClass storageClass) {
156 auto structType = needsExplicitLayout(storageClass)
168 return cast<spirv::ScalarType>(
169 IntegerType::get(ctx,
options.use64bitIndex ? 64 : 32));
174static std::optional<int64_t>
176 if (isa<spirv::ScalarType>(type)) {
190 if (
options.emulateUnsupportedFloatTypes && isa<FloatType>(type)) {
197 if (
auto complexType = dyn_cast<ComplexType>(type)) {
198 auto elementSize = getTypeNumBytes(
options, complexType.getElementType());
201 return 2 * *elementSize;
204 if (
auto vecType = dyn_cast<VectorType>(type)) {
205 auto elementSize = getTypeNumBytes(
options, vecType.getElementType());
208 return vecType.getNumElements() * *elementSize;
211 if (
auto memRefType = dyn_cast<MemRefType>(type)) {
216 if (!memRefType.hasStaticShape() ||
217 failed(memRefType.getStridesAndOffset(strides, offset)))
223 auto elementSize = getTypeNumBytes(
options, memRefType.getElementType());
227 if (memRefType.getRank() == 0)
230 auto dims = memRefType.getShape();
231 if (llvm::is_contained(dims, ShapedType::kDynamic) ||
232 ShapedType::isDynamic(offset) ||
233 llvm::is_contained(strides, ShapedType::kDynamic))
237 for (
const auto &
shape : enumerate(dims))
238 memrefSize = std::max(memrefSize,
shape.value() * strides[
shape.index()]);
240 return (offset + memrefSize) * *elementSize;
243 if (
auto tensorType = dyn_cast<TensorType>(type)) {
244 if (!tensorType.hasStaticShape())
247 auto elementSize = getTypeNumBytes(
options, tensorType.getElementType());
252 for (
auto shape : tensorType.getShape())
266 std::optional<spirv::StorageClass> storageClass = {}) {
270 type.getExtensions(extensions, storageClass);
271 type.getCapabilities(capabilities, storageClass);
274 if (succeeded(checkCapabilityRequirements(type, targetEnv, capabilities)) &&
275 succeeded(checkExtensionRequirements(type, targetEnv, extensions)))
280 if (!
options.emulateLT32BitScalarTypes)
285 LLVM_DEBUG(llvm::dbgs()
287 <<
" not converted to 32-bit for SPIR-V to avoid truncation\n");
291 if (
auto floatType = dyn_cast<FloatType>(type)) {
292 LLVM_DEBUG(llvm::dbgs() << type <<
" converted to 32-bit for SPIR-V\n");
296 auto intType = cast<IntegerType>(type);
297 LLVM_DEBUG(llvm::dbgs() << type <<
" converted to 32-bit for SPIR-V\n");
298 return IntegerType::get(targetEnv.
getContext(), 32,
299 intType.getSignedness());
312 if (type.getWidth() > 8) {
313 LLVM_DEBUG(llvm::dbgs() <<
"not a subbyte type\n");
317 LLVM_DEBUG(llvm::dbgs() <<
"unsupported sub-byte storage kind\n");
321 if (!llvm::isPowerOf2_32(type.getWidth())) {
322 LLVM_DEBUG(llvm::dbgs()
323 <<
"unsupported non-power-of-two bitwidth in sub-byte" << type
328 LLVM_DEBUG(llvm::dbgs() << type <<
" converted to 32-bit for SPIR-V\n");
329 return IntegerType::get(type.getContext(), 32,
330 type.getSignedness());
337 if (!
options.emulateUnsupportedFloatTypes)
340 if (isa<Float8E5M2Type, Float8E4M3Type, Float8E4M3FNType, Float8E5M2FNUZType,
341 Float8E4M3FNUZType, Float8E4M3B11FNUZType, Float8E3M4Type,
342 Float8E8M0FNUType>(type))
343 return IntegerType::get(type.getContext(), type.getWidth());
344 LLVM_DEBUG(llvm::dbgs() <<
"unsupported 8-bit float type: " << type <<
"\n");
352convertShaped8BitFloatType(ShapedType type,
354 if (!
options.emulateUnsupportedFloatTypes)
356 Type srcElementType = type.getElementType();
357 Type convertedElementType =
nullptr;
359 if (isa<Float8E5M2Type, Float8E4M3Type, Float8E4M3FNType, Float8E5M2FNUZType,
360 Float8E4M3FNUZType, Float8E4M3B11FNUZType, Float8E3M4Type,
361 Float8E8M0FNUType>(srcElementType))
362 convertedElementType = IntegerType::get(
365 if (!convertedElementType)
368 return type.clone(convertedElementType);
375convertIndexElementType(ShapedType type,
377 Type indexType = dyn_cast<IndexType>(type.getElementType());
388 std::optional<spirv::StorageClass> storageClass = {}) {
389 type = cast<VectorType>(convertIndexElementType(type,
options));
390 type = cast<VectorType>(convertShaped8BitFloatType(type,
options));
391 auto scalarType = dyn_cast_or_null<spirv::ScalarType>(type.getElementType());
395 auto intType = dyn_cast<IntegerType>(type.getElementType());
397 LLVM_DEBUG(llvm::dbgs()
399 <<
" illegal: cannot convert non-scalar element type\n");
403 Type elementType = convertSubByteIntegerType(
options, intType);
407 if (type.getRank() <= 1 && type.getNumElements() == 1)
410 if (type.getNumElements() > 4) {
411 LLVM_DEBUG(llvm::dbgs()
412 << type <<
" illegal: > 4-element unimplemented\n");
416 return VectorType::get(type.getShape(), elementType);
419 if (type.getRank() <= 1 && type.getNumElements() == 1)
420 return convertScalarType(targetEnv,
options, scalarType, storageClass);
423 LLVM_DEBUG(llvm::dbgs()
424 << type <<
" illegal: not a valid composite type\n");
431 cast<spirv::CompositeType>(type).getExtensions(extensions, storageClass);
432 cast<spirv::CompositeType>(type).getCapabilities(capabilities, storageClass);
435 if (succeeded(checkCapabilityRequirements(type, targetEnv, capabilities)) &&
436 succeeded(checkExtensionRequirements(type, targetEnv, extensions)))
440 convertScalarType(targetEnv,
options, scalarType, storageClass);
442 return VectorType::get(type.getShape(), elementType);
449 std::optional<spirv::StorageClass> storageClass = {}) {
450 auto scalarType = dyn_cast_or_null<spirv::ScalarType>(type.getElementType());
452 LLVM_DEBUG(llvm::dbgs()
453 << type <<
" illegal: cannot convert non-scalar element type\n");
458 convertScalarType(targetEnv,
options, scalarType, storageClass);
461 if (elementType != type.getElementType()) {
462 LLVM_DEBUG(llvm::dbgs()
463 << type <<
" illegal: complex type emulation unsupported\n");
467 return VectorType::get(2, elementType);
480 if (!type.hasStaticShape()) {
481 LLVM_DEBUG(llvm::dbgs()
482 << type <<
" illegal: dynamic shape unimplemented\n");
486 type = cast<TensorType>(convertIndexElementType(type,
options));
487 type = cast<TensorType>(convertShaped8BitFloatType(type,
options));
488 auto scalarType = dyn_cast_or_null<spirv::ScalarType>(type.
getElementType());
490 LLVM_DEBUG(llvm::dbgs()
491 << type <<
" illegal: cannot convert non-scalar element type\n");
495 std::optional<int64_t> scalarSize = getTypeNumBytes(
options, scalarType);
496 std::optional<int64_t> tensorSize = getTypeNumBytes(
options, type);
497 if (!scalarSize || !tensorSize) {
498 LLVM_DEBUG(llvm::dbgs()
499 << type <<
" illegal: cannot deduce element count\n");
503 int64_t arrayElemCount = *tensorSize / *scalarSize;
504 if (arrayElemCount == 0) {
505 LLVM_DEBUG(llvm::dbgs()
506 << type <<
" illegal: cannot handle zero-element tensors\n");
509 if (arrayElemCount > std::numeric_limits<unsigned>::max()) {
510 LLVM_DEBUG(llvm::dbgs()
511 << type <<
" illegal: cannot fit tensor into target type\n");
515 Type arrayElemType = convertScalarType(targetEnv,
options, scalarType);
518 std::optional<int64_t> arrayElemSize =
519 getTypeNumBytes(
options, arrayElemType);
520 if (!arrayElemSize) {
521 LLVM_DEBUG(llvm::dbgs()
522 << type <<
" illegal: cannot deduce converted element size\n");
532 spirv::StorageClass storageClass) {
533 unsigned numBoolBits =
options.boolNumBits;
534 if (numBoolBits != 8) {
535 LLVM_DEBUG(llvm::dbgs()
536 <<
"using non-8-bit storage for bool types unimplemented");
539 auto elementType = dyn_cast<spirv::ScalarType>(
540 IntegerType::get(type.
getContext(), numBoolBits));
544 convertScalarType(targetEnv,
options, elementType, storageClass);
547 std::optional<int64_t> arrayElemSize =
548 getTypeNumBytes(
options, arrayElemType);
549 if (!arrayElemSize) {
550 LLVM_DEBUG(llvm::dbgs()
551 << type <<
" illegal: cannot deduce converted element size\n");
555 if (!type.hasStaticShape()) {
558 if (targetEnv.
allows(spirv::Capability::Kernel))
560 int64_t stride = needsExplicitLayout(storageClass) ? *arrayElemSize : 0;
564 return wrapInStructAndGetPointer(arrayType, storageClass);
567 if (type.getNumElements() == 0) {
568 LLVM_DEBUG(llvm::dbgs()
569 << type <<
" illegal: zero-element memrefs are not supported\n");
573 int64_t memrefSize = llvm::divideCeil(type.getNumElements() * numBoolBits, 8);
574 int64_t arrayElemCount = llvm::divideCeil(memrefSize, *arrayElemSize);
575 int64_t stride = needsExplicitLayout(storageClass) ? *arrayElemSize : 0;
577 if (targetEnv.
allows(spirv::Capability::Kernel))
579 return wrapInStructAndGetPointer(arrayType, storageClass);
585 spirv::StorageClass storageClass) {
586 IntegerType elementType = cast<IntegerType>(type.getElementType());
587 Type arrayElemType = convertSubByteIntegerType(
options, elementType);
592 if (!type.hasStaticShape()) {
595 if (targetEnv.
allows(spirv::Capability::Kernel))
597 int64_t stride = needsExplicitLayout(storageClass) ? arrayElemSize : 0;
601 return wrapInStructAndGetPointer(arrayType, storageClass);
604 if (type.getNumElements() == 0) {
605 LLVM_DEBUG(llvm::dbgs()
606 << type <<
" illegal: zero-element memrefs are not supported\n");
611 llvm::divideCeil(type.getNumElements() * elementType.getWidth(), 8);
612 int64_t arrayElemCount = llvm::divideCeil(memrefSize, arrayElemSize);
613 int64_t stride = needsExplicitLayout(storageClass) ? arrayElemSize : 0;
615 if (targetEnv.
allows(spirv::Capability::Kernel))
617 return wrapInStructAndGetPointer(arrayType, storageClass);
620static spirv::Dim convertRank(
int64_t rank) {
623 return spirv::Dim::Dim1D;
625 return spirv::Dim::Dim2D;
627 return spirv::Dim::Dim3D;
629 llvm_unreachable(
"Invalid memref rank!");
633static spirv::ImageFormat getImageFormat(
Type elementType) {
635 .Case([](Float16Type) {
return spirv::ImageFormat::R16f; })
636 .Case([](Float32Type) {
return spirv::ImageFormat::R32f; })
637 .Case([](IntegerType intType) {
638 auto const isSigned = intType.isSigned() || intType.isSignless();
639#define BIT_WIDTH_CASE(BIT_WIDTH) \
641 return isSigned ? spirv::ImageFormat::R##BIT_WIDTH##i \
642 : spirv::ImageFormat::R##BIT_WIDTH##ui
644 switch (intType.getWidth()) {
648 llvm_unreachable(
"Unhandled integer type!");
651 .DefaultUnreachable(
"Unhandled element type!");
658 auto attr = dyn_cast_or_null<spirv::StorageClassAttr>(type.getMemorySpace());
663 <<
" illegal: expected memory space to be a SPIR-V storage class "
664 "attribute; please use MemorySpaceToStorageClassConverter to map "
665 "numeric memory spaces beforehand\n");
668 spirv::StorageClass storageClass = attr.getValue();
673 if (storageClass == spirv::StorageClass::Image) {
674 const int64_t rank = type.getRank();
675 if (rank < 1 || rank > 3) {
676 LLVM_DEBUG(llvm::dbgs()
677 << type <<
" illegal: cannot lower memref of rank " << rank
678 <<
" to a SPIR-V Image\n");
684 auto elementType = type.getElementType();
685 if (!isa<spirv::ScalarType>(elementType)) {
686 LLVM_DEBUG(llvm::dbgs() << type <<
" illegal: cannot lower memref of "
687 << elementType <<
" to a SPIR-V Image\n");
695 elementType, convertRank(rank), spirv::ImageDepthInfo::DepthUnknown,
696 spirv::ImageArrayedInfo::NonArrayed,
697 spirv::ImageSamplingInfo::SingleSampled,
698 spirv::ImageSamplerUseInfo::NeedSampler, getImageFormat(elementType));
701 spvSampledImageType, spirv::StorageClass::UniformConstant);
705 if (isa<IntegerType>(type.getElementType())) {
706 if (type.getElementTypeBitWidth() == 1)
707 return convertBoolMemrefType(targetEnv,
options, type, storageClass);
708 if (type.getElementTypeBitWidth() < 8)
709 return convertSubByteMemrefType(targetEnv,
options, type, storageClass);
713 Type elementType = type.getElementType();
714 if (
auto vecType = dyn_cast<VectorType>(elementType)) {
716 convertVectorType(targetEnv,
options, vecType, storageClass);
717 }
else if (
auto complexType = dyn_cast<ComplexType>(elementType)) {
719 convertComplexType(targetEnv,
options, complexType, storageClass);
720 }
else if (
auto scalarType = dyn_cast<spirv::ScalarType>(elementType)) {
722 convertScalarType(targetEnv,
options, scalarType, storageClass);
723 }
else if (
auto indexType = dyn_cast<IndexType>(elementType)) {
724 type = cast<MemRefType>(convertIndexElementType(type,
options));
725 arrayElemType = type.getElementType();
726 }
else if (
auto floatType = dyn_cast<FloatType>(elementType)) {
728 type = cast<MemRefType>(convertShaped8BitFloatType(type,
options));
729 arrayElemType = type.getElementType();
734 <<
" unhandled: can only convert scalar or vector element type\n");
740 std::optional<int64_t> arrayElemSize =
741 getTypeNumBytes(
options, arrayElemType);
742 if (!arrayElemSize) {
743 LLVM_DEBUG(llvm::dbgs()
744 << type <<
" illegal: cannot deduce converted element size\n");
748 if (!type.hasStaticShape()) {
751 if (targetEnv.
allows(spirv::Capability::Kernel))
753 int64_t stride = needsExplicitLayout(storageClass) ? *arrayElemSize : 0;
757 return wrapInStructAndGetPointer(arrayType, storageClass);
760 std::optional<int64_t> memrefSize = getTypeNumBytes(
options, type);
762 LLVM_DEBUG(llvm::dbgs()
763 << type <<
" illegal: cannot deduce element count\n");
767 if (*memrefSize == 0) {
768 LLVM_DEBUG(llvm::dbgs()
769 << type <<
" illegal: zero-element memrefs are not supported\n");
773 int64_t arrayElemCount = llvm::divideCeil(*memrefSize, *arrayElemSize);
774 int64_t stride = needsExplicitLayout(storageClass) ? *arrayElemSize : 0;
776 if (targetEnv.
allows(spirv::Capability::Kernel))
778 return wrapInStructAndGetPointer(arrayType, storageClass);
802 if (inputs.size() != 1) {
804 UnrealizedConversionCastOp::create(builder, loc, type, inputs);
805 return castOp.getResult(0);
807 Value input = inputs.front();
810 if (!isa<IntegerType>(type)) {
812 UnrealizedConversionCastOp::create(builder, loc, type, inputs);
813 return castOp.getResult(0);
815 auto inputType = cast<IntegerType>(input.
getType());
817 auto scalarType = dyn_cast<spirv::ScalarType>(type);
820 UnrealizedConversionCastOp::create(builder, loc, type, inputs);
821 return castOp.getResult(0);
827 if (inputType.getIntOrFloatBitWidth() < scalarType.getIntOrFloatBitWidth()) {
829 UnrealizedConversionCastOp::create(builder, loc, type, inputs);
830 return castOp.getResult(0);
835 Value one = spirv::ConstantOp::getOne(inputType, loc, builder);
836 return spirv::IEqualOp::create(builder, loc, input, one);
842 scalarType.getExtensions(exts);
843 scalarType.getCapabilities(caps);
844 if (failed(checkCapabilityRequirements(type, targetEnv, caps)) ||
845 failed(checkExtensionRequirements(type, targetEnv, exts))) {
847 UnrealizedConversionCastOp::create(builder, loc, type, inputs);
848 return castOp.getResult(0);
855 return spirv::SConvertOp::create(builder, loc, type, input);
857 return spirv::UConvertOp::create(builder, loc, type, input);
864static spirv::GlobalVariableOp getBuiltinVariable(
Block &body,
868 for (
auto varOp : body.
getOps<spirv::GlobalVariableOp>()) {
869 if (
auto builtinAttr = varOp->getAttrOfType<StringAttr>(
870 spirv::SPIRVDialect::getAttributeName(
871 spirv::Decoration::BuiltIn))) {
872 auto varBuiltIn = spirv::symbolizeBuiltIn(builtinAttr.getValue());
882std::string getBuiltinVarName(spirv::BuiltIn
builtin, StringRef prefix,
884 return Twine(prefix).concat(stringifyBuiltIn(
builtin)).concat(suffix).str();
888static spirv::GlobalVariableOp
891 StringRef prefix, StringRef suffix) {
892 if (
auto varOp = getBuiltinVariable(body,
builtin))
898 spirv::GlobalVariableOp newVarOp;
900 case spirv::BuiltIn::NumWorkgroups:
901 case spirv::BuiltIn::WorkgroupSize:
902 case spirv::BuiltIn::WorkgroupId:
903 case spirv::BuiltIn::LocalInvocationId:
904 case spirv::BuiltIn::GlobalInvocationId: {
906 spirv::StorageClass::Input);
907 std::string name = getBuiltinVarName(
builtin, prefix, suffix);
909 spirv::GlobalVariableOp::create(builder, loc, ptrType, name,
builtin);
912 case spirv::BuiltIn::SubgroupId:
913 case spirv::BuiltIn::NumSubgroups:
914 case spirv::BuiltIn::SubgroupSize:
915 case spirv::BuiltIn::SubgroupLocalInvocationId: {
918 std::string name = getBuiltinVarName(
builtin, prefix, suffix);
920 spirv::GlobalVariableOp::create(builder, loc, ptrType, name,
builtin);
924 emitError(loc,
"unimplemented builtin variable generation for ")
947static spirv::GlobalVariableOp getPushConstantVariable(
Block &body,
948 unsigned elementCount) {
949 for (
auto varOp : body.
getOps<spirv::GlobalVariableOp>()) {
950 auto ptrType = dyn_cast<spirv::PointerType>(varOp.getType());
957 if (ptrType.getStorageClass() == spirv::StorageClass::PushConstant) {
958 auto numElements = cast<spirv::ArrayType>(
959 cast<spirv::StructType>(ptrType.getPointeeType())
962 if (numElements == elementCount)
971static spirv::GlobalVariableOp
975 if (
auto varOp = getPushConstantVariable(block, elementCount))
979 auto type = getPushConstantStorageType(elementCount, builder, indexType);
980 const char *name =
"__push_constant_var__";
981 return spirv::GlobalVariableOp::create(builder, loc, type, name,
991struct FuncOpConversion final : OpConversionPattern<func::FuncOp> {
995 matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
996 ConversionPatternRewriter &rewriter)
const override {
997 FunctionType fnType = funcOp.getFunctionType();
998 if (fnType.getNumResults() > 1)
1001 TypeConverter::SignatureConversion signatureConverter(
1002 fnType.getNumInputs());
1003 for (
const auto &argType : enumerate(fnType.getInputs())) {
1004 auto convertedType = getTypeConverter()->convertType(argType.value());
1007 signatureConverter.addInputs(argType.index(), convertedType);
1011 if (fnType.getNumResults() == 1) {
1012 resultType = getTypeConverter()->convertType(fnType.getResult(0));
1018 auto newFuncOp = spirv::FuncOp::create(
1019 rewriter, funcOp.getLoc(), funcOp.getName(),
1020 rewriter.getFunctionType(signatureConverter.getConvertedTypes(),
1026 if (namedAttr.getName() != funcOp.getFunctionTypeAttrName() &&
1028 newFuncOp->setAttr(namedAttr.getName(), namedAttr.getValue());
1031 rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
1033 if (failed(rewriter.convertRegionTypes(
1034 &newFuncOp.getBody(), *getTypeConverter(), &signatureConverter)))
1036 rewriter.eraseOp(funcOp);
1046 LogicalResult matchAndRewrite(func::FuncOp funcOp,
1048 FunctionType fnType = funcOp.getFunctionType();
1051 if (funcOp.isDeclaration()) {
1052 LLVM_DEBUG(llvm::dbgs()
1053 << fnType <<
" illegal: declarations are unsupported\n");
1060 if (llvm::any_of(fnType.getInputs(), [](
Type argType) {
1061 auto shapedType = dyn_cast<ShapedType>(argType);
1062 return shapedType && !shapedType.hasStaticShape();
1067 auto newFuncOp = func::FuncOp::create(rewriter, funcOp.getLoc(),
1068 funcOp.getName(), fnType);
1072 Location loc = newFuncOp.getBody().getLoc();
1074 Block &entryBlock = newFuncOp.getBlocks().
front();
1078 TypeConverter::SignatureConversion oneToNTypeMapping(
1079 fnType.getInputs().size());
1085 size_t newInputNo = 0;
1091 llvm::SmallDenseMap<Operation *, size_t> tmpOps;
1094 size_t newOpCount = 0;
1097 for (
auto [origInputNo, origType] : enumerate(fnType.getInputs())) {
1099 auto origVecType = dyn_cast<VectorType>(origType);
1103 rewriter, loc, origType, rewriter.
getZeroAttr(origType));
1105 tmpOps.insert({
result.getDefiningOp(), newInputNo});
1106 oneToNTypeMapping.addInputs(origInputNo, origType);
1116 rewriter, loc, origType, rewriter.
getZeroAttr(origType));
1118 tmpOps.insert({
result.getDefiningOp(), newInputNo});
1119 oneToNTypeMapping.addInputs(origInputNo, origType);
1124 VectorType unrolledType =
1125 VectorType::get(*targetShape, origVecType.getElementType());
1126 auto originalShape =
1127 llvm::to_vector_of<int64_t, 4>(origVecType.getShape());
1131 rewriter, loc, origVecType, rewriter.
getZeroAttr(origVecType));
1134 Value dummy = arith::ConstantOp::create(
1135 rewriter, loc, unrolledType, rewriter.
getZeroAttr(unrolledType));
1143 result = vector::InsertStridedSliceOp::create(rewriter, loc, dummy,
1144 result, offsets, strides);
1145 newTypes.push_back(unrolledType);
1146 unrolledInputNums.push_back(newInputNo);
1151 oneToNTypeMapping.addInputs(origInputNo, newTypes);
1155 auto convertedTypes = oneToNTypeMapping.getConvertedTypes();
1156 auto newFnType = fnType.clone(convertedTypes, fnType.getResults());
1158 [&] { newFuncOp.setFunctionType(newFnType); });
1167 for (
auto &[placeholderOp, argIdx] : tmpOps) {
1178 size_t unrolledInputIdx = 0;
1179 for (
auto [count, op] : enumerate(entryBlock.
getOperations())) {
1184 if (count >= newOpCount)
1186 if (
auto vecOp = dyn_cast<vector::InsertStridedSliceOp>(op)) {
1187 size_t unrolledInputNo = unrolledInputNums[unrolledInputIdx];
1189 curOp.
setOperand(0, newFuncOp.getArgument(unrolledInputNo));
1211 LogicalResult matchAndRewrite(func::ReturnOp returnOp,
1214 auto funcOp = dyn_cast<func::FuncOp>(returnOp->getParentOp());
1218 FunctionType fnType = funcOp.getFunctionType();
1219 TypeConverter::SignatureConversion oneToNTypeMapping(
1220 fnType.getResults().size());
1227 for (
auto [origResultNo, origType] : enumerate(fnType.getResults())) {
1229 auto origVecType = dyn_cast<VectorType>(origType);
1231 oneToNTypeMapping.addInputs(origResultNo, origType);
1232 newOperands.push_back(returnOp.getOperand(origResultNo));
1239 oneToNTypeMapping.addInputs(origResultNo, origType);
1240 newOperands.push_back(returnOp.getOperand(origResultNo));
1243 VectorType unrolledType =
1244 VectorType::get(*targetShape, origVecType.getElementType());
1248 auto originalShape =
1249 llvm::to_vector_of<int64_t, 4>(origVecType.getShape());
1252 extractShape.back() = targetShape->back();
1254 Value returnValue = returnOp.getOperand(origResultNo);
1257 Value result = vector::ExtractStridedSliceOp::create(
1258 rewriter, loc, returnValue, offsets, extractShape, strides);
1259 if (originalShape.size() > 1) {
1262 vector::ExtractOp::create(rewriter, loc,
result, extractIndices);
1264 newOperands.push_back(
result);
1265 newTypes.push_back(unrolledType);
1267 oneToNTypeMapping.addInputs(origResultNo, newTypes);
1273 TypeRange(oneToNTypeMapping.getConvertedTypes()));
1275 [&] { funcOp.setFunctionType(newFnType); });
1280 func::ReturnOp::create(rewriter, loc, newOperands));
1295 StringRef prefix, StringRef suffix) {
1298 op->
emitError(
"expected operation to be within a module-like op");
1302 spirv::GlobalVariableOp varOp =
1304 builtin, integerType, builder, prefix, suffix);
1305 Value ptr = spirv::AddressOfOp::create(builder, op->
getLoc(), varOp);
1306 return spirv::LoadOp::create(builder, op->
getLoc(),
ptr);
1314 unsigned offset,
Type integerType,
1319 op->
emitError(
"expected operation to be within a module-like op");
1323 spirv::GlobalVariableOp varOp = getOrInsertPushConstantVariable(
1324 loc, parent->
getRegion(0).
front(), elementCount, builder, integerType);
1326 Value zeroOp = spirv::ConstantOp::getZero(integerType, loc, builder);
1327 Value offsetOp = spirv::ConstantOp::create(builder, loc, integerType,
1329 auto addrOp = spirv::AddressOfOp::create(builder, loc, varOp);
1330 auto acOp = spirv::AccessChainOp::create(builder, loc, addrOp,
1332 return spirv::LoadOp::create(builder, loc, acOp);
1342 assert(
indices.size() == strides.size() &&
1343 "must provide indices for all dimensions");
1351 loc, integerType, IntegerAttr::get(integerType, offset));
1355 IntegerAttr::get(integerType, strides[
index.index()]));
1359 builder.
createOrFold<spirv::IAddOp>(loc, update, linearizedIndex);
1361 return linearizedIndex;
1365 MemRefType baseType,
Value basePtr,
1372 if (failed(baseType.getStridesAndOffset(strides, offset)) ||
1373 llvm::is_contained(strides, ShapedType::kDynamic) ||
1374 ShapedType::isDynamic(offset)) {
1381 auto zero = spirv::ConstantOp::getZero(indexType, loc, builder);
1384 linearizedIndices.push_back(zero);
1386 if (baseType.getRank() == 0) {
1387 linearizedIndices.push_back(zero);
1389 linearizedIndices.push_back(
1392 return spirv::AccessChainOp::create(builder, loc, basePtr, linearizedIndices);
1396 MemRefType baseType,
Value basePtr,
1403 if (failed(baseType.getStridesAndOffset(strides, offset)) ||
1404 llvm::is_contained(strides, ShapedType::kDynamic) ||
1405 ShapedType::isDynamic(offset)) {
1413 if (baseType.getRank() == 0) {
1414 linearIndex = spirv::ConstantOp::getZero(indexType, loc, builder);
1420 cast<spirv::PointerType>(basePtr.
getType()).getPointeeType();
1421 if (isa<spirv::ArrayType>(pointeeType)) {
1422 linearizedIndices.push_back(linearIndex);
1423 return spirv::AccessChainOp::create(builder, loc, basePtr,
1426 return spirv::PtrAccessChainOp::create(builder, loc, basePtr, linearIndex,
1431 MemRefType baseType,
Value basePtr,
1435 if (typeConverter.
allows(spirv::Capability::Kernel)) {
1449 for (
int i : {4, 3, 2}) {
1458 VectorType srcVectorType = op.getSourceVectorType();
1459 assert(srcVectorType.getRank() == 1);
1462 return {vectorSize};
1467 VectorType vectorType = op.getResultVectorType();
1474std::optional<SmallVector<int64_t>>
1477 if (
auto vecType = dyn_cast<VectorType>(op->
getResultTypes()[0])) {
1478 if (vecType.getRank() == 0)
1479 return std::nullopt;
1488 .Case<vector::ReductionOp, vector::TransposeOp>(
1490 .Default(std::nullopt);
1514 populateVectorUnrollPatterns(patterns,
options);
1524 patterns, vector::VectorTransposeLowering::EltWise);
1535 vector::populateCastAwayVectorLeadingOneDimPatterns(patterns);
1536 vector::ReductionOp::getCanonicalizationPatterns(patterns, context);
1537 vector::TransposeOp::getCanonicalizationPatterns(patterns, context);
1541 vector::populateVectorInsertExtractStridedSliceDecompositionPatterns(
1543 vector::InsertOp::getCanonicalizationPatterns(patterns, context);
1544 vector::ExtractOp::getCanonicalizationPatterns(patterns, context);
1548 vector::BroadcastOp::getCanonicalizationPatterns(patterns, context);
1549 vector::ShapeCastOp::getCanonicalizationPatterns(patterns, context);
1563 : targetEnv(targetAttr), options(options) {
1576 addConversion([
this](IndexType ) {
return getIndexType(); });
1578 addConversion([
this](IntegerType intType) -> std::optional<Type> {
1579 if (
auto scalarType = dyn_cast<spirv::ScalarType>(intType))
1580 return convertScalarType(this->targetEnv, this->options, scalarType);
1581 if (intType.getWidth() < 8)
1582 return convertSubByteIntegerType(this->options, intType);
1586 addConversion([
this](FloatType floatType) -> std::optional<Type> {
1587 if (
auto scalarType = dyn_cast<spirv::ScalarType>(floatType))
1588 return convertScalarType(this->targetEnv, this->options, scalarType);
1589 if (floatType.getWidth() == 8)
1590 return convert8BitFloatType(this->options, floatType);
1594 addConversion([
this](ComplexType complexType) {
1595 return convertComplexType(this->targetEnv, this->options, complexType);
1598 addConversion([
this](VectorType vectorType) {
1599 return convertVectorType(this->targetEnv, this->options, vectorType);
1602 addConversion([
this](
TensorType tensorType) {
1603 return convertTensorType(this->targetEnv, this->options, tensorType);
1606 addConversion([
this](MemRefType memRefType) {
1607 return convertMemrefType(this->targetEnv, this->options, memRefType);
1611 addSourceMaterialization(
1613 return castToSourceType(this->targetEnv, builder, type, inputs, loc);
1617 auto cast = UnrealizedConversionCastOp::create(builder, loc, type, inputs);
1618 return cast.getResult(0);
1623 return ::getIndexType(
getContext(), options);
1626MLIRContext *SPIRVTypeConverter::getContext()
const {
1627 return targetEnv.
getAttr().getContext();
1631 return targetEnv.allows(capability);
1638std::unique_ptr<SPIRVConversionTarget>
1640 std::unique_ptr<SPIRVConversionTarget>
target(
1642 new SPIRVConversionTarget(targetAttr));
1643 SPIRVConversionTarget *targetPtr =
target.get();
1644 target->addDynamicallyLegalDialect<spirv::SPIRVDialect>(
1647 [targetPtr](
Operation *op) {
return targetPtr->isLegalOp(op); });
1654bool SPIRVConversionTarget::isLegalOp(
Operation *op) {
1658 if (
auto minVersionIfx = dyn_cast<spirv::QueryMinVersionInterface>(op)) {
1659 std::optional<spirv::Version> minVersion = minVersionIfx.getMinVersion();
1660 if (minVersion && *minVersion > this->targetEnv.
getVersion()) {
1661 LLVM_DEBUG(llvm::dbgs()
1662 << op->
getName() <<
" illegal: requiring min version "
1663 << spirv::stringifyVersion(*minVersion) <<
"\n");
1667 if (
auto maxVersionIfx = dyn_cast<spirv::QueryMaxVersionInterface>(op)) {
1668 std::optional<spirv::Version> maxVersion = maxVersionIfx.getMaxVersion();
1669 if (maxVersion && *maxVersion < this->targetEnv.getVersion()) {
1670 LLVM_DEBUG(llvm::dbgs()
1671 << op->
getName() <<
" illegal: requiring max version "
1672 << spirv::stringifyVersion(*maxVersion) <<
"\n");
1680 if (
auto extensions = dyn_cast<spirv::QueryExtensionInterface>(op))
1681 if (
failed(checkExtensionRequirements(op->
getName(), this->targetEnv,
1682 extensions.getExtensions())))
1688 if (
auto capabilities = dyn_cast<spirv::QueryCapabilityInterface>(op))
1689 if (
failed(checkCapabilityRequirements(op->
getName(), this->targetEnv,
1690 capabilities.getCapabilities())))
1693 SmallVector<Type, 4> valueTypes;
1698 if (llvm::any_of(valueTypes,
1699 [](Type t) {
return !isa<spirv::SPIRVType>(t); }))
1704 if (
auto globalVar = dyn_cast<spirv::GlobalVariableOp>(op))
1705 valueTypes.push_back(globalVar.getType());
1709 SmallVector<ArrayRef<spirv::Extension>, 4> typeExtensions;
1710 SmallVector<ArrayRef<spirv::Capability>, 8> typeCapabilities;
1711 for (Type valueType : valueTypes) {
1712 typeExtensions.clear();
1713 cast<spirv::SPIRVType>(valueType).getExtensions(typeExtensions);
1714 if (
failed(checkExtensionRequirements(op->
getName(), this->targetEnv,
1718 typeCapabilities.clear();
1719 cast<spirv::SPIRVType>(valueType).getCapabilities(typeCapabilities);
1720 if (
failed(checkCapabilityRequirements(op->
getName(), this->targetEnv,
1734 patterns.
add<FuncOpConversion>(typeConverter, patterns.
getContext());
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static llvm::ManagedStatic< PassManagerOptions > options
#define BIT_WIDTH_CASE(BIT_WIDTH)
static std::optional< SmallVector< int64_t > > getTargetShape(const vector::UnrollVectorOptions &options, Operation *op)
Return the target shape for unrolling for the given op.
Block represents an ordered list of Operations.
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
OpListType & getOperations()
void eraseArguments(unsigned start, unsigned num)
Erases 'num' arguments from the index 'start'.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getI32IntegerAttr(int32_t value)
TypedAttr getZeroAttr(Type type)
MLIRContext * getContext() const
This class allows control over how the GreedyPatternRewriteDriver works.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttribute represents a combination of a name and an Attribute value.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Operation is the basic unit of execution within MLIR.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
void setOperand(unsigned idx, Value value)
operand_type_iterator operand_type_end()
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
result_type_iterator result_type_end()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
result_type_iterator result_type_begin()
OperationName getName()
The name of an operation is the key identifier for it.
result_type_range getResultTypes()
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumResults()
Return the number of results held by this operation.
operand_type_iterator operand_type_begin()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
static std::unique_ptr< SPIRVConversionTarget > get(spirv::TargetEnvAttr targetAttr)
Creates a SPIR-V conversion target for the given target environment.
Type conversion from builtin types to SPIR-V types for shader interface.
Type getIndexType() const
Gets the SPIR-V correspondence for the standard index type.
SPIRVTypeConverter(spirv::TargetEnvAttr targetAttr, const SPIRVConversionOptions &options={})
bool allows(spirv::Capability capability) const
Checks if the SPIR-V capability inquired is supported.
A range-style iterator that allows for iterating over the offsets of all potential tiles of size tile...
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
Type getElementType() const
Returns the element type of this tensor type.
This class provides an abstraction over the various different ranges of value types.
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 isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
bool isInteger() const
Return true if this is an 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 provides an abstraction over the different types of ranges over Values.
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.
static ArrayType get(Type elementType, unsigned elementCount)
static bool isValid(VectorType)
Returns true if the given vector type is valid for the SPIR-V dialect.
static ImageType get(Type elementType, Dim dim, ImageDepthInfo depth=ImageDepthInfo::DepthUnknown, ImageArrayedInfo arrayed=ImageArrayedInfo::NonArrayed, ImageSamplingInfo samplingInfo=ImageSamplingInfo::SingleSampled, ImageSamplerUseInfo samplerUse=ImageSamplerUseInfo::SamplerUnknown, ImageFormat format=ImageFormat::Unknown)
static PointerType get(Type pointeeType, StorageClass storageClass)
static RuntimeArrayType get(Type elementType)
SmallVectorImpl< ArrayRef< Capability > > CapabilityArrayRefVector
The capability requirements for each type are following the ((Capability::A OR Extension::B) AND (Cap...
SmallVectorImpl< ArrayRef< Extension > > ExtensionArrayRefVector
The extension requirements for each type are following the ((Extension::A OR Extension::B) AND (Exten...
static SampledImageType get(Type imageType)
static StructType get(ArrayRef< Type > memberTypes, ArrayRef< OffsetInfo > offsetInfo={}, ArrayRef< MemberDecorationInfo > memberDecorations={}, ArrayRef< StructDecorationInfo > structDecorations={})
Construct a literal StructType with at least one member.
An attribute that specifies the target version, allowed extensions and capabilities,...
A wrapper class around a spirv::TargetEnvAttr to provide query methods for allowed version/capabiliti...
Version getVersion() const
bool allows(Capability) const
Returns true if the given capability is allowed.
TargetEnvAttr getAttr() const
MLIRContext * getContext() const
Returns the MLIRContext.
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
Value getBuiltinVariableValue(Operation *op, BuiltIn builtin, Type integerType, OpBuilder &builder, StringRef prefix="__builtin__", StringRef suffix="__")
Returns the value for the given builtin variable.
Value getElementPtr(const SPIRVTypeConverter &typeConverter, MemRefType baseType, Value basePtr, ValueRange indices, Location loc, OpBuilder &builder)
Performs the index computation to get to the element at indices of the memory pointed to by basePtr,...
Value getOpenCLElementPtr(const SPIRVTypeConverter &typeConverter, MemRefType baseType, Value basePtr, ValueRange indices, Location loc, OpBuilder &builder)
Value getPushConstantValue(Operation *op, unsigned elementCount, unsigned offset, Type integerType, OpBuilder &builder)
Gets the value at the given offset of the push constant storage with a total of elementCount integerT...
std::optional< SmallVector< int64_t > > getNativeVectorShape(Operation *op)
Value linearizeIndex(ValueRange indices, ArrayRef< int64_t > strides, int64_t offset, Type integerType, Location loc, OpBuilder &builder)
Generates IR to perform index linearization with the given indices and their corresponding strides,...
LogicalResult unrollVectorsInFuncBodies(Operation *op)
Value getVulkanElementPtr(const SPIRVTypeConverter &typeConverter, MemRefType baseType, Value basePtr, ValueRange indices, Location loc, OpBuilder &builder)
SmallVector< int64_t > getNativeVectorShapeImpl(vector::ReductionOp op)
int getComputeVectorSize(int64_t size)
LogicalResult unrollVectorsInSignatures(Operation *op)
void populateVectorShapeCastLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
void populateVectorTransposeLoweringPatterns(RewritePatternSet &patterns, VectorTransposeLowering vectorTransposeLowering, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
Include the generated interface declarations.
void populateFuncOpVectorRewritePatterns(RewritePatternSet &patterns)
void populateReturnOpVectorRewritePatterns(RewritePatternSet &patterns)
@ Packed
Sub-byte values are tightly packed without any padding, e.g., 4xi2 -> i8.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
void populateBuiltinFuncToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating the builtin func op to the SPIR-V diale...
llvm::TypeSwitch< T, ResultT > TypeSwitch
@ ExistingOps
Only pre-existing ops are processed.
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Options that control the vector unrolling.
UnrollVectorOptions & setNativeShapeFn(NativeShapeFnType fn)