27#include "llvm/ADT/APFloat.h"
28#include "llvm/ADT/APInt.h"
29#include "llvm/ADT/APSInt.h"
30#include "llvm/ADT/FloatingPointMode.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/ADT/TypeSwitch.h"
40 llvm::RoundingMode::NearestTiesToEven;
49 function_ref<APInt(
const APInt &,
const APInt &)> binFn) {
50 const APInt &lhsVal = llvm::cast<IntegerAttr>(lhs).getValue();
51 const APInt &rhsVal = llvm::cast<IntegerAttr>(rhs).getValue();
52 APInt value = binFn(lhsVal, rhsVal);
53 return IntegerAttr::get(res.getType(), value);
88static IntegerOverflowFlagsAttr
90 IntegerOverflowFlagsAttr val2) {
91 return IntegerOverflowFlagsAttr::get(val1.getContext(),
92 val1.getValue() & val2.getValue());
101 IntegerOverflowFlagsAttr val1, IntegerOverflowFlagsAttr val2,
Attribute lhs,
104 IntegerOverflowFlags flags = val1.getValue() & val2.getValue();
105 if (flags == IntegerOverflowFlags::none)
106 return IntegerOverflowFlagsAttr::get(val1.getContext(), flags);
108 auto lhsAttr = llvm::cast<IntegerAttr>(lhs);
109 auto rhsAttr = llvm::cast<IntegerAttr>(rhs);
111 if (isa<IndexType>(lhsAttr.getType()))
112 return IntegerOverflowFlagsAttr::get(val1.getContext(), flags);
114 const APInt &lhsValue = lhsAttr.getValue();
115 const APInt &rhsValue = rhsAttr.getValue();
117 if (bitEnumContainsAny(flags, IntegerOverflowFlags::nsw)) {
118 (
void)(lhsValue.*signedOp)(rhsValue, overflow);
120 flags = bitEnumClear(flags, IntegerOverflowFlags::nsw);
122 if (bitEnumContainsAny(flags, IntegerOverflowFlags::nuw)) {
123 (
void)(lhsValue.*unsignedOp)(rhsValue, overflow);
125 flags = bitEnumClear(flags, IntegerOverflowFlags::nuw);
127 return IntegerOverflowFlagsAttr::get(val1.getContext(), flags);
133 case arith::CmpIPredicate::eq:
134 return arith::CmpIPredicate::ne;
135 case arith::CmpIPredicate::ne:
136 return arith::CmpIPredicate::eq;
137 case arith::CmpIPredicate::slt:
138 return arith::CmpIPredicate::sge;
139 case arith::CmpIPredicate::sle:
140 return arith::CmpIPredicate::sgt;
141 case arith::CmpIPredicate::sgt:
142 return arith::CmpIPredicate::sle;
143 case arith::CmpIPredicate::sge:
144 return arith::CmpIPredicate::slt;
145 case arith::CmpIPredicate::ult:
146 return arith::CmpIPredicate::uge;
147 case arith::CmpIPredicate::ule:
148 return arith::CmpIPredicate::ugt;
149 case arith::CmpIPredicate::ugt:
150 return arith::CmpIPredicate::ule;
151 case arith::CmpIPredicate::uge:
152 return arith::CmpIPredicate::ult;
154 llvm_unreachable(
"unknown cmpi predicate kind");
163static llvm::RoundingMode
167 switch (*roundingMode) {
168 case RoundingMode::downward:
169 return llvm::RoundingMode::TowardNegative;
170 case RoundingMode::to_nearest_away:
171 return llvm::RoundingMode::NearestTiesToAway;
172 case RoundingMode::to_nearest_even:
173 return llvm::RoundingMode::NearestTiesToEven;
174 case RoundingMode::toward_zero:
175 return llvm::RoundingMode::TowardZero;
176 case RoundingMode::upward:
177 return llvm::RoundingMode::TowardPositive;
179 llvm_unreachable(
"Unhandled rounding mode");
183 return arith::CmpIPredicateAttr::get(pred.getContext(),
209 ShapedType shapedType = dyn_cast_or_null<ShapedType>(type);
213 if (!shapedType.hasStaticShape())
223 ShapedType shapedType = dyn_cast<ShapedType>(type);
226 if (!shapedType.hasStaticShape())
236#include "ArithCanonicalization.inc"
245 auto i1Type = IntegerType::get(type.
getContext(), 1);
246 if (
auto shapedType = dyn_cast<ShapedType>(type))
247 return shapedType.cloneWith(std::nullopt, i1Type);
248 if (llvm::isa<UnrankedTensorType>(type))
249 return UnrankedTensorType::get(i1Type);
257void arith::ConstantOp::getAsmResultNames(
260 if (
auto intCst = dyn_cast<IntegerAttr>(getValue())) {
261 auto intType = dyn_cast<IntegerType>(type);
264 if (intType && intType.getWidth() == 1)
265 return setNameFn(getResult(), (intCst.getInt() ?
"true" :
"false"));
268 SmallString<32> specialNameBuffer;
269 llvm::raw_svector_ostream specialName(specialNameBuffer);
270 specialName <<
'c' << intCst.getValue();
272 specialName <<
'_' << type;
273 setNameFn(getResult(), specialName.str());
275 setNameFn(getResult(),
"cst");
281LogicalResult arith::ConstantOp::verify() {
285 intType && !intType.isSignless())
286 return emitOpError(
"integer return type must be signless");
288 if (!llvm::isa<IntegerAttr, FloatAttr, ElementsAttr>(getValue())) {
290 "value must be an integer, float, or elements attribute");
296 if (isa<ScalableVectorType>(type) && !isa<SplatElementsAttr>(getValue()))
298 "initializing scalable vectors with elements attribute is not supported"
299 " unless it's a vector splat");
303bool arith::ConstantOp::isBuildableWith(Attribute value, Type type) {
305 auto typedAttr = dyn_cast<TypedAttr>(value);
306 if (!typedAttr || typedAttr.getType() != type)
310 if (!intType.isSignless())
314 return llvm::isa<IntegerAttr, FloatAttr, ElementsAttr>(value);
317ConstantOp arith::ConstantOp::materialize(OpBuilder &builder, Attribute value,
318 Type type, Location loc) {
319 if (isBuildableWith(value, type))
320 return arith::ConstantOp::create(builder, loc, cast<TypedAttr>(value));
324OpFoldResult arith::ConstantOp::fold(FoldAdaptor adaptor) {
return getValue(); }
329 arith::ConstantOp::build(builder,
result, type,
339 auto result = dyn_cast<ConstantIntOp>(builder.
create(state));
340 assert(
result &&
"builder didn't return the right type");
352 arith::ConstantOp::build(builder,
result, type,
361 auto result = dyn_cast<ConstantIntOp>(builder.
create(state));
362 assert(
result &&
"builder didn't return the right type");
373 arith::ConstantOp::build(builder,
result, type,
379 const APInt &
value) {
382 auto result = dyn_cast<ConstantIntOp>(builder.
create(state));
383 assert(
result &&
"builder didn't return the right type");
389 const APInt &
value) {
394 if (
auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
395 return constOp.getType().isSignlessInteger();
400 FloatType type,
const APFloat &
value) {
401 arith::ConstantOp::build(builder,
result, type,
408 const APFloat &
value) {
411 auto result = dyn_cast<ConstantFloatOp>(builder.
create(state));
412 assert(
result &&
"builder didn't return the right type");
418 const APFloat &
value) {
423 if (
auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
424 return llvm::isa<FloatType>(constOp.getType());
439 auto result = dyn_cast<ConstantIndexOp>(builder.
create(state));
440 assert(
result &&
"builder didn't return the right type");
450 if (
auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
451 return constOp.getType().isIndex();
459 "type doesn't have a zero representation");
461 assert(zeroAttr &&
"unsupported type for zero attribute");
462 return arith::ConstantOp::create(builder, loc, zeroAttr);
475 if (
auto sub = getLhs().getDefiningOp<SubIOp>())
476 if (getRhs() == sub.getRhs())
480 if (
auto sub = getRhs().getDefiningOp<SubIOp>())
481 if (getLhs() == sub.getRhs())
485 adaptor.getOperands(),
486 [](APInt a,
const APInt &
b) { return std::move(a) + b; });
491 patterns.
add<AddIAddConstant, AddISubConstantRHS, AddISubConstantLHS,
492 AddIMulNegativeOneRhs, AddIMulNegativeOneLhs>(context);
499std::optional<SmallVector<int64_t, 4>>
500arith::AddUIExtendedOp::getIterationShapeToUnroll() {
501 if (
auto vt = dyn_cast<VectorType>(
getType(0)))
502 return llvm::to_vector<4>(vt.getShape());
509 return sum.ult(operand) ? APInt::getAllOnes(1) : APInt::getZero(1);
513arith::AddUIExtendedOp::fold(FoldAdaptor adaptor,
514 SmallVectorImpl<OpFoldResult> &results) {
515 Type overflowTy = getOverflow().getType();
521 results.push_back(getLhs());
522 results.push_back(falseValue);
531 adaptor.getOperands(),
532 [](APInt a,
const APInt &
b) { return std::move(a) + b; })) {
535 results.push_back(sumAttr);
536 results.push_back(sumAttr);
540 ArrayRef({sumAttr, adaptor.getLhs()}),
546 results.push_back(sumAttr);
547 results.push_back(overflowAttr);
554void arith::AddUIExtendedOp::getCanonicalizationPatterns(
555 RewritePatternSet &patterns, MLIRContext *context) {
556 patterns.
add<AddUIExtendedToAddI>(context);
563std::optional<SmallVector<int64_t, 4>>
564arith::SubUIExtendedOp::getIterationShapeToUnroll() {
565 if (
auto vt = dyn_cast<VectorType>(
getType(0)))
566 return llvm::to_vector<4>(vt.getShape());
573 return lhs.ult(rhs) ? APInt::getAllOnes(1) : APInt::getZero(1);
577arith::SubUIExtendedOp::fold(FoldAdaptor adaptor,
578 SmallVectorImpl<OpFoldResult> &results) {
579 Type borrowTy = getBorrow().getType();
585 results.push_back(getLhs());
586 results.push_back(falseValue);
591 if (getLhs() == getRhs()) {
594 auto shapedType = dyn_cast<ShapedType>(getDiff().
getType());
595 if (shapedType && !shapedType.hasStaticShape())
603 results.push_back(zeroDiff);
604 results.push_back(falseValue);
610 adaptor.getOperands(),
611 [](APInt a,
const APInt &
b) { return std::move(a) - b; })) {
614 results.push_back(diffAttr);
615 results.push_back(diffAttr);
619 adaptor.getOperands(),
625 results.push_back(diffAttr);
626 results.push_back(borrowAttr);
633void arith::SubUIExtendedOp::getCanonicalizationPatterns(
634 RewritePatternSet &patterns, MLIRContext *context) {
635 patterns.
add<SubUIExtendedToSubI>(context);
642OpFoldResult arith::SubIOp::fold(FoldAdaptor adaptor) {
644 if (getOperand(0) == getOperand(1)) {
645 auto shapedType = dyn_cast<ShapedType>(
getType());
647 if (!shapedType || shapedType.hasStaticShape())
654 if (
auto add = getLhs().getDefiningOp<AddIOp>()) {
656 if (getRhs() ==
add.getRhs())
659 if (getRhs() ==
add.getLhs())
664 if (
auto sub = getRhs().getDefiningOp<SubIOp>())
665 if (getLhs() == sub.getLhs())
669 adaptor.getOperands(),
670 [](APInt a,
const APInt &
b) { return std::move(a) - b; });
673void arith::SubIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
674 MLIRContext *context) {
675 patterns.
add<SubIRHSAddConstant, SubILHSAddConstant, SubIRHSSubConstantRHS,
676 SubIRHSSubConstantLHS, SubILHSSubConstantRHS,
677 SubILHSSubConstantLHS, SubISubILHSRHSLHS>(context);
684OpFoldResult arith::MulIOp::fold(FoldAdaptor adaptor) {
695 adaptor.getOperands(),
696 [](
const APInt &a,
const APInt &
b) { return a * b; });
699void arith::MulIOp::getAsmResultNames(
701 if (!isa<IndexType>(
getType()))
706 auto isVscale = [](Operation *op) {
707 return op && op->getName().getStringRef() ==
"vector.vscale";
710 IntegerAttr baseValue;
711 auto isVscaleExpr = [&](Value a, Value
b) {
713 isVscale(
b.getDefiningOp());
716 if (!isVscaleExpr(getLhs(), getRhs()) && !isVscaleExpr(getRhs(), getLhs()))
720 SmallString<32> specialNameBuffer;
721 llvm::raw_svector_ostream specialName(specialNameBuffer);
722 specialName <<
'c' << baseValue.getInt() <<
"_vscale";
723 setNameFn(getResult(), specialName.str());
726void arith::MulIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
727 MLIRContext *context) {
728 patterns.
add<MulIMulIConstant>(context);
735std::optional<SmallVector<int64_t, 4>>
736arith::MulSIExtendedOp::getIterationShapeToUnroll() {
737 if (
auto vt = dyn_cast<VectorType>(
getType(0)))
738 return llvm::to_vector<4>(vt.getShape());
743arith::MulSIExtendedOp::fold(FoldAdaptor adaptor,
744 SmallVectorImpl<OpFoldResult> &results) {
747 Attribute zero = adaptor.getRhs();
748 results.push_back(zero);
749 results.push_back(zero);
755 adaptor.getOperands(),
756 [](
const APInt &a,
const APInt &
b) { return a * b; })) {
759 llvm::APIntOps::mulhs);
760 assert(highAttr &&
"Unexpected constant-folding failure");
762 results.push_back(lowAttr);
763 results.push_back(highAttr);
770void arith::MulSIExtendedOp::getCanonicalizationPatterns(
771 RewritePatternSet &patterns, MLIRContext *context) {
772 patterns.
add<MulSIExtendedToMulI, MulSIExtendedRHSOne>(context);
779std::optional<SmallVector<int64_t, 4>>
780arith::MulUIExtendedOp::getIterationShapeToUnroll() {
781 if (
auto vt = dyn_cast<VectorType>(
getType(0)))
782 return llvm::to_vector<4>(vt.getShape());
787arith::MulUIExtendedOp::fold(FoldAdaptor adaptor,
788 SmallVectorImpl<OpFoldResult> &results) {
791 Attribute zero = adaptor.getRhs();
792 results.push_back(zero);
793 results.push_back(zero);
801 results.push_back(getLhs());
802 results.push_back(zero);
808 adaptor.getOperands(),
809 [](
const APInt &a,
const APInt &
b) { return a * b; })) {
812 llvm::APIntOps::mulhu);
813 assert(highAttr &&
"Unexpected constant-folding failure");
815 results.push_back(lowAttr);
816 results.push_back(highAttr);
823void arith::MulUIExtendedOp::getCanonicalizationPatterns(
824 RewritePatternSet &patterns, MLIRContext *context) {
825 patterns.
add<MulUIExtendedToMulI>(context);
834 arith::IntegerOverflowFlags ovfFlags) {
835 auto mul = lhs.getDefiningOp<mlir::arith::MulIOp>();
836 if (!
mul || !bitEnumContainsAll(
mul.getOverflowFlags(), ovfFlags))
839 if (
mul.getLhs() == rhs)
842 if (
mul.getRhs() == rhs)
848OpFoldResult arith::DivUIOp::fold(FoldAdaptor adaptor) {
862 if (getLhs() == getRhs())
866 if (Value val =
foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nuw))
872 [&](APInt a,
const APInt &
b) {
880 return div0 ? Attribute() :
result;
900OpFoldResult arith::DivSIOp::fold(FoldAdaptor adaptor) {
914 if (getLhs() == getRhs())
918 if (Value val =
foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nsw))
922 bool overflowOrDiv0 =
false;
924 adaptor.getOperands(), [&](APInt a,
const APInt &
b) {
925 if (overflowOrDiv0 || !b) {
926 overflowOrDiv0 = true;
929 return a.sdiv_ov(
b, overflowOrDiv0);
932 return overflowOrDiv0 ? Attribute() :
result;
956OpFoldResult arith::CeilDivUIOp::fold(FoldAdaptor adaptor) {
970 if (getLhs() == getRhs())
973 bool overflowOrDiv0 =
false;
975 adaptor.getOperands(), [&](APInt a,
const APInt &
b) {
976 if (overflowOrDiv0 || !b) {
977 overflowOrDiv0 = true;
980 APInt quotient = a.udiv(
b);
983 APInt one(a.getBitWidth(), 1,
true);
984 return quotient.uadd_ov(one, overflowOrDiv0);
987 return overflowOrDiv0 ? Attribute() :
result;
998OpFoldResult arith::CeilDivSIOp::fold(FoldAdaptor adaptor) {
1012 if (getLhs() == getRhs())
1016 bool overflowOrDiv0 =
false;
1018 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
1019 if (overflowOrDiv0 || !b) {
1020 overflowOrDiv0 = true;
1030 bool overflowDiv =
false;
1031 APInt quotient = a.sdiv_ov(
b, overflowDiv);
1035 overflowOrDiv0 =
true;
1038 if (a.isNegative() !=
b.isNegative() || quotient *
b == a)
1044 APInt one(a.getBitWidth(), 1,
true);
1045 return quotient.sadd_ov(one, overflowOrDiv0);
1048 return overflowOrDiv0 ? Attribute() :
result;
1059OpFoldResult arith::FloorDivSIOp::fold(FoldAdaptor adaptor) {
1073 if (getLhs() == getRhs())
1077 bool overflowOrDiv =
false;
1079 adaptor.getOperands(), [&](APInt a,
const APInt &
b) {
1081 overflowOrDiv = true;
1084 return a.sfloordiv_ov(
b, overflowOrDiv);
1087 return overflowOrDiv ? Attribute() :
result;
1094OpFoldResult arith::RemUIOp::fold(FoldAdaptor adaptor) {
1111 [&](APInt a,
const APInt &
b) {
1112 if (div0 || b.isZero()) {
1119 return div0 ? Attribute() :
result;
1130OpFoldResult arith::RemSIOp::fold(FoldAdaptor adaptor) {
1147 [&](APInt a,
const APInt &
b) {
1148 if (div0 || b.isZero()) {
1155 return div0 ? Attribute() :
result;
1173template <
typename OpTy>
1175 for (
bool reversePrev : {
false,
true}) {
1176 auto prev = (reversePrev ? op.getRhs() : op.getLhs())
1177 .template getDefiningOp<OpTy>();
1181 Value other = (reversePrev ? op.getLhs() : op.getRhs());
1182 if (other != prev.getLhs() && other != prev.getRhs())
1185 return prev.getResult();
1190OpFoldResult arith::AndIOp::fold(FoldAdaptor adaptor) {
1197 intValue.isAllOnes())
1202 intValue.isAllOnes())
1207 intValue.isAllOnes())
1215 adaptor.getOperands(),
1216 [](APInt a,
const APInt &
b) { return std::move(a) & b; });
1223OpFoldResult arith::OrIOp::fold(FoldAdaptor adaptor) {
1226 if (rhsVal.isZero())
1229 if (rhsVal.isAllOnes())
1230 return adaptor.getRhs();
1237 intValue.isAllOnes())
1238 return getRhs().getDefiningOp<XOrIOp>().getRhs();
1242 intValue.isAllOnes())
1243 return getLhs().getDefiningOp<XOrIOp>().getRhs();
1250 adaptor.getOperands(),
1251 [](APInt a,
const APInt &
b) { return std::move(a) | b; });
1258OpFoldResult arith::XOrIOp::fold(FoldAdaptor adaptor) {
1263 if (getLhs() == getRhs()) {
1266 auto shapedType = dyn_cast<ShapedType>(
getType());
1267 if (!shapedType || shapedType.hasStaticShape())
1272 if (arith::XOrIOp prev = getLhs().getDefiningOp<arith::XOrIOp>()) {
1273 if (prev.getRhs() == getRhs())
1274 return prev.getLhs();
1275 if (prev.getLhs() == getRhs())
1276 return prev.getRhs();
1280 if (arith::XOrIOp prev = getRhs().getDefiningOp<arith::XOrIOp>()) {
1281 if (prev.getRhs() == getLhs())
1282 return prev.getLhs();
1283 if (prev.getLhs() == getLhs())
1284 return prev.getRhs();
1288 adaptor.getOperands(),
1289 [](APInt a,
const APInt &
b) { return std::move(a) ^ b; });
1292void arith::XOrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1293 MLIRContext *context) {
1294 patterns.
add<XOrIXOrIConstant, XOrINotCmpI, XOrIOfExtUI, XOrIOfExtSI>(
1302OpFoldResult arith::NegFOp::fold(FoldAdaptor adaptor) {
1304 if (
auto op = this->getOperand().getDefiningOp<arith::NegFOp>())
1305 return op.getOperand();
1307 adaptor.getOperands(), [](
const APFloat &a) -> std::optional<APFloat> {
1308 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()))
1309 return std::nullopt;
1318OpFoldResult arith::FlushDenormalsOp::fold(FoldAdaptor adaptor) {
1324 if (
auto op = this->getOperand().getDefiningOp<arith::FlushDenormalsOp>())
1325 return op.getResult();
1329 adaptor.getOperands(), [](
const APFloat &a) {
1331 return APFloat::getZero(a.getSemantics(), a.isNegative());
1340OpFoldResult arith::AddFOp::fold(FoldAdaptor adaptor) {
1345 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1348 auto rm = getRoundingmode();
1350 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1352 result.add(b, convertArithRoundingModeToLLVMIR(rm));
1357void arith::AddFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1358 MLIRContext *context) {
1359 patterns.
add<AddFOfNegFLhs, AddFOfNegFRhs>(context);
1366OpFoldResult arith::SubFOp::fold(FoldAdaptor adaptor) {
1371 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1374 auto rm = getRoundingmode();
1376 adaptor.getOperands(),
1377 [rm](
const APFloat &a,
const APFloat &
b) -> std::optional<APFloat> {
1380 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()) &&
1381 a.compare(b) == APFloat::cmpLessThan)
1382 return std::nullopt;
1384 result.subtract(b, convertArithRoundingModeToLLVMIR(rm));
1389void arith::SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1390 MLIRContext *context) {
1391 patterns.
add<SubFOfNegZero>(context);
1404template <
typename TruncOp,
typename ExtOp,
typename ExtremumOp>
1405struct NarrowExtremum final : OpRewritePattern<TruncOp> {
1406 using OpRewritePattern<TruncOp>::OpRewritePattern;
1408 LogicalResult matchAndRewrite(TruncOp truncOp,
1409 PatternRewriter &rewriter)
const override {
1410 auto extremumOp = truncOp.getIn().template getDefiningOp<ExtremumOp>();
1411 if (!extremumOp || !extremumOp->hasOneUse())
1414 auto lhsExt = extremumOp.getLhs().template getDefiningOp<ExtOp>();
1415 auto rhsExt = extremumOp.getRhs().template getDefiningOp<ExtOp>();
1416 if (!lhsExt || !rhsExt)
1419 Value
lhs = lhsExt.getIn();
1420 Value
rhs = rhsExt.getIn();
1421 Type narrowType = truncOp.getType();
1422 if (
lhs.getType() != narrowType ||
rhs.getType() != narrowType)
1431 if (
auto narrowFloatType =
1433 auto wideFloatType =
1438 const llvm::fltSemantics &narrowSemantics =
1439 narrowFloatType.getFloatSemantics();
1440 const llvm::fltSemantics &wideSemantics =
1441 wideFloatType.getFloatSemantics();
1442 bool ignoreNaNs =
false;
1443 if constexpr (std::is_same_v<TruncOp, TruncFOp>)
1445 bitEnumContainsAll(extremumOp.getFastmath(), FastMathFlags::nnan);
1446 if (!llvm::APFloatBase::isLosslesslyConvertibleTo(
1447 narrowSemantics, wideSemantics, ignoreNaNs))
1453 extremumOp.getProperties(),
1454 extremumOp->getDiscardableAttrDictionary().getValue());
1465OpFoldResult arith::MaximumFOp::fold(FoldAdaptor adaptor) {
1467 if (getLhs() == getRhs())
1481OpFoldResult arith::MaxNumFOp::fold(FoldAdaptor adaptor) {
1483 if (getLhs() == getRhs())
1497OpFoldResult arith::MaximumNumFOp::fold(FoldAdaptor adaptor) {
1499 if (getLhs() == getRhs())
1513OpFoldResult MaxSIOp::fold(FoldAdaptor adaptor) {
1515 if (getLhs() == getRhs())
1521 if (intValue.isMaxSignedValue())
1524 if (intValue.isMinSignedValue())
1529 llvm::APIntOps::smax);
1536OpFoldResult MaxUIOp::fold(FoldAdaptor adaptor) {
1538 if (getLhs() == getRhs())
1544 if (intValue.isMaxValue())
1547 if (intValue.isMinValue())
1552 llvm::APIntOps::umax);
1559OpFoldResult arith::MinimumFOp::fold(FoldAdaptor adaptor) {
1561 if (getLhs() == getRhs())
1575OpFoldResult arith::MinNumFOp::fold(FoldAdaptor adaptor) {
1577 if (getLhs() == getRhs())
1591OpFoldResult arith::MinimumNumFOp::fold(FoldAdaptor adaptor) {
1593 if (getLhs() == getRhs())
1607OpFoldResult MinSIOp::fold(FoldAdaptor adaptor) {
1609 if (getLhs() == getRhs())
1615 if (intValue.isMinSignedValue())
1618 if (intValue.isMaxSignedValue())
1623 llvm::APIntOps::smin);
1630OpFoldResult MinUIOp::fold(FoldAdaptor adaptor) {
1632 if (getLhs() == getRhs())
1638 if (intValue.isMinValue())
1641 if (intValue.isMaxValue())
1646 llvm::APIntOps::umin);
1653OpFoldResult arith::MulFOp::fold(FoldAdaptor adaptor) {
1661 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::nnan |
1662 arith::FastMathFlags::nsz) &&
1663 !(adaptor.getLhs() && adaptor.getRhs())) {
1671 auto rm = getRoundingmode();
1673 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1675 result.multiply(b, convertArithRoundingModeToLLVMIR(rm));
1680void arith::MulFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1681 MLIRContext *context) {
1682 patterns.
add<MulFOfNegF>(context);
1689OpFoldResult arith::DivFOp::fold(FoldAdaptor adaptor) {
1694 auto rm = getRoundingmode();
1696 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1698 result.divide(b, convertArithRoundingModeToLLVMIR(rm));
1703void arith::DivFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1704 MLIRContext *context) {
1705 patterns.
add<DivFOfNegF>(context);
1712OpFoldResult arith::RemFOp::fold(FoldAdaptor adaptor) {
1714 [](
const APFloat &a,
const APFloat &
b) {
1719 (void)result.mod(b);
1728template <
typename... Types>
1734template <
typename... ShapedTypes,
typename... ElementTypes>
1737 if (llvm::isa<ShapedType>(type) && !llvm::isa<ShapedTypes...>(type))
1741 if (!llvm::isa<ElementTypes...>(underlyingType))
1744 return underlyingType;
1748template <
typename... ElementTypes>
1755template <
typename... ElementTypes>
1764 auto rankedTensorA = dyn_cast<RankedTensorType>(typeA);
1765 auto rankedTensorB = dyn_cast<RankedTensorType>(typeB);
1766 if (!rankedTensorA || !rankedTensorB)
1768 return rankedTensorA.getEncoding() == rankedTensorB.getEncoding();
1772 if (inputs.size() != 1 || outputs.size() != 1)
1784template <
typename ValType,
typename Op>
1789 if (llvm::cast<ValType>(srcType).getWidth() >=
1790 llvm::cast<ValType>(dstType).getWidth())
1792 << dstType <<
" must be wider than operand type " << srcType;
1798template <
typename ValType,
typename Op>
1803 if (llvm::cast<ValType>(srcType).getWidth() <=
1804 llvm::cast<ValType>(dstType).getWidth())
1806 << dstType <<
" must be shorter than operand type " << srcType;
1812template <
template <
typename>
class WidthComparator,
typename... ElementTypes>
1817 auto srcType =
getTypeIfLike<ElementTypes...>(inputs.front());
1818 auto dstType =
getTypeIfLike<ElementTypes...>(outputs.front());
1819 if (!srcType || !dstType)
1822 return WidthComparator<unsigned>()(dstType.getIntOrFloatBitWidth(),
1823 srcType.getIntOrFloatBitWidth());
1828static FailureOr<APFloat>
1830 const llvm::fltSemantics &targetSemantics,
1834 using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
1835 if (sourceValue.isInfinity() &&
1836 (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
1837 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
1839 if (sourceValue.isNaN() &&
1840 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
1843 bool losesInfo =
false;
1844 auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
1845 if (losesInfo || status != APFloat::opOK)
1855OpFoldResult arith::ExtUIOp::fold(FoldAdaptor adaptor) {
1856 if (
auto lhs = getIn().getDefiningOp<ExtUIOp>()) {
1859 setNonNeg(
lhs.getNonNeg());
1860 getInMutable().assign(
lhs.getIn());
1865 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1867 adaptor.getOperands(),
getType(),
1868 [bitWidth](
const APInt &a,
bool &castStatus) {
1869 return a.zext(bitWidth);
1877LogicalResult arith::ExtUIOp::verify() {
1885OpFoldResult arith::ExtSIOp::fold(FoldAdaptor adaptor) {
1886 if (
auto lhs = getIn().getDefiningOp<ExtSIOp>()) {
1887 getInMutable().assign(
lhs.getIn());
1892 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1894 adaptor.getOperands(),
getType(),
1895 [bitWidth](
const APInt &a,
bool &castStatus) {
1896 return a.sext(bitWidth);
1904void arith::ExtSIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1905 MLIRContext *context) {
1906 patterns.
add<ExtSIOfExtUI>(context);
1909LogicalResult arith::ExtSIOp::verify() {
1919OpFoldResult arith::ExtFOp::fold(FoldAdaptor adaptor) {
1920 if (
auto truncFOp = getOperand().getDefiningOp<TruncFOp>()) {
1921 if (truncFOp.getOperand().getType() ==
getType()) {
1922 arith::FastMathFlags truncFMF =
1923 truncFOp.getFastmath().value_or(arith::FastMathFlags::none);
1924 bool isTruncContract =
1925 bitEnumContainsAll(truncFMF, arith::FastMathFlags::contract);
1926 arith::FastMathFlags extFMF =
1927 getFastmath().value_or(arith::FastMathFlags::none);
1928 bool isExtContract =
1929 bitEnumContainsAll(extFMF, arith::FastMathFlags::contract);
1930 if (isTruncContract && isExtContract) {
1931 return truncFOp.getOperand();
1937 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
1939 adaptor.getOperands(),
getType(),
1940 [&targetSemantics](
const APFloat &a,
bool &castStatus) {
1965 function_ref<std::optional<APFloat>(
const APFloat &,
const APFloat &)>
1968 if (isa_and_nonnull<ub::PoisonAttr>(inAttr))
1970 if (isa_and_nonnull<ub::PoisonAttr>(scaleAttr))
1973 if (!inAttr || !scaleAttr || !resultType)
1976 if (
auto inFloat = dyn_cast<FloatAttr>(inAttr)) {
1977 auto scaleFloat = dyn_cast<FloatAttr>(scaleAttr);
1980 std::optional<APFloat>
result =
1981 calculate(inFloat.getValue(), scaleFloat.getValue());
1984 return FloatAttr::get(resultType, *
result);
1987 auto inElements = dyn_cast<DenseFPElementsAttr>(inAttr);
1988 auto scaleElements = dyn_cast<DenseFPElementsAttr>(scaleAttr);
1989 auto shapedResultType = dyn_cast<ShapedType>(resultType);
1990 if (!inElements || !scaleElements || !shapedResultType ||
1991 !shapedResultType.hasStaticShape() ||
1992 inElements.getNumElements() != scaleElements.getNumElements())
1996 if (inElements.isSplat() && scaleElements.isSplat()) {
1997 std::optional<APFloat>
result =
1998 calculate(inElements.getSplatValue<APFloat>(),
1999 scaleElements.getSplatValue<APFloat>());
2006 results.reserve(inElements.getNumElements());
2007 for (
const auto &[in, scale] : llvm::zip_equal(inElements, scaleElements)) {
2008 std::optional<APFloat>
result = calculate(in, scale);
2011 results.push_back(*
result);
2024OpFoldResult arith::ScalingExtFOp::fold(FoldAdaptor adaptor) {
2032 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2034 adaptor.getIn(), adaptor.getScale(),
getType(),
2035 [&resSemantics](
const APFloat &in,
2036 const APFloat &scale) -> std::optional<APFloat> {
2040 return std::nullopt;
2047bool arith::ScalingExtFOp::areCastCompatible(
TypeRange inputs,
2052LogicalResult arith::ScalingExtFOp::verify() {
2060OpFoldResult arith::TruncIOp::fold(FoldAdaptor adaptor) {
2063 Value src = getOperand().getDefiningOp()->getOperand(0);
2068 if (llvm::cast<IntegerType>(srcType).getWidth() >
2069 llvm::cast<IntegerType>(dstType).getWidth()) {
2076 if (srcType == dstType)
2081 if (
auto truncOp = getOperand().getDefiningOp<arith::TruncIOp>()) {
2083 setOverflowFlags(getOverflowFlags() & truncOp.getOverflowFlags());
2084 setOperand(truncOp.getIn());
2089 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2091 adaptor.getOperands(),
getType(),
2092 [bitWidth](
const APInt &a,
bool &castStatus) {
2093 return a.trunc(bitWidth);
2101void arith::TruncIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2102 MLIRContext *context) {
2103 patterns.
add<NarrowExtremum<TruncIOp, ExtSIOp, MaxSIOp>,
2104 NarrowExtremum<TruncIOp, ExtSIOp, MinSIOp>,
2105 NarrowExtremum<TruncIOp, ExtUIOp, MaxUIOp>,
2106 NarrowExtremum<TruncIOp, ExtUIOp, MinUIOp>, TruncIExtSIToExtSI,
2107 TruncIExtUIToExtUI, TruncIShrSIToTrunciShrUI>(context);
2110LogicalResult arith::TruncIOp::verify() {
2120OpFoldResult arith::TruncFOp::fold(FoldAdaptor adaptor) {
2122 if (
auto extOp = getOperand().getDefiningOp<arith::ExtFOp>()) {
2123 Value src = extOp.getIn();
2125 auto intermediateType =
2129 if (llvm::APFloatBase::isLosslesslyConvertibleTo(
2130 srcType.getFloatSemantics(),
2131 intermediateType.getFloatSemantics())) {
2133 if (srcType.getWidth() > resElemType.getWidth()) {
2139 if (srcType == resElemType)
2144 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2146 adaptor.getOperands(),
getType(),
2147 [
this, &targetSemantics](
const APFloat &a,
bool &castStatus) {
2148 llvm::RoundingMode llvmRoundingMode =
2150 FailureOr<APFloat>
result =
2160void arith::TruncFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2161 MLIRContext *context) {
2162 patterns.
add<NarrowExtremum<TruncFOp, ExtFOp, MaximumFOp>,
2163 NarrowExtremum<TruncFOp, ExtFOp, MaxNumFOp>,
2164 NarrowExtremum<TruncFOp, ExtFOp, MaximumNumFOp>,
2165 NarrowExtremum<TruncFOp, ExtFOp, MinimumFOp>,
2166 NarrowExtremum<TruncFOp, ExtFOp, MinNumFOp>,
2167 NarrowExtremum<TruncFOp, ExtFOp, MinimumNumFOp>,
2168 TruncFSIToFPToSIToFP, TruncFUIToFPToUIToFP>(context);
2175LogicalResult arith::TruncFOp::verify() {
2183OpFoldResult arith::ConvertFOp::fold(FoldAdaptor adaptor) {
2185 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2187 adaptor.getOperands(),
getType(),
2188 [
this, &targetSemantics](
const APFloat &a,
bool &castStatus) {
2189 llvm::RoundingMode llvmRoundingMode =
2191 FailureOr<APFloat>
result =
2206 if (!srcType || !dstType)
2208 return srcType != dstType &&
2212LogicalResult arith::ConvertFOp::verify() {
2215 if (srcType == dstType)
2216 return emitError(
"result element type ")
2217 << dstType <<
" must be different from operand element type "
2219 if (srcType.getWidth() != dstType.getWidth())
2220 return emitError(
"result element type ")
2221 << dstType <<
" must have the same bitwidth as operand element type "
2230OpFoldResult arith::ScalingTruncFOp::fold(FoldAdaptor adaptor) {
2239 const llvm::fltSemantics &inSemantics = inElemType.getFloatSemantics();
2240 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2241 llvm::RoundingMode roundingMode =
2244 adaptor.getIn(), adaptor.getScale(),
getType(),
2245 [&](
const APFloat &in,
const APFloat &scale) -> std::optional<APFloat> {
2248 return std::nullopt;
2249 APFloat quotient(in);
2251 FailureOr<APFloat>
result =
2254 return std::nullopt;
2259bool arith::ScalingTruncFOp::areCastCompatible(
TypeRange inputs,
2264LogicalResult arith::ScalingTruncFOp::verify() {
2272void arith::AndIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2273 MLIRContext *context) {
2274 patterns.
add<AndIAndIConstant, AndOfExtUI, AndOfExtSI>(context);
2281void arith::OrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2282 MLIRContext *context) {
2283 patterns.
add<OrIOrIConstant, OrOfExtUI, OrOfExtSI>(context);
2290template <
typename From,
typename To>
2298 return srcType && dstType;
2309OpFoldResult arith::UIToFPOp::fold(FoldAdaptor adaptor) {
2312 adaptor.getOperands(),
getType(),
2313 [&resEleType](
const APInt &a,
bool &castStatus) {
2314 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2315 APFloat apf(floatTy.getFloatSemantics(),
2316 APInt::getZero(floatTy.getWidth()));
2317 apf.convertFromAPInt(a,
false,
2318 APFloat::rmNearestTiesToEven);
2323void arith::UIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2324 MLIRContext *context) {
2325 patterns.
add<UIToFPOfExtUI>(context);
2336OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
2339 adaptor.getOperands(),
getType(),
2340 [&resEleType](
const APInt &a,
bool &castStatus) {
2341 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2342 APFloat apf(floatTy.getFloatSemantics(),
2343 APInt::getZero(floatTy.getWidth()));
2344 if (a.isNegative() &&
2345 !APFloat::semanticsHasSignedRepr(floatTy.getFloatSemantics())) {
2349 apf.convertFromAPInt(a,
true,
2350 APFloat::rmNearestTiesToEven);
2355void arith::SIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2356 MLIRContext *context) {
2357 patterns.
add<SIToFPOfExtSI, SIToFPOfExtUI>(context);
2368OpFoldResult arith::FPToUIOp::fold(FoldAdaptor adaptor) {
2370 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2372 adaptor.getOperands(),
getType(),
2373 [&bitWidth](
const APFloat &a,
bool &castStatus) {
2375 APSInt api(bitWidth,
true);
2376 castStatus = APFloat::opInvalidOp !=
2377 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2390OpFoldResult arith::FPToSIOp::fold(FoldAdaptor adaptor) {
2392 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2394 adaptor.getOperands(),
getType(),
2395 [&bitWidth](
const APFloat &a,
bool &castStatus) {
2397 APSInt api(bitWidth,
false);
2398 castStatus = APFloat::opInvalidOp !=
2399 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2413 return intTy.getWidth();
2414 return IndexType::kInternalStorageBitWidth;
2423 if (!srcType || !dstType)
2427 (srcType.isSignlessInteger() && dstType.
isIndex());
2430bool arith::IndexCastOp::areCastCompatible(
TypeRange inputs,
2435OpFoldResult arith::IndexCastOp::fold(FoldAdaptor adaptor) {
2437 unsigned resultBitwidth = 64;
2439 resultBitwidth = intTy.getWidth();
2442 adaptor.getOperands(),
getType(),
2443 [resultBitwidth](
const APInt &a,
bool & ) {
2444 return a.sextOrTrunc(resultBitwidth);
2451 if (
auto inner = getOperand().getDefiningOp<arith::IndexCastOp>()) {
2452 Value x = inner.getOperand();
2461void arith::IndexCastOp::getCanonicalizationPatterns(
2462 RewritePatternSet &patterns, MLIRContext *context) {
2463 patterns.
add<IndexCastOfExtSI>(context);
2470bool arith::IndexCastUIOp::areCastCompatible(
TypeRange inputs,
2475OpFoldResult arith::IndexCastUIOp::fold(FoldAdaptor adaptor) {
2477 unsigned resultBitwidth = 64;
2479 resultBitwidth = intTy.getWidth();
2482 adaptor.getOperands(),
getType(),
2483 [resultBitwidth](
const APInt &a,
bool & ) {
2484 return a.zextOrTrunc(resultBitwidth);
2491 if (
auto inner = getOperand().getDefiningOp<arith::IndexCastUIOp>()) {
2492 Value x = inner.getOperand();
2501void arith::IndexCastUIOp::getCanonicalizationPatterns(
2502 RewritePatternSet &patterns, MLIRContext *context) {
2503 patterns.
add<IndexCastUIOfExtUI>(context);
2516 if (!srcType || !dstType)
2522OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
2524 auto operand = adaptor.getIn();
2529 if (
auto denseAttr = dyn_cast_or_null<DenseElementsAttr>(operand))
2530 return denseAttr.bitcast(llvm::cast<ShapedType>(resType).
getElementType());
2532 if (llvm::isa<ShapedType>(resType))
2540 if (!llvm::isa<FloatAttr, IntegerAttr>(operand))
2543 APInt bits = llvm::isa<FloatAttr>(operand)
2544 ? llvm::cast<FloatAttr>(operand).getValue().bitcastToAPInt()
2545 : llvm::cast<IntegerAttr>(operand).getValue();
2547 "trying to fold on broken IR: operands have incompatible types");
2549 if (
auto resFloatType = dyn_cast<FloatType>(resType))
2550 return FloatAttr::get(resType,
2551 APFloat(resFloatType.getFloatSemantics(), bits));
2552 return IntegerAttr::get(resType, bits);
2555void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2556 MLIRContext *context) {
2557 patterns.
add<BitcastOfBitcast>(context);
2567 const APInt &lhs,
const APInt &rhs) {
2568 switch (predicate) {
2569 case arith::CmpIPredicate::eq:
2571 case arith::CmpIPredicate::ne:
2573 case arith::CmpIPredicate::slt:
2574 return lhs.slt(rhs);
2575 case arith::CmpIPredicate::sle:
2576 return lhs.sle(rhs);
2577 case arith::CmpIPredicate::sgt:
2578 return lhs.sgt(rhs);
2579 case arith::CmpIPredicate::sge:
2580 return lhs.sge(rhs);
2581 case arith::CmpIPredicate::ult:
2582 return lhs.ult(rhs);
2583 case arith::CmpIPredicate::ule:
2584 return lhs.ule(rhs);
2585 case arith::CmpIPredicate::ugt:
2586 return lhs.ugt(rhs);
2587 case arith::CmpIPredicate::uge:
2588 return lhs.uge(rhs);
2590 llvm_unreachable(
"unknown cmpi predicate kind");
2595 switch (predicate) {
2596 case arith::CmpIPredicate::eq:
2597 case arith::CmpIPredicate::sle:
2598 case arith::CmpIPredicate::sge:
2599 case arith::CmpIPredicate::ule:
2600 case arith::CmpIPredicate::uge:
2602 case arith::CmpIPredicate::ne:
2603 case arith::CmpIPredicate::slt:
2604 case arith::CmpIPredicate::sgt:
2605 case arith::CmpIPredicate::ult:
2606 case arith::CmpIPredicate::ugt:
2609 llvm_unreachable(
"unknown cmpi predicate kind");
2613 if (
auto intType = dyn_cast<IntegerType>(t)) {
2614 return intType.getWidth();
2616 if (
auto vectorIntType = dyn_cast<VectorType>(t)) {
2617 return llvm::cast<IntegerType>(vectorIntType.getElementType()).getWidth();
2619 return std::nullopt;
2622OpFoldResult arith::CmpIOp::fold(FoldAdaptor adaptor) {
2624 if (getLhs() == getRhs()) {
2630 if (
auto extOp = getLhs().getDefiningOp<ExtSIOp>()) {
2632 std::optional<int64_t> integerWidth =
2634 if (integerWidth && integerWidth.value() == 1 &&
2635 getPredicate() == arith::CmpIPredicate::ne)
2636 return extOp.getOperand();
2638 if (
auto extOp = getLhs().getDefiningOp<ExtUIOp>()) {
2640 std::optional<int64_t> integerWidth =
2642 if (integerWidth && integerWidth.value() == 1 &&
2643 getPredicate() == arith::CmpIPredicate::ne)
2644 return extOp.getOperand();
2649 getPredicate() == arith::CmpIPredicate::ne)
2656 getPredicate() == arith::CmpIPredicate::eq)
2661 if (adaptor.getLhs() && !adaptor.getRhs()) {
2663 using Pred = CmpIPredicate;
2664 const std::pair<Pred, Pred> invPreds[] = {
2665 {Pred::slt, Pred::sgt}, {Pred::sgt, Pred::slt}, {Pred::sle, Pred::sge},
2666 {Pred::sge, Pred::sle}, {Pred::ult, Pred::ugt}, {Pred::ugt, Pred::ult},
2667 {Pred::ule, Pred::uge}, {Pred::uge, Pred::ule}, {Pred::eq, Pred::eq},
2668 {Pred::ne, Pred::ne},
2670 Pred origPred = getPredicate();
2671 for (
auto pred : invPreds) {
2672 if (origPred == pred.first) {
2673 setPredicate(pred.second);
2674 Value
lhs = getLhs();
2675 Value
rhs = getRhs();
2676 getLhsMutable().assign(
rhs);
2677 getRhsMutable().assign(
lhs);
2681 llvm_unreachable(
"unknown cmpi predicate kind");
2686 if (
auto lhs = dyn_cast_if_present<TypedAttr>(adaptor.getLhs())) {
2689 [pred = getPredicate()](
const APInt &
lhs,
const APInt &
rhs) {
2698void arith::CmpIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2699 MLIRContext *context) {
2700 patterns.
insert<CmpIExtSI, CmpIExtUI>(context);
2710 const APFloat &lhs,
const APFloat &rhs) {
2711 auto cmpResult = lhs.compare(rhs);
2712 switch (predicate) {
2713 case arith::CmpFPredicate::AlwaysFalse:
2715 case arith::CmpFPredicate::OEQ:
2716 return cmpResult == APFloat::cmpEqual;
2717 case arith::CmpFPredicate::OGT:
2718 return cmpResult == APFloat::cmpGreaterThan;
2719 case arith::CmpFPredicate::OGE:
2720 return cmpResult == APFloat::cmpGreaterThan ||
2721 cmpResult == APFloat::cmpEqual;
2722 case arith::CmpFPredicate::OLT:
2723 return cmpResult == APFloat::cmpLessThan;
2724 case arith::CmpFPredicate::OLE:
2725 return cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2726 case arith::CmpFPredicate::ONE:
2727 return cmpResult != APFloat::cmpUnordered && cmpResult != APFloat::cmpEqual;
2728 case arith::CmpFPredicate::ORD:
2729 return cmpResult != APFloat::cmpUnordered;
2730 case arith::CmpFPredicate::UEQ:
2731 return cmpResult == APFloat::cmpUnordered || cmpResult == APFloat::cmpEqual;
2732 case arith::CmpFPredicate::UGT:
2733 return cmpResult == APFloat::cmpUnordered ||
2734 cmpResult == APFloat::cmpGreaterThan;
2735 case arith::CmpFPredicate::UGE:
2736 return cmpResult == APFloat::cmpUnordered ||
2737 cmpResult == APFloat::cmpGreaterThan ||
2738 cmpResult == APFloat::cmpEqual;
2739 case arith::CmpFPredicate::ULT:
2740 return cmpResult == APFloat::cmpUnordered ||
2741 cmpResult == APFloat::cmpLessThan;
2742 case arith::CmpFPredicate::ULE:
2743 return cmpResult == APFloat::cmpUnordered ||
2744 cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2745 case arith::CmpFPredicate::UNE:
2746 return cmpResult != APFloat::cmpEqual;
2747 case arith::CmpFPredicate::UNO:
2748 return cmpResult == APFloat::cmpUnordered;
2749 case arith::CmpFPredicate::AlwaysTrue:
2752 llvm_unreachable(
"unknown cmpf predicate kind");
2756 auto lhs = dyn_cast_if_present<FloatAttr>(adaptor.getLhs());
2757 auto rhs = dyn_cast_if_present<FloatAttr>(adaptor.getRhs());
2760 if (lhs && lhs.getValue().isNaN())
2762 if (rhs && rhs.getValue().isNaN())
2778 using namespace arith;
2780 case CmpFPredicate::UEQ:
2781 case CmpFPredicate::OEQ:
2782 return CmpIPredicate::eq;
2783 case CmpFPredicate::UGT:
2784 case CmpFPredicate::OGT:
2785 return isUnsigned ? CmpIPredicate::ugt : CmpIPredicate::sgt;
2786 case CmpFPredicate::UGE:
2787 case CmpFPredicate::OGE:
2788 return isUnsigned ? CmpIPredicate::uge : CmpIPredicate::sge;
2789 case CmpFPredicate::ULT:
2790 case CmpFPredicate::OLT:
2791 return isUnsigned ? CmpIPredicate::ult : CmpIPredicate::slt;
2792 case CmpFPredicate::ULE:
2793 case CmpFPredicate::OLE:
2794 return isUnsigned ? CmpIPredicate::ule : CmpIPredicate::sle;
2795 case CmpFPredicate::UNE:
2796 case CmpFPredicate::ONE:
2797 return CmpIPredicate::ne;
2799 llvm_unreachable(
"Unexpected predicate!");
2809 const APFloat &rhs = flt.getValue();
2817 FloatType floatTy = llvm::cast<FloatType>(op.getRhs().getType());
2818 int mantissaWidth = floatTy.getFPMantissaWidth();
2819 if (mantissaWidth <= 0)
2825 if (
auto si = op.getLhs().getDefiningOp<SIToFPOp>()) {
2827 intVal = si.getIn();
2828 }
else if (
auto ui = op.getLhs().getDefiningOp<UIToFPOp>()) {
2830 intVal = ui.getIn();
2837 auto intTy = llvm::cast<IntegerType>(intVal.
getType());
2838 auto intWidth = intTy.getWidth();
2841 auto valueBits = isUnsigned ? intWidth : (intWidth - 1);
2843 const llvm::fltSemantics &semantics = floatTy.getFloatSemantics();
2847 if (!APFloat::semanticsHasZero(semantics))
2852 if (!isUnsigned && !APFloat::semanticsHasSignedRepr(semantics))
2855 if (!APFloat::semanticsHasInf(semantics)) {
2866 if (APFloat::semanticsMaxExponent(semantics) <
2867 static_cast<int>(valueBits))
2874 if ((
int)intWidth > mantissaWidth) {
2876 int exponent = ilogb(rhs);
2877 if (exponent == APFloat::IEK_Inf) {
2878 int maxExponent = ilogb(APFloat::getLargest(rhs.getSemantics()));
2879 if (maxExponent <
static_cast<int>(valueBits)) {
2887 if (mantissaWidth <= exponent && exponent <= (
int)valueBits) {
2896 switch (op.getPredicate()) {
2897 case CmpFPredicate::AlwaysTrue:
2898 case CmpFPredicate::ORD:
2903 case CmpFPredicate::AlwaysFalse:
2904 case CmpFPredicate::UNO:
2917 APFloat signedMax(rhs.getSemantics());
2918 signedMax.convertFromAPInt(APInt::getSignedMaxValue(intWidth),
true,
2919 APFloat::rmNearestTiesToEven);
2920 if (signedMax < rhs) {
2921 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::slt ||
2922 pred == CmpIPredicate::sle)
2933 APFloat unsignedMax(rhs.getSemantics());
2934 unsignedMax.convertFromAPInt(APInt::getMaxValue(intWidth),
false,
2935 APFloat::rmNearestTiesToEven);
2936 if (unsignedMax < rhs) {
2937 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ult ||
2938 pred == CmpIPredicate::ule)
2950 APFloat signedMin(rhs.getSemantics());
2951 signedMin.convertFromAPInt(APInt::getSignedMinValue(intWidth),
true,
2952 APFloat::rmNearestTiesToEven);
2953 if (signedMin > rhs) {
2954 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::sgt ||
2955 pred == CmpIPredicate::sge)
2965 APFloat unsignedMin(rhs.getSemantics());
2966 unsignedMin.convertFromAPInt(APInt::getMinValue(intWidth),
false,
2967 APFloat::rmNearestTiesToEven);
2968 if (unsignedMin > rhs) {
2969 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ugt ||
2970 pred == CmpIPredicate::uge)
2985 APSInt rhsInt(intWidth, isUnsigned);
2986 if (APFloat::opInvalidOp ==
2987 rhs.convertToInteger(rhsInt, APFloat::rmTowardZero, &ignored)) {
2993 if (!rhs.isZero()) {
2994 APFloat apf(floatTy.getFloatSemantics(),
2995 APInt::getZero(floatTy.getWidth()));
2996 apf.convertFromAPInt(rhsInt, !isUnsigned, APFloat::rmNearestTiesToEven);
2998 bool equal = apf == rhs;
3004 case CmpIPredicate::ne:
3008 case CmpIPredicate::eq:
3012 case CmpIPredicate::ule:
3015 if (rhs.isNegative()) {
3021 case CmpIPredicate::sle:
3024 if (rhs.isNegative())
3025 pred = CmpIPredicate::slt;
3027 case CmpIPredicate::ult:
3030 if (rhs.isNegative()) {
3035 pred = CmpIPredicate::ule;
3037 case CmpIPredicate::slt:
3040 if (!rhs.isNegative())
3041 pred = CmpIPredicate::sle;
3043 case CmpIPredicate::ugt:
3046 if (rhs.isNegative()) {
3052 case CmpIPredicate::sgt:
3055 if (rhs.isNegative())
3056 pred = CmpIPredicate::sge;
3058 case CmpIPredicate::uge:
3061 if (rhs.isNegative()) {
3066 pred = CmpIPredicate::ugt;
3068 case CmpIPredicate::sge:
3071 if (!rhs.isNegative())
3072 pred = CmpIPredicate::sgt;
3082 ConstantOp::create(rewriter, op.getLoc(), intVal.
getType(),
3088void arith::CmpFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
3089 MLIRContext *context) {
3090 patterns.
insert<CmpFIntToFPConst>(context);
3104 if (!llvm::isa<IntegerType>(op.getType()) || op.getType().isInteger(1))
3120 arith::XOrIOp::create(
3121 rewriter, op.getLoc(), op.getCondition(),
3123 op.getCondition().
getType(), 1)));
3131void arith::SelectOp::getCanonicalizationPatterns(RewritePatternSet &results,
3132 MLIRContext *context) {
3133 results.
add<RedundantSelectFalse, RedundantSelectTrue, SelectNotCond,
3134 SelectI1ToNot, SelectCmpISgeToMaxSI, SelectCmpISgeToMinSI,
3135 SelectCmpISgtToMaxSI, SelectCmpISgtToMinSI, SelectCmpISleToMaxSI,
3136 SelectCmpISleToMinSI, SelectCmpISltToMaxSI, SelectCmpISltToMinSI,
3137 SelectCmpIUgeToMaxUI, SelectCmpIUgeToMinUI, SelectCmpIUgtToMaxUI,
3138 SelectCmpIUgtToMinUI, SelectCmpIUleToMaxUI, SelectCmpIUleToMinUI,
3139 SelectCmpIUltToMaxUI, SelectCmpIUltToMinUI, SelectToExtUI>(
3143OpFoldResult arith::SelectOp::fold(FoldAdaptor adaptor) {
3144 Value trueVal = getTrueValue();
3145 Value falseVal = getFalseValue();
3146 if (trueVal == falseVal)
3149 Value condition = getCondition();
3167 if (
getType().isSignlessInteger(1) &&
3173 auto pred = cmp.getPredicate();
3174 if (pred == arith::CmpIPredicate::eq || pred == arith::CmpIPredicate::ne) {
3175 auto cmpLhs = cmp.getLhs();
3176 auto cmpRhs = cmp.getRhs();
3184 if ((cmpLhs == trueVal && cmpRhs == falseVal) ||
3185 (cmpRhs == trueVal && cmpLhs == falseVal))
3186 return pred == arith::CmpIPredicate::ne ? trueVal : falseVal;
3193 dyn_cast_if_present<DenseElementsAttr>(adaptor.getCondition())) {
3195 assert(cond.getType().hasStaticShape() &&
3196 "DenseElementsAttr must have static shape");
3198 dyn_cast_if_present<DenseElementsAttr>(adaptor.getTrueValue())) {
3200 dyn_cast_if_present<DenseElementsAttr>(adaptor.getFalseValue())) {
3201 SmallVector<Attribute> results;
3202 results.reserve(
static_cast<size_t>(cond.getNumElements()));
3203 auto condVals = llvm::make_range(cond.value_begin<BoolAttr>(),
3204 cond.value_end<BoolAttr>());
3205 auto lhsVals = llvm::make_range(
lhs.value_begin<Attribute>(),
3206 lhs.value_end<Attribute>());
3207 auto rhsVals = llvm::make_range(
rhs.value_begin<Attribute>(),
3208 rhs.value_end<Attribute>());
3210 for (
auto [condVal, lhsVal, rhsVal] :
3211 llvm::zip_equal(condVals, lhsVals, rhsVals))
3212 results.push_back(condVal.getValue() ? lhsVal : rhsVal);
3222ParseResult SelectOp::parse(OpAsmParser &parser, OperationState &
result) {
3223 Type conditionType, resultType;
3224 SmallVector<OpAsmParser::UnresolvedOperand, 3> operands;
3232 conditionType = resultType;
3239 result.addTypes(resultType);
3241 {conditionType, resultType, resultType},
3245void arith::SelectOp::print(OpAsmPrinter &p) {
3246 p <<
" " << getOperands();
3249 if (ShapedType condType = dyn_cast<ShapedType>(getCondition().
getType()))
3250 p << condType <<
", ";
3254LogicalResult arith::SelectOp::verify() {
3255 Type conditionType = getCondition().getType();
3262 if (!llvm::isa<TensorType, VectorType>(resultType))
3263 return emitOpError() <<
"expected condition to be a signless i1, but got "
3266 if (conditionType != shapedConditionType) {
3267 return emitOpError() <<
"expected condition type to have the same shape "
3268 "as the result type, expected "
3269 << shapedConditionType <<
", but got "
3278OpFoldResult arith::ShLIOp::fold(FoldAdaptor adaptor) {
3292 bool bounded =
false;
3294 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3295 bounded = b.ult(b.getBitWidth());
3298 return bounded ?
result : Attribute();
3305OpFoldResult arith::ShRUIOp::fold(FoldAdaptor adaptor) {
3320 if (getLhs() == getRhs())
3323 bool bounded =
false;
3325 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3326 bounded = b.ult(b.getBitWidth());
3329 return bounded ?
result : Attribute();
3336OpFoldResult arith::ShRSIOp::fold(FoldAdaptor adaptor) {
3351 if (getLhs() == getRhs())
3359 bool bounded =
false;
3361 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3362 bounded = b.ult(b.getBitWidth());
3365 return bounded ?
result : Attribute();
3375 bool useOnlyFiniteValue) {
3377 case AtomicRMWKind::maximumf: {
3378 const llvm::fltSemantics &semantic =
3379 llvm::cast<FloatType>(resultType).getFloatSemantics();
3380 APFloat identity = useOnlyFiniteValue
3381 ? APFloat::getLargest(semantic,
true)
3382 : APFloat::getInf(semantic,
true);
3385 case AtomicRMWKind::maxnumf: {
3386 const llvm::fltSemantics &semantic =
3387 llvm::cast<FloatType>(resultType).getFloatSemantics();
3388 APFloat identity = useOnlyFiniteValue
3389 ? APFloat::getLargest(semantic,
true)
3390 : APFloat::getNaN(semantic,
true);
3393 case AtomicRMWKind::addf:
3394 case AtomicRMWKind::addi:
3395 case AtomicRMWKind::maxu:
3396 case AtomicRMWKind::ori:
3397 case AtomicRMWKind::xori:
3399 case AtomicRMWKind::andi:
3402 APInt::getAllOnes(llvm::cast<IntegerType>(resultType).getWidth()));
3403 case AtomicRMWKind::maxs:
3405 resultType, APInt::getSignedMinValue(
3406 llvm::cast<IntegerType>(resultType).getWidth()));
3407 case AtomicRMWKind::minimumf: {
3408 const llvm::fltSemantics &semantic =
3409 llvm::cast<FloatType>(resultType).getFloatSemantics();
3410 APFloat identity = useOnlyFiniteValue
3411 ? APFloat::getLargest(semantic,
false)
3412 : APFloat::getInf(semantic,
false);
3416 case AtomicRMWKind::minnumf: {
3417 const llvm::fltSemantics &semantic =
3418 llvm::cast<FloatType>(resultType).getFloatSemantics();
3419 APFloat identity = useOnlyFiniteValue
3420 ? APFloat::getLargest(semantic,
false)
3421 : APFloat::getNaN(semantic,
false);
3424 case AtomicRMWKind::mins:
3426 resultType, APInt::getSignedMaxValue(
3427 llvm::cast<IntegerType>(resultType).getWidth()));
3428 case AtomicRMWKind::minu:
3431 APInt::getMaxValue(llvm::cast<IntegerType>(resultType).getWidth()));
3432 case AtomicRMWKind::muli:
3434 case AtomicRMWKind::mulf:
3437 case AtomicRMWKind::assign:
3446 std::optional<AtomicRMWKind> maybeKind =
3449 .Case([](arith::AddFOp op) {
return AtomicRMWKind::addf; })
3450 .Case([](arith::MulFOp op) {
return AtomicRMWKind::mulf; })
3451 .Case([](arith::MaximumFOp op) {
return AtomicRMWKind::maximumf; })
3452 .Case([](arith::MinimumFOp op) {
return AtomicRMWKind::minimumf; })
3453 .Case([](arith::MaxNumFOp op) {
return AtomicRMWKind::maxnumf; })
3454 .Case([](arith::MinNumFOp op) {
return AtomicRMWKind::minnumf; })
3456 .Case([](arith::AddIOp op) {
return AtomicRMWKind::addi; })
3457 .Case([](arith::OrIOp op) {
return AtomicRMWKind::ori; })
3458 .Case([](arith::XOrIOp op) {
return AtomicRMWKind::xori; })
3459 .Case([](arith::AndIOp op) {
return AtomicRMWKind::andi; })
3460 .Case([](arith::MaxUIOp op) {
return AtomicRMWKind::maxu; })
3461 .Case([](arith::MinUIOp op) {
return AtomicRMWKind::minu; })
3462 .Case([](arith::MaxSIOp op) {
return AtomicRMWKind::maxs; })
3463 .Case([](arith::MinSIOp op) {
return AtomicRMWKind::mins; })
3464 .Case([](arith::MulIOp op) {
return AtomicRMWKind::muli; })
3465 .Default(std::nullopt);
3467 return std::nullopt;
3470 bool useOnlyFiniteValue =
false;
3471 auto fmfOpInterface = dyn_cast<ArithFastMathInterface>(op);
3472 if (fmfOpInterface) {
3473 arith::FastMathFlagsAttr fmfAttr = fmfOpInterface.getFastMathFlagsAttr();
3474 useOnlyFiniteValue =
3475 bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::ninf);
3476 if (*maybeKind == AtomicRMWKind::maxnumf ||
3477 *maybeKind == AtomicRMWKind::minnumf) {
3478 if (bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::nnan)) {
3479 *maybeKind = *maybeKind == AtomicRMWKind::maxnumf
3480 ? AtomicRMWKind::maximumf
3481 : AtomicRMWKind::minimumf;
3483 useOnlyFiniteValue =
false;
3493 useOnlyFiniteValue);
3499 bool useOnlyFiniteValue) {
3501 useOnlyFiniteValue))
3502 return arith::ConstantOp::create(builder, loc, attr);
3511 case AtomicRMWKind::addf:
3512 return arith::AddFOp::create(builder, loc, lhs, rhs);
3513 case AtomicRMWKind::addi:
3514 return arith::AddIOp::create(builder, loc, lhs, rhs);
3515 case AtomicRMWKind::mulf:
3516 return arith::MulFOp::create(builder, loc, lhs, rhs);
3517 case AtomicRMWKind::muli:
3518 return arith::MulIOp::create(builder, loc, lhs, rhs);
3519 case AtomicRMWKind::maximumf:
3520 return arith::MaximumFOp::create(builder, loc, lhs, rhs);
3521 case AtomicRMWKind::minimumf:
3522 return arith::MinimumFOp::create(builder, loc, lhs, rhs);
3523 case AtomicRMWKind::maxnumf:
3524 return arith::MaxNumFOp::create(builder, loc, lhs, rhs);
3525 case AtomicRMWKind::minnumf:
3526 return arith::MinNumFOp::create(builder, loc, lhs, rhs);
3527 case AtomicRMWKind::maxs:
3528 return arith::MaxSIOp::create(builder, loc, lhs, rhs);
3529 case AtomicRMWKind::mins:
3530 return arith::MinSIOp::create(builder, loc, lhs, rhs);
3531 case AtomicRMWKind::maxu:
3532 return arith::MaxUIOp::create(builder, loc, lhs, rhs);
3533 case AtomicRMWKind::minu:
3534 return arith::MinUIOp::create(builder, loc, lhs, rhs);
3535 case AtomicRMWKind::ori:
3536 return arith::OrIOp::create(builder, loc, lhs, rhs);
3537 case AtomicRMWKind::andi:
3538 return arith::AndIOp::create(builder, loc, lhs, rhs);
3539 case AtomicRMWKind::xori:
3540 return arith::XOrIOp::create(builder, loc, lhs, rhs);
3542 case AtomicRMWKind::assign:
3553#define GET_OP_CLASSES
3554#include "mlir/Dialect/Arith/IR/ArithOps.cpp.inc"
3560#include "mlir/Dialect/Arith/IR/ArithOpsEnums.cpp.inc"
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static Speculation::Speculatability getDivUISpeculatability(Value divisor)
Returns whether an unsigned division by divisor is speculatable.
static bool checkWidthChangeCast(TypeRange inputs, TypeRange outputs)
Validate a cast that changes the width of a type.
static IntegerAttr mulIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static IntegerOverflowFlagsAttr mergeOverflowFlags(IntegerOverflowFlagsAttr val1, IntegerOverflowFlagsAttr val2)
static Value foldIdempotentOfSameOp(OpTy op)
Fold op(a, op(a, b)) to op(a, b) for an associative, commutative and idempotent op (e....
static constexpr llvm::RoundingMode kDefaultRoundingMode
Default rounding mode according to default LLVM floating-point environment.
static Type getTypeIfLike(Type type)
Get allowed underlying types for vectors and tensors.
static bool applyCmpPredicateToEqualOperands(arith::CmpIPredicate predicate)
Returns true if the predicate is true for two equal operands.
static FailureOr< APFloat > convertFloatValue(APFloat sourceValue, const llvm::fltSemantics &targetSemantics, llvm::RoundingMode roundingMode=kDefaultRoundingMode)
Attempts to convert sourceValue to an APFloat value with targetSemantics and roundingMode,...
static Attribute foldScalingCastOp(Attribute inAttr, Attribute scaleAttr, Type resultType, function_ref< std::optional< APFloat >(const APFloat &, const APFloat &)> calculate)
Fold calculate element-wise over the operands of a scaling cast op.
static Value foldDivMul(Value lhs, Value rhs, arith::IntegerOverflowFlags ovfFlags)
Fold (a * b) / b -> a
static bool hasSameEncoding(Type typeA, Type typeB)
Return false if both types are ranked tensor with mismatching encoding.
static llvm::RoundingMode convertArithRoundingModeToLLVMIR(std::optional< RoundingMode > roundingMode)
Equivalent to convertRoundingModeToLLVM(convertArithRoundingModeToLLVM(roundingMode)).
static Type getUnderlyingType(Type type, type_list< ShapedTypes... >, type_list< ElementTypes... >)
Returns a non-null type only if the provided type is one of the allowed types or one of the allowed s...
static std::optional< int64_t > getIntegerWidth(Type t)
APInt(APInt::*)(const APInt &, bool &) const OverflowCheckedBinaryOp
static Speculation::Speculatability getDivSISpeculatability(Value divisor)
Returns whether a signed division by divisor is speculatable.
static IntegerAttr orIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static IntegerAttr addIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static Attribute getBoolAttribute(Type type, bool value)
static bool areIndexCastCompatible(TypeRange inputs, TypeRange outputs)
static bool isFoldableScalingScale(Value scale)
Only scales that already are f8E8M0FNU fold.
static bool checkIntFloatCast(TypeRange inputs, TypeRange outputs)
static LogicalResult verifyExtOp(Op op)
static IntegerAttr subIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static Attribute getIntegerAttrOfType(Type type, int64_t value)
Return a scalar or splat integer attribute of type (an integer/index type or a shaped type thereof) h...
static IntegerAttr andIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static int64_t getScalarOrElementWidth(Type type)
static Type getTypeIfLikeOrMemRef(Type type)
Get allowed underlying types for vectors, tensors, and memrefs.
static Type getI1SameShape(Type type)
Return the type of the same shape (scalar, vector or tensor) containing i1.
static bool areValidCastInputsAndOutputs(TypeRange inputs, TypeRange outputs)
static IntegerOverflowFlagsAttr mergeOverflowFlagsForReassociation(IntegerOverflowFlagsAttr val1, IntegerOverflowFlagsAttr val2, Attribute lhs, Attribute rhs, OverflowCheckedBinaryOp signedOp, OverflowCheckedBinaryOp unsignedOp)
static IntegerAttr xorIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
static APInt calculateUnsignedBorrow(const APInt &lhs, const APInt &rhs)
std::tuple< Types... > * type_list
static IntegerAttr applyToIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs, function_ref< APInt(const APInt &, const APInt &)> binFn)
static APInt calculateUnsignedOverflow(const APInt &sum, const APInt &operand)
static FailureOr< APInt > getIntOrSplatIntValue(Attribute attr)
static unsigned getIndexCastWidth(Type t)
Return the bit-width of t for the purpose of index_cast width checks.
static LogicalResult verifyTruncateOp(Op op)
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
LogicalResult matchAndRewrite(CmpFOp op, PatternRewriter &rewriter) const override
static CmpIPredicate convertToIntegerPredicate(CmpFPredicate pred, bool isUnsigned)
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
Attributes are known-constant values of operations.
static BoolAttr get(MLIRContext *context, bool value)
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
FloatAttr getFloatAttr(Type type, double value)
IntegerType getIntegerType(unsigned width)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
TypedAttr getZeroAttr(Type type)
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Location getLoc() const
Accessors for the implied location.
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.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
This class helps build Operations.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
This class represents a single result from folding an operation.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
MLIRContext * getContext()
Return the context this operation is associated with.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
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 isSignlessInteger() const
Return true if this is a signless 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.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Specialization of arith.constant op that returns a floating point value.
static ConstantFloatOp create(OpBuilder &builder, Location location, FloatType type, const APFloat &value)
static bool classof(Operation *op)
static void build(OpBuilder &builder, OperationState &result, FloatType type, const APFloat &value)
Build a constant float op that produces a float of the specified type.
Specialization of arith.constant op that returns an integer of index type.
static void build(OpBuilder &builder, OperationState &result, int64_t value)
Build a constant int op that produces an index.
static bool classof(Operation *op)
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Specialization of arith.constant op that returns an integer value.
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
static void build(OpBuilder &builder, OperationState &result, int64_t value, unsigned width)
Build a constant int op that produces an integer of the specified width.
static bool classof(Operation *op)
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
std::optional< TypedAttr > getNeutralElement(Operation *op)
Return the identity numeric value associated to the give op.
bool applyCmpPredicate(arith::CmpIPredicate predicate, const APInt &lhs, const APInt &rhs)
Compute lhs pred rhs, where pred is one of the known integer comparison predicates.
TypedAttr getIdentityValueAttr(AtomicRMWKind kind, Type resultType, OpBuilder &builder, Location loc, bool useOnlyFiniteValue=false)
Returns the identity value attribute associated with an AtomicRMWKind op.
Value getReductionOp(AtomicRMWKind op, OpBuilder &builder, Location loc, Value lhs, Value rhs)
Returns the value obtained by applying the reduction operation kind associated with a binary AtomicRM...
Value getIdentityValue(AtomicRMWKind op, Type resultType, OpBuilder &builder, Location loc, bool useOnlyFiniteValue=false)
Returns the identity value associated with an AtomicRMWKind op.
arith::CmpIPredicate invertPredicate(arith::CmpIPredicate pred)
Invert an integer comparison predicate.
Value getZeroConstant(OpBuilder &builder, Location loc, Type type)
Creates an arith.constant operation with a zero value of type type.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Attribute constFoldBinaryOpConditional(ArrayRef< Attribute > operands, Type resultType, CalculationT &&calculate)
Performs constant folding calculate with element-wise behavior on the two attributes in operands and ...
detail::constant_float_predicate_matcher m_NaNFloat()
Matches a constant scalar / vector splat / tensor splat float ones.
LogicalResult verifyCompatibleShapes(TypeRange types1, TypeRange types2)
Returns success if the given two arrays have the same number of elements and each pair wise entries h...
Attribute constFoldCastOp(ArrayRef< Attribute > operands, Type resType, CalculationT &&calculate)
Attribute constFoldBinaryOp(ArrayRef< Attribute > operands, Type resultType, CalculationT &&calculate)
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
detail::constant_int_range_predicate_matcher m_IntRangeWithoutNegOneS()
Matches a constant scalar / vector splat / tensor splat integer or a signed integer range that does n...
LogicalResult emitOptionalError(std::optional< Location > loc, Args &&...args)
Overloads of the above emission functions that take an optionally null location.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
detail::constant_float_predicate_matcher m_PosZeroFloat()
Matches a constant scalar / vector splat / tensor splat float positive zero.
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
Attribute constFoldUnaryOpConditional(ArrayRef< Attribute > operands, Type resultType, CalculationT &&calculate)
Performs constant folding calculate with element-wise behavior on the one attributes in operands and ...
detail::constant_float_predicate_matcher m_AnyZeroFloat()
Matches a constant scalar / vector splat / tensor splat float (both positive and negative) zero.
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
detail::constant_float_predicate_matcher m_NegInfFloat()
Matches a constant scalar / vector splat / tensor splat float negative infinity.
detail::constant_float_predicate_matcher m_NegZeroFloat()
Matches a constant scalar / vector splat / tensor splat float negative zero.
detail::constant_int_range_predicate_matcher m_IntRangeWithoutZeroS()
Matches a constant scalar / vector splat / tensor splat integer or a signed integer range that does n...
detail::op_matcher< OpClass > m_Op()
Matches the given OpClass.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Attribute constFoldUnaryOp(ArrayRef< Attribute > operands, Type resultType, CalculationT &&calculate)
detail::constant_float_predicate_matcher m_PosInfFloat()
Matches a constant scalar / vector splat / tensor splat float positive infinity.
llvm::function_ref< Fn > function_ref
detail::constant_float_predicate_matcher m_OneFloat()
Matches a constant scalar / vector splat / tensor splat float ones.
detail::constant_int_range_predicate_matcher m_IntRangeWithoutZeroU()
Matches a constant scalar / vector splat / tensor splat integer or a unsigned integer range that does...
LogicalResult matchAndRewrite(arith::SelectOp op, PatternRewriter &rewriter) const override
OpRewritePattern Base
Type alias to allow derived classes to inherit constructors with using Base::Base;.
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.