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;
1093 Value divisor = getRhs();
1105OpFoldResult arith::RemUIOp::fold(FoldAdaptor adaptor) {
1122 [&](APInt a,
const APInt &
b) {
1123 if (div0 || b.isZero()) {
1130 return div0 ? Attribute() :
result;
1141OpFoldResult arith::RemSIOp::fold(FoldAdaptor adaptor) {
1158 [&](APInt a,
const APInt &
b) {
1159 if (div0 || b.isZero()) {
1166 return div0 ? Attribute() :
result;
1184template <
typename OpTy>
1186 for (
bool reversePrev : {
false,
true}) {
1187 auto prev = (reversePrev ? op.getRhs() : op.getLhs())
1188 .template getDefiningOp<OpTy>();
1192 Value other = (reversePrev ? op.getLhs() : op.getRhs());
1193 if (other != prev.getLhs() && other != prev.getRhs())
1196 return prev.getResult();
1201OpFoldResult arith::AndIOp::fold(FoldAdaptor adaptor) {
1208 intValue.isAllOnes())
1213 intValue.isAllOnes())
1218 intValue.isAllOnes())
1226 adaptor.getOperands(),
1227 [](APInt a,
const APInt &
b) { return std::move(a) & b; });
1234OpFoldResult arith::OrIOp::fold(FoldAdaptor adaptor) {
1237 if (rhsVal.isZero())
1240 if (rhsVal.isAllOnes())
1241 return adaptor.getRhs();
1248 intValue.isAllOnes())
1249 return getRhs().getDefiningOp<XOrIOp>().getRhs();
1253 intValue.isAllOnes())
1254 return getLhs().getDefiningOp<XOrIOp>().getRhs();
1261 adaptor.getOperands(),
1262 [](APInt a,
const APInt &
b) { return std::move(a) | b; });
1269OpFoldResult arith::XOrIOp::fold(FoldAdaptor adaptor) {
1274 if (getLhs() == getRhs()) {
1277 auto shapedType = dyn_cast<ShapedType>(
getType());
1278 if (!shapedType || shapedType.hasStaticShape())
1283 if (arith::XOrIOp prev = getLhs().getDefiningOp<arith::XOrIOp>()) {
1284 if (prev.getRhs() == getRhs())
1285 return prev.getLhs();
1286 if (prev.getLhs() == getRhs())
1287 return prev.getRhs();
1291 if (arith::XOrIOp prev = getRhs().getDefiningOp<arith::XOrIOp>()) {
1292 if (prev.getRhs() == getLhs())
1293 return prev.getLhs();
1294 if (prev.getLhs() == getLhs())
1295 return prev.getRhs();
1299 adaptor.getOperands(),
1300 [](APInt a,
const APInt &
b) { return std::move(a) ^ b; });
1303void arith::XOrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1304 MLIRContext *context) {
1305 patterns.
add<XOrIXOrIConstant, XOrINotCmpI, XOrIOfExtUI, XOrIOfExtSI>(
1313OpFoldResult arith::NegFOp::fold(FoldAdaptor adaptor) {
1315 if (
auto op = this->getOperand().getDefiningOp<arith::NegFOp>())
1316 return op.getOperand();
1318 adaptor.getOperands(), [](
const APFloat &a) -> std::optional<APFloat> {
1319 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()))
1320 return std::nullopt;
1329OpFoldResult arith::FlushDenormalsOp::fold(FoldAdaptor adaptor) {
1335 if (
auto op = this->getOperand().getDefiningOp<arith::FlushDenormalsOp>())
1336 return op.getResult();
1340 adaptor.getOperands(), [](
const APFloat &a) {
1342 return APFloat::getZero(a.getSemantics(), a.isNegative());
1351OpFoldResult arith::AddFOp::fold(FoldAdaptor adaptor) {
1356 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1359 auto rm = getRoundingmode();
1361 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1363 result.add(b, convertArithRoundingModeToLLVMIR(rm));
1368void arith::AddFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1369 MLIRContext *context) {
1370 patterns.
add<AddFOfNegFLhs, AddFOfNegFRhs>(context);
1377OpFoldResult arith::SubFOp::fold(FoldAdaptor adaptor) {
1382 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1385 auto rm = getRoundingmode();
1387 adaptor.getOperands(),
1388 [rm](
const APFloat &a,
const APFloat &
b) -> std::optional<APFloat> {
1391 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()) &&
1392 a.compare(b) == APFloat::cmpLessThan)
1393 return std::nullopt;
1395 result.subtract(b, convertArithRoundingModeToLLVMIR(rm));
1400void arith::SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1401 MLIRContext *context) {
1402 patterns.
add<SubFOfNegZero>(context);
1415template <
typename TruncOp,
typename ExtOp,
typename ExtremumOp>
1416struct NarrowExtremum final : OpRewritePattern<TruncOp> {
1417 using OpRewritePattern<TruncOp>::OpRewritePattern;
1419 LogicalResult matchAndRewrite(TruncOp truncOp,
1420 PatternRewriter &rewriter)
const override {
1421 auto extremumOp = truncOp.getIn().template getDefiningOp<ExtremumOp>();
1422 if (!extremumOp || !extremumOp->hasOneUse())
1425 auto lhsExt = extremumOp.getLhs().template getDefiningOp<ExtOp>();
1426 auto rhsExt = extremumOp.getRhs().template getDefiningOp<ExtOp>();
1427 if (!lhsExt || !rhsExt)
1430 Value
lhs = lhsExt.getIn();
1431 Value
rhs = rhsExt.getIn();
1432 Type narrowType = truncOp.getType();
1433 if (
lhs.getType() != narrowType ||
rhs.getType() != narrowType)
1442 if (
auto narrowFloatType =
1444 auto wideFloatType =
1449 const llvm::fltSemantics &narrowSemantics =
1450 narrowFloatType.getFloatSemantics();
1451 const llvm::fltSemantics &wideSemantics =
1452 wideFloatType.getFloatSemantics();
1453 bool ignoreNaNs =
false;
1454 if constexpr (std::is_same_v<TruncOp, TruncFOp>)
1456 bitEnumContainsAll(extremumOp.getFastmath(), FastMathFlags::nnan);
1457 if (!llvm::APFloatBase::isLosslesslyConvertibleTo(
1458 narrowSemantics, wideSemantics, ignoreNaNs))
1464 extremumOp.getProperties(),
1465 extremumOp->getDiscardableAttrDictionary().getValue());
1476OpFoldResult arith::MaximumFOp::fold(FoldAdaptor adaptor) {
1478 if (getLhs() == getRhs())
1492OpFoldResult arith::MaxNumFOp::fold(FoldAdaptor adaptor) {
1494 if (getLhs() == getRhs())
1508OpFoldResult arith::MaximumNumFOp::fold(FoldAdaptor adaptor) {
1510 if (getLhs() == getRhs())
1524OpFoldResult MaxSIOp::fold(FoldAdaptor adaptor) {
1526 if (getLhs() == getRhs())
1532 if (intValue.isMaxSignedValue())
1535 if (intValue.isMinSignedValue())
1540 llvm::APIntOps::smax);
1547OpFoldResult MaxUIOp::fold(FoldAdaptor adaptor) {
1549 if (getLhs() == getRhs())
1555 if (intValue.isMaxValue())
1558 if (intValue.isMinValue())
1563 llvm::APIntOps::umax);
1570OpFoldResult arith::MinimumFOp::fold(FoldAdaptor adaptor) {
1572 if (getLhs() == getRhs())
1586OpFoldResult arith::MinNumFOp::fold(FoldAdaptor adaptor) {
1588 if (getLhs() == getRhs())
1602OpFoldResult arith::MinimumNumFOp::fold(FoldAdaptor adaptor) {
1604 if (getLhs() == getRhs())
1618OpFoldResult MinSIOp::fold(FoldAdaptor adaptor) {
1620 if (getLhs() == getRhs())
1626 if (intValue.isMinSignedValue())
1629 if (intValue.isMaxSignedValue())
1634 llvm::APIntOps::smin);
1641OpFoldResult MinUIOp::fold(FoldAdaptor adaptor) {
1643 if (getLhs() == getRhs())
1649 if (intValue.isMinValue())
1652 if (intValue.isMaxValue())
1657 llvm::APIntOps::umin);
1664OpFoldResult arith::MulFOp::fold(FoldAdaptor adaptor) {
1672 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::nnan |
1673 arith::FastMathFlags::nsz) &&
1674 !(adaptor.getLhs() && adaptor.getRhs())) {
1682 auto rm = getRoundingmode();
1684 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1686 result.multiply(b, convertArithRoundingModeToLLVMIR(rm));
1691void arith::MulFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1692 MLIRContext *context) {
1693 patterns.
add<MulFOfNegF>(context);
1700OpFoldResult arith::DivFOp::fold(FoldAdaptor adaptor) {
1705 auto rm = getRoundingmode();
1707 adaptor.getOperands(), [rm](
const APFloat &a,
const APFloat &
b) {
1709 result.divide(b, convertArithRoundingModeToLLVMIR(rm));
1714void arith::DivFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1715 MLIRContext *context) {
1716 patterns.
add<DivFOfNegF>(context);
1723OpFoldResult arith::RemFOp::fold(FoldAdaptor adaptor) {
1725 [](
const APFloat &a,
const APFloat &
b) {
1730 (void)result.mod(b);
1739template <
typename... Types>
1745template <
typename... ShapedTypes,
typename... ElementTypes>
1748 if (llvm::isa<ShapedType>(type) && !llvm::isa<ShapedTypes...>(type))
1752 if (!llvm::isa<ElementTypes...>(underlyingType))
1755 return underlyingType;
1759template <
typename... ElementTypes>
1766template <
typename... ElementTypes>
1775 auto rankedTensorA = dyn_cast<RankedTensorType>(typeA);
1776 auto rankedTensorB = dyn_cast<RankedTensorType>(typeB);
1777 if (!rankedTensorA || !rankedTensorB)
1779 return rankedTensorA.getEncoding() == rankedTensorB.getEncoding();
1783 if (inputs.size() != 1 || outputs.size() != 1)
1795template <
typename ValType,
typename Op>
1800 if (llvm::cast<ValType>(srcType).getWidth() >=
1801 llvm::cast<ValType>(dstType).getWidth())
1803 << dstType <<
" must be wider than operand type " << srcType;
1809template <
typename ValType,
typename Op>
1814 if (llvm::cast<ValType>(srcType).getWidth() <=
1815 llvm::cast<ValType>(dstType).getWidth())
1817 << dstType <<
" must be shorter than operand type " << srcType;
1823template <
template <
typename>
class WidthComparator,
typename... ElementTypes>
1828 auto srcType =
getTypeIfLike<ElementTypes...>(inputs.front());
1829 auto dstType =
getTypeIfLike<ElementTypes...>(outputs.front());
1830 if (!srcType || !dstType)
1833 return WidthComparator<unsigned>()(dstType.getIntOrFloatBitWidth(),
1834 srcType.getIntOrFloatBitWidth());
1839static FailureOr<APFloat>
1841 const llvm::fltSemantics &targetSemantics,
1845 using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
1846 if (sourceValue.isInfinity() &&
1847 (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
1848 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
1850 if (sourceValue.isNaN() &&
1851 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
1854 bool losesInfo =
false;
1855 auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
1856 if (losesInfo || status != APFloat::opOK)
1866OpFoldResult arith::ExtUIOp::fold(FoldAdaptor adaptor) {
1867 if (
auto lhs = getIn().getDefiningOp<ExtUIOp>()) {
1870 setNonNeg(
lhs.getNonNeg());
1871 getInMutable().assign(
lhs.getIn());
1876 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1878 adaptor.getOperands(),
getType(),
1879 [bitWidth](
const APInt &a,
bool &castStatus) {
1880 return a.zext(bitWidth);
1888LogicalResult arith::ExtUIOp::verify() {
1896OpFoldResult arith::ExtSIOp::fold(FoldAdaptor adaptor) {
1897 if (
auto lhs = getIn().getDefiningOp<ExtSIOp>()) {
1898 getInMutable().assign(
lhs.getIn());
1903 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1905 adaptor.getOperands(),
getType(),
1906 [bitWidth](
const APInt &a,
bool &castStatus) {
1907 return a.sext(bitWidth);
1915void arith::ExtSIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1916 MLIRContext *context) {
1917 patterns.
add<ExtSIOfExtUI>(context);
1920LogicalResult arith::ExtSIOp::verify() {
1930OpFoldResult arith::ExtFOp::fold(FoldAdaptor adaptor) {
1931 if (
auto truncFOp = getOperand().getDefiningOp<TruncFOp>()) {
1932 if (truncFOp.getOperand().getType() ==
getType()) {
1933 arith::FastMathFlags truncFMF =
1934 truncFOp.getFastmath().value_or(arith::FastMathFlags::none);
1935 bool isTruncContract =
1936 bitEnumContainsAll(truncFMF, arith::FastMathFlags::contract);
1937 arith::FastMathFlags extFMF =
1938 getFastmath().value_or(arith::FastMathFlags::none);
1939 bool isExtContract =
1940 bitEnumContainsAll(extFMF, arith::FastMathFlags::contract);
1941 if (isTruncContract && isExtContract) {
1942 return truncFOp.getOperand();
1948 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
1950 adaptor.getOperands(),
getType(),
1951 [&targetSemantics](
const APFloat &a,
bool &castStatus) {
1976 function_ref<std::optional<APFloat>(
const APFloat &,
const APFloat &)>
1979 if (isa_and_nonnull<ub::PoisonAttr>(inAttr))
1981 if (isa_and_nonnull<ub::PoisonAttr>(scaleAttr))
1984 if (!inAttr || !scaleAttr || !resultType)
1987 if (
auto inFloat = dyn_cast<FloatAttr>(inAttr)) {
1988 auto scaleFloat = dyn_cast<FloatAttr>(scaleAttr);
1991 std::optional<APFloat>
result =
1992 calculate(inFloat.getValue(), scaleFloat.getValue());
1995 return FloatAttr::get(resultType, *
result);
1998 auto inElements = dyn_cast<DenseFPElementsAttr>(inAttr);
1999 auto scaleElements = dyn_cast<DenseFPElementsAttr>(scaleAttr);
2000 auto shapedResultType = dyn_cast<ShapedType>(resultType);
2001 if (!inElements || !scaleElements || !shapedResultType ||
2002 !shapedResultType.hasStaticShape() ||
2003 inElements.getNumElements() != scaleElements.getNumElements())
2007 if (inElements.isSplat() && scaleElements.isSplat()) {
2008 std::optional<APFloat>
result =
2009 calculate(inElements.getSplatValue<APFloat>(),
2010 scaleElements.getSplatValue<APFloat>());
2017 results.reserve(inElements.getNumElements());
2018 for (
const auto &[in, scale] : llvm::zip_equal(inElements, scaleElements)) {
2019 std::optional<APFloat>
result = calculate(in, scale);
2022 results.push_back(*
result);
2035OpFoldResult arith::ScalingExtFOp::fold(FoldAdaptor adaptor) {
2043 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2045 adaptor.getIn(), adaptor.getScale(),
getType(),
2046 [&resSemantics](
const APFloat &in,
2047 const APFloat &scale) -> std::optional<APFloat> {
2051 return std::nullopt;
2058bool arith::ScalingExtFOp::areCastCompatible(
TypeRange inputs,
2063LogicalResult arith::ScalingExtFOp::verify() {
2071OpFoldResult arith::TruncIOp::fold(FoldAdaptor adaptor) {
2074 Value src = getOperand().getDefiningOp()->getOperand(0);
2079 if (llvm::cast<IntegerType>(srcType).getWidth() >
2080 llvm::cast<IntegerType>(dstType).getWidth()) {
2087 if (srcType == dstType)
2092 if (
auto truncOp = getOperand().getDefiningOp<arith::TruncIOp>()) {
2094 setOverflowFlags(getOverflowFlags() & truncOp.getOverflowFlags());
2095 setOperand(truncOp.getIn());
2100 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2102 adaptor.getOperands(),
getType(),
2103 [bitWidth](
const APInt &a,
bool &castStatus) {
2104 return a.trunc(bitWidth);
2112void arith::TruncIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2113 MLIRContext *context) {
2114 patterns.
add<NarrowExtremum<TruncIOp, ExtSIOp, MaxSIOp>,
2115 NarrowExtremum<TruncIOp, ExtSIOp, MinSIOp>,
2116 NarrowExtremum<TruncIOp, ExtUIOp, MaxUIOp>,
2117 NarrowExtremum<TruncIOp, ExtUIOp, MinUIOp>, TruncIExtSIToExtSI,
2118 TruncIExtUIToExtUI, TruncIShrSIToTrunciShrUI>(context);
2121LogicalResult arith::TruncIOp::verify() {
2131OpFoldResult arith::TruncFOp::fold(FoldAdaptor adaptor) {
2133 if (
auto extOp = getOperand().getDefiningOp<arith::ExtFOp>()) {
2134 Value src = extOp.getIn();
2136 auto intermediateType =
2140 if (llvm::APFloatBase::isLosslesslyConvertibleTo(
2141 srcType.getFloatSemantics(),
2142 intermediateType.getFloatSemantics())) {
2144 if (srcType.getWidth() > resElemType.getWidth()) {
2150 if (srcType == resElemType)
2155 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2157 adaptor.getOperands(),
getType(),
2158 [
this, &targetSemantics](
const APFloat &a,
bool &castStatus) {
2159 llvm::RoundingMode llvmRoundingMode =
2161 FailureOr<APFloat>
result =
2171void arith::TruncFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2172 MLIRContext *context) {
2173 patterns.
add<NarrowExtremum<TruncFOp, ExtFOp, MaximumFOp>,
2174 NarrowExtremum<TruncFOp, ExtFOp, MaxNumFOp>,
2175 NarrowExtremum<TruncFOp, ExtFOp, MaximumNumFOp>,
2176 NarrowExtremum<TruncFOp, ExtFOp, MinimumFOp>,
2177 NarrowExtremum<TruncFOp, ExtFOp, MinNumFOp>,
2178 NarrowExtremum<TruncFOp, ExtFOp, MinimumNumFOp>,
2179 TruncFSIToFPToSIToFP, TruncFUIToFPToUIToFP>(context);
2186LogicalResult arith::TruncFOp::verify() {
2194OpFoldResult arith::ConvertFOp::fold(FoldAdaptor adaptor) {
2196 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2198 adaptor.getOperands(),
getType(),
2199 [
this, &targetSemantics](
const APFloat &a,
bool &castStatus) {
2200 llvm::RoundingMode llvmRoundingMode =
2202 FailureOr<APFloat>
result =
2217 if (!srcType || !dstType)
2219 return srcType != dstType &&
2223LogicalResult arith::ConvertFOp::verify() {
2226 if (srcType == dstType)
2227 return emitError(
"result element type ")
2228 << dstType <<
" must be different from operand element type "
2230 if (srcType.getWidth() != dstType.getWidth())
2231 return emitError(
"result element type ")
2232 << dstType <<
" must have the same bitwidth as operand element type "
2241OpFoldResult arith::ScalingTruncFOp::fold(FoldAdaptor adaptor) {
2250 const llvm::fltSemantics &inSemantics = inElemType.getFloatSemantics();
2251 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2252 llvm::RoundingMode roundingMode =
2255 adaptor.getIn(), adaptor.getScale(),
getType(),
2256 [&](
const APFloat &in,
const APFloat &scale) -> std::optional<APFloat> {
2259 return std::nullopt;
2260 APFloat quotient(in);
2262 FailureOr<APFloat>
result =
2265 return std::nullopt;
2270bool arith::ScalingTruncFOp::areCastCompatible(
TypeRange inputs,
2275LogicalResult arith::ScalingTruncFOp::verify() {
2283void arith::AndIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2284 MLIRContext *context) {
2285 patterns.
add<AndIAndIConstant, AndOfExtUI, AndOfExtSI>(context);
2292void arith::OrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2293 MLIRContext *context) {
2294 patterns.
add<OrIOrIConstant, OrOfExtUI, OrOfExtSI>(context);
2301template <
typename From,
typename To>
2309 return srcType && dstType;
2320OpFoldResult arith::UIToFPOp::fold(FoldAdaptor adaptor) {
2323 adaptor.getOperands(),
getType(),
2324 [&resEleType](
const APInt &a,
bool &castStatus) {
2325 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2326 APFloat apf(floatTy.getFloatSemantics(),
2327 APInt::getZero(floatTy.getWidth()));
2328 apf.convertFromAPInt(a,
false,
2329 APFloat::rmNearestTiesToEven);
2334void arith::UIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2335 MLIRContext *context) {
2336 patterns.
add<UIToFPOfExtUI>(context);
2347OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
2350 adaptor.getOperands(),
getType(),
2351 [&resEleType](
const APInt &a,
bool &castStatus) {
2352 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2353 APFloat apf(floatTy.getFloatSemantics(),
2354 APInt::getZero(floatTy.getWidth()));
2355 if (a.isNegative() &&
2356 !APFloat::semanticsHasSignedRepr(floatTy.getFloatSemantics())) {
2360 apf.convertFromAPInt(a,
true,
2361 APFloat::rmNearestTiesToEven);
2366void arith::SIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2367 MLIRContext *context) {
2368 patterns.
add<SIToFPOfExtSI, SIToFPOfExtUI>(context);
2379OpFoldResult arith::FPToUIOp::fold(FoldAdaptor adaptor) {
2381 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2383 adaptor.getOperands(),
getType(),
2384 [&bitWidth](
const APFloat &a,
bool &castStatus) {
2386 APSInt api(bitWidth,
true);
2387 castStatus = APFloat::opInvalidOp !=
2388 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2401OpFoldResult arith::FPToSIOp::fold(FoldAdaptor adaptor) {
2403 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2405 adaptor.getOperands(),
getType(),
2406 [&bitWidth](
const APFloat &a,
bool &castStatus) {
2408 APSInt api(bitWidth,
false);
2409 castStatus = APFloat::opInvalidOp !=
2410 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2424 return intTy.getWidth();
2425 return IndexType::kInternalStorageBitWidth;
2434 if (!srcType || !dstType)
2438 (srcType.isSignlessInteger() && dstType.
isIndex());
2441bool arith::IndexCastOp::areCastCompatible(
TypeRange inputs,
2446OpFoldResult arith::IndexCastOp::fold(FoldAdaptor adaptor) {
2448 unsigned resultBitwidth = 64;
2450 resultBitwidth = intTy.getWidth();
2453 adaptor.getOperands(),
getType(),
2454 [resultBitwidth](
const APInt &a,
bool & ) {
2455 return a.sextOrTrunc(resultBitwidth);
2462 if (
auto inner = getOperand().getDefiningOp<arith::IndexCastOp>()) {
2463 Value x = inner.getOperand();
2472void arith::IndexCastOp::getCanonicalizationPatterns(
2473 RewritePatternSet &patterns, MLIRContext *context) {
2474 patterns.
add<IndexCastOfExtSI>(context);
2481bool arith::IndexCastUIOp::areCastCompatible(
TypeRange inputs,
2486OpFoldResult arith::IndexCastUIOp::fold(FoldAdaptor adaptor) {
2488 unsigned resultBitwidth = 64;
2490 resultBitwidth = intTy.getWidth();
2493 adaptor.getOperands(),
getType(),
2494 [resultBitwidth](
const APInt &a,
bool & ) {
2495 return a.zextOrTrunc(resultBitwidth);
2502 if (
auto inner = getOperand().getDefiningOp<arith::IndexCastUIOp>()) {
2503 Value x = inner.getOperand();
2512void arith::IndexCastUIOp::getCanonicalizationPatterns(
2513 RewritePatternSet &patterns, MLIRContext *context) {
2514 patterns.
add<IndexCastUIOfExtUI>(context);
2527 if (!srcType || !dstType)
2533OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
2535 auto operand = adaptor.getIn();
2540 if (
auto denseAttr = dyn_cast_or_null<DenseElementsAttr>(operand))
2541 return denseAttr.bitcast(llvm::cast<ShapedType>(resType).
getElementType());
2543 if (llvm::isa<ShapedType>(resType))
2551 if (!llvm::isa<FloatAttr, IntegerAttr>(operand))
2554 APInt bits = llvm::isa<FloatAttr>(operand)
2555 ? llvm::cast<FloatAttr>(operand).getValue().bitcastToAPInt()
2556 : llvm::cast<IntegerAttr>(operand).getValue();
2558 "trying to fold on broken IR: operands have incompatible types");
2560 if (
auto resFloatType = dyn_cast<FloatType>(resType))
2561 return FloatAttr::get(resType,
2562 APFloat(resFloatType.getFloatSemantics(), bits));
2563 return IntegerAttr::get(resType, bits);
2566void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2567 MLIRContext *context) {
2568 patterns.
add<BitcastOfBitcast>(context);
2578 const APInt &lhs,
const APInt &rhs) {
2579 switch (predicate) {
2580 case arith::CmpIPredicate::eq:
2582 case arith::CmpIPredicate::ne:
2584 case arith::CmpIPredicate::slt:
2585 return lhs.slt(rhs);
2586 case arith::CmpIPredicate::sle:
2587 return lhs.sle(rhs);
2588 case arith::CmpIPredicate::sgt:
2589 return lhs.sgt(rhs);
2590 case arith::CmpIPredicate::sge:
2591 return lhs.sge(rhs);
2592 case arith::CmpIPredicate::ult:
2593 return lhs.ult(rhs);
2594 case arith::CmpIPredicate::ule:
2595 return lhs.ule(rhs);
2596 case arith::CmpIPredicate::ugt:
2597 return lhs.ugt(rhs);
2598 case arith::CmpIPredicate::uge:
2599 return lhs.uge(rhs);
2601 llvm_unreachable(
"unknown cmpi predicate kind");
2606 switch (predicate) {
2607 case arith::CmpIPredicate::eq:
2608 case arith::CmpIPredicate::sle:
2609 case arith::CmpIPredicate::sge:
2610 case arith::CmpIPredicate::ule:
2611 case arith::CmpIPredicate::uge:
2613 case arith::CmpIPredicate::ne:
2614 case arith::CmpIPredicate::slt:
2615 case arith::CmpIPredicate::sgt:
2616 case arith::CmpIPredicate::ult:
2617 case arith::CmpIPredicate::ugt:
2620 llvm_unreachable(
"unknown cmpi predicate kind");
2624 if (
auto intType = dyn_cast<IntegerType>(t)) {
2625 return intType.getWidth();
2627 if (
auto vectorIntType = dyn_cast<VectorType>(t)) {
2628 return llvm::cast<IntegerType>(vectorIntType.getElementType()).getWidth();
2630 return std::nullopt;
2633OpFoldResult arith::CmpIOp::fold(FoldAdaptor adaptor) {
2635 if (getLhs() == getRhs()) {
2641 if (
auto extOp = getLhs().getDefiningOp<ExtSIOp>()) {
2643 std::optional<int64_t> integerWidth =
2645 if (integerWidth && integerWidth.value() == 1 &&
2646 getPredicate() == arith::CmpIPredicate::ne)
2647 return extOp.getOperand();
2649 if (
auto extOp = getLhs().getDefiningOp<ExtUIOp>()) {
2651 std::optional<int64_t> integerWidth =
2653 if (integerWidth && integerWidth.value() == 1 &&
2654 getPredicate() == arith::CmpIPredicate::ne)
2655 return extOp.getOperand();
2660 getPredicate() == arith::CmpIPredicate::ne)
2667 getPredicate() == arith::CmpIPredicate::eq)
2672 if (adaptor.getLhs() && !adaptor.getRhs()) {
2674 using Pred = CmpIPredicate;
2675 const std::pair<Pred, Pred> invPreds[] = {
2676 {Pred::slt, Pred::sgt}, {Pred::sgt, Pred::slt}, {Pred::sle, Pred::sge},
2677 {Pred::sge, Pred::sle}, {Pred::ult, Pred::ugt}, {Pred::ugt, Pred::ult},
2678 {Pred::ule, Pred::uge}, {Pred::uge, Pred::ule}, {Pred::eq, Pred::eq},
2679 {Pred::ne, Pred::ne},
2681 Pred origPred = getPredicate();
2682 for (
auto pred : invPreds) {
2683 if (origPred == pred.first) {
2684 setPredicate(pred.second);
2685 Value
lhs = getLhs();
2686 Value
rhs = getRhs();
2687 getLhsMutable().assign(
rhs);
2688 getRhsMutable().assign(
lhs);
2692 llvm_unreachable(
"unknown cmpi predicate kind");
2697 if (
auto lhs = dyn_cast_if_present<TypedAttr>(adaptor.getLhs())) {
2700 [pred = getPredicate()](
const APInt &
lhs,
const APInt &
rhs) {
2709void arith::CmpIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2710 MLIRContext *context) {
2711 patterns.
insert<CmpIExtSI, CmpIExtUI>(context);
2721 const APFloat &lhs,
const APFloat &rhs) {
2722 auto cmpResult = lhs.compare(rhs);
2723 switch (predicate) {
2724 case arith::CmpFPredicate::AlwaysFalse:
2726 case arith::CmpFPredicate::OEQ:
2727 return cmpResult == APFloat::cmpEqual;
2728 case arith::CmpFPredicate::OGT:
2729 return cmpResult == APFloat::cmpGreaterThan;
2730 case arith::CmpFPredicate::OGE:
2731 return cmpResult == APFloat::cmpGreaterThan ||
2732 cmpResult == APFloat::cmpEqual;
2733 case arith::CmpFPredicate::OLT:
2734 return cmpResult == APFloat::cmpLessThan;
2735 case arith::CmpFPredicate::OLE:
2736 return cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2737 case arith::CmpFPredicate::ONE:
2738 return cmpResult != APFloat::cmpUnordered && cmpResult != APFloat::cmpEqual;
2739 case arith::CmpFPredicate::ORD:
2740 return cmpResult != APFloat::cmpUnordered;
2741 case arith::CmpFPredicate::UEQ:
2742 return cmpResult == APFloat::cmpUnordered || cmpResult == APFloat::cmpEqual;
2743 case arith::CmpFPredicate::UGT:
2744 return cmpResult == APFloat::cmpUnordered ||
2745 cmpResult == APFloat::cmpGreaterThan;
2746 case arith::CmpFPredicate::UGE:
2747 return cmpResult == APFloat::cmpUnordered ||
2748 cmpResult == APFloat::cmpGreaterThan ||
2749 cmpResult == APFloat::cmpEqual;
2750 case arith::CmpFPredicate::ULT:
2751 return cmpResult == APFloat::cmpUnordered ||
2752 cmpResult == APFloat::cmpLessThan;
2753 case arith::CmpFPredicate::ULE:
2754 return cmpResult == APFloat::cmpUnordered ||
2755 cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2756 case arith::CmpFPredicate::UNE:
2757 return cmpResult != APFloat::cmpEqual;
2758 case arith::CmpFPredicate::UNO:
2759 return cmpResult == APFloat::cmpUnordered;
2760 case arith::CmpFPredicate::AlwaysTrue:
2763 llvm_unreachable(
"unknown cmpf predicate kind");
2767 auto lhs = dyn_cast_if_present<FloatAttr>(adaptor.getLhs());
2768 auto rhs = dyn_cast_if_present<FloatAttr>(adaptor.getRhs());
2771 if (lhs && lhs.getValue().isNaN())
2773 if (rhs && rhs.getValue().isNaN())
2784class CmpFIntToFPConst final :
public OpRewritePattern<CmpFOp> {
2788 static CmpIPredicate convertToIntegerPredicate(CmpFPredicate pred,
2790 using namespace arith;
2792 case CmpFPredicate::UEQ:
2793 case CmpFPredicate::OEQ:
2794 return CmpIPredicate::eq;
2795 case CmpFPredicate::UGT:
2796 case CmpFPredicate::OGT:
2797 return isUnsigned ? CmpIPredicate::ugt : CmpIPredicate::sgt;
2798 case CmpFPredicate::UGE:
2799 case CmpFPredicate::OGE:
2800 return isUnsigned ? CmpIPredicate::uge : CmpIPredicate::sge;
2801 case CmpFPredicate::ULT:
2802 case CmpFPredicate::OLT:
2803 return isUnsigned ? CmpIPredicate::ult : CmpIPredicate::slt;
2804 case CmpFPredicate::ULE:
2805 case CmpFPredicate::OLE:
2806 return isUnsigned ? CmpIPredicate::ule : CmpIPredicate::sle;
2807 case CmpFPredicate::UNE:
2808 case CmpFPredicate::ONE:
2809 return CmpIPredicate::ne;
2811 llvm_unreachable(
"Unexpected predicate!");
2815 LogicalResult matchAndRewrite(CmpFOp op,
2816 PatternRewriter &rewriter)
const override {
2821 const APFloat &
rhs = flt.getValue();
2829 FloatType floatTy = llvm::cast<FloatType>(op.getRhs().getType());
2830 int mantissaWidth = floatTy.getFPMantissaWidth();
2831 if (mantissaWidth <= 0)
2837 if (
auto si = op.getLhs().getDefiningOp<SIToFPOp>()) {
2839 intVal = si.getIn();
2840 }
else if (
auto ui = op.getLhs().getDefiningOp<UIToFPOp>()) {
2842 intVal = ui.getIn();
2849 auto intTy = llvm::cast<IntegerType>(intVal.
getType());
2850 auto intWidth = intTy.getWidth();
2853 auto valueBits = isUnsigned ? intWidth : (intWidth - 1);
2855 const llvm::fltSemantics &semantics = floatTy.getFloatSemantics();
2859 if (!APFloat::semanticsHasZero(semantics))
2864 if (!isUnsigned && !APFloat::semanticsHasSignedRepr(semantics))
2867 if (!APFloat::semanticsHasInf(semantics)) {
2878 if (APFloat::semanticsMaxExponent(semantics) <
2879 static_cast<int>(valueBits))
2886 if ((
int)intWidth > mantissaWidth) {
2888 int exponent = ilogb(
rhs);
2889 if (exponent == APFloat::IEK_Inf) {
2890 int maxExponent = ilogb(APFloat::getLargest(
rhs.getSemantics()));
2891 if (maxExponent <
static_cast<int>(valueBits)) {
2899 if (mantissaWidth <= exponent && exponent <= (
int)valueBits) {
2908 switch (op.getPredicate()) {
2909 case CmpFPredicate::AlwaysTrue:
2910 case CmpFPredicate::ORD:
2915 case CmpFPredicate::AlwaysFalse:
2916 case CmpFPredicate::UNO:
2922 pred = convertToIntegerPredicate(op.getPredicate(), isUnsigned);
2929 APFloat signedMax(
rhs.getSemantics());
2930 signedMax.convertFromAPInt(APInt::getSignedMaxValue(intWidth),
true,
2931 APFloat::rmNearestTiesToEven);
2932 if (signedMax <
rhs) {
2933 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::slt ||
2934 pred == CmpIPredicate::sle)
2945 APFloat unsignedMax(
rhs.getSemantics());
2946 unsignedMax.convertFromAPInt(APInt::getMaxValue(intWidth),
false,
2947 APFloat::rmNearestTiesToEven);
2948 if (unsignedMax <
rhs) {
2949 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ult ||
2950 pred == CmpIPredicate::ule)
2962 APFloat signedMin(
rhs.getSemantics());
2963 signedMin.convertFromAPInt(APInt::getSignedMinValue(intWidth),
true,
2964 APFloat::rmNearestTiesToEven);
2965 if (signedMin >
rhs) {
2966 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::sgt ||
2967 pred == CmpIPredicate::sge)
2977 APFloat unsignedMin(
rhs.getSemantics());
2978 unsignedMin.convertFromAPInt(APInt::getMinValue(intWidth),
false,
2979 APFloat::rmNearestTiesToEven);
2980 if (unsignedMin >
rhs) {
2981 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ugt ||
2982 pred == CmpIPredicate::uge)
2997 APSInt rhsInt(intWidth, isUnsigned);
2998 if (APFloat::opInvalidOp ==
2999 rhs.convertToInteger(rhsInt, APFloat::rmTowardZero, &ignored)) {
3005 if (!
rhs.isZero()) {
3006 APFloat apf(floatTy.getFloatSemantics(),
3007 APInt::getZero(floatTy.getWidth()));
3008 apf.convertFromAPInt(rhsInt, !isUnsigned, APFloat::rmNearestTiesToEven);
3010 bool equal = apf ==
rhs;
3016 case CmpIPredicate::ne:
3020 case CmpIPredicate::eq:
3024 case CmpIPredicate::ule:
3027 if (
rhs.isNegative()) {
3033 case CmpIPredicate::sle:
3036 if (
rhs.isNegative())
3037 pred = CmpIPredicate::slt;
3039 case CmpIPredicate::ult:
3042 if (
rhs.isNegative()) {
3047 pred = CmpIPredicate::ule;
3049 case CmpIPredicate::slt:
3052 if (!
rhs.isNegative())
3053 pred = CmpIPredicate::sle;
3055 case CmpIPredicate::ugt:
3058 if (
rhs.isNegative()) {
3064 case CmpIPredicate::sgt:
3067 if (
rhs.isNegative())
3068 pred = CmpIPredicate::sge;
3070 case CmpIPredicate::uge:
3073 if (
rhs.isNegative()) {
3078 pred = CmpIPredicate::ugt;
3080 case CmpIPredicate::sge:
3083 if (!
rhs.isNegative())
3084 pred = CmpIPredicate::sgt;
3094 ConstantOp::create(rewriter, op.getLoc(), intVal.getType(),
3101void arith::CmpFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
3102 MLIRContext *context) {
3103 patterns.
insert<CmpFIntToFPConst>(context);
3112struct SelectToExtUI :
public OpRewritePattern<arith::SelectOp> {
3115 LogicalResult matchAndRewrite(arith::SelectOp op,
3116 PatternRewriter &rewriter)
const override {
3118 if (!llvm::isa<IntegerType>(op.getType()) || op.getType().isInteger(1))
3134 arith::XOrIOp::create(
3135 rewriter, op.getLoc(), op.getCondition(),
3137 op.getCondition().getType(), 1)));
3146void arith::SelectOp::getCanonicalizationPatterns(RewritePatternSet &results,
3147 MLIRContext *context) {
3148 results.
add<RedundantSelectFalse, RedundantSelectTrue, SelectNotCond,
3149 SelectI1ToNot, SelectCmpISgeToMaxSI, SelectCmpISgeToMinSI,
3150 SelectCmpISgtToMaxSI, SelectCmpISgtToMinSI, SelectCmpISleToMaxSI,
3151 SelectCmpISleToMinSI, SelectCmpISltToMaxSI, SelectCmpISltToMinSI,
3152 SelectCmpIUgeToMaxUI, SelectCmpIUgeToMinUI, SelectCmpIUgtToMaxUI,
3153 SelectCmpIUgtToMinUI, SelectCmpIUleToMaxUI, SelectCmpIUleToMinUI,
3154 SelectCmpIUltToMaxUI, SelectCmpIUltToMinUI, SelectToExtUI>(
3158OpFoldResult arith::SelectOp::fold(FoldAdaptor adaptor) {
3159 Value trueVal = getTrueValue();
3160 Value falseVal = getFalseValue();
3161 if (trueVal == falseVal)
3164 Value condition = getCondition();
3182 if (
getType().isSignlessInteger(1) &&
3188 auto pred = cmp.getPredicate();
3189 if (pred == arith::CmpIPredicate::eq || pred == arith::CmpIPredicate::ne) {
3190 auto cmpLhs = cmp.getLhs();
3191 auto cmpRhs = cmp.getRhs();
3199 if ((cmpLhs == trueVal && cmpRhs == falseVal) ||
3200 (cmpRhs == trueVal && cmpLhs == falseVal))
3201 return pred == arith::CmpIPredicate::ne ? trueVal : falseVal;
3208 dyn_cast_if_present<DenseElementsAttr>(adaptor.getCondition())) {
3210 assert(cond.getType().hasStaticShape() &&
3211 "DenseElementsAttr must have static shape");
3213 dyn_cast_if_present<DenseElementsAttr>(adaptor.getTrueValue())) {
3215 dyn_cast_if_present<DenseElementsAttr>(adaptor.getFalseValue())) {
3216 SmallVector<Attribute> results;
3217 results.reserve(
static_cast<size_t>(cond.getNumElements()));
3218 auto condVals = llvm::make_range(cond.value_begin<BoolAttr>(),
3219 cond.value_end<BoolAttr>());
3220 auto lhsVals = llvm::make_range(
lhs.value_begin<Attribute>(),
3221 lhs.value_end<Attribute>());
3222 auto rhsVals = llvm::make_range(
rhs.value_begin<Attribute>(),
3223 rhs.value_end<Attribute>());
3225 for (
auto [condVal, lhsVal, rhsVal] :
3226 llvm::zip_equal(condVals, lhsVals, rhsVals))
3227 results.push_back(condVal.getValue() ? lhsVal : rhsVal);
3237ParseResult SelectOp::parse(OpAsmParser &parser, OperationState &
result) {
3238 Type conditionType, resultType;
3239 SmallVector<OpAsmParser::UnresolvedOperand, 3> operands;
3247 conditionType = resultType;
3254 result.addTypes(resultType);
3256 {conditionType, resultType, resultType},
3260void arith::SelectOp::print(OpAsmPrinter &p) {
3261 p <<
" " << getOperands();
3264 if (ShapedType condType = dyn_cast<ShapedType>(getCondition().
getType()))
3265 p << condType <<
", ";
3269LogicalResult arith::SelectOp::verify() {
3270 Type conditionType = getCondition().getType();
3277 if (!llvm::isa<TensorType, VectorType>(resultType))
3278 return emitOpError() <<
"expected condition to be a signless i1, but got "
3281 if (conditionType != shapedConditionType) {
3282 return emitOpError() <<
"expected condition type to have the same shape "
3283 "as the result type, expected "
3284 << shapedConditionType <<
", but got "
3293OpFoldResult arith::ShLIOp::fold(FoldAdaptor adaptor) {
3307 bool bounded =
false;
3309 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3310 bounded = b.ult(b.getBitWidth());
3313 return bounded ?
result : Attribute();
3320OpFoldResult arith::ShRUIOp::fold(FoldAdaptor adaptor) {
3335 if (getLhs() == getRhs())
3338 bool bounded =
false;
3340 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3341 bounded = b.ult(b.getBitWidth());
3344 return bounded ?
result : Attribute();
3351OpFoldResult arith::ShRSIOp::fold(FoldAdaptor adaptor) {
3366 if (getLhs() == getRhs())
3374 bool bounded =
false;
3376 adaptor.getOperands(), [&](
const APInt &a,
const APInt &
b) {
3377 bounded = b.ult(b.getBitWidth());
3380 return bounded ?
result : Attribute();
3390 bool useOnlyFiniteValue) {
3392 case AtomicRMWKind::maximumf: {
3393 const llvm::fltSemantics &semantic =
3394 llvm::cast<FloatType>(resultType).getFloatSemantics();
3395 APFloat identity = useOnlyFiniteValue
3396 ? APFloat::getLargest(semantic,
true)
3397 : APFloat::getInf(semantic,
true);
3400 case AtomicRMWKind::maxnumf: {
3401 const llvm::fltSemantics &semantic =
3402 llvm::cast<FloatType>(resultType).getFloatSemantics();
3403 APFloat identity = useOnlyFiniteValue
3404 ? APFloat::getLargest(semantic,
true)
3405 : APFloat::getNaN(semantic,
true);
3408 case AtomicRMWKind::addf:
3409 case AtomicRMWKind::addi:
3410 case AtomicRMWKind::maxu:
3411 case AtomicRMWKind::ori:
3412 case AtomicRMWKind::xori:
3414 case AtomicRMWKind::andi:
3417 APInt::getAllOnes(llvm::cast<IntegerType>(resultType).getWidth()));
3418 case AtomicRMWKind::maxs:
3420 resultType, APInt::getSignedMinValue(
3421 llvm::cast<IntegerType>(resultType).getWidth()));
3422 case AtomicRMWKind::minimumf: {
3423 const llvm::fltSemantics &semantic =
3424 llvm::cast<FloatType>(resultType).getFloatSemantics();
3425 APFloat identity = useOnlyFiniteValue
3426 ? APFloat::getLargest(semantic,
false)
3427 : APFloat::getInf(semantic,
false);
3431 case AtomicRMWKind::minnumf: {
3432 const llvm::fltSemantics &semantic =
3433 llvm::cast<FloatType>(resultType).getFloatSemantics();
3434 APFloat identity = useOnlyFiniteValue
3435 ? APFloat::getLargest(semantic,
false)
3436 : APFloat::getNaN(semantic,
false);
3439 case AtomicRMWKind::mins:
3441 resultType, APInt::getSignedMaxValue(
3442 llvm::cast<IntegerType>(resultType).getWidth()));
3443 case AtomicRMWKind::minu:
3446 APInt::getMaxValue(llvm::cast<IntegerType>(resultType).getWidth()));
3447 case AtomicRMWKind::muli:
3449 case AtomicRMWKind::mulf:
3452 case AtomicRMWKind::assign:
3461 std::optional<AtomicRMWKind> maybeKind =
3464 .Case([](arith::AddFOp op) {
return AtomicRMWKind::addf; })
3465 .Case([](arith::MulFOp op) {
return AtomicRMWKind::mulf; })
3466 .Case([](arith::MaximumFOp op) {
return AtomicRMWKind::maximumf; })
3467 .Case([](arith::MinimumFOp op) {
return AtomicRMWKind::minimumf; })
3468 .Case([](arith::MaxNumFOp op) {
return AtomicRMWKind::maxnumf; })
3469 .Case([](arith::MinNumFOp op) {
return AtomicRMWKind::minnumf; })
3471 .Case([](arith::AddIOp op) {
return AtomicRMWKind::addi; })
3472 .Case([](arith::OrIOp op) {
return AtomicRMWKind::ori; })
3473 .Case([](arith::XOrIOp op) {
return AtomicRMWKind::xori; })
3474 .Case([](arith::AndIOp op) {
return AtomicRMWKind::andi; })
3475 .Case([](arith::MaxUIOp op) {
return AtomicRMWKind::maxu; })
3476 .Case([](arith::MinUIOp op) {
return AtomicRMWKind::minu; })
3477 .Case([](arith::MaxSIOp op) {
return AtomicRMWKind::maxs; })
3478 .Case([](arith::MinSIOp op) {
return AtomicRMWKind::mins; })
3479 .Case([](arith::MulIOp op) {
return AtomicRMWKind::muli; })
3480 .Default(std::nullopt);
3482 return std::nullopt;
3485 bool useOnlyFiniteValue =
false;
3486 auto fmfOpInterface = dyn_cast<ArithFastMathInterface>(op);
3487 if (fmfOpInterface) {
3488 arith::FastMathFlagsAttr fmfAttr = fmfOpInterface.getFastMathFlagsAttr();
3489 useOnlyFiniteValue =
3490 bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::ninf);
3491 if (*maybeKind == AtomicRMWKind::maxnumf ||
3492 *maybeKind == AtomicRMWKind::minnumf) {
3493 if (bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::nnan)) {
3494 *maybeKind = *maybeKind == AtomicRMWKind::maxnumf
3495 ? AtomicRMWKind::maximumf
3496 : AtomicRMWKind::minimumf;
3498 useOnlyFiniteValue =
false;
3508 useOnlyFiniteValue);
3514 bool useOnlyFiniteValue) {
3516 useOnlyFiniteValue))
3517 return arith::ConstantOp::create(builder, loc, attr);
3526 case AtomicRMWKind::addf:
3527 return arith::AddFOp::create(builder, loc, lhs, rhs);
3528 case AtomicRMWKind::addi:
3529 return arith::AddIOp::create(builder, loc, lhs, rhs);
3530 case AtomicRMWKind::mulf:
3531 return arith::MulFOp::create(builder, loc, lhs, rhs);
3532 case AtomicRMWKind::muli:
3533 return arith::MulIOp::create(builder, loc, lhs, rhs);
3534 case AtomicRMWKind::maximumf:
3535 return arith::MaximumFOp::create(builder, loc, lhs, rhs);
3536 case AtomicRMWKind::minimumf:
3537 return arith::MinimumFOp::create(builder, loc, lhs, rhs);
3538 case AtomicRMWKind::maxnumf:
3539 return arith::MaxNumFOp::create(builder, loc, lhs, rhs);
3540 case AtomicRMWKind::minnumf:
3541 return arith::MinNumFOp::create(builder, loc, lhs, rhs);
3542 case AtomicRMWKind::maxs:
3543 return arith::MaxSIOp::create(builder, loc, lhs, rhs);
3544 case AtomicRMWKind::mins:
3545 return arith::MinSIOp::create(builder, loc, lhs, rhs);
3546 case AtomicRMWKind::maxu:
3547 return arith::MaxUIOp::create(builder, loc, lhs, rhs);
3548 case AtomicRMWKind::minu:
3549 return arith::MinUIOp::create(builder, loc, lhs, rhs);
3550 case AtomicRMWKind::ori:
3551 return arith::OrIOp::create(builder, loc, lhs, rhs);
3552 case AtomicRMWKind::andi:
3553 return arith::AndIOp::create(builder, loc, lhs, rhs);
3554 case AtomicRMWKind::xori:
3555 return arith::XOrIOp::create(builder, loc, lhs, rhs);
3557 case AtomicRMWKind::assign:
3568#define GET_OP_CLASSES
3569#include "mlir/Dialect/Arith/IR/ArithOps.cpp.inc"
3575#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,...
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)
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...
This represents an operation in an abstracted form, suitable for use with the builder APIs.