21#include "llvm/ADT/APInt.h"
22#include "llvm/ADT/ArrayRef.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/Support/Debug.h"
25#include "llvm/Support/MathExtras.h"
30#define GEN_PASS_DEF_CONVERTARITHTOSPIRVPASS
31#include "mlir/Conversion/Passes.h.inc"
34#define DEBUG_TYPE "arith-to-spirv-pattern"
45 if (
auto boolAttr = dyn_cast<BoolAttr>(srcAttr))
47 if (
auto intAttr = dyn_cast<IntegerAttr>(srcAttr))
48 return builder.
getBoolAttr(intAttr.getValue().getBoolValue());
58 if (srcAttr.getValue().isIntN(dstType.getWidth()))
66 if (srcAttr.getValue().isSignedIntN(dstType.getWidth())) {
68 LLVM_DEBUG(llvm::dbgs() <<
"attribute '" << srcAttr <<
"' converted to '"
69 << dstAttr <<
"' for type '" << dstType <<
"'\n");
73 LLVM_DEBUG(llvm::dbgs() <<
"attribute '" << srcAttr
74 <<
"' illegal: cannot fit into target type '"
88 APFloat dstVal = srcAttr.getValue();
89 bool losesInfo =
false;
90 APFloat::opStatus status =
91 dstVal.convert(APFloat::IEEEsingle(), APFloat::rmTowardZero, &losesInfo);
92 if (status != APFloat::opOK || losesInfo) {
93 LLVM_DEBUG(llvm::dbgs()
94 << srcAttr <<
" illegal: cannot fit into converted type '"
108 APInt intVal = floatAttr.getValue().bitcastToAPInt();
109 auto srcType = IntegerType::get(dstType.getContext(), intVal.getBitWidth());
116 assert(type &&
"Not a valid type");
120 if (
auto vecType = dyn_cast<VectorType>(type))
121 return vecType.getElementType().isInteger(1);
129 if (
auto vectorType = dyn_cast<VectorType>(type)) {
130 Attribute element = IntegerAttr::get(vectorType.getElementType(), value);
132 return spirv::ConstantOp::create(builder, loc, vectorType, attr);
135 if (
auto intType = dyn_cast<IntegerType>(type))
136 return spirv::ConstantOp::create(builder, loc, type,
145 auto getNumBitwidth = [](
Type type) {
147 if (type.isIntOrFloat())
148 bw = type.getIntOrFloatBitWidth();
149 else if (
auto vecType = dyn_cast<VectorType>(type))
150 bw = vecType.getElementTypeBitWidth() * vecType.getNumElements();
153 unsigned aBW = getNumBitwidth(a);
154 unsigned bBW = getNumBitwidth(
b);
155 return aBW != 0 && bBW != 0 && aBW == bBW;
162 return rewriter.notifyMatchFailure(
164 llvm::formatv(
"failed to convert source type '{0}'", srcType));
178template <
typename Op,
typename SPIRVOp>
179struct ElementwiseArithOpPattern final : OpConversionPattern<Op> {
180 using OpConversionPattern<
Op>::OpConversionPattern;
183 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
184 ConversionPatternRewriter &rewriter)
const override {
185 assert(adaptor.getOperands().size() <= 3);
188 if (!adaptor.getOperands().empty() &&
191 auto converter = this->
template getTypeConverter<SPIRVTypeConverter>();
192 Type dstType = converter->convertType(op.getType());
194 return rewriter.notifyMatchFailure(
196 llvm::formatv(
"failed to convert type {0} for SPIR-V", op.getType()));
199 if (SPIRVOp::template hasTrait<OpTrait::spirv::UnsignedOp>() &&
201 dstType != op.getType()) {
202 return op.
emitError(
"bitwidth emulation is not implemented yet on "
203 "unsigned op pattern version");
206 auto overflowFlags = arith::IntegerOverflowFlags::none;
207 if (
auto overflowIface =
208 dyn_cast<arith::ArithIntegerOverflowFlagsInterface>(*op)) {
209 if (converter->getTargetEnv().allows(
210 spirv::Extension::SPV_KHR_no_integer_wrap_decoration))
211 overflowFlags = overflowIface.getOverflowAttr().getValue();
214 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
215 op, dstType, adaptor.getOperands());
217 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nsw))
218 newOp->setDiscardableAttr(
220 rewriter.getUnitAttr());
222 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nuw))
223 newOp->setDiscardableAttr(
225 rewriter.getUnitAttr());
236struct ConstantCompositeOpPattern final
237 :
public OpConversionPattern<arith::ConstantOp> {
241 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
242 ConversionPatternRewriter &rewriter)
const override {
243 auto srcType = dyn_cast<ShapedType>(constOp.getType());
244 if (!srcType || srcType.getNumElements() == 1)
249 if (!isa<VectorType, RankedTensorType>(srcType))
250 return rewriter.notifyMatchFailure(constOp,
"unsupported ShapedType");
252 Type dstType = getTypeConverter()->convertType(srcType);
259 if (
auto denseElementsAttr =
260 dyn_cast<DenseElementsAttr>(constOp.getValue())) {
261 dstElementsAttr = denseElementsAttr;
262 }
else if (
auto resourceAttr =
263 dyn_cast<DenseResourceElementsAttr>(constOp.getValue())) {
267 return constOp->emitError(
"could not find resource blob");
274 return constOp->emitError(
"resource is not a valid buffer");
279 return constOp->emitError(
"unsupported elements attribute");
282 ShapedType dstAttrType = dstElementsAttr.
getType();
286 if (srcType.getRank() > 1) {
287 if (isa<RankedTensorType>(srcType)) {
288 dstAttrType = RankedTensorType::get(srcType.getNumElements(),
289 srcType.getElementType());
290 dstElementsAttr = dstElementsAttr.
reshape(dstAttrType);
297 Type srcElemType = srcType.getElementType();
301 if (
auto arrayType = dyn_cast<spirv::ArrayType>(dstType))
302 dstElemType = arrayType.getElementType();
304 dstElemType = cast<VectorType>(dstType).getElementType();
308 if (srcElemType != dstElemType) {
310 if (isa<FloatType>(srcElemType)) {
311 for (FloatAttr srcAttr : dstElementsAttr.
getValues<FloatAttr>()) {
314 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
315 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
317 isa<IntegerType>(dstElemType)) {
319 srcAttr, cast<IntegerType>(dstElemType), rewriter);
326 elements.push_back(dstAttr);
331 for (IntegerAttr srcAttr : dstElementsAttr.
getValues<IntegerAttr>()) {
333 srcAttr, cast<IntegerType>(dstElemType), rewriter);
336 elements.push_back(dstAttr);
344 if (isa<RankedTensorType>(dstAttrType))
346 RankedTensorType::get(dstAttrType.getShape(), dstElemType);
348 dstAttrType = VectorType::get(dstAttrType.getShape(), dstElemType);
353 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType,
360struct ConstantScalarOpPattern final
361 :
public OpConversionPattern<arith::ConstantOp> {
365 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
366 ConversionPatternRewriter &rewriter)
const override {
367 Type srcType = constOp.getType();
368 if (
auto shapedType = dyn_cast<ShapedType>(srcType)) {
369 if (shapedType.getNumElements() != 1)
371 srcType = shapedType.getElementType();
376 Attribute cstAttr = constOp.getValue();
377 if (
auto elementsAttr = dyn_cast<DenseElementsAttr>(cstAttr))
378 cstAttr = elementsAttr.getSplatValue<Attribute>();
380 Type dstType = getTypeConverter()->convertType(srcType);
385 if (isa<FloatType>(srcType)) {
386 auto srcAttr = cast<FloatAttr>(cstAttr);
387 Attribute dstAttr = srcAttr;
391 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
392 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
396 srcAttr, cast<IntegerType>(dstType), rewriter);
399 }
else if (srcType != dstType) {
405 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
416 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
422 auto srcAttr = cast<IntegerAttr>(cstAttr);
423 IntegerAttr dstAttr =
427 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
443template <
typename SignedAbsOp>
446 assert(lhs.getType() == rhs.getType());
447 assert(lhs == signOperand || rhs == signOperand);
449 Type type = lhs.getType();
452 Value lhsAbs = SignedAbsOp::create(builder, loc, type, lhs);
453 Value rhsAbs = SignedAbsOp::create(builder, loc, type, rhs);
454 Value abs = spirv::UModOp::create(builder, loc, lhsAbs, rhsAbs);
458 if (lhs == signOperand)
459 isPositive = spirv::IEqualOp::create(builder, loc, lhs, lhsAbs);
461 isPositive = spirv::IEqualOp::create(builder, loc, rhs, rhsAbs);
462 Value absNegate = spirv::SNegateOp::create(builder, loc, type, abs);
463 return spirv::SelectOp::create(builder, loc, type, isPositive, abs,
471struct RemSIOpGLPattern final :
public OpConversionPattern<arith::RemSIOp> {
475 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
476 ConversionPatternRewriter &rewriter)
const override {
477 Value
result = emulateSignedRemainder<spirv::GLSAbsOp>(
478 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
479 adaptor.getOperands()[0], rewriter);
480 rewriter.replaceOp(op,
result);
487struct RemSIOpCLPattern final :
public OpConversionPattern<arith::RemSIOp> {
491 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
492 ConversionPatternRewriter &rewriter)
const override {
493 Value
result = emulateSignedRemainder<spirv::CLSAbsOp>(
494 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
495 adaptor.getOperands()[0], rewriter);
496 rewriter.replaceOp(op,
result);
510template <
typename Op,
typename SPIRVLogicalOp,
typename SPIRVBitwiseOp>
511struct BitwiseOpPattern final :
public OpConversionPattern<Op> {
512 using OpConversionPattern<
Op>::OpConversionPattern;
515 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
516 ConversionPatternRewriter &rewriter)
const override {
517 assert(adaptor.getOperands().size() == 2);
518 Type dstType = this->getTypeConverter()->convertType(op.getType());
523 rewriter.template replaceOpWithNewOp<SPIRVLogicalOp>(
524 op, dstType, adaptor.getOperands());
526 rewriter.template replaceOpWithNewOp<SPIRVBitwiseOp>(
527 op, dstType, adaptor.getOperands());
538struct XOrIOpLogicalPattern final :
public OpConversionPattern<arith::XOrIOp> {
542 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
543 ConversionPatternRewriter &rewriter)
const override {
544 assert(adaptor.getOperands().size() == 2);
549 Type dstType = getTypeConverter()->convertType(op.getType());
553 rewriter.replaceOpWithNewOp<spirv::BitwiseXorOp>(op, dstType,
554 adaptor.getOperands());
562struct XOrIOpBooleanPattern final :
public OpConversionPattern<arith::XOrIOp> {
566 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
567 ConversionPatternRewriter &rewriter)
const override {
568 assert(adaptor.getOperands().size() == 2);
573 Type dstType = getTypeConverter()->convertType(op.getType());
577 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
578 op, dstType, adaptor.getOperands());
598template <
typename ArithOp,
typename SPIRVOp>
599struct BoolIOpPattern final :
public OpConversionPattern<ArithOp> {
600 BoolIOpPattern(
const TypeConverter &converter, MLIRContext *context)
603 : OpConversionPattern<ArithOp>(converter, context, 2) {}
606 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
607 ConversionPatternRewriter &rewriter)
const override {
611 Type dstType = this->getTypeConverter()->convertType(op.getType());
615 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
629template <
typename ArithOp>
630struct BoolIOpAndNotPattern final :
public OpConversionPattern<ArithOp> {
631 BoolIOpAndNotPattern(
const TypeConverter &converter, MLIRContext *context)
634 : OpConversionPattern<ArithOp>(converter, context, 2) {}
637 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
638 ConversionPatternRewriter &rewriter)
const override {
642 Type dstType = this->getTypeConverter()->convertType(op.getType());
646 Location loc = op.getLoc();
647 Value notRhs = spirv::LogicalNotOp::create(rewriter, loc, dstType,
648 adaptor.getOperands()[1]);
649 rewriter.replaceOpWithNewOp<spirv::LogicalAndOp>(
650 op, dstType, adaptor.getOperands()[0], notRhs);
657struct ShRSIBoolPattern final :
public OpConversionPattern<arith::ShRSIOp> {
658 ShRSIBoolPattern(
const TypeConverter &converter, MLIRContext *context)
661 : OpConversionPattern<arith::ShRSIOp>(converter, context,
665 matchAndRewrite(arith::ShRSIOp op, OpAdaptor adaptor,
666 ConversionPatternRewriter &rewriter)
const override {
670 rewriter.replaceOp(op, adaptor.getOperands().front());
682template <
typename ArithOp>
683struct BoolToValuePattern final :
public OpConversionPattern<ArithOp> {
684 using OpConversionPattern<ArithOp>::OpConversionPattern;
687 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
688 ConversionPatternRewriter &rewriter)
const override {
689 Type srcType = adaptor.getOperands().front().getType();
693 Type dstType = this->getTypeConverter()->convertType(op.getType());
697 Location loc = op.getLoc();
698 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
699 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
700 rewriter.replaceOpWithNewOp<spirv::SelectOp>(
701 op, dstType, adaptor.getOperands().front(), one, zero);
717template <
typename ArithOp,
typename SPIRVOp,
bool IsSigned>
718struct IntToFPPattern final :
public OpConversionPattern<ArithOp> {
719 using OpConversionPattern<ArithOp>::OpConversionPattern;
722 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
723 ConversionPatternRewriter &rewriter)
const override {
724 Type srcType = adaptor.getOperands().front().getType();
728 Type dstType = this->getTypeConverter()->convertType(op.getType());
733 unsigned originalBitwidth =
738 return rewriter.notifyMatchFailure(op,
739 "unsupported type for uitofp/sitofp");
744 if (originalBitwidth >= convertedBitwidth) {
745 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
750 Location loc = op.getLoc();
752 if constexpr (IsSigned) {
754 unsigned shiftAmount = convertedBitwidth - originalBitwidth;
757 Value shifted = spirv::ShiftLeftLogicalOp::create(
758 rewriter, loc, srcType, adaptor.getIn(), shiftSize);
759 cleaned = spirv::ShiftRightArithmeticOp::create(rewriter, loc, srcType,
764 srcType, llvm::maskTrailingOnes<uint64_t>(originalBitwidth), rewriter,
766 cleaned = spirv::BitwiseAndOp::create(rewriter, loc, srcType,
767 adaptor.getIn(), mask);
769 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, cleaned);
779struct IndexCastIndexI1Pattern final
780 :
public OpConversionPattern<arith::IndexCastOp> {
784 matchAndRewrite(arith::IndexCastOp op, OpAdaptor adaptor,
785 ConversionPatternRewriter &rewriter)
const override {
789 Type dstType = getTypeConverter()->convertType(op.getType());
793 Location loc = op.getLoc();
795 spirv::ConstantOp::getZero(adaptor.getIn().getType(), loc, rewriter);
796 rewriter.replaceOpWithNewOp<spirv::INotEqualOp>(op, dstType, zeroIdx,
808struct ExtSII1Pattern final :
public OpConversionPattern<arith::ExtSIOp> {
812 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
813 ConversionPatternRewriter &rewriter)
const override {
814 Value operand = adaptor.getIn();
818 Location loc = op.getLoc();
819 Type dstType = getTypeConverter()->convertType(op.getType());
824 if (
auto intTy = dyn_cast<IntegerType>(dstType)) {
825 unsigned componentBitwidth = intTy.getWidth();
826 allOnes = spirv::ConstantOp::create(
827 rewriter, loc, intTy,
828 rewriter.getIntegerAttr(intTy, APInt::getAllOnes(componentBitwidth)));
829 }
else if (
auto vectorTy = dyn_cast<VectorType>(dstType)) {
830 unsigned componentBitwidth = vectorTy.getElementTypeBitWidth();
831 allOnes = spirv::ConstantOp::create(
832 rewriter, loc, vectorTy,
834 APInt::getAllOnes(componentBitwidth)));
836 return rewriter.notifyMatchFailure(
837 loc, llvm::formatv(
"unhandled type: {0}", dstType));
840 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
841 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, operand, allOnes,
849struct ExtSIPattern final :
public OpConversionPattern<arith::ExtSIOp> {
853 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
854 ConversionPatternRewriter &rewriter)
const override {
855 Type srcType = adaptor.getIn().getType();
859 Type dstType = getTypeConverter()->convertType(op.getType());
863 if (dstType == srcType) {
871 assert(srcBW < dstBW);
873 rewriter, op.getLoc());
875 return rewriter.notifyMatchFailure(op,
"unsupported type for shift");
880 auto shiftLOp = spirv::ShiftLeftLogicalOp::create(
881 rewriter, op.getLoc(), dstType, adaptor.getIn(), shiftSize);
885 rewriter.replaceOpWithNewOp<spirv::ShiftRightArithmeticOp>(
886 op, dstType, shiftLOp, shiftSize);
888 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
889 adaptor.getOperands());
902struct ExtUIPattern final :
public OpConversionPattern<arith::ExtUIOp> {
906 matchAndRewrite(arith::ExtUIOp op, OpAdaptor adaptor,
907 ConversionPatternRewriter &rewriter)
const override {
908 Type srcType = adaptor.getIn().getType();
912 Type dstType = getTypeConverter()->convertType(op.getType());
916 if (dstType == srcType) {
924 dstType, llvm::maskTrailingOnes<uint64_t>(bitwidth), rewriter,
927 return rewriter.notifyMatchFailure(op,
"unsupported type for mask");
928 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
929 adaptor.getIn(), mask);
931 rewriter.replaceOpWithNewOp<spirv::UConvertOp>(op, dstType,
932 adaptor.getOperands());
944struct TruncII1Pattern final :
public OpConversionPattern<arith::TruncIOp> {
948 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
949 ConversionPatternRewriter &rewriter)
const override {
950 Type dstType = getTypeConverter()->convertType(op.getType());
957 Location loc = op.getLoc();
958 auto srcType = adaptor.getOperands().front().getType();
960 Value mask = spirv::ConstantOp::getOne(srcType, loc, rewriter);
961 Value maskedSrc = spirv::BitwiseAndOp::create(
962 rewriter, loc, srcType, adaptor.getOperands()[0], mask);
963 Value isOne = spirv::IEqualOp::create(rewriter, loc, maskedSrc, mask);
965 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
966 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
967 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, isOne, one, zero);
974struct TruncIPattern final :
public OpConversionPattern<arith::TruncIOp> {
978 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
979 ConversionPatternRewriter &rewriter)
const override {
980 Type srcType = adaptor.getIn().getType();
981 Type dstType = getTypeConverter()->convertType(op.getType());
988 if (dstType == srcType) {
995 dstType, llvm::maskTrailingOnes<uint64_t>(bw), rewriter, op.getLoc());
997 return rewriter.notifyMatchFailure(op,
"unsupported type for mask");
998 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
999 adaptor.getIn(), mask);
1002 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
1003 adaptor.getOperands());
1013static std::optional<spirv::FPRoundingMode>
1014convertArithRoundingModeToSPIRV(arith::RoundingMode roundingMode) {
1015 switch (roundingMode) {
1016 case arith::RoundingMode::downward:
1017 return spirv::FPRoundingMode::RTN;
1018 case arith::RoundingMode::to_nearest_even:
1019 return spirv::FPRoundingMode::RTE;
1020 case arith::RoundingMode::toward_zero:
1021 return spirv::FPRoundingMode::RTZ;
1022 case arith::RoundingMode::upward:
1023 return spirv::FPRoundingMode::RTP;
1024 case arith::RoundingMode::to_nearest_away:
1027 return std::nullopt;
1029 llvm_unreachable(
"Unhandled rounding mode");
1033template <
typename Op,
typename SPIRVOp>
1034struct TypeCastingOpPattern final :
public OpConversionPattern<Op> {
1035 using OpConversionPattern<
Op>::OpConversionPattern;
1038 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
1039 ConversionPatternRewriter &rewriter)
const override {
1040 Type srcType = llvm::getSingleElement(adaptor.getOperands()).getType();
1041 Type dstType = this->getTypeConverter()->convertType(op.getType());
1048 if (dstType == srcType) {
1051 rewriter.replaceOp(op, adaptor.getOperands().front());
1054 std::optional<spirv::FPRoundingMode> rm = std::nullopt;
1055 if (
auto roundingModeOp =
1056 dyn_cast<arith::ArithRoundingModeInterface>(*op)) {
1057 if (arith::RoundingModeAttr roundingMode =
1058 roundingModeOp.getRoundingModeAttr()) {
1060 convertArithRoundingModeToSPIRV(roundingMode.getValue()))) {
1061 return rewriter.notifyMatchFailure(
1063 llvm::formatv(
"unsupported rounding mode '{0}'", roundingMode));
1068 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
1069 op, dstType, adaptor.getOperands());
1071 newOp->setDiscardableAttr(
1073 spirv::FPRoundingModeAttr::get(rewriter.getContext(), *rm));
1085class CmpIOpBooleanPattern final :
public OpConversionPattern<arith::CmpIOp> {
1090 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1091 ConversionPatternRewriter &rewriter)
const override {
1092 Type srcType = op.getLhs().getType();
1095 Type dstType = getTypeConverter()->convertType(srcType);
1099 switch (op.getPredicate()) {
1100 case arith::CmpIPredicate::eq: {
1101 rewriter.replaceOpWithNewOp<spirv::LogicalEqualOp>(op, adaptor.getLhs(),
1105 case arith::CmpIPredicate::ne: {
1106 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
1107 op, adaptor.getLhs(), adaptor.getRhs());
1110 case arith::CmpIPredicate::uge:
1111 case arith::CmpIPredicate::ugt:
1112 case arith::CmpIPredicate::ule:
1113 case arith::CmpIPredicate::ult: {
1116 Type type = rewriter.getI32Type();
1117 if (
auto vectorType = dyn_cast<VectorType>(dstType))
1118 type = VectorType::get(vectorType.getShape(), type);
1120 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getLhs());
1122 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getRhs());
1124 rewriter.replaceOpWithNewOp<arith::CmpIOp>(op, op.getPredicate(), extLhs,
1136class CmpIOpPattern final :
public OpConversionPattern<arith::CmpIOp> {
1141 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1142 ConversionPatternRewriter &rewriter)
const override {
1143 Type srcType = op.getLhs().getType();
1146 Type dstType = getTypeConverter()->convertType(srcType);
1150 switch (op.getPredicate()) {
1151#define DISPATCH(cmpPredicate, spirvOp) \
1152 case cmpPredicate: \
1153 if (spirvOp::template hasTrait<OpTrait::spirv::UnsignedOp>() && \
1154 !getElementTypeOrSelf(srcType).isIndex() && srcType != dstType && \
1155 !hasSameBitwidth(srcType, dstType)) { \
1156 return op.emitError( \
1157 "bitwidth emulation is not implemented yet on unsigned op"); \
1159 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1160 adaptor.getRhs()); \
1163 DISPATCH(arith::CmpIPredicate::eq, spirv::IEqualOp);
1164 DISPATCH(arith::CmpIPredicate::ne, spirv::INotEqualOp);
1165 DISPATCH(arith::CmpIPredicate::slt, spirv::SLessThanOp);
1166 DISPATCH(arith::CmpIPredicate::sle, spirv::SLessThanEqualOp);
1167 DISPATCH(arith::CmpIPredicate::sgt, spirv::SGreaterThanOp);
1168 DISPATCH(arith::CmpIPredicate::sge, spirv::SGreaterThanEqualOp);
1169 DISPATCH(arith::CmpIPredicate::ult, spirv::ULessThanOp);
1170 DISPATCH(arith::CmpIPredicate::ule, spirv::ULessThanEqualOp);
1171 DISPATCH(arith::CmpIPredicate::ugt, spirv::UGreaterThanOp);
1172 DISPATCH(arith::CmpIPredicate::uge, spirv::UGreaterThanEqualOp);
1185class CmpFOpPattern final :
public OpConversionPattern<arith::CmpFOp> {
1190 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1191 ConversionPatternRewriter &rewriter)
const override {
1192 switch (op.getPredicate()) {
1193#define DISPATCH(cmpPredicate, spirvOp) \
1194 case cmpPredicate: \
1195 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1196 adaptor.getRhs()); \
1200 DISPATCH(arith::CmpFPredicate::OEQ, spirv::FOrdEqualOp);
1201 DISPATCH(arith::CmpFPredicate::OGT, spirv::FOrdGreaterThanOp);
1202 DISPATCH(arith::CmpFPredicate::OGE, spirv::FOrdGreaterThanEqualOp);
1203 DISPATCH(arith::CmpFPredicate::OLT, spirv::FOrdLessThanOp);
1204 DISPATCH(arith::CmpFPredicate::OLE, spirv::FOrdLessThanEqualOp);
1205 DISPATCH(arith::CmpFPredicate::ONE, spirv::FOrdNotEqualOp);
1207 DISPATCH(arith::CmpFPredicate::UEQ, spirv::FUnordEqualOp);
1208 DISPATCH(arith::CmpFPredicate::UGT, spirv::FUnordGreaterThanOp);
1209 DISPATCH(arith::CmpFPredicate::UGE, spirv::FUnordGreaterThanEqualOp);
1210 DISPATCH(arith::CmpFPredicate::ULT, spirv::FUnordLessThanOp);
1211 DISPATCH(arith::CmpFPredicate::ULE, spirv::FUnordLessThanEqualOp);
1212 DISPATCH(arith::CmpFPredicate::UNE, spirv::FUnordNotEqualOp);
1225class CmpFOpNanKernelPattern final :
public OpConversionPattern<arith::CmpFOp> {
1230 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1231 ConversionPatternRewriter &rewriter)
const override {
1232 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1233 rewriter.replaceOpWithNewOp<spirv::OrderedOp>(op, adaptor.getLhs(),
1238 if (op.getPredicate() == arith::CmpFPredicate::UNO) {
1239 rewriter.replaceOpWithNewOp<spirv::UnorderedOp>(op, adaptor.getLhs(),
1250class CmpFOpNanNonePattern final :
public OpConversionPattern<arith::CmpFOp> {
1255 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1256 ConversionPatternRewriter &rewriter)
const override {
1257 if (op.getPredicate() != arith::CmpFPredicate::ORD &&
1258 op.getPredicate() != arith::CmpFPredicate::UNO)
1261 Location loc = op.getLoc();
1264 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1265 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1267 replace = spirv::ConstantOp::getOne(op.getType(), loc, rewriter);
1270 replace = spirv::ConstantOp::getZero(op.getType(), loc, rewriter);
1273 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1274 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1276 replace = spirv::LogicalOrOp::create(rewriter, loc, lhsIsNan, rhsIsNan);
1277 if (op.getPredicate() == arith::CmpFPredicate::ORD)
1278 replace = spirv::LogicalNotOp::create(rewriter, loc, replace);
1281 rewriter.replaceOp(op, replace);
1292template <
typename ArithExtendedOp,
typename SPIRVExtendedOp>
1293class BinaryExtendedOpPattern final
1294 :
public OpConversionPattern<ArithExtendedOp> {
1296 using OpConversionPattern<ArithExtendedOp>::OpConversionPattern;
1298 matchAndRewrite(ArithExtendedOp op,
typename ArithExtendedOp::Adaptor adaptor,
1299 ConversionPatternRewriter &rewriter)
const override {
1300 Type dstElemTy = adaptor.getLhs().getType();
1301 Location loc = op->getLoc();
1302 Value
result = SPIRVExtendedOp::create(rewriter, loc, adaptor.getLhs(),
1305 Value valueResult = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1307 Value flagValue = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1311 Value one = spirv::ConstantOp::getOne(dstElemTy, loc, rewriter);
1312 Value flagResult = spirv::IEqualOp::create(rewriter, loc, flagValue, one);
1314 rewriter.replaceOp(op, {valueResult, flagResult});
1324template <
typename ArithMulOp,
typename SPIRVMulOp>
1325class MulIExtendedOpPattern final :
public OpConversionPattern<ArithMulOp> {
1327 using OpConversionPattern<ArithMulOp>::OpConversionPattern;
1329 matchAndRewrite(ArithMulOp op,
typename ArithMulOp::Adaptor adaptor,
1330 ConversionPatternRewriter &rewriter)
const override {
1331 Location loc = op->getLoc();
1333 SPIRVMulOp::create(rewriter, loc, adaptor.getLhs(), adaptor.getRhs());
1335 Value low = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1337 Value high = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1340 rewriter.replaceOp(op, {low, high});
1350class SelectOpPattern final :
public OpConversionPattern<arith::SelectOp> {
1354 matchAndRewrite(arith::SelectOp op, OpAdaptor adaptor,
1355 ConversionPatternRewriter &rewriter)
const override {
1356 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, adaptor.getCondition(),
1357 adaptor.getTrueValue(),
1358 adaptor.getFalseValue());
1369template <
typename Op,
typename SPIRVOp>
1370class MinimumMaximumFOpPattern final :
public OpConversionPattern<Op> {
1372 using OpConversionPattern<
Op>::OpConversionPattern;
1374 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
1375 ConversionPatternRewriter &rewriter)
const override {
1376 auto *converter = this->
template getTypeConverter<SPIRVTypeConverter>();
1377 Type dstType = converter->convertType(op.getType());
1389 Location loc = op.
getLoc();
1391 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1393 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1394 rewriter.replaceOp(op, spirvOp);
1398 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1399 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1401 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1402 adaptor.getLhs(), spirvOp);
1403 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1404 adaptor.getRhs(), select1);
1406 rewriter.replaceOp(op, select2);
1417template <
typename Op,
typename SPIRVOp>
1418class MinNumMaxNumFOpPattern final :
public OpConversionPattern<Op> {
1419 template <
typename TargetOp>
1420 constexpr bool shouldInsertNanGuards()
const {
1421 return llvm::is_one_of<TargetOp, spirv::GLFMaxOp, spirv::GLFMinOp>::value;
1425 using OpConversionPattern<
Op>::OpConversionPattern;
1427 matchAndRewrite(
Op op,
typename Op::Adaptor adaptor,
1428 ConversionPatternRewriter &rewriter)
const override {
1429 auto *converter = this->
template getTypeConverter<SPIRVTypeConverter>();
1430 Type dstType = converter->convertType(op.getType());
1445 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1447 if (!shouldInsertNanGuards<SPIRVOp>() ||
1448 bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1449 rewriter.replaceOp(op, spirvOp);
1453 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1454 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1456 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1457 adaptor.getRhs(), spirvOp);
1458 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1459 adaptor.getLhs(), select1);
1461 rewriter.replaceOp(op, select2);
1476 ConstantCompositeOpPattern,
1477 ConstantScalarOpPattern,
1478 BoolIOpPattern<arith::AddIOp, spirv::LogicalNotEqualOp>,
1479 ElementwiseArithOpPattern<arith::AddIOp, spirv::IAddOp>,
1480 BoolIOpPattern<arith::SubIOp, spirv::LogicalNotEqualOp>,
1481 ElementwiseArithOpPattern<arith::SubIOp, spirv::ISubOp>,
1482 BoolIOpPattern<arith::MulIOp, spirv::LogicalAndOp>,
1483 ElementwiseArithOpPattern<arith::MulIOp, spirv::IMulOp>,
1484 BoolIOpPattern<arith::DivUIOp, spirv::LogicalAndOp>,
1486 BoolIOpPattern<arith::DivSIOp, spirv::LogicalAndOp>,
1488 BoolIOpAndNotPattern<arith::RemUIOp>,
1490 BoolIOpAndNotPattern<arith::RemSIOp>,
1491 RemSIOpGLPattern, RemSIOpCLPattern,
1492 BitwiseOpPattern<arith::AndIOp, spirv::LogicalAndOp, spirv::BitwiseAndOp>,
1493 BitwiseOpPattern<arith::OrIOp, spirv::LogicalOrOp, spirv::BitwiseOrOp>,
1494 XOrIOpLogicalPattern, XOrIOpBooleanPattern,
1495 BoolIOpAndNotPattern<arith::ShLIOp>,
1496 ElementwiseArithOpPattern<arith::ShLIOp, spirv::ShiftLeftLogicalOp>,
1497 BoolIOpAndNotPattern<arith::ShRUIOp>,
1507 ExtUIPattern, BoolToValuePattern<arith::ExtUIOp>,
1508 ExtSIPattern, ExtSII1Pattern,
1509 TypeCastingOpPattern<arith::ExtFOp, spirv::FConvertOp>,
1510 TruncIPattern, TruncII1Pattern,
1511 TypeCastingOpPattern<arith::TruncFOp, spirv::FConvertOp>,
1512 TypeCastingOpPattern<arith::ConvertFOp, spirv::FConvertOp>,
1513 IntToFPPattern<arith::UIToFPOp, spirv::ConvertUToFOp, false>,
1514 BoolToValuePattern<arith::UIToFPOp>,
1515 IntToFPPattern<arith::SIToFPOp, spirv::ConvertSToFOp, true>,
1516 TypeCastingOpPattern<arith::FPToUIOp, spirv::ConvertFToUOp>,
1517 TypeCastingOpPattern<arith::FPToSIOp, spirv::ConvertFToSOp>,
1518 TypeCastingOpPattern<arith::IndexCastOp, spirv::SConvertOp>,
1519 IndexCastIndexI1Pattern, BoolToValuePattern<arith::IndexCastOp>,
1520 TypeCastingOpPattern<arith::IndexCastUIOp, spirv::UConvertOp>,
1521 TypeCastingOpPattern<arith::BitcastOp, spirv::BitcastOp>,
1522 CmpIOpBooleanPattern, CmpIOpPattern,
1523 CmpFOpNanNonePattern, CmpFOpPattern,
1524 BinaryExtendedOpPattern<arith::AddUIExtendedOp, spirv::IAddCarryOp>,
1525 BinaryExtendedOpPattern<arith::SubUIExtendedOp, spirv::ISubBorrowOp>,
1526 MulIExtendedOpPattern<arith::MulSIExtendedOp, spirv::SMulExtendedOp>,
1527 MulIExtendedOpPattern<arith::MulUIExtendedOp, spirv::UMulExtendedOp>,
1530 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::GLFMaxOp>,
1531 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::GLFMinOp>,
1532 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::GLNMaxOp>,
1533 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::GLNMinOp>,
1534 BoolIOpPattern<arith::MaxSIOp, spirv::LogicalAndOp>,
1535 BoolIOpPattern<arith::MaxUIOp, spirv::LogicalOrOp>,
1536 BoolIOpPattern<arith::MinSIOp, spirv::LogicalOrOp>,
1537 BoolIOpPattern<arith::MinUIOp, spirv::LogicalAndOp>,
1543 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::CLFMaxOp>,
1544 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::CLFMinOp>,
1545 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::CLFMaxOp>,
1546 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::CLFMinOp>,
1556 patterns.
add<CmpFOpNanKernelPattern>(typeConverter, patterns.
getContext(),
1565struct ConvertArithToSPIRVPass
1569 void runOnOperation()
override {
1572 std::unique_ptr<SPIRVConversionTarget>
target =
1576 options.emulateLT32BitScalarTypes = this->emulateLT32BitScalarTypes;
1577 options.emulateUnsupportedFloatTypes = this->emulateUnsupportedFloatTypes;
1582 target->addLegalOp<UnrealizedConversionCastOp>();
1585 target->addIllegalDialect<arith::ArithDialect>();
1590 if (failed(applyPartialConversion(op, *
target, std::move(patterns))))
1591 signalPassFailure();
static bool hasSameBitwidth(Type a, Type b)
Returns true if scalar/vector type a and b have the same number of bitwidth.
static Value getScalarOrVectorConstInt(Type type, uint64_t value, OpBuilder &builder, Location loc)
Creates a scalar/vector integer constant.
static LogicalResult getTypeConversionFailure(ConversionPatternRewriter &rewriter, Operation *op, Type srcType)
Returns a source type conversion failure for srcType and operation op.
static IntegerAttr getIntegerAttrFromFloatAttr(FloatAttr floatAttr, IntegerType dstType, Builder builder)
static FloatAttr convertFloatAttr(FloatAttr srcAttr, FloatType dstType, Builder builder)
Converts the given srcAttr to a new attribute of the given dstType.
static BoolAttr convertBoolAttr(Attribute srcAttr, Builder builder)
Converts the given srcAttr into a boolean attribute if it holds an integral value.
static bool isBoolScalarOrVector(Type type)
Returns true if the given type is a boolean scalar or vector type.
#define DISPATCH(cmpPredicate, spirvOp)
static IntegerAttr convertIntegerAttr(IntegerAttr srcAttr, IntegerType dstType, Builder builder)
Converts the given srcAttr to a new attribute of the given dstType.
static llvm::ManagedStatic< PassManagerOptions > options
This class represents a processed binary blob of data.
ArrayRef< char > getData() const
Return the raw underlying data of this blob.
Attributes are known-constant values of operations.
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIntegerAttr(Type type, int64_t value)
BoolAttr getBoolAttr(bool value)
FloatAttr getF32FloatAttr(float value)
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
static DenseElementsAttr getFromRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Construct a dense elements attribute from a raw buffer representing the data for this attribute.
static bool isValidRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Returns true if the given buffer is a valid raw buffer for the given type.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
DenseElementsAttr reshape(ShapedType newType)
Return a new DenseElementsAttr that has the same data as the current attribute, but has been reshaped...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Location getLoc()
The source location the operation was defined or derived from.
result_type_range getResultTypes()
unsigned getNumResults()
Return the number of results held by this operation.
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.
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
bool isInteger() const
Return true if this is an integer type (with the specified width).
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Type front()
Return first type in the range.
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.
ConvertArithToSPIRVPassBase Base
An attribute that specifies the target version, allowed extensions and capabilities,...
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
void populateArithToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
TargetEnvAttr lookupTargetEnvOrDefault(Operation *op)
Queries the target environment recursively from enclosing symbol table ops containing the given op or...
std::string getDecorationString(Decoration decoration)
Converts a SPIR-V Decoration enum value to its snake_case string representation for use in MLIR attri...
Include the generated interface declarations.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
Converts elementwise unary, binary and ternary standard operations to SPIR-V operations.