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 '"
107 ConversionPatternRewriter &rewriter) {
108 APFloat floatVal = floatAttr.getValue();
109 APInt intVal = floatVal.bitcastToAPInt();
110 return rewriter.getIntegerAttr(dstType, intVal);
115 assert(type &&
"Not a valid type");
119 if (
auto vecType = dyn_cast<VectorType>(type))
120 return vecType.getElementType().isInteger(1);
128 if (
auto vectorType = dyn_cast<VectorType>(type)) {
129 Attribute element = IntegerAttr::get(vectorType.getElementType(), value);
131 return spirv::ConstantOp::create(builder, loc, vectorType, attr);
134 if (
auto intType = dyn_cast<IntegerType>(type))
135 return spirv::ConstantOp::create(builder, loc, type,
144 auto getNumBitwidth = [](
Type type) {
146 if (type.isIntOrFloat())
147 bw = type.getIntOrFloatBitWidth();
148 else if (
auto vecType = dyn_cast<VectorType>(type))
149 bw = vecType.getElementTypeBitWidth() * vecType.getNumElements();
152 unsigned aBW = getNumBitwidth(a);
153 unsigned bBW = getNumBitwidth(
b);
154 return aBW != 0 && bBW != 0 && aBW == bBW;
161 return rewriter.notifyMatchFailure(
163 llvm::formatv(
"failed to convert source type '{0}'", srcType));
177template <
typename Op,
typename SPIRVOp>
178struct ElementwiseArithOpPattern final : OpConversionPattern<Op> {
179 using OpConversionPattern<
Op>::OpConversionPattern;
182 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
183 ConversionPatternRewriter &rewriter)
const override {
184 assert(adaptor.getOperands().size() <= 3);
187 if (!adaptor.getOperands().empty() &&
190 auto converter = this->
template getTypeConverter<SPIRVTypeConverter>();
191 Type dstType = converter->convertType(op.getType());
193 return rewriter.notifyMatchFailure(
195 llvm::formatv(
"failed to convert type {0} for SPIR-V", op.getType()));
198 if (SPIRVOp::template hasTrait<OpTrait::spirv::UnsignedOp>() &&
200 dstType != op.getType()) {
201 return op.
emitError(
"bitwidth emulation is not implemented yet on "
202 "unsigned op pattern version");
205 auto overflowFlags = arith::IntegerOverflowFlags::none;
206 if (
auto overflowIface =
207 dyn_cast<arith::ArithIntegerOverflowFlagsInterface>(*op)) {
208 if (converter->getTargetEnv().allows(
209 spirv::Extension::SPV_KHR_no_integer_wrap_decoration))
210 overflowFlags = overflowIface.getOverflowAttr().getValue();
213 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
214 op, dstType, adaptor.getOperands());
216 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nsw))
217 newOp->setDiscardableAttr(
219 rewriter.getUnitAttr());
221 if (bitEnumContainsAny(overflowFlags, arith::IntegerOverflowFlags::nuw))
222 newOp->setDiscardableAttr(
224 rewriter.getUnitAttr());
235struct ConstantCompositeOpPattern final
236 :
public OpConversionPattern<arith::ConstantOp> {
240 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
241 ConversionPatternRewriter &rewriter)
const override {
242 auto srcType = dyn_cast<ShapedType>(constOp.getType());
243 if (!srcType || srcType.getNumElements() == 1)
248 if (!isa<VectorType, RankedTensorType>(srcType))
249 return rewriter.notifyMatchFailure(constOp,
"unsupported ShapedType");
251 Type dstType = getTypeConverter()->convertType(srcType);
258 if (
auto denseElementsAttr =
259 dyn_cast<DenseElementsAttr>(constOp.getValue())) {
260 dstElementsAttr = denseElementsAttr;
261 }
else if (
auto resourceAttr =
262 dyn_cast<DenseResourceElementsAttr>(constOp.getValue())) {
266 return constOp->emitError(
"could not find resource blob");
273 return constOp->emitError(
"resource is not a valid buffer");
278 return constOp->emitError(
"unsupported elements attribute");
281 ShapedType dstAttrType = dstElementsAttr.
getType();
285 if (srcType.getRank() > 1) {
286 if (isa<RankedTensorType>(srcType)) {
287 dstAttrType = RankedTensorType::get(srcType.getNumElements(),
288 srcType.getElementType());
289 dstElementsAttr = dstElementsAttr.
reshape(dstAttrType);
296 Type srcElemType = srcType.getElementType();
300 if (
auto arrayType = dyn_cast<spirv::ArrayType>(dstType))
301 dstElemType = arrayType.getElementType();
303 dstElemType = cast<VectorType>(dstType).getElementType();
307 if (srcElemType != dstElemType) {
309 if (isa<FloatType>(srcElemType)) {
310 for (FloatAttr srcAttr : dstElementsAttr.
getValues<FloatAttr>()) {
313 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
314 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
316 isa<IntegerType>(dstElemType)) {
325 elements.push_back(dstAttr);
330 for (IntegerAttr srcAttr : dstElementsAttr.
getValues<IntegerAttr>()) {
332 srcAttr, cast<IntegerType>(dstElemType), rewriter);
335 elements.push_back(dstAttr);
343 if (isa<RankedTensorType>(dstAttrType))
345 RankedTensorType::get(dstAttrType.getShape(), dstElemType);
347 dstAttrType = VectorType::get(dstAttrType.getShape(), dstElemType);
352 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType,
359struct ConstantScalarOpPattern final
360 :
public OpConversionPattern<arith::ConstantOp> {
364 matchAndRewrite(arith::ConstantOp constOp, OpAdaptor adaptor,
365 ConversionPatternRewriter &rewriter)
const override {
366 Type srcType = constOp.getType();
367 if (
auto shapedType = dyn_cast<ShapedType>(srcType)) {
368 if (shapedType.getNumElements() != 1)
370 srcType = shapedType.getElementType();
375 Attribute cstAttr = constOp.getValue();
376 if (
auto elementsAttr = dyn_cast<DenseElementsAttr>(cstAttr))
377 cstAttr = elementsAttr.getSplatValue<Attribute>();
379 Type dstType = getTypeConverter()->convertType(srcType);
384 if (isa<FloatType>(srcType)) {
385 auto srcAttr = cast<FloatAttr>(cstAttr);
386 Attribute dstAttr = srcAttr;
390 auto *typeConverter = getTypeConverter<SPIRVTypeConverter>();
391 if (typeConverter->getOptions().emulateUnsupportedFloatTypes &&
393 dstType.getIntOrFloatBitWidth() == 8) {
398 }
else if (srcType != dstType) {
404 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
415 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
421 auto srcAttr = cast<IntegerAttr>(cstAttr);
422 IntegerAttr dstAttr =
426 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(constOp, dstType, dstAttr);
442template <
typename SignedAbsOp>
445 assert(
lhs.getType() ==
rhs.getType());
446 assert(
lhs == signOperand ||
rhs == signOperand);
451 Value lhsAbs = SignedAbsOp::create(builder, loc, type,
lhs);
452 Value rhsAbs = SignedAbsOp::create(builder, loc, type,
rhs);
453 Value abs = spirv::UModOp::create(builder, loc, lhsAbs, rhsAbs);
457 if (
lhs == signOperand)
458 isPositive = spirv::IEqualOp::create(builder, loc,
lhs, lhsAbs);
460 isPositive = spirv::IEqualOp::create(builder, loc,
rhs, rhsAbs);
461 Value absNegate = spirv::SNegateOp::create(builder, loc, type, abs);
462 return spirv::SelectOp::create(builder, loc, type, isPositive, abs,
470struct RemSIOpGLPattern final :
public OpConversionPattern<arith::RemSIOp> {
474 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
475 ConversionPatternRewriter &rewriter)
const override {
476 Value
result = emulateSignedRemainder<spirv::GLSAbsOp>(
477 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
478 adaptor.getOperands()[0], rewriter);
479 rewriter.replaceOp(op,
result);
486struct RemSIOpCLPattern final :
public OpConversionPattern<arith::RemSIOp> {
490 matchAndRewrite(arith::RemSIOp op, OpAdaptor adaptor,
491 ConversionPatternRewriter &rewriter)
const override {
492 Value
result = emulateSignedRemainder<spirv::CLSAbsOp>(
493 op.getLoc(), adaptor.getOperands()[0], adaptor.getOperands()[1],
494 adaptor.getOperands()[0], rewriter);
495 rewriter.replaceOp(op,
result);
509template <
typename Op,
typename SPIRVLogicalOp,
typename SPIRVBitwiseOp>
510struct BitwiseOpPattern final :
public OpConversionPattern<Op> {
511 using OpConversionPattern<
Op>::OpConversionPattern;
514 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
515 ConversionPatternRewriter &rewriter)
const override {
516 assert(adaptor.getOperands().size() == 2);
517 Type dstType = this->getTypeConverter()->convertType(op.getType());
522 rewriter.template replaceOpWithNewOp<SPIRVLogicalOp>(
523 op, dstType, adaptor.getOperands());
525 rewriter.template replaceOpWithNewOp<SPIRVBitwiseOp>(
526 op, dstType, adaptor.getOperands());
537struct XOrIOpLogicalPattern final :
public OpConversionPattern<arith::XOrIOp> {
541 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
542 ConversionPatternRewriter &rewriter)
const override {
543 assert(adaptor.getOperands().size() == 2);
548 Type dstType = getTypeConverter()->convertType(op.getType());
552 rewriter.replaceOpWithNewOp<spirv::BitwiseXorOp>(op, dstType,
553 adaptor.getOperands());
561struct XOrIOpBooleanPattern final :
public OpConversionPattern<arith::XOrIOp> {
565 matchAndRewrite(arith::XOrIOp op, OpAdaptor adaptor,
566 ConversionPatternRewriter &rewriter)
const override {
567 assert(adaptor.getOperands().size() == 2);
572 Type dstType = getTypeConverter()->convertType(op.getType());
576 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
577 op, dstType, adaptor.getOperands());
597template <
typename ArithOp,
typename SPIRVOp>
598struct BoolIOpPattern final :
public OpConversionPattern<ArithOp> {
599 BoolIOpPattern(
const TypeConverter &converter, MLIRContext *context)
602 : OpConversionPattern<ArithOp>(converter, context, 2) {}
605 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
606 ConversionPatternRewriter &rewriter)
const override {
610 Type dstType = this->getTypeConverter()->convertType(op.getType());
614 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
628template <
typename ArithOp>
629struct BoolIOpAndNotPattern final :
public OpConversionPattern<ArithOp> {
630 BoolIOpAndNotPattern(
const TypeConverter &converter, MLIRContext *context)
633 : OpConversionPattern<ArithOp>(converter, context, 2) {}
636 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
637 ConversionPatternRewriter &rewriter)
const override {
641 Type dstType = this->getTypeConverter()->convertType(op.getType());
645 Location loc = op.getLoc();
646 Value notRhs = spirv::LogicalNotOp::create(rewriter, loc, dstType,
647 adaptor.getOperands()[1]);
648 rewriter.replaceOpWithNewOp<spirv::LogicalAndOp>(
649 op, dstType, adaptor.getOperands()[0], notRhs);
656struct ShRSIBoolPattern final :
public OpConversionPattern<arith::ShRSIOp> {
657 ShRSIBoolPattern(
const TypeConverter &converter, MLIRContext *context)
660 : OpConversionPattern<arith::ShRSIOp>(converter, context,
664 matchAndRewrite(arith::ShRSIOp op, OpAdaptor adaptor,
665 ConversionPatternRewriter &rewriter)
const override {
669 rewriter.replaceOp(op, adaptor.getOperands().front());
681template <
typename ArithOp>
682struct BoolToValuePattern final :
public OpConversionPattern<ArithOp> {
683 using OpConversionPattern<ArithOp>::OpConversionPattern;
686 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
687 ConversionPatternRewriter &rewriter)
const override {
688 Type srcType = adaptor.getOperands().front().getType();
692 Type dstType = this->getTypeConverter()->convertType(op.getType());
696 Location loc = op.getLoc();
697 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
698 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
699 rewriter.replaceOpWithNewOp<spirv::SelectOp>(
700 op, dstType, adaptor.getOperands().front(), one, zero);
716template <
typename ArithOp,
typename SPIRVOp,
bool IsSigned>
717struct IntToFPPattern final :
public OpConversionPattern<ArithOp> {
718 using OpConversionPattern<ArithOp>::OpConversionPattern;
721 matchAndRewrite(ArithOp op,
typename ArithOp::Adaptor adaptor,
722 ConversionPatternRewriter &rewriter)
const override {
723 Type srcType = adaptor.getOperands().front().getType();
727 Type dstType = this->getTypeConverter()->convertType(op.getType());
732 unsigned originalBitwidth =
734 unsigned convertedBitwidth =
737 if (originalBitwidth >= convertedBitwidth) {
738 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, adaptor.getOperands());
743 Location loc = op.getLoc();
745 if constexpr (IsSigned) {
747 unsigned shiftAmount = convertedBitwidth - originalBitwidth;
750 Value shifted = spirv::ShiftLeftLogicalOp::create(
751 rewriter, loc, srcType, adaptor.getIn(), shiftSize);
752 cleaned = spirv::ShiftRightArithmeticOp::create(rewriter, loc, srcType,
757 srcType, llvm::maskTrailingOnes<uint64_t>(originalBitwidth), rewriter,
759 cleaned = spirv::BitwiseAndOp::create(rewriter, loc, srcType,
760 adaptor.getIn(), mask);
762 rewriter.replaceOpWithNewOp<SPIRVOp>(op, dstType, cleaned);
772struct IndexCastIndexI1Pattern final
773 :
public OpConversionPattern<arith::IndexCastOp> {
777 matchAndRewrite(arith::IndexCastOp op, OpAdaptor adaptor,
778 ConversionPatternRewriter &rewriter)
const override {
782 Type dstType = getTypeConverter()->convertType(op.getType());
786 Location loc = op.getLoc();
788 spirv::ConstantOp::getZero(adaptor.getIn().getType(), loc, rewriter);
789 rewriter.replaceOpWithNewOp<spirv::INotEqualOp>(op, dstType, zeroIdx,
801struct ExtSII1Pattern final :
public OpConversionPattern<arith::ExtSIOp> {
805 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
806 ConversionPatternRewriter &rewriter)
const override {
807 Value operand = adaptor.getIn();
811 Location loc = op.getLoc();
812 Type dstType = getTypeConverter()->convertType(op.getType());
817 if (
auto intTy = dyn_cast<IntegerType>(dstType)) {
818 unsigned componentBitwidth = intTy.getWidth();
819 allOnes = spirv::ConstantOp::create(
820 rewriter, loc, intTy,
821 rewriter.getIntegerAttr(intTy, APInt::getAllOnes(componentBitwidth)));
822 }
else if (
auto vectorTy = dyn_cast<VectorType>(dstType)) {
823 unsigned componentBitwidth = vectorTy.getElementTypeBitWidth();
824 allOnes = spirv::ConstantOp::create(
825 rewriter, loc, vectorTy,
826 SplatElementsAttr::get(vectorTy,
827 APInt::getAllOnes(componentBitwidth)));
829 return rewriter.notifyMatchFailure(
830 loc, llvm::formatv(
"unhandled type: {0}", dstType));
833 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
834 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, operand, allOnes,
842struct ExtSIPattern final :
public OpConversionPattern<arith::ExtSIOp> {
846 matchAndRewrite(arith::ExtSIOp op, OpAdaptor adaptor,
847 ConversionPatternRewriter &rewriter)
const override {
848 Type srcType = adaptor.getIn().getType();
852 Type dstType = getTypeConverter()->convertType(op.getType());
856 if (dstType == srcType) {
864 assert(srcBW < dstBW);
866 rewriter, op.getLoc());
868 return rewriter.notifyMatchFailure(op,
"unsupported type for shift");
873 auto shiftLOp = spirv::ShiftLeftLogicalOp::create(
874 rewriter, op.getLoc(), dstType, adaptor.getIn(), shiftSize);
878 rewriter.replaceOpWithNewOp<spirv::ShiftRightArithmeticOp>(
879 op, dstType, shiftLOp, shiftSize);
881 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
882 adaptor.getOperands());
895struct ExtUIPattern final :
public OpConversionPattern<arith::ExtUIOp> {
899 matchAndRewrite(arith::ExtUIOp op, OpAdaptor adaptor,
900 ConversionPatternRewriter &rewriter)
const override {
901 Type srcType = adaptor.getIn().getType();
905 Type dstType = getTypeConverter()->convertType(op.getType());
909 if (dstType == srcType) {
917 dstType, llvm::maskTrailingOnes<uint64_t>(bitwidth), rewriter,
920 return rewriter.notifyMatchFailure(op,
"unsupported type for mask");
921 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
922 adaptor.getIn(), mask);
924 rewriter.replaceOpWithNewOp<spirv::UConvertOp>(op, dstType,
925 adaptor.getOperands());
937struct TruncII1Pattern final :
public OpConversionPattern<arith::TruncIOp> {
941 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
942 ConversionPatternRewriter &rewriter)
const override {
943 Type dstType = getTypeConverter()->convertType(op.getType());
950 Location loc = op.getLoc();
951 auto srcType = adaptor.getOperands().front().getType();
953 Value mask = spirv::ConstantOp::getOne(srcType, loc, rewriter);
954 Value maskedSrc = spirv::BitwiseAndOp::create(
955 rewriter, loc, srcType, adaptor.getOperands()[0], mask);
956 Value isOne = spirv::IEqualOp::create(rewriter, loc, maskedSrc, mask);
958 Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
959 Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
960 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, dstType, isOne, one, zero);
967struct TruncIPattern final :
public OpConversionPattern<arith::TruncIOp> {
971 matchAndRewrite(arith::TruncIOp op, OpAdaptor adaptor,
972 ConversionPatternRewriter &rewriter)
const override {
973 Type srcType = adaptor.getIn().getType();
974 Type dstType = getTypeConverter()->convertType(op.getType());
981 if (dstType == srcType) {
988 dstType, llvm::maskTrailingOnes<uint64_t>(bw), rewriter, op.getLoc());
990 return rewriter.notifyMatchFailure(op,
"unsupported type for mask");
991 rewriter.replaceOpWithNewOp<spirv::BitwiseAndOp>(op, dstType,
992 adaptor.getIn(), mask);
995 rewriter.replaceOpWithNewOp<spirv::SConvertOp>(op, dstType,
996 adaptor.getOperands());
1006static std::optional<spirv::FPRoundingMode>
1007convertArithRoundingModeToSPIRV(arith::RoundingMode roundingMode) {
1008 switch (roundingMode) {
1009 case arith::RoundingMode::downward:
1010 return spirv::FPRoundingMode::RTN;
1011 case arith::RoundingMode::to_nearest_even:
1012 return spirv::FPRoundingMode::RTE;
1013 case arith::RoundingMode::toward_zero:
1014 return spirv::FPRoundingMode::RTZ;
1015 case arith::RoundingMode::upward:
1016 return spirv::FPRoundingMode::RTP;
1017 case arith::RoundingMode::to_nearest_away:
1020 return std::nullopt;
1022 llvm_unreachable(
"Unhandled rounding mode");
1026template <
typename Op,
typename SPIRVOp>
1027struct TypeCastingOpPattern final :
public OpConversionPattern<Op> {
1028 using OpConversionPattern<
Op>::OpConversionPattern;
1031 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
1032 ConversionPatternRewriter &rewriter)
const override {
1033 Type srcType = llvm::getSingleElement(adaptor.getOperands()).getType();
1034 Type dstType = this->getTypeConverter()->convertType(op.getType());
1041 if (dstType == srcType) {
1044 rewriter.replaceOp(op, adaptor.getOperands().front());
1047 std::optional<spirv::FPRoundingMode> rm = std::nullopt;
1048 if (
auto roundingModeOp =
1049 dyn_cast<arith::ArithRoundingModeInterface>(*op)) {
1050 if (arith::RoundingModeAttr roundingMode =
1051 roundingModeOp.getRoundingModeAttr()) {
1053 convertArithRoundingModeToSPIRV(roundingMode.getValue()))) {
1054 return rewriter.notifyMatchFailure(
1056 llvm::formatv(
"unsupported rounding mode '{0}'", roundingMode));
1061 auto newOp = rewriter.template replaceOpWithNewOp<SPIRVOp>(
1062 op, dstType, adaptor.getOperands());
1064 newOp->setDiscardableAttr(
1066 spirv::FPRoundingModeAttr::get(rewriter.getContext(), *rm));
1078class CmpIOpBooleanPattern final :
public OpConversionPattern<arith::CmpIOp> {
1083 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1084 ConversionPatternRewriter &rewriter)
const override {
1085 Type srcType = op.getLhs().getType();
1088 Type dstType = getTypeConverter()->convertType(srcType);
1092 switch (op.getPredicate()) {
1093 case arith::CmpIPredicate::eq: {
1094 rewriter.replaceOpWithNewOp<spirv::LogicalEqualOp>(op, adaptor.getLhs(),
1098 case arith::CmpIPredicate::ne: {
1099 rewriter.replaceOpWithNewOp<spirv::LogicalNotEqualOp>(
1100 op, adaptor.getLhs(), adaptor.getRhs());
1103 case arith::CmpIPredicate::uge:
1104 case arith::CmpIPredicate::ugt:
1105 case arith::CmpIPredicate::ule:
1106 case arith::CmpIPredicate::ult: {
1109 Type type = rewriter.getI32Type();
1110 if (
auto vectorType = dyn_cast<VectorType>(dstType))
1111 type = VectorType::get(vectorType.getShape(), type);
1113 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getLhs());
1115 arith::ExtUIOp::create(rewriter, op.getLoc(), type, adaptor.getRhs());
1117 rewriter.replaceOpWithNewOp<arith::CmpIOp>(op, op.getPredicate(), extLhs,
1129class CmpIOpPattern final :
public OpConversionPattern<arith::CmpIOp> {
1134 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
1135 ConversionPatternRewriter &rewriter)
const override {
1136 Type srcType = op.getLhs().getType();
1139 Type dstType = getTypeConverter()->convertType(srcType);
1143 switch (op.getPredicate()) {
1144#define DISPATCH(cmpPredicate, spirvOp) \
1145 case cmpPredicate: \
1146 if (spirvOp::template hasTrait<OpTrait::spirv::UnsignedOp>() && \
1147 !getElementTypeOrSelf(srcType).isIndex() && srcType != dstType && \
1148 !hasSameBitwidth(srcType, dstType)) { \
1149 return op.emitError( \
1150 "bitwidth emulation is not implemented yet on unsigned op"); \
1152 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1153 adaptor.getRhs()); \
1156 DISPATCH(arith::CmpIPredicate::eq, spirv::IEqualOp);
1157 DISPATCH(arith::CmpIPredicate::ne, spirv::INotEqualOp);
1158 DISPATCH(arith::CmpIPredicate::slt, spirv::SLessThanOp);
1159 DISPATCH(arith::CmpIPredicate::sle, spirv::SLessThanEqualOp);
1160 DISPATCH(arith::CmpIPredicate::sgt, spirv::SGreaterThanOp);
1161 DISPATCH(arith::CmpIPredicate::sge, spirv::SGreaterThanEqualOp);
1162 DISPATCH(arith::CmpIPredicate::ult, spirv::ULessThanOp);
1163 DISPATCH(arith::CmpIPredicate::ule, spirv::ULessThanEqualOp);
1164 DISPATCH(arith::CmpIPredicate::ugt, spirv::UGreaterThanOp);
1165 DISPATCH(arith::CmpIPredicate::uge, spirv::UGreaterThanEqualOp);
1178class CmpFOpPattern final :
public OpConversionPattern<arith::CmpFOp> {
1183 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1184 ConversionPatternRewriter &rewriter)
const override {
1185 switch (op.getPredicate()) {
1186#define DISPATCH(cmpPredicate, spirvOp) \
1187 case cmpPredicate: \
1188 rewriter.replaceOpWithNewOp<spirvOp>(op, adaptor.getLhs(), \
1189 adaptor.getRhs()); \
1193 DISPATCH(arith::CmpFPredicate::OEQ, spirv::FOrdEqualOp);
1194 DISPATCH(arith::CmpFPredicate::OGT, spirv::FOrdGreaterThanOp);
1195 DISPATCH(arith::CmpFPredicate::OGE, spirv::FOrdGreaterThanEqualOp);
1196 DISPATCH(arith::CmpFPredicate::OLT, spirv::FOrdLessThanOp);
1197 DISPATCH(arith::CmpFPredicate::OLE, spirv::FOrdLessThanEqualOp);
1198 DISPATCH(arith::CmpFPredicate::ONE, spirv::FOrdNotEqualOp);
1200 DISPATCH(arith::CmpFPredicate::UEQ, spirv::FUnordEqualOp);
1201 DISPATCH(arith::CmpFPredicate::UGT, spirv::FUnordGreaterThanOp);
1202 DISPATCH(arith::CmpFPredicate::UGE, spirv::FUnordGreaterThanEqualOp);
1203 DISPATCH(arith::CmpFPredicate::ULT, spirv::FUnordLessThanOp);
1204 DISPATCH(arith::CmpFPredicate::ULE, spirv::FUnordLessThanEqualOp);
1205 DISPATCH(arith::CmpFPredicate::UNE, spirv::FUnordNotEqualOp);
1218class CmpFOpNanKernelPattern final :
public OpConversionPattern<arith::CmpFOp> {
1223 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1224 ConversionPatternRewriter &rewriter)
const override {
1225 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1226 rewriter.replaceOpWithNewOp<spirv::OrderedOp>(op, adaptor.getLhs(),
1231 if (op.getPredicate() == arith::CmpFPredicate::UNO) {
1232 rewriter.replaceOpWithNewOp<spirv::UnorderedOp>(op, adaptor.getLhs(),
1243class CmpFOpNanNonePattern final :
public OpConversionPattern<arith::CmpFOp> {
1248 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
1249 ConversionPatternRewriter &rewriter)
const override {
1250 if (op.getPredicate() != arith::CmpFPredicate::ORD &&
1251 op.getPredicate() != arith::CmpFPredicate::UNO)
1254 Location loc = op.getLoc();
1257 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1258 if (op.getPredicate() == arith::CmpFPredicate::ORD) {
1260 replace = spirv::ConstantOp::getOne(op.getType(), loc, rewriter);
1263 replace = spirv::ConstantOp::getZero(op.getType(), loc, rewriter);
1266 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1267 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1269 replace = spirv::LogicalOrOp::create(rewriter, loc, lhsIsNan, rhsIsNan);
1270 if (op.getPredicate() == arith::CmpFPredicate::ORD)
1271 replace = spirv::LogicalNotOp::create(rewriter, loc, replace);
1274 rewriter.replaceOp(op, replace);
1285template <
typename ArithExtendedOp,
typename SPIRVExtendedOp>
1286class BinaryExtendedOpPattern final
1287 :
public OpConversionPattern<ArithExtendedOp> {
1289 using OpConversionPattern<ArithExtendedOp>::OpConversionPattern;
1291 matchAndRewrite(ArithExtendedOp op,
typename ArithExtendedOp::Adaptor adaptor,
1292 ConversionPatternRewriter &rewriter)
const override {
1293 Type dstElemTy = adaptor.getLhs().getType();
1294 Location loc = op->getLoc();
1295 Value
result = SPIRVExtendedOp::create(rewriter, loc, adaptor.getLhs(),
1298 Value valueResult = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1300 Value flagValue = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1304 Value one = spirv::ConstantOp::getOne(dstElemTy, loc, rewriter);
1305 Value flagResult = spirv::IEqualOp::create(rewriter, loc, flagValue, one);
1307 rewriter.replaceOp(op, {valueResult, flagResult});
1317template <
typename ArithMulOp,
typename SPIRVMulOp>
1318class MulIExtendedOpPattern final :
public OpConversionPattern<ArithMulOp> {
1320 using OpConversionPattern<ArithMulOp>::OpConversionPattern;
1322 matchAndRewrite(ArithMulOp op,
typename ArithMulOp::Adaptor adaptor,
1323 ConversionPatternRewriter &rewriter)
const override {
1324 Location loc = op->getLoc();
1326 SPIRVMulOp::create(rewriter, loc, adaptor.getLhs(), adaptor.getRhs());
1328 Value low = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1330 Value high = spirv::CompositeExtractOp::create(rewriter, loc,
result,
1333 rewriter.replaceOp(op, {low, high});
1343class SelectOpPattern final :
public OpConversionPattern<arith::SelectOp> {
1347 matchAndRewrite(arith::SelectOp op, OpAdaptor adaptor,
1348 ConversionPatternRewriter &rewriter)
const override {
1349 rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, adaptor.getCondition(),
1350 adaptor.getTrueValue(),
1351 adaptor.getFalseValue());
1362template <
typename Op,
typename SPIRVOp>
1363class MinimumMaximumFOpPattern final :
public OpConversionPattern<Op> {
1365 using OpConversionPattern<
Op>::OpConversionPattern;
1367 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
1368 ConversionPatternRewriter &rewriter)
const override {
1369 auto *converter = this->
template getTypeConverter<SPIRVTypeConverter>();
1370 Type dstType = converter->convertType(op.getType());
1382 Location loc = op.
getLoc();
1384 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1386 if (bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1387 rewriter.replaceOp(op, spirvOp);
1391 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1392 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1394 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1395 adaptor.getLhs(), spirvOp);
1396 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1397 adaptor.getRhs(), select1);
1399 rewriter.replaceOp(op, select2);
1410template <
typename Op,
typename SPIRVOp>
1411class MinNumMaxNumFOpPattern final :
public OpConversionPattern<Op> {
1412 template <
typename TargetOp>
1413 constexpr bool shouldInsertNanGuards()
const {
1414 return llvm::is_one_of<TargetOp, spirv::GLFMaxOp, spirv::GLFMinOp>::value;
1418 using OpConversionPattern<
Op>::OpConversionPattern;
1420 matchAndRewrite(
Op op,
typename Op::Adaptor adaptor,
1421 ConversionPatternRewriter &rewriter)
const override {
1422 auto *converter = this->
template getTypeConverter<SPIRVTypeConverter>();
1423 Type dstType = converter->convertType(op.getType());
1438 SPIRVOp::create(rewriter, loc, dstType, adaptor.getOperands());
1440 if (!shouldInsertNanGuards<SPIRVOp>() ||
1441 bitEnumContainsAll(op.getFastmath(), arith::FastMathFlags::nnan)) {
1442 rewriter.replaceOp(op, spirvOp);
1446 Value lhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getLhs());
1447 Value rhsIsNan = spirv::IsNanOp::create(rewriter, loc, adaptor.getRhs());
1449 Value select1 = spirv::SelectOp::create(rewriter, loc, dstType, lhsIsNan,
1450 adaptor.getRhs(), spirvOp);
1451 Value select2 = spirv::SelectOp::create(rewriter, loc, dstType, rhsIsNan,
1452 adaptor.getLhs(), select1);
1454 rewriter.replaceOp(op, select2);
1469 ConstantCompositeOpPattern,
1470 ConstantScalarOpPattern,
1471 BoolIOpPattern<arith::AddIOp, spirv::LogicalNotEqualOp>,
1472 ElementwiseArithOpPattern<arith::AddIOp, spirv::IAddOp>,
1473 BoolIOpPattern<arith::SubIOp, spirv::LogicalNotEqualOp>,
1474 ElementwiseArithOpPattern<arith::SubIOp, spirv::ISubOp>,
1475 BoolIOpPattern<arith::MulIOp, spirv::LogicalAndOp>,
1476 ElementwiseArithOpPattern<arith::MulIOp, spirv::IMulOp>,
1477 BoolIOpPattern<arith::DivUIOp, spirv::LogicalAndOp>,
1479 BoolIOpPattern<arith::DivSIOp, spirv::LogicalAndOp>,
1481 BoolIOpAndNotPattern<arith::RemUIOp>,
1483 BoolIOpAndNotPattern<arith::RemSIOp>,
1484 RemSIOpGLPattern, RemSIOpCLPattern,
1485 BitwiseOpPattern<arith::AndIOp, spirv::LogicalAndOp, spirv::BitwiseAndOp>,
1486 BitwiseOpPattern<arith::OrIOp, spirv::LogicalOrOp, spirv::BitwiseOrOp>,
1487 XOrIOpLogicalPattern, XOrIOpBooleanPattern,
1488 BoolIOpAndNotPattern<arith::ShLIOp>,
1489 ElementwiseArithOpPattern<arith::ShLIOp, spirv::ShiftLeftLogicalOp>,
1490 BoolIOpAndNotPattern<arith::ShRUIOp>,
1500 ExtUIPattern, BoolToValuePattern<arith::ExtUIOp>,
1501 ExtSIPattern, ExtSII1Pattern,
1502 TypeCastingOpPattern<arith::ExtFOp, spirv::FConvertOp>,
1503 TruncIPattern, TruncII1Pattern,
1504 TypeCastingOpPattern<arith::TruncFOp, spirv::FConvertOp>,
1505 IntToFPPattern<arith::UIToFPOp, spirv::ConvertUToFOp, false>,
1506 BoolToValuePattern<arith::UIToFPOp>,
1507 IntToFPPattern<arith::SIToFPOp, spirv::ConvertSToFOp, true>,
1508 TypeCastingOpPattern<arith::FPToUIOp, spirv::ConvertFToUOp>,
1509 TypeCastingOpPattern<arith::FPToSIOp, spirv::ConvertFToSOp>,
1510 TypeCastingOpPattern<arith::IndexCastOp, spirv::SConvertOp>,
1511 IndexCastIndexI1Pattern, BoolToValuePattern<arith::IndexCastOp>,
1512 TypeCastingOpPattern<arith::IndexCastUIOp, spirv::UConvertOp>,
1513 TypeCastingOpPattern<arith::BitcastOp, spirv::BitcastOp>,
1514 CmpIOpBooleanPattern, CmpIOpPattern,
1515 CmpFOpNanNonePattern, CmpFOpPattern,
1516 BinaryExtendedOpPattern<arith::AddUIExtendedOp, spirv::IAddCarryOp>,
1517 BinaryExtendedOpPattern<arith::SubUIExtendedOp, spirv::ISubBorrowOp>,
1518 MulIExtendedOpPattern<arith::MulSIExtendedOp, spirv::SMulExtendedOp>,
1519 MulIExtendedOpPattern<arith::MulUIExtendedOp, spirv::UMulExtendedOp>,
1522 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::GLFMaxOp>,
1523 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::GLFMinOp>,
1524 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::GLNMaxOp>,
1525 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::GLNMinOp>,
1526 BoolIOpPattern<arith::MaxSIOp, spirv::LogicalAndOp>,
1527 BoolIOpPattern<arith::MaxUIOp, spirv::LogicalOrOp>,
1528 BoolIOpPattern<arith::MinSIOp, spirv::LogicalOrOp>,
1529 BoolIOpPattern<arith::MinUIOp, spirv::LogicalAndOp>,
1535 MinimumMaximumFOpPattern<arith::MaximumFOp, spirv::CLFMaxOp>,
1536 MinimumMaximumFOpPattern<arith::MinimumFOp, spirv::CLFMinOp>,
1537 MinNumMaxNumFOpPattern<arith::MaxNumFOp, spirv::CLFMaxOp>,
1538 MinNumMaxNumFOpPattern<arith::MinNumFOp, spirv::CLFMinOp>,
1548 patterns.
add<CmpFOpNanKernelPattern>(typeConverter, patterns.
getContext(),
1557struct ConvertArithToSPIRVPass
1558 :
public impl::ConvertArithToSPIRVPassBase<ConvertArithToSPIRVPass> {
1561 void runOnOperation()
override {
1564 std::unique_ptr<SPIRVConversionTarget>
target =
1568 options.emulateLT32BitScalarTypes = this->emulateLT32BitScalarTypes;
1569 options.emulateUnsupportedFloatTypes = this->emulateUnsupportedFloatTypes;
1574 target->addLegalOp<UnrealizedConversionCastOp>();
1577 target->addIllegalDialect<arith::ArithDialect>();
1582 if (failed(applyPartialConversion(op, *
target, std::move(patterns))))
1583 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, Type dstType, ConversionPatternRewriter &rewriter)
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).
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.
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.