MLIR 24.0.0git
ArithOps.cpp
Go to the documentation of this file.
1//===- ArithOps.cpp - MLIR Arith dialect ops implementation -----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <cassert>
10#include <cstdint>
11#include <functional>
12#include <type_traits>
13#include <utility>
14
18#include "mlir/IR/Builders.h"
21#include "mlir/IR/Matchers.h"
26
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"
34
35using namespace mlir;
36using namespace mlir::arith;
37
38/// Default rounding mode according to default LLVM floating-point environment.
39static constexpr llvm::RoundingMode kDefaultRoundingMode =
40 llvm::RoundingMode::NearestTiesToEven;
41
42//===----------------------------------------------------------------------===//
43// Pattern helpers
44//===----------------------------------------------------------------------===//
45
46static IntegerAttr
48 Attribute rhs,
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);
54}
55
56static IntegerAttr addIntegerAttrs(PatternRewriter &builder, Value res,
57 Attribute lhs, Attribute rhs) {
58 return applyToIntegerAttrs(builder, res, lhs, rhs, std::plus<APInt>());
59}
60
61static IntegerAttr subIntegerAttrs(PatternRewriter &builder, Value res,
62 Attribute lhs, Attribute rhs) {
63 return applyToIntegerAttrs(builder, res, lhs, rhs, std::minus<APInt>());
64}
65
66static IntegerAttr mulIntegerAttrs(PatternRewriter &builder, Value res,
67 Attribute lhs, Attribute rhs) {
68 return applyToIntegerAttrs(builder, res, lhs, rhs, std::multiplies<APInt>());
69}
70
71static IntegerAttr andIntegerAttrs(PatternRewriter &builder, Value res,
72 Attribute lhs, Attribute rhs) {
73 return applyToIntegerAttrs(builder, res, lhs, rhs, std::bit_and<APInt>());
74}
75
76static IntegerAttr orIntegerAttrs(PatternRewriter &builder, Value res,
77 Attribute lhs, Attribute rhs) {
78 return applyToIntegerAttrs(builder, res, lhs, rhs, std::bit_or<APInt>());
79}
80
81static IntegerAttr xorIntegerAttrs(PatternRewriter &builder, Value res,
82 Attribute lhs, Attribute rhs) {
83 return applyToIntegerAttrs(builder, res, lhs, rhs, std::bit_xor<APInt>());
84}
85
86// Merge overflow flags from 2 ops, selecting the most conservative combination.
87static IntegerOverflowFlagsAttr
88mergeOverflowFlags(IntegerOverflowFlagsAttr val1,
89 IntegerOverflowFlagsAttr val2) {
90 return IntegerOverflowFlagsAttr::get(val1.getContext(),
91 val1.getValue() & val2.getValue());
92}
93
94/// Invert an integer comparison predicate.
95arith::CmpIPredicate arith::invertPredicate(arith::CmpIPredicate pred) {
96 switch (pred) {
97 case arith::CmpIPredicate::eq:
98 return arith::CmpIPredicate::ne;
99 case arith::CmpIPredicate::ne:
100 return arith::CmpIPredicate::eq;
101 case arith::CmpIPredicate::slt:
102 return arith::CmpIPredicate::sge;
103 case arith::CmpIPredicate::sle:
104 return arith::CmpIPredicate::sgt;
105 case arith::CmpIPredicate::sgt:
106 return arith::CmpIPredicate::sle;
107 case arith::CmpIPredicate::sge:
108 return arith::CmpIPredicate::slt;
109 case arith::CmpIPredicate::ult:
110 return arith::CmpIPredicate::uge;
111 case arith::CmpIPredicate::ule:
112 return arith::CmpIPredicate::ugt;
113 case arith::CmpIPredicate::ugt:
114 return arith::CmpIPredicate::ule;
115 case arith::CmpIPredicate::uge:
116 return arith::CmpIPredicate::ult;
117 }
118 llvm_unreachable("unknown cmpi predicate kind");
119}
120
121/// Equivalent to
122/// convertRoundingModeToLLVM(convertArithRoundingModeToLLVM(roundingMode)).
123///
124/// Not possible to implement as chain of calls as this would introduce a
125/// circular dependency with MLIRArithAttrToLLVMConversion and make arith depend
126/// on the LLVM dialect and on translation to LLVM.
127static llvm::RoundingMode
128convertArithRoundingModeToLLVMIR(std::optional<RoundingMode> roundingMode) {
129 if (!roundingMode)
131 switch (*roundingMode) {
132 case RoundingMode::downward:
133 return llvm::RoundingMode::TowardNegative;
134 case RoundingMode::to_nearest_away:
135 return llvm::RoundingMode::NearestTiesToAway;
136 case RoundingMode::to_nearest_even:
137 return llvm::RoundingMode::NearestTiesToEven;
138 case RoundingMode::toward_zero:
139 return llvm::RoundingMode::TowardZero;
140 case RoundingMode::upward:
141 return llvm::RoundingMode::TowardPositive;
142 }
143 llvm_unreachable("Unhandled rounding mode");
144}
145
146static arith::CmpIPredicateAttr invertPredicate(arith::CmpIPredicateAttr pred) {
147 return arith::CmpIPredicateAttr::get(pred.getContext(),
148 invertPredicate(pred.getValue()));
149}
150
152 Type elemTy = getElementTypeOrSelf(type);
153 if (elemTy.isIntOrFloat())
154 return elemTy.getIntOrFloatBitWidth();
155
156 return -1;
157}
158
160 return getScalarOrElementWidth(value.getType());
161}
162
163static FailureOr<APInt> getIntOrSplatIntValue(Attribute attr) {
164 APInt value;
165 if (matchPattern(attr, m_ConstantInt(&value)))
166 return value;
167
168 return failure();
169}
170
171static Attribute getBoolAttribute(Type type, bool value) {
172 auto boolAttr = BoolAttr::get(type.getContext(), value);
173 ShapedType shapedType = dyn_cast_or_null<ShapedType>(type);
174 if (!shapedType)
175 return boolAttr;
176 // DenseElementsAttr requires a static shape.
177 if (!shapedType.hasStaticShape())
178 return {};
179 return DenseElementsAttr::get(shapedType, boolAttr);
180}
181
182/// Return a scalar or splat integer attribute of `type` (an integer/index type
183/// or a shaped type thereof) holding `value`. Returns a null attribute for
184/// shaped types with a dynamic shape, so callers can bail out of folding.
186 auto scalarAttr = IntegerAttr::get(getElementTypeOrSelf(type), value);
187 ShapedType shapedType = dyn_cast<ShapedType>(type);
188 if (!shapedType)
189 return scalarAttr;
190 if (!shapedType.hasStaticShape())
191 return {};
192 return DenseElementsAttr::get(shapedType, scalarAttr);
193}
194
195//===----------------------------------------------------------------------===//
196// TableGen'd canonicalization patterns
197//===----------------------------------------------------------------------===//
198
199namespace {
200#include "ArithCanonicalization.inc"
201} // namespace
202
203//===----------------------------------------------------------------------===//
204// Common helpers
205//===----------------------------------------------------------------------===//
206
207/// Return the type of the same shape (scalar, vector or tensor) containing i1.
209 auto i1Type = IntegerType::get(type.getContext(), 1);
210 if (auto shapedType = dyn_cast<ShapedType>(type))
211 return shapedType.cloneWith(std::nullopt, i1Type);
212 if (llvm::isa<UnrankedTensorType>(type))
213 return UnrankedTensorType::get(i1Type);
214 return i1Type;
215}
216
217//===----------------------------------------------------------------------===//
218// ConstantOp
219//===----------------------------------------------------------------------===//
220
221void arith::ConstantOp::getAsmResultNames(
222 function_ref<void(Value, StringRef)> setNameFn) {
223 auto type = getType();
224 if (auto intCst = dyn_cast<IntegerAttr>(getValue())) {
225 auto intType = dyn_cast<IntegerType>(type);
226
227 // Sugar i1 constants with 'true' and 'false'.
228 if (intType && intType.getWidth() == 1)
229 return setNameFn(getResult(), (intCst.getInt() ? "true" : "false"));
230
231 // Otherwise, build a complex name with the value and type.
232 SmallString<32> specialNameBuffer;
233 llvm::raw_svector_ostream specialName(specialNameBuffer);
234 specialName << 'c' << intCst.getValue();
235 if (intType)
236 specialName << '_' << type;
237 setNameFn(getResult(), specialName.str());
238 } else {
239 setNameFn(getResult(), "cst");
240 }
241}
242
243/// TODO: disallow arith.constant to return anything other than signless integer
244/// or float like.
245LogicalResult arith::ConstantOp::verify() {
246 auto type = getType();
247 // Integer values must be signless.
248 if (auto intType = dyn_cast<IntegerType>(getElementTypeOrSelf(type));
249 intType && !intType.isSignless())
250 return emitOpError("integer return type must be signless");
251 // Any float or elements attribute are acceptable.
252 if (!llvm::isa<IntegerAttr, FloatAttr, ElementsAttr>(getValue())) {
253 return emitOpError(
254 "value must be an integer, float, or elements attribute");
255 }
256
257 // Note, we could relax this for vectors with 1 scalable dim, e.g.:
258 // * arith.constant dense<[[3, 3], [1, 1]]> : vector<2 x [2] x i32>
259 // However, this would most likely require updating the lowerings to LLVM.
260 if (isa<ScalableVectorType>(type) && !isa<SplatElementsAttr>(getValue()))
261 return emitOpError(
262 "initializing scalable vectors with elements attribute is not supported"
263 " unless it's a vector splat");
264 return success();
265}
266
267bool arith::ConstantOp::isBuildableWith(Attribute value, Type type) {
268 // The value's type must be the same as the provided type.
269 auto typedAttr = dyn_cast<TypedAttr>(value);
270 if (!typedAttr || typedAttr.getType() != type)
271 return false;
272 // Integer values must be signless.
273 if (auto intType = dyn_cast<IntegerType>(getElementTypeOrSelf(type))) {
274 if (!intType.isSignless())
275 return false;
276 }
277 // Integer, float, and element attributes are buildable.
278 return llvm::isa<IntegerAttr, FloatAttr, ElementsAttr>(value);
279}
280
281ConstantOp arith::ConstantOp::materialize(OpBuilder &builder, Attribute value,
282 Type type, Location loc) {
283 if (isBuildableWith(value, type))
284 return arith::ConstantOp::create(builder, loc, cast<TypedAttr>(value));
285 return nullptr;
286}
287
288OpFoldResult arith::ConstantOp::fold(FoldAdaptor adaptor) { return getValue(); }
289
291 int64_t value, unsigned width) {
292 auto type = builder.getIntegerType(width);
293 arith::ConstantOp::build(builder, result, type,
294 builder.getIntegerAttr(type, value));
295}
296
298 Location location,
300 unsigned width) {
301 mlir::OperationState state(location, getOperationName());
302 build(builder, state, value, width);
303 auto result = dyn_cast<ConstantIntOp>(builder.create(state));
304 assert(result && "builder didn't return the right type");
305 return result;
306}
307
310 unsigned width) {
311 return create(builder, builder.getLoc(), value, width);
312}
313
315 Type type, int64_t value) {
316 arith::ConstantOp::build(builder, result, type,
317 builder.getIntegerAttr(type, value));
318}
319
321 Location location, Type type,
322 int64_t value) {
323 mlir::OperationState state(location, getOperationName());
324 build(builder, state, type, value);
325 auto result = dyn_cast<ConstantIntOp>(builder.create(state));
326 assert(result && "builder didn't return the right type");
327 return result;
328}
329
331 Type type, int64_t value) {
332 return create(builder, builder.getLoc(), type, value);
333}
334
336 Type type, const APInt &value) {
337 arith::ConstantOp::build(builder, result, type,
338 builder.getIntegerAttr(type, value));
339}
340
342 Location location, Type type,
343 const APInt &value) {
344 mlir::OperationState state(location, getOperationName());
345 build(builder, state, type, value);
346 auto result = dyn_cast<ConstantIntOp>(builder.create(state));
347 assert(result && "builder didn't return the right type");
348 return result;
349}
350
352 Type type,
353 const APInt &value) {
354 return create(builder, builder.getLoc(), type, value);
355}
356
358 if (auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
359 return constOp.getType().isSignlessInteger();
360 return false;
361}
362
364 FloatType type, const APFloat &value) {
365 arith::ConstantOp::build(builder, result, type,
366 builder.getFloatAttr(type, value));
367}
368
370 Location location,
371 FloatType type,
372 const APFloat &value) {
373 mlir::OperationState state(location, getOperationName());
374 build(builder, state, type, value);
375 auto result = dyn_cast<ConstantFloatOp>(builder.create(state));
376 assert(result && "builder didn't return the right type");
377 return result;
378}
379
382 const APFloat &value) {
383 return create(builder, builder.getLoc(), type, value);
384}
385
387 if (auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
388 return llvm::isa<FloatType>(constOp.getType());
389 return false;
390}
391
393 int64_t value) {
394 arith::ConstantOp::build(builder, result, builder.getIndexType(),
395 builder.getIndexAttr(value));
396}
397
399 Location location,
400 int64_t value) {
401 mlir::OperationState state(location, getOperationName());
402 build(builder, state, value);
403 auto result = dyn_cast<ConstantIndexOp>(builder.create(state));
404 assert(result && "builder didn't return the right type");
405 return result;
406}
407
410 return create(builder, builder.getLoc(), value);
411}
412
414 if (auto constOp = dyn_cast_or_null<arith::ConstantOp>(op))
415 return constOp.getType().isIndex();
416 return false;
417}
418
420 Type type) {
421 // TODO: Incorporate this check to `FloatAttr::get*`.
422 assert(!isa<Float8E8M0FNUType>(getElementTypeOrSelf(type)) &&
423 "type doesn't have a zero representation");
424 TypedAttr zeroAttr = builder.getZeroAttr(type);
425 assert(zeroAttr && "unsupported type for zero attribute");
426 return arith::ConstantOp::create(builder, loc, zeroAttr);
427}
428
429//===----------------------------------------------------------------------===//
430// AddIOp
431//===----------------------------------------------------------------------===//
432
433OpFoldResult arith::AddIOp::fold(FoldAdaptor adaptor) {
434 // addi(x, 0) -> x
435 if (matchPattern(adaptor.getRhs(), m_Zero()))
436 return getLhs();
437
438 // addi(subi(a, b), b) -> a
439 if (auto sub = getLhs().getDefiningOp<SubIOp>())
440 if (getRhs() == sub.getRhs())
441 return sub.getLhs();
442
443 // addi(b, subi(a, b)) -> a
444 if (auto sub = getRhs().getDefiningOp<SubIOp>())
445 if (getLhs() == sub.getRhs())
446 return sub.getLhs();
447
449 adaptor.getOperands(),
450 [](APInt a, const APInt &b) { return std::move(a) + b; });
451}
452
453void arith::AddIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
454 MLIRContext *context) {
455 patterns.add<AddIAddConstant, AddISubConstantRHS, AddISubConstantLHS,
456 AddIMulNegativeOneRhs, AddIMulNegativeOneLhs>(context);
457}
458
459//===----------------------------------------------------------------------===//
460// AddUIExtendedOp
461//===----------------------------------------------------------------------===//
462
463std::optional<SmallVector<int64_t, 4>>
464arith::AddUIExtendedOp::getShapeForUnroll() {
465 if (auto vt = dyn_cast<VectorType>(getType(0)))
466 return llvm::to_vector<4>(vt.getShape());
467 return std::nullopt;
468}
469
470// Returns the overflow bit, assuming that `sum` is the result of unsigned
471// addition of `operand` and another number.
472static APInt calculateUnsignedOverflow(const APInt &sum, const APInt &operand) {
473 return sum.ult(operand) ? APInt::getAllOnes(1) : APInt::getZero(1);
474}
475
476LogicalResult
477arith::AddUIExtendedOp::fold(FoldAdaptor adaptor,
478 SmallVectorImpl<OpFoldResult> &results) {
479 Type overflowTy = getOverflow().getType();
480 // addui_extended(x, 0) -> x, false
481 if (matchPattern(getRhs(), m_Zero())) {
482 Builder builder(getContext());
483 auto falseValue = builder.getZeroAttr(overflowTy);
484
485 results.push_back(getLhs());
486 results.push_back(falseValue);
487 return success();
488 }
489
490 // addui_extended(constant_a, constant_b) -> constant_sum, constant_carry
491 // Let the `constFoldBinaryOp` utility attempt to fold the sum of both
492 // operands. If that succeeds, calculate the overflow bit based on the sum
493 // and the first (constant) operand, `lhs`.
494 if (Attribute sumAttr = constFoldBinaryOp<IntegerAttr>(
495 adaptor.getOperands(),
496 [](APInt a, const APInt &b) { return std::move(a) + b; })) {
497 // If any operand is poison, propagate poison to both results.
498 if (matchPattern(sumAttr, ub::m_Poison())) {
499 results.push_back(sumAttr);
500 results.push_back(sumAttr);
501 return success();
502 }
503 Attribute overflowAttr = constFoldBinaryOp<IntegerAttr>(
504 ArrayRef({sumAttr, adaptor.getLhs()}),
505 getI1SameShape(llvm::cast<TypedAttr>(sumAttr).getType()),
507 if (!overflowAttr)
508 return failure();
509
510 results.push_back(sumAttr);
511 results.push_back(overflowAttr);
512 return success();
513 }
514
515 return failure();
516}
517
518void arith::AddUIExtendedOp::getCanonicalizationPatterns(
519 RewritePatternSet &patterns, MLIRContext *context) {
520 patterns.add<AddUIExtendedToAddI>(context);
521}
522
523//===----------------------------------------------------------------------===//
524// SubUIExtendedOp
525//===----------------------------------------------------------------------===//
526
527std::optional<SmallVector<int64_t, 4>>
528arith::SubUIExtendedOp::getShapeForUnroll() {
529 if (auto vt = dyn_cast<VectorType>(getType(0)))
530 return llvm::to_vector<4>(vt.getShape());
531 return std::nullopt;
532}
533
534// Returns the borrow bit, assuming `lhs` and `rhs` are operands of an unsigned
535// subtraction whose mathematical result underflows iff `lhs < rhs`.
536static APInt calculateUnsignedBorrow(const APInt &lhs, const APInt &rhs) {
537 return lhs.ult(rhs) ? APInt::getAllOnes(1) : APInt::getZero(1);
538}
539
540LogicalResult
541arith::SubUIExtendedOp::fold(FoldAdaptor adaptor,
542 SmallVectorImpl<OpFoldResult> &results) {
543 Type borrowTy = getBorrow().getType();
544 // subui_extended(x, 0) -> x, false
545 if (matchPattern(getRhs(), m_Zero())) {
546 Builder builder(getContext());
547 auto falseValue = builder.getZeroAttr(borrowTy);
548
549 results.push_back(getLhs());
550 results.push_back(falseValue);
551 return success();
552 }
553
554 // subui_extended(x, x) -> 0, false
555 if (getLhs() == getRhs()) {
556 // A dynamically-shaped result cannot be a constant; bail before
557 // getZeroAttr, which would assert on a non-static shape.
558 auto shapedType = dyn_cast<ShapedType>(getDiff().getType());
559 if (shapedType && !shapedType.hasStaticShape())
560 return failure();
561 Builder builder(getContext());
562 auto zeroDiff = builder.getZeroAttr(getDiff().getType());
563 auto falseValue = builder.getZeroAttr(borrowTy);
564 if (!zeroDiff)
565 return failure();
566
567 results.push_back(zeroDiff);
568 results.push_back(falseValue);
569 return success();
570 }
571
572 // subui_extended(constant_a, constant_b) -> constant_diff, constant_borrow
573 if (Attribute diffAttr = constFoldBinaryOp<IntegerAttr>(
574 adaptor.getOperands(),
575 [](APInt a, const APInt &b) { return std::move(a) - b; })) {
576 // If any operand is poison, propagate poison to both results.
577 if (matchPattern(diffAttr, ub::m_Poison())) {
578 results.push_back(diffAttr);
579 results.push_back(diffAttr);
580 return success();
581 }
582 Attribute borrowAttr = constFoldBinaryOp<IntegerAttr>(
583 adaptor.getOperands(),
584 getI1SameShape(llvm::cast<TypedAttr>(diffAttr).getType()),
586 if (!borrowAttr)
587 return failure();
588
589 results.push_back(diffAttr);
590 results.push_back(borrowAttr);
591 return success();
592 }
593
594 return failure();
595}
596
597void arith::SubUIExtendedOp::getCanonicalizationPatterns(
598 RewritePatternSet &patterns, MLIRContext *context) {
599 patterns.add<SubUIExtendedToSubI>(context);
600}
601
602//===----------------------------------------------------------------------===//
603// SubIOp
604//===----------------------------------------------------------------------===//
605
606OpFoldResult arith::SubIOp::fold(FoldAdaptor adaptor) {
607 // subi(x,x) -> 0
608 if (getOperand(0) == getOperand(1)) {
609 auto shapedType = dyn_cast<ShapedType>(getType());
610 // We can't generate a constant with a dynamic shaped tensor.
611 if (!shapedType || shapedType.hasStaticShape())
612 return Builder(getContext()).getZeroAttr(getType());
613 }
614 // subi(x,0) -> x
615 if (matchPattern(adaptor.getRhs(), m_Zero()))
616 return getLhs();
617
618 if (auto add = getLhs().getDefiningOp<AddIOp>()) {
619 // subi(addi(a, b), b) -> a
620 if (getRhs() == add.getRhs())
621 return add.getLhs();
622 // subi(addi(a, b), a) -> b
623 if (getRhs() == add.getLhs())
624 return add.getRhs();
625 }
626
627 // subi(a, subi(a, b)) -> b
628 if (auto sub = getRhs().getDefiningOp<SubIOp>())
629 if (getLhs() == sub.getLhs())
630 return sub.getRhs();
631
633 adaptor.getOperands(),
634 [](APInt a, const APInt &b) { return std::move(a) - b; });
635}
636
637void arith::SubIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
638 MLIRContext *context) {
639 patterns.add<SubIRHSAddConstant, SubILHSAddConstant, SubIRHSSubConstantRHS,
640 SubIRHSSubConstantLHS, SubILHSSubConstantRHS,
641 SubILHSSubConstantLHS, SubISubILHSRHSLHS>(context);
642}
643
644//===----------------------------------------------------------------------===//
645// MulIOp
646//===----------------------------------------------------------------------===//
647
648OpFoldResult arith::MulIOp::fold(FoldAdaptor adaptor) {
649 // muli(x, 0) -> 0
650 if (matchPattern(adaptor.getRhs(), m_Zero()))
651 return getRhs();
652 // muli(x, 1) -> x
653 if (matchPattern(adaptor.getRhs(), m_One()))
654 return getLhs();
655 // TODO: Handle the overflow case.
656
657 // default folder
659 adaptor.getOperands(),
660 [](const APInt &a, const APInt &b) { return a * b; });
661}
662
663void arith::MulIOp::getAsmResultNames(
664 function_ref<void(Value, StringRef)> setNameFn) {
665 if (!isa<IndexType>(getType()))
666 return;
667
668 // Match vector.vscale by name to avoid depending on the vector dialect (which
669 // is a circular dependency).
670 auto isVscale = [](Operation *op) {
671 return op && op->getName().getStringRef() == "vector.vscale";
672 };
673
674 IntegerAttr baseValue;
675 auto isVscaleExpr = [&](Value a, Value b) {
676 return matchPattern(a, m_Constant(&baseValue)) &&
677 isVscale(b.getDefiningOp());
678 };
679
680 if (!isVscaleExpr(getLhs(), getRhs()) && !isVscaleExpr(getRhs(), getLhs()))
681 return;
682
683 // Name `base * vscale` or `vscale * base` as `c<base_value>_vscale`.
684 SmallString<32> specialNameBuffer;
685 llvm::raw_svector_ostream specialName(specialNameBuffer);
686 specialName << 'c' << baseValue.getInt() << "_vscale";
687 setNameFn(getResult(), specialName.str());
688}
689
690void arith::MulIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
691 MLIRContext *context) {
692 patterns.add<MulIMulIConstant>(context);
693}
694
695//===----------------------------------------------------------------------===//
696// MulSIExtendedOp
697//===----------------------------------------------------------------------===//
698
699std::optional<SmallVector<int64_t, 4>>
700arith::MulSIExtendedOp::getShapeForUnroll() {
701 if (auto vt = dyn_cast<VectorType>(getType(0)))
702 return llvm::to_vector<4>(vt.getShape());
703 return std::nullopt;
704}
705
706LogicalResult
707arith::MulSIExtendedOp::fold(FoldAdaptor adaptor,
708 SmallVectorImpl<OpFoldResult> &results) {
709 // mulsi_extended(x, 0) -> 0, 0
710 if (matchPattern(adaptor.getRhs(), m_Zero())) {
711 Attribute zero = adaptor.getRhs();
712 results.push_back(zero);
713 results.push_back(zero);
714 return success();
715 }
716
717 // mulsi_extended(cst_a, cst_b) -> cst_low, cst_high
718 if (Attribute lowAttr = constFoldBinaryOp<IntegerAttr>(
719 adaptor.getOperands(),
720 [](const APInt &a, const APInt &b) { return a * b; })) {
721 // Invoke the constant fold helper again to calculate the 'high' result.
722 Attribute highAttr = constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
723 llvm::APIntOps::mulhs);
724 assert(highAttr && "Unexpected constant-folding failure");
725
726 results.push_back(lowAttr);
727 results.push_back(highAttr);
728 return success();
729 }
730
731 return failure();
732}
733
734void arith::MulSIExtendedOp::getCanonicalizationPatterns(
735 RewritePatternSet &patterns, MLIRContext *context) {
736 patterns.add<MulSIExtendedToMulI, MulSIExtendedRHSOne>(context);
737}
738
739//===----------------------------------------------------------------------===//
740// MulUIExtendedOp
741//===----------------------------------------------------------------------===//
742
743std::optional<SmallVector<int64_t, 4>>
744arith::MulUIExtendedOp::getShapeForUnroll() {
745 if (auto vt = dyn_cast<VectorType>(getType(0)))
746 return llvm::to_vector<4>(vt.getShape());
747 return std::nullopt;
748}
749
750LogicalResult
751arith::MulUIExtendedOp::fold(FoldAdaptor adaptor,
752 SmallVectorImpl<OpFoldResult> &results) {
753 // mului_extended(x, 0) -> 0, 0
754 if (matchPattern(adaptor.getRhs(), m_Zero())) {
755 Attribute zero = adaptor.getRhs();
756 results.push_back(zero);
757 results.push_back(zero);
758 return success();
759 }
760
761 // mului_extended(x, 1) -> x, 0
762 if (matchPattern(adaptor.getRhs(), m_One())) {
763 Builder builder(getContext());
764 Attribute zero = builder.getZeroAttr(getLhs().getType());
765 results.push_back(getLhs());
766 results.push_back(zero);
767 return success();
768 }
769
770 // mului_extended(cst_a, cst_b) -> cst_low, cst_high
771 if (Attribute lowAttr = constFoldBinaryOp<IntegerAttr>(
772 adaptor.getOperands(),
773 [](const APInt &a, const APInt &b) { return a * b; })) {
774 // Invoke the constant fold helper again to calculate the 'high' result.
775 Attribute highAttr = constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
776 llvm::APIntOps::mulhu);
777 assert(highAttr && "Unexpected constant-folding failure");
778
779 results.push_back(lowAttr);
780 results.push_back(highAttr);
781 return success();
782 }
783
784 return failure();
785}
786
787void arith::MulUIExtendedOp::getCanonicalizationPatterns(
788 RewritePatternSet &patterns, MLIRContext *context) {
789 patterns.add<MulUIExtendedToMulI>(context);
790}
791
792//===----------------------------------------------------------------------===//
793// DivUIOp
794//===----------------------------------------------------------------------===//
795
796/// Fold `(a * b) / b -> a`
797static Value foldDivMul(Value lhs, Value rhs,
798 arith::IntegerOverflowFlags ovfFlags) {
799 auto mul = lhs.getDefiningOp<mlir::arith::MulIOp>();
800 if (!mul || !bitEnumContainsAll(mul.getOverflowFlags(), ovfFlags))
801 return {};
802
803 if (mul.getLhs() == rhs)
804 return mul.getRhs();
805
806 if (mul.getRhs() == rhs)
807 return mul.getLhs();
808
809 return {};
810}
811
812OpFoldResult arith::DivUIOp::fold(FoldAdaptor adaptor) {
813 // TODO: divui (x, 0) -> poison. Division by zero is undefined behaviour and
814 // could fold to poison, but that would make the arith dialect depend on the
815 // ub dialect to materialize ub.poison; left out for now.
816
817 // divui (x, 1) -> x.
818 if (matchPattern(adaptor.getRhs(), m_One()))
819 return getLhs();
820
821 // divui (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
822 if (matchPattern(adaptor.getLhs(), m_Zero()))
823 return getLhs();
824
825 // divui (x, x) -> 1.
826 if (getLhs() == getRhs())
827 return getIntegerAttrOfType(getType(), 1);
828
829 // (a * b) / b -> a
830 if (Value val = foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nuw))
831 return val;
832
833 // Don't fold if it would require a division by zero.
834 bool div0 = false;
835 auto result = constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
836 [&](APInt a, const APInt &b) {
837 if (div0 || !b) {
838 div0 = true;
839 return a;
840 }
841 return a.udiv(b);
842 });
843
844 return div0 ? Attribute() : result;
845}
846
847/// Returns whether an unsigned division by `divisor` is speculatable.
849 // X / 0 => UB
850 if (matchPattern(divisor, m_IntRangeWithoutZeroU()))
852
854}
855
856Speculation::Speculatability arith::DivUIOp::getSpeculatability() {
857 return getDivUISpeculatability(getRhs());
858}
859
860//===----------------------------------------------------------------------===//
861// DivSIOp
862//===----------------------------------------------------------------------===//
863
864OpFoldResult arith::DivSIOp::fold(FoldAdaptor adaptor) {
865 // TODO: divsi (x, 0) -> poison. Division by zero is undefined behaviour and
866 // could fold to poison, but that would make the arith dialect depend on the
867 // ub dialect to materialize ub.poison; left out for now.
868
869 // divsi (x, 1) -> x.
870 if (matchPattern(adaptor.getRhs(), m_One()))
871 return getLhs();
872
873 // divsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
874 if (matchPattern(adaptor.getLhs(), m_Zero()))
875 return getLhs();
876
877 // divsi (x, x) -> 1.
878 if (getLhs() == getRhs())
879 return getIntegerAttrOfType(getType(), 1);
880
881 // (a * b) / b -> a
882 if (Value val = foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nsw))
883 return val;
884
885 // Don't fold if it would overflow or if it requires a division by zero.
886 bool overflowOrDiv0 = false;
888 adaptor.getOperands(), [&](APInt a, const APInt &b) {
889 if (overflowOrDiv0 || !b) {
890 overflowOrDiv0 = true;
891 return a;
892 }
893 return a.sdiv_ov(b, overflowOrDiv0);
894 });
895
896 return overflowOrDiv0 ? Attribute() : result;
897}
898
899/// Returns whether a signed division by `divisor` is speculatable. This
900/// function conservatively assumes that all signed division by -1 are not
901/// speculatable.
903 // X / 0 => UB
904 // INT_MIN / -1 => UB
905 if (matchPattern(divisor, m_IntRangeWithoutZeroS()) &&
908
910}
911
912Speculation::Speculatability arith::DivSIOp::getSpeculatability() {
913 return getDivSISpeculatability(getRhs());
914}
915
916//===----------------------------------------------------------------------===//
917// CeilDivUIOp
918//===----------------------------------------------------------------------===//
919
920OpFoldResult arith::CeilDivUIOp::fold(FoldAdaptor adaptor) {
921 // TODO: ceildivui (x, 0) -> poison. Division by zero is undefined behaviour
922 // and could fold to poison, but that would make the arith dialect depend on
923 // the ub dialect to materialize ub.poison; left out for now.
924
925 // ceildivui (x, 1) -> x.
926 if (matchPattern(adaptor.getRhs(), m_One()))
927 return getLhs();
928
929 // ceildivui (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
930 if (matchPattern(adaptor.getLhs(), m_Zero()))
931 return getLhs();
932
933 // ceildivui (x, x) -> 1.
934 if (getLhs() == getRhs())
935 return getIntegerAttrOfType(getType(), 1);
936
937 bool overflowOrDiv0 = false;
939 adaptor.getOperands(), [&](APInt a, const APInt &b) {
940 if (overflowOrDiv0 || !b) {
941 overflowOrDiv0 = true;
942 return a;
943 }
944 APInt quotient = a.udiv(b);
945 if (!a.urem(b))
946 return quotient;
947 APInt one(a.getBitWidth(), 1, true);
948 return quotient.uadd_ov(one, overflowOrDiv0);
949 });
950
951 return overflowOrDiv0 ? Attribute() : result;
952}
953
954Speculation::Speculatability arith::CeilDivUIOp::getSpeculatability() {
955 return getDivUISpeculatability(getRhs());
956}
957
958//===----------------------------------------------------------------------===//
959// CeilDivSIOp
960//===----------------------------------------------------------------------===//
961
962OpFoldResult arith::CeilDivSIOp::fold(FoldAdaptor adaptor) {
963 // TODO: ceildivsi (x, 0) -> poison. Division by zero is undefined behaviour
964 // and could fold to poison, but that would make the arith dialect depend on
965 // the ub dialect to materialize ub.poison; left out for now.
966
967 // ceildivsi (x, 1) -> x.
968 if (matchPattern(adaptor.getRhs(), m_One()))
969 return getLhs();
970
971 // ceildivsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
972 if (matchPattern(adaptor.getLhs(), m_Zero()))
973 return getLhs();
974
975 // ceildivsi (x, x) -> 1.
976 if (getLhs() == getRhs())
977 return getIntegerAttrOfType(getType(), 1);
978
979 // Don't fold if it would overflow or if it requires a division by zero.
980 bool overflowOrDiv0 = false;
982 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
983 if (overflowOrDiv0 || !b) {
984 overflowOrDiv0 = true;
985 return a;
986 }
987 // Compute the ceiling without negating either operand, so that MININT
988 // operands still fold whenever the result is representable.
989 //
990 // sdiv truncates towards zero, so it already rounds up whenever the
991 // exact quotient is negative. When the exact quotient is positive, i.e.
992 // when the operands have the same sign, an inexact division has to be
993 // corrected by one. This mirrors the expansion in ExpandOps.cpp.
994 bool overflowDiv = false;
995 APInt quotient = a.sdiv_ov(b, overflowDiv);
996 if (overflowDiv) {
997 // MININT / -1. The exact result is -MININT, which is not
998 // representable.
999 overflowOrDiv0 = true;
1000 return a;
1001 }
1002 if (a.isNegative() != b.isNegative() || quotient * b == a)
1003 return quotient;
1004
1005 // The correction cannot overflow: it only applies when the exact
1006 // quotient is positive and the division is inexact, which bounds the
1007 // quotient well below the maximum. Check anyway, at no cost.
1008 APInt one(a.getBitWidth(), 1, /*isSigned=*/true);
1009 return quotient.sadd_ov(one, overflowOrDiv0);
1010 });
1011
1012 return overflowOrDiv0 ? Attribute() : result;
1013}
1014
1015Speculation::Speculatability arith::CeilDivSIOp::getSpeculatability() {
1016 return getDivSISpeculatability(getRhs());
1017}
1018
1019//===----------------------------------------------------------------------===//
1020// FloorDivSIOp
1021//===----------------------------------------------------------------------===//
1022
1023OpFoldResult arith::FloorDivSIOp::fold(FoldAdaptor adaptor) {
1024 // TODO: floordivsi (x, 0) -> poison. Division by zero is undefined behaviour
1025 // and could fold to poison, but that would make the arith dialect depend on
1026 // the ub dialect to materialize ub.poison; left out for now.
1027
1028 // floordivsi (x, 1) -> x.
1029 if (matchPattern(adaptor.getRhs(), m_One()))
1030 return getLhs();
1031
1032 // floordivsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
1033 if (matchPattern(adaptor.getLhs(), m_Zero()))
1034 return getLhs();
1035
1036 // floordivsi (x, x) -> 1.
1037 if (getLhs() == getRhs())
1038 return getIntegerAttrOfType(getType(), 1);
1039
1040 // Don't fold if it would overflow or if it requires a division by zero.
1041 bool overflowOrDiv = false;
1043 adaptor.getOperands(), [&](APInt a, const APInt &b) {
1044 if (b.isZero()) {
1045 overflowOrDiv = true;
1046 return a;
1047 }
1048 return a.sfloordiv_ov(b, overflowOrDiv);
1049 });
1050
1051 return overflowOrDiv ? Attribute() : result;
1052}
1053
1054//===----------------------------------------------------------------------===//
1055// RemUIOp
1056//===----------------------------------------------------------------------===//
1057
1058OpFoldResult arith::RemUIOp::fold(FoldAdaptor adaptor) {
1059 // TODO: remui (x, 0) -> poison. Remainder by zero is undefined behaviour and
1060 // could fold to poison, but that would make the arith dialect depend on the
1061 // ub dialect to materialize ub.poison; left out for now.
1062
1063 // remui (x, 1) -> 0.
1064 if (matchPattern(adaptor.getRhs(), m_One()))
1065 return getIntegerAttrOfType(getType(), 0);
1066
1067 // remui (0, x) -> 0 and remui (x, x) -> 0. Division by zero is UB, so
1068 // refining to 0 is valid.
1069 if (matchPattern(adaptor.getLhs(), m_Zero()) || getLhs() == getRhs())
1070 return getIntegerAttrOfType(getType(), 0);
1071
1072 // Don't fold if it would require a division by zero.
1073 bool div0 = false;
1074 auto result = constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1075 [&](APInt a, const APInt &b) {
1076 if (div0 || b.isZero()) {
1077 div0 = true;
1078 return a;
1079 }
1080 return a.urem(b);
1081 });
1082
1083 return div0 ? Attribute() : result;
1084}
1085
1086Speculation::Speculatability arith::RemUIOp::getSpeculatability() {
1087 return getDivUISpeculatability(getRhs());
1088}
1089
1090//===----------------------------------------------------------------------===//
1091// RemSIOp
1092//===----------------------------------------------------------------------===//
1093
1094OpFoldResult arith::RemSIOp::fold(FoldAdaptor adaptor) {
1095 // TODO: remsi (x, 0) -> poison. Remainder by zero is undefined behaviour and
1096 // could fold to poison, but that would make the arith dialect depend on the
1097 // ub dialect to materialize ub.poison; left out for now.
1098
1099 // remsi (x, 1) -> 0.
1100 if (matchPattern(adaptor.getRhs(), m_One()))
1101 return getIntegerAttrOfType(getType(), 0);
1102
1103 // remsi (0, x) -> 0 and remsi (x, x) -> 0. Division by zero is UB, so
1104 // refining to 0 is valid.
1105 if (matchPattern(adaptor.getLhs(), m_Zero()) || getLhs() == getRhs())
1106 return getIntegerAttrOfType(getType(), 0);
1107
1108 // Don't fold if it would require a division by zero.
1109 bool div0 = false;
1110 auto result = constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1111 [&](APInt a, const APInt &b) {
1112 if (div0 || b.isZero()) {
1113 div0 = true;
1114 return a;
1115 }
1116 return a.srem(b);
1117 });
1118
1119 return div0 ? Attribute() : result;
1120}
1121
1122Speculation::Speculatability arith::RemSIOp::getSpeculatability() {
1123 // X % 0 => UB
1124 // X % -1 is well-defined (always 0), unlike X / -1 which can overflow.
1125 if (matchPattern(getRhs(), m_IntRangeWithoutZeroS()))
1127
1129}
1130
1131//===----------------------------------------------------------------------===//
1132// AndIOp
1133//===----------------------------------------------------------------------===//
1134
1135/// Fold `op(a, op(a, b))` to `op(a, b)` for an associative, commutative and
1136/// idempotent `op` (e.g. `and`, `or`).
1137template <typename OpTy>
1139 for (bool reversePrev : {false, true}) {
1140 auto prev = (reversePrev ? op.getRhs() : op.getLhs())
1141 .template getDefiningOp<OpTy>();
1142 if (!prev)
1143 continue;
1144
1145 Value other = (reversePrev ? op.getLhs() : op.getRhs());
1146 if (other != prev.getLhs() && other != prev.getRhs())
1147 continue;
1148
1149 return prev.getResult();
1150 }
1151 return {};
1152}
1153
1154OpFoldResult arith::AndIOp::fold(FoldAdaptor adaptor) {
1155 /// and(x, 0) -> 0
1156 if (matchPattern(adaptor.getRhs(), m_Zero()))
1157 return getRhs();
1158 /// and(x, allOnes) -> x
1159 APInt intValue;
1160 if (matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue)) &&
1161 intValue.isAllOnes())
1162 return getLhs();
1163 /// and(x, not(x)) -> 0
1164 if (matchPattern(getRhs(), m_Op<XOrIOp>(matchers::m_Val(getLhs()),
1165 m_ConstantInt(&intValue))) &&
1166 intValue.isAllOnes())
1167 return Builder(getContext()).getZeroAttr(getType());
1168 /// and(not(x), x) -> 0
1169 if (matchPattern(getLhs(), m_Op<XOrIOp>(matchers::m_Val(getRhs()),
1170 m_ConstantInt(&intValue))) &&
1171 intValue.isAllOnes())
1172 return Builder(getContext()).getZeroAttr(getType());
1173
1174 /// and(a, and(a, b)) -> and(a, b)
1175 if (Value result = foldIdempotentOfSameOp(*this))
1176 return result;
1177
1179 adaptor.getOperands(),
1180 [](APInt a, const APInt &b) { return std::move(a) & b; });
1181}
1182
1183//===----------------------------------------------------------------------===//
1184// OrIOp
1185//===----------------------------------------------------------------------===//
1186
1187OpFoldResult arith::OrIOp::fold(FoldAdaptor adaptor) {
1188 if (APInt rhsVal; matchPattern(adaptor.getRhs(), m_ConstantInt(&rhsVal))) {
1189 /// or(x, 0) -> x
1190 if (rhsVal.isZero())
1191 return getLhs();
1192 /// or(x, <all ones>) -> <all ones>
1193 if (rhsVal.isAllOnes())
1194 return adaptor.getRhs();
1195 }
1196
1197 APInt intValue;
1198 /// or(x, xor(x, 1)) -> 1
1199 if (matchPattern(getRhs(), m_Op<XOrIOp>(matchers::m_Val(getLhs()),
1200 m_ConstantInt(&intValue))) &&
1201 intValue.isAllOnes())
1202 return getRhs().getDefiningOp<XOrIOp>().getRhs();
1203 /// or(xor(x, 1), x) -> 1
1204 if (matchPattern(getLhs(), m_Op<XOrIOp>(matchers::m_Val(getRhs()),
1205 m_ConstantInt(&intValue))) &&
1206 intValue.isAllOnes())
1207 return getLhs().getDefiningOp<XOrIOp>().getRhs();
1208
1209 /// or(a, or(a, b)) -> or(a, b)
1210 if (Value result = foldIdempotentOfSameOp(*this))
1211 return result;
1212
1214 adaptor.getOperands(),
1215 [](APInt a, const APInt &b) { return std::move(a) | b; });
1216}
1217
1218//===----------------------------------------------------------------------===//
1219// XOrIOp
1220//===----------------------------------------------------------------------===//
1221
1222OpFoldResult arith::XOrIOp::fold(FoldAdaptor adaptor) {
1223 /// xor(x, 0) -> x
1224 if (matchPattern(adaptor.getRhs(), m_Zero()))
1225 return getLhs();
1226 /// xor(x, x) -> 0
1227 if (getLhs() == getRhs()) {
1228 // A dynamically-shaped result cannot be a constant; bail before
1229 // getZeroAttr, which would assert on a non-static shape.
1230 auto shapedType = dyn_cast<ShapedType>(getType());
1231 if (!shapedType || shapedType.hasStaticShape())
1232 return Builder(getContext()).getZeroAttr(getType());
1233 }
1234 /// xor(xor(x, a), a) -> x
1235 /// xor(xor(a, x), a) -> x
1236 if (arith::XOrIOp prev = getLhs().getDefiningOp<arith::XOrIOp>()) {
1237 if (prev.getRhs() == getRhs())
1238 return prev.getLhs();
1239 if (prev.getLhs() == getRhs())
1240 return prev.getRhs();
1241 }
1242 /// xor(a, xor(x, a)) -> x
1243 /// xor(a, xor(a, x)) -> x
1244 if (arith::XOrIOp prev = getRhs().getDefiningOp<arith::XOrIOp>()) {
1245 if (prev.getRhs() == getLhs())
1246 return prev.getLhs();
1247 if (prev.getLhs() == getLhs())
1248 return prev.getRhs();
1249 }
1250
1252 adaptor.getOperands(),
1253 [](APInt a, const APInt &b) { return std::move(a) ^ b; });
1254}
1255
1256void arith::XOrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1257 MLIRContext *context) {
1258 patterns.add<XOrIXOrIConstant, XOrINotCmpI, XOrIOfExtUI, XOrIOfExtSI>(
1259 context);
1260}
1261
1262//===----------------------------------------------------------------------===//
1263// NegFOp
1264//===----------------------------------------------------------------------===//
1265
1266OpFoldResult arith::NegFOp::fold(FoldAdaptor adaptor) {
1267 /// negf(negf(x)) -> x
1268 if (auto op = this->getOperand().getDefiningOp<arith::NegFOp>())
1269 return op.getOperand();
1271 adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> {
1272 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()))
1273 return std::nullopt;
1274 return -a;
1275 });
1276}
1277
1278//===----------------------------------------------------------------------===//
1279// FlushDenormalsOp
1280//===----------------------------------------------------------------------===//
1281
1282OpFoldResult arith::FlushDenormalsOp::fold(FoldAdaptor adaptor) {
1283 // TODO: Fold flush_denormals if the floating-point type does not support
1284 // denormals. There is currently no API to query this information from
1285 // APFloat.
1286
1287 // flush_denormals(flush_denormals(x)) -> flush_denormals(x)
1288 if (auto op = this->getOperand().getDefiningOp<arith::FlushDenormalsOp>())
1289 return op.getResult();
1290
1291 // Constant-fold flush_denormals if the operand is a constant.
1293 adaptor.getOperands(), [](const APFloat &a) {
1294 if (a.isDenormal())
1295 return APFloat::getZero(a.getSemantics(), a.isNegative());
1296 return a;
1297 });
1298}
1299
1300//===----------------------------------------------------------------------===//
1301// AddFOp
1302//===----------------------------------------------------------------------===//
1303
1304OpFoldResult arith::AddFOp::fold(FoldAdaptor adaptor) {
1305 // addf(x, -0) -> x
1306 if (matchPattern(adaptor.getRhs(), m_NegZeroFloat()))
1307 return getLhs();
1308 if (matchPattern(adaptor.getRhs(), m_PosZeroFloat()) &&
1309 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1310 return getLhs();
1311
1312 auto rm = getRoundingmode();
1314 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1315 APFloat result(a);
1316 result.add(b, convertArithRoundingModeToLLVMIR(rm));
1317 return result;
1318 });
1319}
1320
1321void arith::AddFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1322 MLIRContext *context) {
1323 patterns.add<AddFOfNegFLhs, AddFOfNegFRhs>(context);
1324}
1325
1326//===----------------------------------------------------------------------===//
1327// SubFOp
1328//===----------------------------------------------------------------------===//
1329
1330OpFoldResult arith::SubFOp::fold(FoldAdaptor adaptor) {
1331 // subf(x, +0) -> x
1332 if (matchPattern(adaptor.getRhs(), m_PosZeroFloat()))
1333 return getLhs();
1334 if (matchPattern(adaptor.getRhs(), m_NegZeroFloat()) &&
1335 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1336 return getLhs();
1337
1338 auto rm = getRoundingmode();
1340 adaptor.getOperands(),
1341 [rm](const APFloat &a, const APFloat &b) -> std::optional<APFloat> {
1342 // A negative difference is not representable in types such as
1343 // f8E8M0FNU, and APFloat::subtract does not support computing it.
1344 if (!APFloat::semanticsHasSignedRepr(a.getSemantics()) &&
1345 a.compare(b) == APFloat::cmpLessThan)
1346 return std::nullopt;
1347 APFloat result(a);
1348 result.subtract(b, convertArithRoundingModeToLLVMIR(rm));
1349 return result;
1350 });
1351}
1352
1353void arith::SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1354 MLIRContext *context) {
1355 patterns.add<SubFOfNegZero>(context);
1356}
1357
1358namespace {
1359
1360/// Narrow an extremum whose operands were extended from the result type:
1361///
1362/// trunc(extremum(ext(lhs), ext(rhs))) -> extremum(lhs, rhs)
1363///
1364/// The concrete extension is part of the pattern so each extremum is only
1365/// registered with extensions that preserve its ordering.
1366/// For floating-point types, also require the extension to preserve every
1367/// source value relevant under the extremum's fast-math flags.
1368template <typename TruncOp, typename ExtOp, typename ExtremumOp>
1369struct NarrowExtremum final : OpRewritePattern<TruncOp> {
1370 using OpRewritePattern<TruncOp>::OpRewritePattern;
1371
1372 LogicalResult matchAndRewrite(TruncOp truncOp,
1373 PatternRewriter &rewriter) const override {
1374 auto extremumOp = truncOp.getIn().template getDefiningOp<ExtremumOp>();
1375 if (!extremumOp || !extremumOp->hasOneUse())
1376 return failure();
1377
1378 auto lhsExt = extremumOp.getLhs().template getDefiningOp<ExtOp>();
1379 auto rhsExt = extremumOp.getRhs().template getDefiningOp<ExtOp>();
1380 if (!lhsExt || !rhsExt)
1381 return failure();
1382
1383 Value lhs = lhsExt.getIn();
1384 Value rhs = rhsExt.getIn();
1385 Type narrowType = truncOp.getType();
1386 if (lhs.getType() != narrowType || rhs.getType() != narrowType)
1387 return failure();
1388
1389 // A floating-point extension is not necessarily lossless between arbitrary
1390 // floating-point semantics, even when the destination has a larger bit
1391 // width. In particular, it may lose the sign of zero or quiet a signaling
1392 // NaN, either of which can change an extremum's result. `nnan` lets us
1393 // disregard NaN representation differences, but all other relevant source
1394 // values must be preserved.
1395 if (auto narrowFloatType =
1396 dyn_cast<FloatType>(getElementTypeOrSelf(narrowType))) {
1397 auto wideFloatType =
1398 dyn_cast<FloatType>(getElementTypeOrSelf(extremumOp.getType()));
1399 if (!wideFloatType)
1400 return failure();
1401
1402 const llvm::fltSemantics &narrowSemantics =
1403 narrowFloatType.getFloatSemantics();
1404 const llvm::fltSemantics &wideSemantics =
1405 wideFloatType.getFloatSemantics();
1406 bool ignoreNaNs = false;
1407 if constexpr (std::is_same_v<TruncOp, TruncFOp>)
1408 ignoreNaNs =
1409 bitEnumContainsAll(extremumOp.getFastmath(), FastMathFlags::nnan);
1410 if (!llvm::APFloatBase::isLosslesslyConvertibleTo(
1411 narrowSemantics, wideSemantics, ignoreNaNs))
1412 return failure();
1413 }
1414
1415 rewriter.replaceOpWithNewOp<ExtremumOp>(
1416 truncOp, TypeRange{narrowType}, ValueRange{lhs, rhs},
1417 extremumOp.getProperties(),
1418 extremumOp->getDiscardableAttrDictionary().getValue());
1419 return success();
1420 }
1421};
1422
1423} // namespace
1424
1425//===----------------------------------------------------------------------===//
1426// MaximumFOp
1427//===----------------------------------------------------------------------===//
1428
1429OpFoldResult arith::MaximumFOp::fold(FoldAdaptor adaptor) {
1430 // maximumf(x,x) -> x
1431 if (getLhs() == getRhs())
1432 return getRhs();
1433
1434 // maximumf(x, -inf) -> x
1435 if (matchPattern(adaptor.getRhs(), m_NegInfFloat()))
1436 return getLhs();
1437
1438 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::maximum);
1439}
1440
1441//===----------------------------------------------------------------------===//
1442// MaxNumFOp
1443//===----------------------------------------------------------------------===//
1444
1445OpFoldResult arith::MaxNumFOp::fold(FoldAdaptor adaptor) {
1446 // maxnumf(x,x) -> x
1447 if (getLhs() == getRhs())
1448 return getRhs();
1449
1450 // maxnumf(x, NaN) -> x
1451 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1452 return getLhs();
1453
1454 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::maxnum);
1455}
1456
1457//===----------------------------------------------------------------------===//
1458// MaximumNumFOp
1459//===----------------------------------------------------------------------===//
1460
1461OpFoldResult arith::MaximumNumFOp::fold(FoldAdaptor adaptor) {
1462 // maximumnumf(x,x) -> x
1463 if (getLhs() == getRhs())
1464 return getRhs();
1465
1466 // maximumnumf(x, NaN) -> x
1467 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1468 return getLhs();
1469
1470 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::maximumnum);
1471}
1472
1473//===----------------------------------------------------------------------===//
1474// MaxSIOp
1475//===----------------------------------------------------------------------===//
1476
1477OpFoldResult MaxSIOp::fold(FoldAdaptor adaptor) {
1478 // maxsi(x,x) -> x
1479 if (getLhs() == getRhs())
1480 return getRhs();
1481
1482 if (APInt intValue;
1483 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1484 // maxsi(x,MAX_INT) -> MAX_INT
1485 if (intValue.isMaxSignedValue())
1486 return getRhs();
1487 // maxsi(x, MIN_INT) -> x
1488 if (intValue.isMinSignedValue())
1489 return getLhs();
1490 }
1491
1492 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1493 llvm::APIntOps::smax);
1494}
1495
1496//===----------------------------------------------------------------------===//
1497// MaxUIOp
1498//===----------------------------------------------------------------------===//
1499
1500OpFoldResult MaxUIOp::fold(FoldAdaptor adaptor) {
1501 // maxui(x,x) -> x
1502 if (getLhs() == getRhs())
1503 return getRhs();
1504
1505 if (APInt intValue;
1506 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1507 // maxui(x,MAX_INT) -> MAX_INT
1508 if (intValue.isMaxValue())
1509 return getRhs();
1510 // maxui(x, MIN_INT) -> x
1511 if (intValue.isMinValue())
1512 return getLhs();
1513 }
1514
1515 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1516 llvm::APIntOps::umax);
1517}
1518
1519//===----------------------------------------------------------------------===//
1520// MinimumFOp
1521//===----------------------------------------------------------------------===//
1522
1523OpFoldResult arith::MinimumFOp::fold(FoldAdaptor adaptor) {
1524 // minimumf(x,x) -> x
1525 if (getLhs() == getRhs())
1526 return getRhs();
1527
1528 // minimumf(x, +inf) -> x
1529 if (matchPattern(adaptor.getRhs(), m_PosInfFloat()))
1530 return getLhs();
1531
1532 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::minimum);
1533}
1534
1535//===----------------------------------------------------------------------===//
1536// MinNumFOp
1537//===----------------------------------------------------------------------===//
1538
1539OpFoldResult arith::MinNumFOp::fold(FoldAdaptor adaptor) {
1540 // minnumf(x,x) -> x
1541 if (getLhs() == getRhs())
1542 return getRhs();
1543
1544 // minnumf(x, NaN) -> x
1545 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1546 return getLhs();
1547
1548 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::minnum);
1549}
1550
1551//===----------------------------------------------------------------------===//
1552// MinimumNumFOp
1553//===----------------------------------------------------------------------===//
1554
1555OpFoldResult arith::MinimumNumFOp::fold(FoldAdaptor adaptor) {
1556 // minimumnumf(x,x) -> x
1557 if (getLhs() == getRhs())
1558 return getRhs();
1559
1560 // minimumnumf(x, NaN) -> x
1561 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1562 return getLhs();
1563
1564 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::minimumnum);
1565}
1566
1567//===----------------------------------------------------------------------===//
1568// MinSIOp
1569//===----------------------------------------------------------------------===//
1570
1571OpFoldResult MinSIOp::fold(FoldAdaptor adaptor) {
1572 // minsi(x,x) -> x
1573 if (getLhs() == getRhs())
1574 return getRhs();
1575
1576 if (APInt intValue;
1577 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1578 // minsi(x,MIN_INT) -> MIN_INT
1579 if (intValue.isMinSignedValue())
1580 return getRhs();
1581 // minsi(x, MAX_INT) -> x
1582 if (intValue.isMaxSignedValue())
1583 return getLhs();
1584 }
1585
1586 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1587 llvm::APIntOps::smin);
1588}
1589
1590//===----------------------------------------------------------------------===//
1591// MinUIOp
1592//===----------------------------------------------------------------------===//
1593
1594OpFoldResult MinUIOp::fold(FoldAdaptor adaptor) {
1595 // minui(x,x) -> x
1596 if (getLhs() == getRhs())
1597 return getRhs();
1598
1599 if (APInt intValue;
1600 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1601 // minui(x,MIN_INT) -> MIN_INT
1602 if (intValue.isMinValue())
1603 return getRhs();
1604 // minui(x, MAX_INT) -> x
1605 if (intValue.isMaxValue())
1606 return getLhs();
1607 }
1608
1609 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1610 llvm::APIntOps::umin);
1611}
1612
1613//===----------------------------------------------------------------------===//
1614// MulFOp
1615//===----------------------------------------------------------------------===//
1616
1617OpFoldResult arith::MulFOp::fold(FoldAdaptor adaptor) {
1618 // mulf(x, 1) -> x
1619 if (matchPattern(adaptor.getRhs(), m_OneFloat()))
1620 return getLhs();
1621
1622 // Match LLVM InstSimplify: with nnan+nsz, X * 0 -> 0 for a non-constant X.
1623 // When both operands are constants, fall through to APFloat so IEEE signed
1624 // zeros are preserved (e.g. (-c) * +0.0 == -0.0).
1625 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::nnan |
1626 arith::FastMathFlags::nsz) &&
1627 !(adaptor.getLhs() && adaptor.getRhs())) {
1628 // mulf(x, 0) -> 0
1629 if (matchPattern(adaptor.getRhs(), m_AnyZeroFloat()))
1630 return getRhs();
1631 if (matchPattern(adaptor.getLhs(), m_AnyZeroFloat()))
1632 return getLhs();
1633 }
1634
1635 auto rm = getRoundingmode();
1637 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1638 APFloat result(a);
1639 result.multiply(b, convertArithRoundingModeToLLVMIR(rm));
1640 return result;
1641 });
1642}
1643
1644void arith::MulFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1645 MLIRContext *context) {
1646 patterns.add<MulFOfNegF>(context);
1647}
1648
1649//===----------------------------------------------------------------------===//
1650// DivFOp
1651//===----------------------------------------------------------------------===//
1652
1653OpFoldResult arith::DivFOp::fold(FoldAdaptor adaptor) {
1654 // divf(x, 1) -> x
1655 if (matchPattern(adaptor.getRhs(), m_OneFloat()))
1656 return getLhs();
1657
1658 auto rm = getRoundingmode();
1660 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1661 APFloat result(a);
1662 result.divide(b, convertArithRoundingModeToLLVMIR(rm));
1663 return result;
1664 });
1665}
1666
1667void arith::DivFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1668 MLIRContext *context) {
1669 patterns.add<DivFOfNegF>(context);
1670}
1671
1672//===----------------------------------------------------------------------===//
1673// RemFOp
1674//===----------------------------------------------------------------------===//
1675
1676OpFoldResult arith::RemFOp::fold(FoldAdaptor adaptor) {
1677 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(),
1678 [](const APFloat &a, const APFloat &b) {
1679 APFloat result(a);
1680 // APFloat::mod() offers the remainder
1681 // behavior we want, i.e. the result has
1682 // the sign of LHS operand.
1683 (void)result.mod(b);
1684 return result;
1685 });
1686}
1687
1688//===----------------------------------------------------------------------===//
1689// Utility functions for verifying cast ops
1690//===----------------------------------------------------------------------===//
1691
1692template <typename... Types>
1693using type_list = std::tuple<Types...> *;
1694
1695/// Returns a non-null type only if the provided type is one of the allowed
1696/// types or one of the allowed shaped types of the allowed types. Returns the
1697/// element type if a valid shaped type is provided.
1698template <typename... ShapedTypes, typename... ElementTypes>
1701 if (llvm::isa<ShapedType>(type) && !llvm::isa<ShapedTypes...>(type))
1702 return {};
1703
1704 auto underlyingType = getElementTypeOrSelf(type);
1705 if (!llvm::isa<ElementTypes...>(underlyingType))
1706 return {};
1707
1708 return underlyingType;
1709}
1710
1711/// Get allowed underlying types for vectors and tensors.
1712template <typename... ElementTypes>
1717
1718/// Get allowed underlying types for vectors, tensors, and memrefs.
1719template <typename... ElementTypes>
1725
1726/// Return false if both types are ranked tensor with mismatching encoding.
1727static bool hasSameEncoding(Type typeA, Type typeB) {
1728 auto rankedTensorA = dyn_cast<RankedTensorType>(typeA);
1729 auto rankedTensorB = dyn_cast<RankedTensorType>(typeB);
1730 if (!rankedTensorA || !rankedTensorB)
1731 return true;
1732 return rankedTensorA.getEncoding() == rankedTensorB.getEncoding();
1733}
1734
1736 if (inputs.size() != 1 || outputs.size() != 1)
1737 return false;
1738 if (!hasSameEncoding(inputs.front(), outputs.front()))
1739 return false;
1740 return succeeded(verifyCompatibleShapes(inputs.front(), outputs.front()));
1741}
1742
1743//===----------------------------------------------------------------------===//
1744// Verifiers for integer and floating point extension/truncation ops
1745//===----------------------------------------------------------------------===//
1746
1747// Extend ops can only extend to a wider type.
1748template <typename ValType, typename Op>
1749static LogicalResult verifyExtOp(Op op) {
1750 Type srcType = getElementTypeOrSelf(op.getIn().getType());
1751 Type dstType = getElementTypeOrSelf(op.getType());
1752
1753 if (llvm::cast<ValType>(srcType).getWidth() >=
1754 llvm::cast<ValType>(dstType).getWidth())
1755 return op.emitError("result type ")
1756 << dstType << " must be wider than operand type " << srcType;
1757
1758 return success();
1759}
1760
1761// Truncate ops can only truncate to a shorter type.
1762template <typename ValType, typename Op>
1763static LogicalResult verifyTruncateOp(Op op) {
1764 Type srcType = getElementTypeOrSelf(op.getIn().getType());
1765 Type dstType = getElementTypeOrSelf(op.getType());
1766
1767 if (llvm::cast<ValType>(srcType).getWidth() <=
1768 llvm::cast<ValType>(dstType).getWidth())
1769 return op.emitError("result type ")
1770 << dstType << " must be shorter than operand type " << srcType;
1771
1772 return success();
1773}
1774
1775/// Validate a cast that changes the width of a type.
1776template <template <typename> class WidthComparator, typename... ElementTypes>
1777static bool checkWidthChangeCast(TypeRange inputs, TypeRange outputs) {
1778 if (!areValidCastInputsAndOutputs(inputs, outputs))
1779 return false;
1780
1781 auto srcType = getTypeIfLike<ElementTypes...>(inputs.front());
1782 auto dstType = getTypeIfLike<ElementTypes...>(outputs.front());
1783 if (!srcType || !dstType)
1784 return false;
1785
1786 return WidthComparator<unsigned>()(dstType.getIntOrFloatBitWidth(),
1787 srcType.getIntOrFloatBitWidth());
1788}
1789
1790/// Attempts to convert `sourceValue` to an APFloat value with
1791/// `targetSemantics` and `roundingMode`, without any information loss.
1792static FailureOr<APFloat>
1793convertFloatValue(APFloat sourceValue,
1794 const llvm::fltSemantics &targetSemantics,
1795 llvm::RoundingMode roundingMode = kDefaultRoundingMode) {
1796 // Reject special values that are not representable in the target type before
1797 // calling APFloat::convert, which would llvm_unreachable on them.
1798 using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
1799 if (sourceValue.isInfinity() &&
1800 (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
1801 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
1802 return failure();
1803 if (sourceValue.isNaN() &&
1804 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
1805 return failure();
1806
1807 bool losesInfo = false;
1808 auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
1809 if (losesInfo || status != APFloat::opOK)
1810 return failure();
1811
1812 return sourceValue;
1813}
1814
1815//===----------------------------------------------------------------------===//
1816// ExtUIOp
1817//===----------------------------------------------------------------------===//
1818
1819OpFoldResult arith::ExtUIOp::fold(FoldAdaptor adaptor) {
1820 if (auto lhs = getIn().getDefiningOp<ExtUIOp>()) {
1821 // Only the inner extension's nneg speaks about the surviving source; the
1822 // outer flag described the already-extended value.
1823 setNonNeg(lhs.getNonNeg());
1824 getInMutable().assign(lhs.getIn());
1825 return getResult();
1826 }
1827
1828 Type resType = getElementTypeOrSelf(getType());
1829 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1831 adaptor.getOperands(), getType(),
1832 [bitWidth](const APInt &a, bool &castStatus) {
1833 return a.zext(bitWidth);
1834 });
1835}
1836
1837bool arith::ExtUIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1839}
1840
1841LogicalResult arith::ExtUIOp::verify() {
1842 return verifyExtOp<IntegerType>(*this);
1843}
1844
1845//===----------------------------------------------------------------------===//
1846// ExtSIOp
1847//===----------------------------------------------------------------------===//
1848
1849OpFoldResult arith::ExtSIOp::fold(FoldAdaptor adaptor) {
1850 if (auto lhs = getIn().getDefiningOp<ExtSIOp>()) {
1851 getInMutable().assign(lhs.getIn());
1852 return getResult();
1853 }
1854
1855 Type resType = getElementTypeOrSelf(getType());
1856 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1858 adaptor.getOperands(), getType(),
1859 [bitWidth](const APInt &a, bool &castStatus) {
1860 return a.sext(bitWidth);
1861 });
1862}
1863
1864bool arith::ExtSIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1866}
1867
1868void arith::ExtSIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1869 MLIRContext *context) {
1870 patterns.add<ExtSIOfExtUI>(context);
1871}
1872
1873LogicalResult arith::ExtSIOp::verify() {
1874 return verifyExtOp<IntegerType>(*this);
1875}
1876
1877//===----------------------------------------------------------------------===//
1878// ExtFOp
1879//===----------------------------------------------------------------------===//
1880
1881/// Fold extension of float constants when there is no information loss due the
1882/// difference in fp semantics.
1883OpFoldResult arith::ExtFOp::fold(FoldAdaptor adaptor) {
1884 if (auto truncFOp = getOperand().getDefiningOp<TruncFOp>()) {
1885 if (truncFOp.getOperand().getType() == getType()) {
1886 arith::FastMathFlags truncFMF =
1887 truncFOp.getFastmath().value_or(arith::FastMathFlags::none);
1888 bool isTruncContract =
1889 bitEnumContainsAll(truncFMF, arith::FastMathFlags::contract);
1890 arith::FastMathFlags extFMF =
1891 getFastmath().value_or(arith::FastMathFlags::none);
1892 bool isExtContract =
1893 bitEnumContainsAll(extFMF, arith::FastMathFlags::contract);
1894 if (isTruncContract && isExtContract) {
1895 return truncFOp.getOperand();
1896 }
1897 }
1898 }
1899
1900 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
1901 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
1903 adaptor.getOperands(), getType(),
1904 [&targetSemantics](const APFloat &a, bool &castStatus) {
1905 FailureOr<APFloat> result = convertFloatValue(a, targetSemantics);
1906 if (failed(result)) {
1907 castStatus = false;
1908 return a;
1909 }
1910 return *result;
1911 });
1912}
1913
1914bool arith::ExtFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1915 return checkWidthChangeCast<std::greater, FloatType>(inputs, outputs);
1916}
1917
1918LogicalResult arith::ExtFOp::verify() { return verifyExtOp<FloatType>(*this); }
1919
1920//===----------------------------------------------------------------------===//
1921// ScalingExtFOp
1922//===----------------------------------------------------------------------===//
1923
1924/// Fold `calculate` element-wise over the operands of a scaling cast op. The
1925/// `constFoldBinaryOp` helpers cannot be used: they bail out unless both
1926/// operands have the same type, and `in` and `scale` never do.
1928 Attribute inAttr, Attribute scaleAttr, Type resultType,
1929 function_ref<std::optional<APFloat>(const APFloat &, const APFloat &)>
1930 calculate) {
1931 // Poison propagates, as it does in the generic constant folders.
1932 if (isa_and_nonnull<ub::PoisonAttr>(inAttr))
1933 return inAttr;
1934 if (isa_and_nonnull<ub::PoisonAttr>(scaleAttr))
1935 return scaleAttr;
1936
1937 if (!inAttr || !scaleAttr || !resultType)
1938 return {};
1939
1940 if (auto inFloat = dyn_cast<FloatAttr>(inAttr)) {
1941 auto scaleFloat = dyn_cast<FloatAttr>(scaleAttr);
1942 if (!scaleFloat)
1943 return {};
1944 std::optional<APFloat> result =
1945 calculate(inFloat.getValue(), scaleFloat.getValue());
1946 if (!result)
1947 return {};
1948 return FloatAttr::get(resultType, *result);
1949 }
1950
1951 auto inElements = dyn_cast<DenseFPElementsAttr>(inAttr);
1952 auto scaleElements = dyn_cast<DenseFPElementsAttr>(scaleAttr);
1953 auto shapedResultType = dyn_cast<ShapedType>(resultType);
1954 if (!inElements || !scaleElements || !shapedResultType ||
1955 !shapedResultType.hasStaticShape() ||
1956 inElements.getNumElements() != scaleElements.getNumElements())
1957 return {};
1958
1959 // Both operands are splats, so avoid expanding the elements out.
1960 if (inElements.isSplat() && scaleElements.isSplat()) {
1961 std::optional<APFloat> result =
1962 calculate(inElements.getSplatValue<APFloat>(),
1963 scaleElements.getSplatValue<APFloat>());
1964 if (!result)
1965 return {};
1966 return DenseElementsAttr::get(shapedResultType, *result);
1967 }
1968
1969 SmallVector<APFloat> results;
1970 results.reserve(inElements.getNumElements());
1971 for (const auto &[in, scale] : llvm::zip_equal(inElements, scaleElements)) {
1972 std::optional<APFloat> result = calculate(in, scale);
1973 if (!result)
1974 return {};
1975 results.push_back(*result);
1976 }
1977 return DenseElementsAttr::get(shapedResultType, results);
1978}
1979
1980/// Only scales that already are f8E8M0FNU fold. What a wider scale means is
1981/// unsettled -- the tree does not say whether truncating one to f8E8M0FNU
1982/// rounds or takes its exponent -- so a folder should not settle it, see
1983/// https://github.com/llvm/llvm-project/issues/215295.
1984static bool isFoldableScalingScale(Value scale) {
1985 return isa<Float8E8M0FNUType>(getElementTypeOrSelf(scale.getType()));
1986}
1987
1988OpFoldResult arith::ScalingExtFOp::fold(FoldAdaptor adaptor) {
1989 // scaling_extf(in, scale) -> mulf(extf(in), extf(scale)), matching the
1990 // expansion in ExpandOps.cpp. As in arith.extf, the widening steps only fold
1991 // when they are lossless.
1992 if (!isFoldableScalingScale(getScale()))
1993 return {};
1994
1995 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
1996 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
1997 return foldScalingCastOp(
1998 adaptor.getIn(), adaptor.getScale(), getType(),
1999 [&resSemantics](const APFloat &in,
2000 const APFloat &scale) -> std::optional<APFloat> {
2001 FailureOr<APFloat> inExt = convertFloatValue(in, resSemantics);
2002 FailureOr<APFloat> scaleExt = convertFloatValue(scale, resSemantics);
2003 if (failed(inExt) || failed(scaleExt))
2004 return std::nullopt;
2005 APFloat result(*inExt);
2006 result.multiply(*scaleExt, kDefaultRoundingMode);
2007 return result;
2008 });
2009}
2010
2011bool arith::ScalingExtFOp::areCastCompatible(TypeRange inputs,
2012 TypeRange outputs) {
2013 return checkWidthChangeCast<std::greater, FloatType>(inputs.front(), outputs);
2014}
2015
2016LogicalResult arith::ScalingExtFOp::verify() {
2017 return verifyExtOp<FloatType>(*this);
2018}
2019
2020//===----------------------------------------------------------------------===//
2021// TruncIOp
2022//===----------------------------------------------------------------------===//
2023
2024OpFoldResult arith::TruncIOp::fold(FoldAdaptor adaptor) {
2025 if (matchPattern(getOperand(), m_Op<arith::ExtUIOp>()) ||
2026 matchPattern(getOperand(), m_Op<arith::ExtSIOp>())) {
2027 Value src = getOperand().getDefiningOp()->getOperand(0);
2028 Type srcType = getElementTypeOrSelf(src.getType());
2029 Type dstType = getElementTypeOrSelf(getType());
2030 // trunci(zexti(a)) -> trunci(a)
2031 // trunci(sexti(a)) -> trunci(a)
2032 if (llvm::cast<IntegerType>(srcType).getWidth() >
2033 llvm::cast<IntegerType>(dstType).getWidth()) {
2034 setOperand(src);
2035 return getResult();
2036 }
2037
2038 // trunci(zexti(a)) -> a
2039 // trunci(sexti(a)) -> a
2040 if (srcType == dstType)
2041 return src;
2042 }
2043
2044 // trunci(trunci(a)) -> trunci(a))
2045 if (matchPattern(getOperand(), m_Op<arith::TruncIOp>())) {
2046 setOperand(getOperand().getDefiningOp()->getOperand(0));
2047 return getResult();
2048 }
2049
2050 Type resType = getElementTypeOrSelf(getType());
2051 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2053 adaptor.getOperands(), getType(),
2054 [bitWidth](const APInt &a, bool &castStatus) {
2055 return a.trunc(bitWidth);
2056 });
2057}
2058
2059bool arith::TruncIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2060 return checkWidthChangeCast<std::less, IntegerType>(inputs, outputs);
2061}
2062
2063void arith::TruncIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2064 MLIRContext *context) {
2065 patterns.add<NarrowExtremum<TruncIOp, ExtSIOp, MaxSIOp>,
2066 NarrowExtremum<TruncIOp, ExtSIOp, MinSIOp>,
2067 NarrowExtremum<TruncIOp, ExtUIOp, MaxUIOp>,
2068 NarrowExtremum<TruncIOp, ExtUIOp, MinUIOp>, TruncIExtSIToExtSI,
2069 TruncIExtUIToExtUI, TruncIShrSIToTrunciShrUI>(context);
2070}
2071
2072LogicalResult arith::TruncIOp::verify() {
2073 return verifyTruncateOp<IntegerType>(*this);
2074}
2075
2076//===----------------------------------------------------------------------===//
2077// TruncFOp
2078//===----------------------------------------------------------------------===//
2079
2080/// Perform safe const propagation for truncf, i.e., only propagate if FP value
2081/// can be represented without precision loss.
2082OpFoldResult arith::TruncFOp::fold(FoldAdaptor adaptor) {
2083 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2084 if (auto extOp = getOperand().getDefiningOp<arith::ExtFOp>()) {
2085 Value src = extOp.getIn();
2086 auto srcType = cast<FloatType>(getElementTypeOrSelf(src.getType()));
2087 auto intermediateType =
2088 cast<FloatType>(getElementTypeOrSelf(extOp.getType()));
2089 // Check whether every source value round-trips through the intermediate
2090 // type, including signaling NaNs and signed zero.
2091 if (llvm::APFloatBase::isLosslesslyConvertibleTo(
2092 srcType.getFloatSemantics(),
2093 intermediateType.getFloatSemantics())) {
2094 // truncf(extf(a)) -> truncf(a)
2095 if (srcType.getWidth() > resElemType.getWidth()) {
2096 setOperand(src);
2097 return getResult();
2098 }
2099
2100 // truncf(extf(a)) -> a
2101 if (srcType == resElemType)
2102 return src;
2103 }
2104 }
2105
2106 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2108 adaptor.getOperands(), getType(),
2109 [this, &targetSemantics](const APFloat &a, bool &castStatus) {
2110 llvm::RoundingMode llvmRoundingMode =
2111 convertArithRoundingModeToLLVMIR(getRoundingmode());
2112 FailureOr<APFloat> result =
2113 convertFloatValue(a, targetSemantics, llvmRoundingMode);
2114 if (failed(result)) {
2115 castStatus = false;
2116 return a;
2117 }
2118 return *result;
2119 });
2120}
2121
2122void arith::TruncFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2123 MLIRContext *context) {
2124 patterns.add<NarrowExtremum<TruncFOp, ExtFOp, MaximumFOp>,
2125 NarrowExtremum<TruncFOp, ExtFOp, MaxNumFOp>,
2126 NarrowExtremum<TruncFOp, ExtFOp, MaximumNumFOp>,
2127 NarrowExtremum<TruncFOp, ExtFOp, MinimumFOp>,
2128 NarrowExtremum<TruncFOp, ExtFOp, MinNumFOp>,
2129 NarrowExtremum<TruncFOp, ExtFOp, MinimumNumFOp>,
2130 TruncFSIToFPToSIToFP, TruncFUIToFPToUIToFP>(context);
2131}
2132
2133bool arith::TruncFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2134 return checkWidthChangeCast<std::less, FloatType>(inputs, outputs);
2135}
2136
2137LogicalResult arith::TruncFOp::verify() {
2138 return verifyTruncateOp<FloatType>(*this);
2139}
2140
2141//===----------------------------------------------------------------------===//
2142// ConvertFOp
2143//===----------------------------------------------------------------------===//
2144
2145OpFoldResult arith::ConvertFOp::fold(FoldAdaptor adaptor) {
2146 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2147 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2149 adaptor.getOperands(), getType(),
2150 [this, &targetSemantics](const APFloat &a, bool &castStatus) {
2151 llvm::RoundingMode llvmRoundingMode =
2152 convertArithRoundingModeToLLVMIR(getRoundingmode());
2153 FailureOr<APFloat> result =
2154 convertFloatValue(a, targetSemantics, llvmRoundingMode);
2155 if (failed(result)) {
2156 castStatus = false;
2157 return a;
2158 }
2159 return *result;
2160 });
2161}
2162
2163bool arith::ConvertFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2164 if (!areValidCastInputsAndOutputs(inputs, outputs))
2165 return false;
2166 auto srcType = getTypeIfLike<FloatType>(inputs.front());
2167 auto dstType = getTypeIfLike<FloatType>(outputs.front());
2168 if (!srcType || !dstType)
2169 return false;
2170 return srcType != dstType &&
2171 srcType.getIntOrFloatBitWidth() == dstType.getIntOrFloatBitWidth();
2172}
2173
2174LogicalResult arith::ConvertFOp::verify() {
2175 auto srcType = cast<FloatType>(getElementTypeOrSelf(getIn().getType()));
2176 auto dstType = cast<FloatType>(getElementTypeOrSelf(getType()));
2177 if (srcType == dstType)
2178 return emitError("result element type ")
2179 << dstType << " must be different from operand element type "
2180 << srcType;
2181 if (srcType.getWidth() != dstType.getWidth())
2182 return emitError("result element type ")
2183 << dstType << " must have the same bitwidth as operand element type "
2184 << srcType;
2185 return success();
2186}
2187
2188//===----------------------------------------------------------------------===//
2189// ScalingTruncFOp
2190//===----------------------------------------------------------------------===//
2191
2192OpFoldResult arith::ScalingTruncFOp::fold(FoldAdaptor adaptor) {
2193 // scaling_truncf(in, scale) -> truncf(in / extf(scale)), matching the
2194 // expansion in ExpandOps.cpp. Unlike scaling_extf, the scale is widened to
2195 // the type of `in` rather than to the result type.
2196 if (!isFoldableScalingScale(getScale()))
2197 return {};
2198
2199 auto inElemType = cast<FloatType>(getElementTypeOrSelf(getIn().getType()));
2200 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2201 const llvm::fltSemantics &inSemantics = inElemType.getFloatSemantics();
2202 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2203 llvm::RoundingMode roundingMode =
2204 convertArithRoundingModeToLLVMIR(getRoundingmode());
2205 return foldScalingCastOp(
2206 adaptor.getIn(), adaptor.getScale(), getType(),
2207 [&](const APFloat &in, const APFloat &scale) -> std::optional<APFloat> {
2208 FailureOr<APFloat> scaleExt = convertFloatValue(scale, inSemantics);
2209 if (failed(scaleExt))
2210 return std::nullopt;
2211 APFloat quotient(in);
2212 quotient.divide(*scaleExt, kDefaultRoundingMode);
2213 FailureOr<APFloat> result =
2214 convertFloatValue(quotient, resSemantics, roundingMode);
2215 if (failed(result))
2216 return std::nullopt;
2217 return *result;
2218 });
2219}
2220
2221bool arith::ScalingTruncFOp::areCastCompatible(TypeRange inputs,
2222 TypeRange outputs) {
2223 return checkWidthChangeCast<std::less, FloatType>(inputs.front(), outputs);
2224}
2225
2226LogicalResult arith::ScalingTruncFOp::verify() {
2227 return verifyTruncateOp<FloatType>(*this);
2228}
2229
2230//===----------------------------------------------------------------------===//
2231// AndIOp
2232//===----------------------------------------------------------------------===//
2233
2234void arith::AndIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2235 MLIRContext *context) {
2236 patterns.add<AndIAndIConstant, AndOfExtUI, AndOfExtSI>(context);
2237}
2238
2239//===----------------------------------------------------------------------===//
2240// OrIOp
2241//===----------------------------------------------------------------------===//
2242
2243void arith::OrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2244 MLIRContext *context) {
2245 patterns.add<OrIOrIConstant, OrOfExtUI, OrOfExtSI>(context);
2246}
2247
2248//===----------------------------------------------------------------------===//
2249// Verifiers for casts between integers and floats.
2250//===----------------------------------------------------------------------===//
2251
2252template <typename From, typename To>
2253static bool checkIntFloatCast(TypeRange inputs, TypeRange outputs) {
2254 if (!areValidCastInputsAndOutputs(inputs, outputs))
2255 return false;
2256
2257 auto srcType = getTypeIfLike<From>(inputs.front());
2258 auto dstType = getTypeIfLike<To>(outputs.back());
2259
2260 return srcType && dstType;
2261}
2262
2263//===----------------------------------------------------------------------===//
2264// UIToFPOp
2265//===----------------------------------------------------------------------===//
2266
2267bool arith::UIToFPOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2268 return checkIntFloatCast<IntegerType, FloatType>(inputs, outputs);
2269}
2270
2271OpFoldResult arith::UIToFPOp::fold(FoldAdaptor adaptor) {
2272 Type resEleType = getElementTypeOrSelf(getType());
2274 adaptor.getOperands(), getType(),
2275 [&resEleType](const APInt &a, bool &castStatus) {
2276 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2277 APFloat apf(floatTy.getFloatSemantics(),
2278 APInt::getZero(floatTy.getWidth()));
2279 apf.convertFromAPInt(a, /*IsSigned=*/false,
2280 APFloat::rmNearestTiesToEven);
2281 return apf;
2282 });
2283}
2284
2285void arith::UIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2286 MLIRContext *context) {
2287 patterns.add<UIToFPOfExtUI>(context);
2288}
2289
2290//===----------------------------------------------------------------------===//
2291// SIToFPOp
2292//===----------------------------------------------------------------------===//
2293
2294bool arith::SIToFPOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2295 return checkIntFloatCast<IntegerType, FloatType>(inputs, outputs);
2296}
2297
2298OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
2299 Type resEleType = getElementTypeOrSelf(getType());
2301 adaptor.getOperands(), getType(),
2302 [&resEleType](const APInt &a, bool &castStatus) {
2303 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2304 APFloat apf(floatTy.getFloatSemantics(),
2305 APInt::getZero(floatTy.getWidth()));
2306 if (a.isNegative() &&
2307 !APFloat::semanticsHasSignedRepr(floatTy.getFloatSemantics())) {
2308 castStatus = false;
2309 return apf;
2310 }
2311 apf.convertFromAPInt(a, /*IsSigned=*/true,
2312 APFloat::rmNearestTiesToEven);
2313 return apf;
2314 });
2315}
2316
2317void arith::SIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2318 MLIRContext *context) {
2319 patterns.add<SIToFPOfExtSI, SIToFPOfExtUI>(context);
2320}
2321
2322//===----------------------------------------------------------------------===//
2323// FPToUIOp
2324//===----------------------------------------------------------------------===//
2325
2326bool arith::FPToUIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2327 return checkIntFloatCast<FloatType, IntegerType>(inputs, outputs);
2328}
2329
2330OpFoldResult arith::FPToUIOp::fold(FoldAdaptor adaptor) {
2331 Type resType = getElementTypeOrSelf(getType());
2332 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2334 adaptor.getOperands(), getType(),
2335 [&bitWidth](const APFloat &a, bool &castStatus) {
2336 bool ignored;
2337 APSInt api(bitWidth, /*isUnsigned=*/true);
2338 castStatus = APFloat::opInvalidOp !=
2339 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2340 return api;
2341 });
2342}
2343
2344//===----------------------------------------------------------------------===//
2345// FPToSIOp
2346//===----------------------------------------------------------------------===//
2347
2348bool arith::FPToSIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2349 return checkIntFloatCast<FloatType, IntegerType>(inputs, outputs);
2350}
2351
2352OpFoldResult arith::FPToSIOp::fold(FoldAdaptor adaptor) {
2353 Type resType = getElementTypeOrSelf(getType());
2354 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2356 adaptor.getOperands(), getType(),
2357 [&bitWidth](const APFloat &a, bool &castStatus) {
2358 bool ignored;
2359 APSInt api(bitWidth, /*isUnsigned=*/false);
2360 castStatus = APFloat::opInvalidOp !=
2361 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2362 return api;
2363 });
2364}
2365
2366//===----------------------------------------------------------------------===//
2367// IndexCastOp
2368//===----------------------------------------------------------------------===//
2369
2370/// Return the bit-width of \p t for the purpose of index_cast width checks.
2371/// For vector types use the element type; index maps to its internal storage
2372/// width (64 on all current targets).
2373static unsigned getIndexCastWidth(Type t) {
2374 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(t)))
2375 return intTy.getWidth();
2376 return IndexType::kInternalStorageBitWidth;
2377}
2378
2379static bool areIndexCastCompatible(TypeRange inputs, TypeRange outputs) {
2380 if (!areValidCastInputsAndOutputs(inputs, outputs))
2381 return false;
2382
2383 auto srcType = getTypeIfLikeOrMemRef<IntegerType, IndexType>(inputs.front());
2384 auto dstType = getTypeIfLikeOrMemRef<IntegerType, IndexType>(outputs.front());
2385 if (!srcType || !dstType)
2386 return false;
2387
2388 return (srcType.isIndex() && dstType.isSignlessInteger()) ||
2389 (srcType.isSignlessInteger() && dstType.isIndex());
2390}
2391
2392bool arith::IndexCastOp::areCastCompatible(TypeRange inputs,
2393 TypeRange outputs) {
2394 return areIndexCastCompatible(inputs, outputs);
2395}
2396
2397OpFoldResult arith::IndexCastOp::fold(FoldAdaptor adaptor) {
2398 // index_cast(constant) -> constant
2399 unsigned resultBitwidth = 64; // Default for index integer attributes.
2400 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(getType())))
2401 resultBitwidth = intTy.getWidth();
2402
2403 if (auto foldResult = constFoldCastOp<IntegerAttr, IntegerAttr>(
2404 adaptor.getOperands(), getType(),
2405 [resultBitwidth](const APInt &a, bool & /*castStatus*/) {
2406 return a.sextOrTrunc(resultBitwidth);
2407 }))
2408 return foldResult;
2409
2410 // index_cast(index_cast(x : A) : B) : A -> x, but only when B is at least
2411 // as wide as A. If B is narrower, the inner cast truncates and the outer
2412 // cast sign-extends, so the round-trip is lossy.
2413 if (auto inner = getOperand().getDefiningOp<arith::IndexCastOp>()) {
2414 Value x = inner.getOperand();
2415 if (x.getType() == getType()) {
2416 if (getIndexCastWidth(inner.getType()) >= getIndexCastWidth(x.getType()))
2417 return x;
2418 }
2419 }
2420 return {};
2421}
2422
2423void arith::IndexCastOp::getCanonicalizationPatterns(
2424 RewritePatternSet &patterns, MLIRContext *context) {
2425 patterns.add<IndexCastOfExtSI>(context);
2426}
2427
2428//===----------------------------------------------------------------------===//
2429// IndexCastUIOp
2430//===----------------------------------------------------------------------===//
2431
2432bool arith::IndexCastUIOp::areCastCompatible(TypeRange inputs,
2433 TypeRange outputs) {
2434 return areIndexCastCompatible(inputs, outputs);
2435}
2436
2437OpFoldResult arith::IndexCastUIOp::fold(FoldAdaptor adaptor) {
2438 // index_castui(constant) -> constant
2439 unsigned resultBitwidth = 64; // Default for index integer attributes.
2440 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(getType())))
2441 resultBitwidth = intTy.getWidth();
2442
2443 if (auto foldResult = constFoldCastOp<IntegerAttr, IntegerAttr>(
2444 adaptor.getOperands(), getType(),
2445 [resultBitwidth](const APInt &a, bool & /*castStatus*/) {
2446 return a.zextOrTrunc(resultBitwidth);
2447 }))
2448 return foldResult;
2449
2450 // index_castui(index_castui(x : A) : B) : A -> x, but only when B is at
2451 // least as wide as A. If B is narrower, the inner cast truncates and the
2452 // outer cast zero-extends, so the round-trip is lossy.
2453 if (auto inner = getOperand().getDefiningOp<arith::IndexCastUIOp>()) {
2454 Value x = inner.getOperand();
2455 if (x.getType() == getType()) {
2456 if (getIndexCastWidth(inner.getType()) >= getIndexCastWidth(x.getType()))
2457 return x;
2458 }
2459 }
2460 return {};
2461}
2462
2463void arith::IndexCastUIOp::getCanonicalizationPatterns(
2464 RewritePatternSet &patterns, MLIRContext *context) {
2465 patterns.add<IndexCastUIOfExtUI>(context);
2466}
2467
2468//===----------------------------------------------------------------------===//
2469// BitcastOp
2470//===----------------------------------------------------------------------===//
2471
2472bool arith::BitcastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2473 if (!areValidCastInputsAndOutputs(inputs, outputs))
2474 return false;
2475
2476 auto srcType = getTypeIfLikeOrMemRef<IntegerType, FloatType>(inputs.front());
2477 auto dstType = getTypeIfLikeOrMemRef<IntegerType, FloatType>(outputs.front());
2478 if (!srcType || !dstType)
2479 return false;
2480
2481 return srcType.getIntOrFloatBitWidth() == dstType.getIntOrFloatBitWidth();
2482}
2483
2484OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
2485 auto resType = getType();
2486 auto operand = adaptor.getIn();
2487 if (!operand)
2488 return {};
2489
2490 /// Bitcast dense elements.
2491 if (auto denseAttr = dyn_cast_or_null<DenseElementsAttr>(operand))
2492 return denseAttr.bitcast(llvm::cast<ShapedType>(resType).getElementType());
2493 /// Other shaped types unhandled.
2494 if (llvm::isa<ShapedType>(resType))
2495 return {};
2496
2497 /// Bitcast poison.
2498 if (matchPattern(operand, ub::m_Poison()))
2499 return ub::PoisonAttr::get(getContext());
2500
2501 /// Bitcast integer or float to integer or float.
2502 if (!llvm::isa<FloatAttr, IntegerAttr>(operand))
2503 return {};
2504
2505 APInt bits = llvm::isa<FloatAttr>(operand)
2506 ? llvm::cast<FloatAttr>(operand).getValue().bitcastToAPInt()
2507 : llvm::cast<IntegerAttr>(operand).getValue();
2508 assert(resType.getIntOrFloatBitWidth() == bits.getBitWidth() &&
2509 "trying to fold on broken IR: operands have incompatible types");
2510
2511 if (auto resFloatType = dyn_cast<FloatType>(resType))
2512 return FloatAttr::get(resType,
2513 APFloat(resFloatType.getFloatSemantics(), bits));
2514 return IntegerAttr::get(resType, bits);
2515}
2516
2517void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2518 MLIRContext *context) {
2519 patterns.add<BitcastOfBitcast>(context);
2520}
2521
2522//===----------------------------------------------------------------------===//
2523// CmpIOp
2524//===----------------------------------------------------------------------===//
2525
2526/// Compute `lhs` `pred` `rhs`, where `pred` is one of the known integer
2527/// comparison predicates.
2528bool mlir::arith::applyCmpPredicate(arith::CmpIPredicate predicate,
2529 const APInt &lhs, const APInt &rhs) {
2530 switch (predicate) {
2531 case arith::CmpIPredicate::eq:
2532 return lhs.eq(rhs);
2533 case arith::CmpIPredicate::ne:
2534 return lhs.ne(rhs);
2535 case arith::CmpIPredicate::slt:
2536 return lhs.slt(rhs);
2537 case arith::CmpIPredicate::sle:
2538 return lhs.sle(rhs);
2539 case arith::CmpIPredicate::sgt:
2540 return lhs.sgt(rhs);
2541 case arith::CmpIPredicate::sge:
2542 return lhs.sge(rhs);
2543 case arith::CmpIPredicate::ult:
2544 return lhs.ult(rhs);
2545 case arith::CmpIPredicate::ule:
2546 return lhs.ule(rhs);
2547 case arith::CmpIPredicate::ugt:
2548 return lhs.ugt(rhs);
2549 case arith::CmpIPredicate::uge:
2550 return lhs.uge(rhs);
2551 }
2552 llvm_unreachable("unknown cmpi predicate kind");
2553}
2554
2555/// Returns true if the predicate is true for two equal operands.
2556static bool applyCmpPredicateToEqualOperands(arith::CmpIPredicate predicate) {
2557 switch (predicate) {
2558 case arith::CmpIPredicate::eq:
2559 case arith::CmpIPredicate::sle:
2560 case arith::CmpIPredicate::sge:
2561 case arith::CmpIPredicate::ule:
2562 case arith::CmpIPredicate::uge:
2563 return true;
2564 case arith::CmpIPredicate::ne:
2565 case arith::CmpIPredicate::slt:
2566 case arith::CmpIPredicate::sgt:
2567 case arith::CmpIPredicate::ult:
2568 case arith::CmpIPredicate::ugt:
2569 return false;
2570 }
2571 llvm_unreachable("unknown cmpi predicate kind");
2572}
2573
2574static std::optional<int64_t> getIntegerWidth(Type t) {
2575 if (auto intType = dyn_cast<IntegerType>(t)) {
2576 return intType.getWidth();
2577 }
2578 if (auto vectorIntType = dyn_cast<VectorType>(t)) {
2579 return llvm::cast<IntegerType>(vectorIntType.getElementType()).getWidth();
2580 }
2581 return std::nullopt;
2582}
2583
2584OpFoldResult arith::CmpIOp::fold(FoldAdaptor adaptor) {
2585 // cmpi(pred, x, x)
2586 if (getLhs() == getRhs()) {
2587 auto val = applyCmpPredicateToEqualOperands(getPredicate());
2588 return getBoolAttribute(getType(), val);
2589 }
2590
2591 if (matchPattern(adaptor.getRhs(), m_Zero())) {
2592 if (auto extOp = getLhs().getDefiningOp<ExtSIOp>()) {
2593 // extsi(%x : i1 -> iN) != 0 -> %x
2594 std::optional<int64_t> integerWidth =
2595 getIntegerWidth(extOp.getOperand().getType());
2596 if (integerWidth && integerWidth.value() == 1 &&
2597 getPredicate() == arith::CmpIPredicate::ne)
2598 return extOp.getOperand();
2599 }
2600 if (auto extOp = getLhs().getDefiningOp<ExtUIOp>()) {
2601 // extui(%x : i1 -> iN) != 0 -> %x
2602 std::optional<int64_t> integerWidth =
2603 getIntegerWidth(extOp.getOperand().getType());
2604 if (integerWidth && integerWidth.value() == 1 &&
2605 getPredicate() == arith::CmpIPredicate::ne)
2606 return extOp.getOperand();
2607 }
2608
2609 // arith.cmpi ne, %val, %zero : i1 -> %val
2610 if (getElementTypeOrSelf(getLhs().getType()).isInteger(1) &&
2611 getPredicate() == arith::CmpIPredicate::ne)
2612 return getLhs();
2613 }
2614
2615 if (matchPattern(adaptor.getRhs(), m_One())) {
2616 // arith.cmpi eq, %val, %one : i1 -> %val
2617 if (getElementTypeOrSelf(getLhs().getType()).isInteger(1) &&
2618 getPredicate() == arith::CmpIPredicate::eq)
2619 return getLhs();
2620 }
2621
2622 // Move constant to the right side.
2623 if (adaptor.getLhs() && !adaptor.getRhs()) {
2624 // Do not use invertPredicate, as it will change eq to ne and vice versa.
2625 using Pred = CmpIPredicate;
2626 const std::pair<Pred, Pred> invPreds[] = {
2627 {Pred::slt, Pred::sgt}, {Pred::sgt, Pred::slt}, {Pred::sle, Pred::sge},
2628 {Pred::sge, Pred::sle}, {Pred::ult, Pred::ugt}, {Pred::ugt, Pred::ult},
2629 {Pred::ule, Pred::uge}, {Pred::uge, Pred::ule}, {Pred::eq, Pred::eq},
2630 {Pred::ne, Pred::ne},
2631 };
2632 Pred origPred = getPredicate();
2633 for (auto pred : invPreds) {
2634 if (origPred == pred.first) {
2635 setPredicate(pred.second);
2636 Value lhs = getLhs();
2637 Value rhs = getRhs();
2638 getLhsMutable().assign(rhs);
2639 getRhsMutable().assign(lhs);
2640 return getResult();
2641 }
2642 }
2643 llvm_unreachable("unknown cmpi predicate kind");
2644 }
2645
2646 // We are moving constants to the right side; So if lhs is constant rhs is
2647 // guaranteed to be a constant.
2648 if (auto lhs = dyn_cast_if_present<TypedAttr>(adaptor.getLhs())) {
2650 adaptor.getOperands(), getI1SameShape(lhs.getType()),
2651 [pred = getPredicate()](const APInt &lhs, const APInt &rhs) {
2652 return APInt(1,
2653 static_cast<int64_t>(applyCmpPredicate(pred, lhs, rhs)));
2654 });
2655 }
2656
2657 return {};
2658}
2659
2660void arith::CmpIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2661 MLIRContext *context) {
2662 patterns.insert<CmpIExtSI, CmpIExtUI>(context);
2663}
2664
2665//===----------------------------------------------------------------------===//
2666// CmpFOp
2667//===----------------------------------------------------------------------===//
2668
2669/// Compute `lhs` `pred` `rhs`, where `pred` is one of the known floating point
2670/// comparison predicates.
2671bool mlir::arith::applyCmpPredicate(arith::CmpFPredicate predicate,
2672 const APFloat &lhs, const APFloat &rhs) {
2673 auto cmpResult = lhs.compare(rhs);
2674 switch (predicate) {
2675 case arith::CmpFPredicate::AlwaysFalse:
2676 return false;
2677 case arith::CmpFPredicate::OEQ:
2678 return cmpResult == APFloat::cmpEqual;
2679 case arith::CmpFPredicate::OGT:
2680 return cmpResult == APFloat::cmpGreaterThan;
2681 case arith::CmpFPredicate::OGE:
2682 return cmpResult == APFloat::cmpGreaterThan ||
2683 cmpResult == APFloat::cmpEqual;
2684 case arith::CmpFPredicate::OLT:
2685 return cmpResult == APFloat::cmpLessThan;
2686 case arith::CmpFPredicate::OLE:
2687 return cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2688 case arith::CmpFPredicate::ONE:
2689 return cmpResult != APFloat::cmpUnordered && cmpResult != APFloat::cmpEqual;
2690 case arith::CmpFPredicate::ORD:
2691 return cmpResult != APFloat::cmpUnordered;
2692 case arith::CmpFPredicate::UEQ:
2693 return cmpResult == APFloat::cmpUnordered || cmpResult == APFloat::cmpEqual;
2694 case arith::CmpFPredicate::UGT:
2695 return cmpResult == APFloat::cmpUnordered ||
2696 cmpResult == APFloat::cmpGreaterThan;
2697 case arith::CmpFPredicate::UGE:
2698 return cmpResult == APFloat::cmpUnordered ||
2699 cmpResult == APFloat::cmpGreaterThan ||
2700 cmpResult == APFloat::cmpEqual;
2701 case arith::CmpFPredicate::ULT:
2702 return cmpResult == APFloat::cmpUnordered ||
2703 cmpResult == APFloat::cmpLessThan;
2704 case arith::CmpFPredicate::ULE:
2705 return cmpResult == APFloat::cmpUnordered ||
2706 cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2707 case arith::CmpFPredicate::UNE:
2708 return cmpResult != APFloat::cmpEqual;
2709 case arith::CmpFPredicate::UNO:
2710 return cmpResult == APFloat::cmpUnordered;
2711 case arith::CmpFPredicate::AlwaysTrue:
2712 return true;
2713 }
2714 llvm_unreachable("unknown cmpf predicate kind");
2715}
2716
2717OpFoldResult arith::CmpFOp::fold(FoldAdaptor adaptor) {
2718 auto lhs = dyn_cast_if_present<FloatAttr>(adaptor.getLhs());
2719 auto rhs = dyn_cast_if_present<FloatAttr>(adaptor.getRhs());
2720
2721 // If one operand is NaN, making them both NaN does not change the result.
2722 if (lhs && lhs.getValue().isNaN())
2723 rhs = lhs;
2724 if (rhs && rhs.getValue().isNaN())
2725 lhs = rhs;
2726
2727 if (!lhs || !rhs)
2728 return {};
2729
2730 auto val = applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
2731 return BoolAttr::get(getContext(), val);
2732}
2733
2734class CmpFIntToFPConst final : public OpRewritePattern<CmpFOp> {
2735public:
2736 using Base::Base;
2737
2738 static CmpIPredicate convertToIntegerPredicate(CmpFPredicate pred,
2739 bool isUnsigned) {
2740 using namespace arith;
2741 switch (pred) {
2742 case CmpFPredicate::UEQ:
2743 case CmpFPredicate::OEQ:
2744 return CmpIPredicate::eq;
2745 case CmpFPredicate::UGT:
2746 case CmpFPredicate::OGT:
2747 return isUnsigned ? CmpIPredicate::ugt : CmpIPredicate::sgt;
2748 case CmpFPredicate::UGE:
2749 case CmpFPredicate::OGE:
2750 return isUnsigned ? CmpIPredicate::uge : CmpIPredicate::sge;
2751 case CmpFPredicate::ULT:
2752 case CmpFPredicate::OLT:
2753 return isUnsigned ? CmpIPredicate::ult : CmpIPredicate::slt;
2754 case CmpFPredicate::ULE:
2755 case CmpFPredicate::OLE:
2756 return isUnsigned ? CmpIPredicate::ule : CmpIPredicate::sle;
2757 case CmpFPredicate::UNE:
2758 case CmpFPredicate::ONE:
2759 return CmpIPredicate::ne;
2760 default:
2761 llvm_unreachable("Unexpected predicate!");
2762 }
2763 }
2764
2765 LogicalResult matchAndRewrite(CmpFOp op,
2766 PatternRewriter &rewriter) const override {
2767 FloatAttr flt;
2768 if (!matchPattern(op.getRhs(), m_Constant(&flt)))
2769 return failure();
2770
2771 const APFloat &rhs = flt.getValue();
2772
2773 // Don't attempt to fold a nan.
2774 if (rhs.isNaN())
2775 return failure();
2776
2777 // Get the width of the mantissa. We don't want to hack on conversions that
2778 // might lose information from the integer, e.g. "i64 -> float"
2779 FloatType floatTy = llvm::cast<FloatType>(op.getRhs().getType());
2780 int mantissaWidth = floatTy.getFPMantissaWidth();
2781 if (mantissaWidth <= 0)
2782 return failure();
2783
2784 bool isUnsigned;
2785 Value intVal;
2786
2787 if (auto si = op.getLhs().getDefiningOp<SIToFPOp>()) {
2788 isUnsigned = false;
2789 intVal = si.getIn();
2790 } else if (auto ui = op.getLhs().getDefiningOp<UIToFPOp>()) {
2791 isUnsigned = true;
2792 intVal = ui.getIn();
2793 } else {
2794 return failure();
2795 }
2796
2797 // Check to see that the input is converted from an integer type that is
2798 // small enough that preserves all bits.
2799 auto intTy = llvm::cast<IntegerType>(intVal.getType());
2800 auto intWidth = intTy.getWidth();
2801
2802 // Number of bits representing values, as opposed to the sign
2803 auto valueBits = isUnsigned ? intWidth : (intWidth - 1);
2804
2805 // Following test does NOT adjust intWidth downwards for signed inputs,
2806 // because the most negative value still requires all the mantissa bits
2807 // to distinguish it from one less than that value.
2808 if ((int)intWidth > mantissaWidth) {
2809 // Conversion would lose accuracy. Check if loss can impact comparison.
2810 int exponent = ilogb(rhs);
2811 if (exponent == APFloat::IEK_Inf) {
2812 int maxExponent = ilogb(APFloat::getLargest(rhs.getSemantics()));
2813 if (maxExponent < (int)valueBits) {
2814 // Conversion could create infinity.
2815 return failure();
2816 }
2817 } else {
2818 // Note that if rhs is zero or NaN, then Exp is negative
2819 // and first condition is trivially false.
2820 if (mantissaWidth <= exponent && exponent <= (int)valueBits) {
2821 // Conversion could affect comparison.
2822 return failure();
2823 }
2824 }
2825 }
2826
2827 // Convert to equivalent cmpi predicate
2828 CmpIPredicate pred;
2829 switch (op.getPredicate()) {
2830 case CmpFPredicate::ORD:
2831 // Int to fp conversion doesn't create a nan (ord checks neither is a nan)
2832 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2833 /*width=*/1);
2834 return success();
2835 case CmpFPredicate::UNO:
2836 // Int to fp conversion doesn't create a nan (uno checks either is a nan)
2837 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2838 /*width=*/1);
2839 return success();
2840 default:
2841 pred = convertToIntegerPredicate(op.getPredicate(), isUnsigned);
2842 break;
2843 }
2844
2845 if (!isUnsigned) {
2846 // If the rhs value is > SignedMax, fold the comparison. This handles
2847 // +INF and large values.
2848 APFloat signedMax(rhs.getSemantics());
2849 signedMax.convertFromAPInt(APInt::getSignedMaxValue(intWidth), true,
2850 APFloat::rmNearestTiesToEven);
2851 if (signedMax < rhs) { // smax < 13123.0
2852 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::slt ||
2853 pred == CmpIPredicate::sle)
2854 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2855 /*width=*/1);
2856 else
2857 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2858 /*width=*/1);
2859 return success();
2860 }
2861 } else {
2862 // If the rhs value is > UnsignedMax, fold the comparison. This handles
2863 // +INF and large values.
2864 APFloat unsignedMax(rhs.getSemantics());
2865 unsignedMax.convertFromAPInt(APInt::getMaxValue(intWidth), false,
2866 APFloat::rmNearestTiesToEven);
2867 if (unsignedMax < rhs) { // umax < 13123.0
2868 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ult ||
2869 pred == CmpIPredicate::ule)
2870 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2871 /*width=*/1);
2872 else
2873 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2874 /*width=*/1);
2875 return success();
2876 }
2877 }
2878
2879 if (!isUnsigned) {
2880 // See if the rhs value is < SignedMin.
2881 APFloat signedMin(rhs.getSemantics());
2882 signedMin.convertFromAPInt(APInt::getSignedMinValue(intWidth), true,
2883 APFloat::rmNearestTiesToEven);
2884 if (signedMin > rhs) { // smin > 12312.0
2885 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::sgt ||
2886 pred == CmpIPredicate::sge)
2887 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2888 /*width=*/1);
2889 else
2890 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2891 /*width=*/1);
2892 return success();
2893 }
2894 } else {
2895 // See if the rhs value is < UnsignedMin.
2896 APFloat unsignedMin(rhs.getSemantics());
2897 unsignedMin.convertFromAPInt(APInt::getMinValue(intWidth), false,
2898 APFloat::rmNearestTiesToEven);
2899 if (unsignedMin > rhs) { // umin > 12312.0
2900 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ugt ||
2901 pred == CmpIPredicate::uge)
2902 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2903 /*width=*/1);
2904 else
2905 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2906 /*width=*/1);
2907 return success();
2908 }
2909 }
2910
2911 // Okay, now we know that the FP constant fits in the range [SMIN, SMAX] or
2912 // [0, UMAX], but it may still be fractional. See if it is fractional by
2913 // casting the FP value to the integer value and back, checking for
2914 // equality. Don't do this for zero, because -0.0 is not fractional.
2915 bool ignored;
2916 APSInt rhsInt(intWidth, isUnsigned);
2917 if (APFloat::opInvalidOp ==
2918 rhs.convertToInteger(rhsInt, APFloat::rmTowardZero, &ignored)) {
2919 // Undefined behavior invoked - the destination type can't represent
2920 // the input constant.
2921 return failure();
2922 }
2923
2924 if (!rhs.isZero()) {
2925 APFloat apf(floatTy.getFloatSemantics(),
2926 APInt::getZero(floatTy.getWidth()));
2927 apf.convertFromAPInt(rhsInt, !isUnsigned, APFloat::rmNearestTiesToEven);
2928
2929 bool equal = apf == rhs;
2930 if (!equal) {
2931 // If we had a comparison against a fractional value, we have to adjust
2932 // the compare predicate and sometimes the value. rhsInt is rounded
2933 // towards zero at this point.
2934 switch (pred) {
2935 case CmpIPredicate::ne: // (float)int != 4.4 --> true
2936 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2937 /*width=*/1);
2938 return success();
2939 case CmpIPredicate::eq: // (float)int == 4.4 --> false
2940 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2941 /*width=*/1);
2942 return success();
2943 case CmpIPredicate::ule:
2944 // (float)int <= 4.4 --> int <= 4
2945 // (float)int <= -4.4 --> false
2946 if (rhs.isNegative()) {
2947 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2948 /*width=*/1);
2949 return success();
2950 }
2951 break;
2952 case CmpIPredicate::sle:
2953 // (float)int <= 4.4 --> int <= 4
2954 // (float)int <= -4.4 --> int < -4
2955 if (rhs.isNegative())
2956 pred = CmpIPredicate::slt;
2957 break;
2958 case CmpIPredicate::ult:
2959 // (float)int < -4.4 --> false
2960 // (float)int < 4.4 --> int <= 4
2961 if (rhs.isNegative()) {
2962 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2963 /*width=*/1);
2964 return success();
2965 }
2966 pred = CmpIPredicate::ule;
2967 break;
2968 case CmpIPredicate::slt:
2969 // (float)int < -4.4 --> int < -4
2970 // (float)int < 4.4 --> int <= 4
2971 if (!rhs.isNegative())
2972 pred = CmpIPredicate::sle;
2973 break;
2974 case CmpIPredicate::ugt:
2975 // (float)int > 4.4 --> int > 4
2976 // (float)int > -4.4 --> true
2977 if (rhs.isNegative()) {
2978 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2979 /*width=*/1);
2980 return success();
2981 }
2982 break;
2983 case CmpIPredicate::sgt:
2984 // (float)int > 4.4 --> int > 4
2985 // (float)int > -4.4 --> int >= -4
2986 if (rhs.isNegative())
2987 pred = CmpIPredicate::sge;
2988 break;
2989 case CmpIPredicate::uge:
2990 // (float)int >= -4.4 --> true
2991 // (float)int >= 4.4 --> int > 4
2992 if (rhs.isNegative()) {
2993 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2994 /*width=*/1);
2995 return success();
2996 }
2997 pred = CmpIPredicate::ugt;
2998 break;
2999 case CmpIPredicate::sge:
3000 // (float)int >= -4.4 --> int >= -4
3001 // (float)int >= 4.4 --> int > 4
3002 if (!rhs.isNegative())
3003 pred = CmpIPredicate::sgt;
3004 break;
3005 }
3006 }
3007 }
3008
3009 // Lower this FP comparison into an appropriate integer version of the
3010 // comparison.
3011 rewriter.replaceOpWithNewOp<CmpIOp>(
3012 op, pred, intVal,
3013 ConstantOp::create(rewriter, op.getLoc(), intVal.getType(),
3014 rewriter.getIntegerAttr(intVal.getType(), rhsInt)));
3015 return success();
3016 }
3017};
3018
3019void arith::CmpFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
3020 MLIRContext *context) {
3021 patterns.insert<CmpFIntToFPConst>(context);
3022}
3023
3024//===----------------------------------------------------------------------===//
3025// SelectOp
3026//===----------------------------------------------------------------------===//
3027
3028// select %arg, %c1, %c0 => extui %arg
3029struct SelectToExtUI : public OpRewritePattern<arith::SelectOp> {
3030 using Base::Base;
3031
3032 LogicalResult matchAndRewrite(arith::SelectOp op,
3033 PatternRewriter &rewriter) const override {
3034 // Cannot extui i1 to i1, or i1 to f32
3035 if (!llvm::isa<IntegerType>(op.getType()) || op.getType().isInteger(1))
3036 return failure();
3037
3038 // select %x, c1, %c0 => extui %arg
3039 if (matchPattern(op.getTrueValue(), m_One()) &&
3040 matchPattern(op.getFalseValue(), m_Zero())) {
3041 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(op, op.getType(),
3042 op.getCondition());
3043 return success();
3044 }
3045
3046 // select %x, c0, %c1 => extui (xor %arg, true)
3047 if (matchPattern(op.getTrueValue(), m_Zero()) &&
3048 matchPattern(op.getFalseValue(), m_One())) {
3049 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(
3050 op, op.getType(),
3051 arith::XOrIOp::create(
3052 rewriter, op.getLoc(), op.getCondition(),
3053 arith::ConstantIntOp::create(rewriter, op.getLoc(),
3054 op.getCondition().getType(), 1)));
3055 return success();
3056 }
3057
3058 return failure();
3059 }
3060};
3061
3062void arith::SelectOp::getCanonicalizationPatterns(RewritePatternSet &results,
3063 MLIRContext *context) {
3064 results.add<RedundantSelectFalse, RedundantSelectTrue, SelectNotCond,
3065 SelectI1ToNot, SelectCmpISgeToMaxSI, SelectCmpISgeToMinSI,
3066 SelectCmpISgtToMaxSI, SelectCmpISgtToMinSI, SelectCmpISleToMaxSI,
3067 SelectCmpISleToMinSI, SelectCmpISltToMaxSI, SelectCmpISltToMinSI,
3068 SelectCmpIUgeToMaxUI, SelectCmpIUgeToMinUI, SelectCmpIUgtToMaxUI,
3069 SelectCmpIUgtToMinUI, SelectCmpIUleToMaxUI, SelectCmpIUleToMinUI,
3070 SelectCmpIUltToMaxUI, SelectCmpIUltToMinUI, SelectToExtUI>(
3071 context);
3072}
3073
3074OpFoldResult arith::SelectOp::fold(FoldAdaptor adaptor) {
3075 Value trueVal = getTrueValue();
3076 Value falseVal = getFalseValue();
3077 if (trueVal == falseVal)
3078 return trueVal;
3079
3080 Value condition = getCondition();
3081
3082 // select true, %0, %1 => %0
3083 if (matchPattern(adaptor.getCondition(), m_One()))
3084 return trueVal;
3085
3086 // select false, %0, %1 => %1
3087 if (matchPattern(adaptor.getCondition(), m_Zero()))
3088 return falseVal;
3089
3090 // If either operand is fully poisoned, return the other.
3091 if (matchPattern(adaptor.getTrueValue(), ub::m_Poison()))
3092 return falseVal;
3093
3094 if (matchPattern(adaptor.getFalseValue(), ub::m_Poison()))
3095 return trueVal;
3096
3097 // select %x, true, false => %x
3098 if (getType().isSignlessInteger(1) &&
3099 matchPattern(adaptor.getTrueValue(), m_One()) &&
3100 matchPattern(adaptor.getFalseValue(), m_Zero()))
3101 return condition;
3102
3103 if (auto cmp = condition.getDefiningOp<arith::CmpIOp>()) {
3104 auto pred = cmp.getPredicate();
3105 if (pred == arith::CmpIPredicate::eq || pred == arith::CmpIPredicate::ne) {
3106 auto cmpLhs = cmp.getLhs();
3107 auto cmpRhs = cmp.getRhs();
3108
3109 // %0 = arith.cmpi eq, %arg0, %arg1
3110 // %1 = arith.select %0, %arg0, %arg1 => %arg1
3111
3112 // %0 = arith.cmpi ne, %arg0, %arg1
3113 // %1 = arith.select %0, %arg0, %arg1 => %arg0
3114
3115 if ((cmpLhs == trueVal && cmpRhs == falseVal) ||
3116 (cmpRhs == trueVal && cmpLhs == falseVal))
3117 return pred == arith::CmpIPredicate::ne ? trueVal : falseVal;
3118 }
3119 }
3120
3121 // Constant-fold constant operands over non-splat constant condition.
3122 // select %cst_vec, %cst0, %cst1 => %cst2
3123 if (auto cond =
3124 dyn_cast_if_present<DenseElementsAttr>(adaptor.getCondition())) {
3125 // DenseElementsAttr by construction always has a static shape.
3126 assert(cond.getType().hasStaticShape() &&
3127 "DenseElementsAttr must have static shape");
3128 if (auto lhs =
3129 dyn_cast_if_present<DenseElementsAttr>(adaptor.getTrueValue())) {
3130 if (auto rhs =
3131 dyn_cast_if_present<DenseElementsAttr>(adaptor.getFalseValue())) {
3132 SmallVector<Attribute> results;
3133 results.reserve(static_cast<size_t>(cond.getNumElements()));
3134 auto condVals = llvm::make_range(cond.value_begin<BoolAttr>(),
3135 cond.value_end<BoolAttr>());
3136 auto lhsVals = llvm::make_range(lhs.value_begin<Attribute>(),
3137 lhs.value_end<Attribute>());
3138 auto rhsVals = llvm::make_range(rhs.value_begin<Attribute>(),
3139 rhs.value_end<Attribute>());
3140
3141 for (auto [condVal, lhsVal, rhsVal] :
3142 llvm::zip_equal(condVals, lhsVals, rhsVals))
3143 results.push_back(condVal.getValue() ? lhsVal : rhsVal);
3144
3145 return DenseElementsAttr::get(lhs.getType(), results);
3146 }
3147 }
3148 }
3149
3150 return nullptr;
3151}
3152
3153ParseResult SelectOp::parse(OpAsmParser &parser, OperationState &result) {
3154 Type conditionType, resultType;
3155 SmallVector<OpAsmParser::UnresolvedOperand, 3> operands;
3156 if (parser.parseOperandList(operands, /*requiredOperandCount=*/3) ||
3157 parser.parseOptionalAttrDict(result.attributes) ||
3158 parser.parseColonType(resultType))
3159 return failure();
3160
3161 // Check for the explicit condition type if this is a masked tensor or vector.
3162 if (succeeded(parser.parseOptionalComma())) {
3163 conditionType = resultType;
3164 if (parser.parseType(resultType))
3165 return failure();
3166 } else {
3167 conditionType = parser.getBuilder().getI1Type();
3168 }
3169
3170 result.addTypes(resultType);
3171 return parser.resolveOperands(operands,
3172 {conditionType, resultType, resultType},
3173 parser.getNameLoc(), result.operands);
3174}
3175
3176void arith::SelectOp::print(OpAsmPrinter &p) {
3177 p << " " << getOperands();
3178 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
3179 p << " : ";
3180 if (ShapedType condType = dyn_cast<ShapedType>(getCondition().getType()))
3181 p << condType << ", ";
3182 p << getType();
3183}
3184
3185LogicalResult arith::SelectOp::verify() {
3186 Type conditionType = getCondition().getType();
3187 if (conditionType.isSignlessInteger(1))
3188 return success();
3189
3190 // If the result type is a vector or tensor, the type can be a mask with the
3191 // same elements.
3192 Type resultType = getType();
3193 if (!llvm::isa<TensorType, VectorType>(resultType))
3194 return emitOpError() << "expected condition to be a signless i1, but got "
3195 << conditionType;
3196 Type shapedConditionType = getI1SameShape(resultType);
3197 if (conditionType != shapedConditionType) {
3198 return emitOpError() << "expected condition type to have the same shape "
3199 "as the result type, expected "
3200 << shapedConditionType << ", but got "
3201 << conditionType;
3202 }
3203 return success();
3204}
3205//===----------------------------------------------------------------------===//
3206// ShLIOp
3207//===----------------------------------------------------------------------===//
3208
3209OpFoldResult arith::ShLIOp::fold(FoldAdaptor adaptor) {
3210 // TODO: shli(x, c) -> poison when c is out of range (c >= bit width). An
3211 // out-of-range shift amount is undefined behaviour and could fold to poison,
3212 // but that would make the arith dialect depend on the ub dialect to
3213 // materialize ub.poison; left out for now.
3214
3215 // shli(x, 0) -> x
3216 if (matchPattern(adaptor.getRhs(), m_Zero()))
3217 return getLhs();
3218 // shli(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3219 // it to 0 is valid.
3220 if (matchPattern(adaptor.getLhs(), m_Zero()))
3221 return getLhs();
3222 // Don't fold if shifting more or equal than the bit width.
3223 bool bounded = false;
3225 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3226 bounded = b.ult(b.getBitWidth());
3227 return a.shl(b);
3228 });
3229 return bounded ? result : Attribute();
3230}
3231
3232//===----------------------------------------------------------------------===//
3233// ShRUIOp
3234//===----------------------------------------------------------------------===//
3235
3236OpFoldResult arith::ShRUIOp::fold(FoldAdaptor adaptor) {
3237 // TODO: shrui(x, c) -> poison when c is out of range (c >= bit width). An
3238 // out-of-range shift amount is undefined behaviour and could fold to poison,
3239 // but that would make the arith dialect depend on the ub dialect to
3240 // materialize ub.poison; left out for now.
3241
3242 // shrui(x, 0) -> x
3243 if (matchPattern(adaptor.getRhs(), m_Zero()))
3244 return getLhs();
3245 // shrui(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3246 // it to 0 is valid.
3247 if (matchPattern(adaptor.getLhs(), m_Zero()))
3248 return getLhs();
3249 // shrui(x, x) -> 0. For any in-range shift amount v < bitwidth, v >> v == 0
3250 // (v < 2^v); out-of-range amounts yield poison, so 0 is a valid refinement.
3251 if (getLhs() == getRhs())
3252 return getIntegerAttrOfType(getType(), 0);
3253 // Don't fold if shifting more or equal than the bit width.
3254 bool bounded = false;
3256 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3257 bounded = b.ult(b.getBitWidth());
3258 return a.lshr(b);
3259 });
3260 return bounded ? result : Attribute();
3261}
3262
3263//===----------------------------------------------------------------------===//
3264// ShRSIOp
3265//===----------------------------------------------------------------------===//
3266
3267OpFoldResult arith::ShRSIOp::fold(FoldAdaptor adaptor) {
3268 // TODO: shrsi(x, c) -> poison when c is out of range (c >= bit width). An
3269 // out-of-range shift amount is undefined behaviour and could fold to poison,
3270 // but that would make the arith dialect depend on the ub dialect to
3271 // materialize ub.poison; left out for now.
3272
3273 // shrsi(x, 0) -> x
3274 if (matchPattern(adaptor.getRhs(), m_Zero()))
3275 return getLhs();
3276 // shrsi(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3277 // it to 0 is valid.
3278 if (matchPattern(adaptor.getLhs(), m_Zero()))
3279 return getLhs();
3280 // shrsi(x, x) -> 0. For any in-range shift amount v < bitwidth, v is a small
3281 // non-negative value and v >> v == 0; out-of-range amounts yield poison.
3282 if (getLhs() == getRhs())
3283 return getIntegerAttrOfType(getType(), 0);
3284 // shrsi(-1, x) -> -1. Arithmetic shift of all-ones is all-ones for any
3285 // in-range amount; out-of-range amounts yield poison.
3286 if (APInt val;
3287 matchPattern(adaptor.getLhs(), m_ConstantInt(&val)) && val.isAllOnes())
3288 return getLhs();
3289 // Don't fold if shifting more or equal than the bit width.
3290 bool bounded = false;
3292 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3293 bounded = b.ult(b.getBitWidth());
3294 return a.ashr(b);
3295 });
3296 return bounded ? result : Attribute();
3297}
3298
3299//===----------------------------------------------------------------------===//
3300// Atomic Enum
3301//===----------------------------------------------------------------------===//
3302
3303/// Returns the identity value attribute associated with an AtomicRMWKind op.
3304TypedAttr mlir::arith::getIdentityValueAttr(AtomicRMWKind kind, Type resultType,
3305 OpBuilder &builder, Location loc,
3306 bool useOnlyFiniteValue) {
3307 switch (kind) {
3308 case AtomicRMWKind::maximumf: {
3309 const llvm::fltSemantics &semantic =
3310 llvm::cast<FloatType>(resultType).getFloatSemantics();
3311 APFloat identity = useOnlyFiniteValue
3312 ? APFloat::getLargest(semantic, /*Negative=*/true)
3313 : APFloat::getInf(semantic, /*Negative=*/true);
3314 return builder.getFloatAttr(resultType, identity);
3315 }
3316 case AtomicRMWKind::maxnumf: {
3317 const llvm::fltSemantics &semantic =
3318 llvm::cast<FloatType>(resultType).getFloatSemantics();
3319 APFloat identity = APFloat::getNaN(semantic, /*Negative=*/true);
3320 return builder.getFloatAttr(resultType, identity);
3321 }
3322 case AtomicRMWKind::addf:
3323 case AtomicRMWKind::addi:
3324 case AtomicRMWKind::maxu:
3325 case AtomicRMWKind::ori:
3326 case AtomicRMWKind::xori:
3327 return builder.getZeroAttr(resultType);
3328 case AtomicRMWKind::andi:
3329 return builder.getIntegerAttr(
3330 resultType,
3331 APInt::getAllOnes(llvm::cast<IntegerType>(resultType).getWidth()));
3332 case AtomicRMWKind::maxs:
3333 return builder.getIntegerAttr(
3334 resultType, APInt::getSignedMinValue(
3335 llvm::cast<IntegerType>(resultType).getWidth()));
3336 case AtomicRMWKind::minimumf: {
3337 const llvm::fltSemantics &semantic =
3338 llvm::cast<FloatType>(resultType).getFloatSemantics();
3339 APFloat identity = useOnlyFiniteValue
3340 ? APFloat::getLargest(semantic, /*Negative=*/false)
3341 : APFloat::getInf(semantic, /*Negative=*/false);
3342
3343 return builder.getFloatAttr(resultType, identity);
3344 }
3345 case AtomicRMWKind::minnumf: {
3346 const llvm::fltSemantics &semantic =
3347 llvm::cast<FloatType>(resultType).getFloatSemantics();
3348 APFloat identity = APFloat::getNaN(semantic, /*Negative=*/false);
3349 return builder.getFloatAttr(resultType, identity);
3350 }
3351 case AtomicRMWKind::mins:
3352 return builder.getIntegerAttr(
3353 resultType, APInt::getSignedMaxValue(
3354 llvm::cast<IntegerType>(resultType).getWidth()));
3355 case AtomicRMWKind::minu:
3356 return builder.getIntegerAttr(
3357 resultType,
3358 APInt::getMaxValue(llvm::cast<IntegerType>(resultType).getWidth()));
3359 case AtomicRMWKind::muli:
3360 return builder.getIntegerAttr(resultType, 1);
3361 case AtomicRMWKind::mulf:
3362 return builder.getFloatAttr(resultType, 1);
3363 // `assign` is not a reduction and has no identity element.
3364 case AtomicRMWKind::assign:
3365 break;
3366 }
3367 (void)emitOptionalError(loc, "Reduction operation type not supported");
3368 return nullptr;
3369}
3370
3371/// Returns the identity numeric value of the given op.
3372std::optional<TypedAttr> mlir::arith::getNeutralElement(Operation *op) {
3373 std::optional<AtomicRMWKind> maybeKind =
3375 // Floating-point operations.
3376 .Case([](arith::AddFOp op) { return AtomicRMWKind::addf; })
3377 .Case([](arith::MulFOp op) { return AtomicRMWKind::mulf; })
3378 .Case([](arith::MaximumFOp op) { return AtomicRMWKind::maximumf; })
3379 .Case([](arith::MinimumFOp op) { return AtomicRMWKind::minimumf; })
3380 .Case([](arith::MaxNumFOp op) { return AtomicRMWKind::maxnumf; })
3381 .Case([](arith::MinNumFOp op) { return AtomicRMWKind::minnumf; })
3382 // Integer operations.
3383 .Case([](arith::AddIOp op) { return AtomicRMWKind::addi; })
3384 .Case([](arith::OrIOp op) { return AtomicRMWKind::ori; })
3385 .Case([](arith::XOrIOp op) { return AtomicRMWKind::xori; })
3386 .Case([](arith::AndIOp op) { return AtomicRMWKind::andi; })
3387 .Case([](arith::MaxUIOp op) { return AtomicRMWKind::maxu; })
3388 .Case([](arith::MinUIOp op) { return AtomicRMWKind::minu; })
3389 .Case([](arith::MaxSIOp op) { return AtomicRMWKind::maxs; })
3390 .Case([](arith::MinSIOp op) { return AtomicRMWKind::mins; })
3391 .Case([](arith::MulIOp op) { return AtomicRMWKind::muli; })
3392 .Default(std::nullopt);
3393 if (!maybeKind) {
3394 return std::nullopt;
3395 }
3396
3397 bool useOnlyFiniteValue = false;
3398 auto fmfOpInterface = dyn_cast<ArithFastMathInterface>(op);
3399 if (fmfOpInterface) {
3400 arith::FastMathFlagsAttr fmfAttr = fmfOpInterface.getFastMathFlagsAttr();
3401 useOnlyFiniteValue =
3402 bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::ninf);
3403 }
3404
3405 // Builder only used as helper for attribute creation.
3406 OpBuilder b(op->getContext());
3407 Type resultType = op->getResult(0).getType();
3408
3409 return getIdentityValueAttr(*maybeKind, resultType, b, op->getLoc(),
3410 useOnlyFiniteValue);
3411}
3412
3413/// Returns the identity value associated with an AtomicRMWKind op.
3414Value mlir::arith::getIdentityValue(AtomicRMWKind op, Type resultType,
3415 OpBuilder &builder, Location loc,
3416 bool useOnlyFiniteValue) {
3417 if (auto attr = getIdentityValueAttr(op, resultType, builder, loc,
3418 useOnlyFiniteValue))
3419 return arith::ConstantOp::create(builder, loc, attr);
3420 return {};
3421}
3422
3423/// Return the value obtained by applying the reduction operation kind
3424/// associated with a binary AtomicRMWKind op to `lhs` and `rhs`.
3426 Location loc, Value lhs, Value rhs) {
3427 switch (op) {
3428 case AtomicRMWKind::addf:
3429 return arith::AddFOp::create(builder, loc, lhs, rhs);
3430 case AtomicRMWKind::addi:
3431 return arith::AddIOp::create(builder, loc, lhs, rhs);
3432 case AtomicRMWKind::mulf:
3433 return arith::MulFOp::create(builder, loc, lhs, rhs);
3434 case AtomicRMWKind::muli:
3435 return arith::MulIOp::create(builder, loc, lhs, rhs);
3436 case AtomicRMWKind::maximumf:
3437 return arith::MaximumFOp::create(builder, loc, lhs, rhs);
3438 case AtomicRMWKind::minimumf:
3439 return arith::MinimumFOp::create(builder, loc, lhs, rhs);
3440 case AtomicRMWKind::maxnumf:
3441 return arith::MaxNumFOp::create(builder, loc, lhs, rhs);
3442 case AtomicRMWKind::minnumf:
3443 return arith::MinNumFOp::create(builder, loc, lhs, rhs);
3444 case AtomicRMWKind::maxs:
3445 return arith::MaxSIOp::create(builder, loc, lhs, rhs);
3446 case AtomicRMWKind::mins:
3447 return arith::MinSIOp::create(builder, loc, lhs, rhs);
3448 case AtomicRMWKind::maxu:
3449 return arith::MaxUIOp::create(builder, loc, lhs, rhs);
3450 case AtomicRMWKind::minu:
3451 return arith::MinUIOp::create(builder, loc, lhs, rhs);
3452 case AtomicRMWKind::ori:
3453 return arith::OrIOp::create(builder, loc, lhs, rhs);
3454 case AtomicRMWKind::andi:
3455 return arith::AndIOp::create(builder, loc, lhs, rhs);
3456 case AtomicRMWKind::xori:
3457 return arith::XOrIOp::create(builder, loc, lhs, rhs);
3458 // `assign` is not a reduction and has no corresponding binary operation.
3459 case AtomicRMWKind::assign:
3460 break;
3461 }
3462 (void)emitOptionalError(loc, "Reduction operation type not supported");
3463 return nullptr;
3464}
3465
3466//===----------------------------------------------------------------------===//
3467// TableGen'd op method definitions
3468//===----------------------------------------------------------------------===//
3469
3470#define GET_OP_CLASSES
3471#include "mlir/Dialect/Arith/IR/ArithOps.cpp.inc"
3472
3473//===----------------------------------------------------------------------===//
3474// TableGen'd enum attribute definitions
3475//===----------------------------------------------------------------------===//
3476
3477#include "mlir/Dialect/Arith/IR/ArithOpsEnums.cpp.inc"
return success()
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static Speculation::Speculatability getDivUISpeculatability(Value divisor)
Returns whether an unsigned division by divisor is speculatable.
Definition ArithOps.cpp:848
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)
Definition ArithOps.cpp:66
static IntegerOverflowFlagsAttr mergeOverflowFlags(IntegerOverflowFlagsAttr val1, IntegerOverflowFlagsAttr val2)
Definition ArithOps.cpp:88
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.
Definition ArithOps.cpp:39
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
Definition ArithOps.cpp:797
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)).
Definition ArithOps.cpp:128
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)
static Speculation::Speculatability getDivSISpeculatability(Value divisor)
Returns whether a signed division by divisor is speculatable.
Definition ArithOps.cpp:902
static IntegerAttr orIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
Definition ArithOps.cpp:76
static IntegerAttr addIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
Definition ArithOps.cpp:56
static Attribute getBoolAttribute(Type type, bool value)
Definition ArithOps.cpp:171
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)
Definition ArithOps.cpp:61
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...
Definition ArithOps.cpp:185
static IntegerAttr andIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
Definition ArithOps.cpp:71
static int64_t getScalarOrElementWidth(Type type)
Definition ArithOps.cpp:151
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.
Definition ArithOps.cpp:208
static bool areValidCastInputsAndOutputs(TypeRange inputs, TypeRange outputs)
static IntegerAttr xorIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs)
Definition ArithOps.cpp:81
static APInt calculateUnsignedBorrow(const APInt &lhs, const APInt &rhs)
Definition ArithOps.cpp:536
std::tuple< Types... > * type_list
static IntegerAttr applyToIntegerAttrs(PatternRewriter &builder, Value res, Attribute lhs, Attribute rhs, function_ref< APInt(const APInt &, const APInt &)> binFn)
Definition ArithOps.cpp:47
static APInt calculateUnsignedOverflow(const APInt &sum, const APInt &operand)
Definition ArithOps.cpp:472
static FailureOr< APInt > getIntOrSplatIntValue(Attribute attr)
Definition ArithOps.cpp:163
static unsigned getIndexCastWidth(Type t)
Return the bit-width of t for the purpose of index_cast width checks.
static LogicalResult verifyTruncateOp(Op op)
lhs
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
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,...
Definition SPIRVOps.cpp:229
#define mul(a, b)
#define add(a, b)
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.
Definition Attributes.h:25
static BoolAttr get(MLIRContext *context, bool value)
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
Definition Builders.h:94
TypedAttr getZeroAttr(Type type)
Definition Builders.cpp:333
IntegerType getI1Type()
Definition Builders.cpp:61
IndexType getIndexType()
Definition Builders.cpp:59
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...
Definition Builders.h:632
Location getLoc() const
Accessors for the implied location.
Definition Builders.h:665
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
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.
Definition Builders.h:210
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition Builders.cpp:466
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.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
Definition Types.cpp:66
bool isIndex() const
Definition Types.cpp:56
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Specialization of arith.constant op that returns a floating point value.
Definition Arith.h:72
static ConstantFloatOp create(OpBuilder &builder, Location location, FloatType type, const APFloat &value)
Definition ArithOps.cpp:369
static bool classof(Operation *op)
Definition ArithOps.cpp:386
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.
Definition ArithOps.cpp:363
Specialization of arith.constant op that returns an integer of index type.
Definition Arith.h:93
static void build(OpBuilder &builder, OperationState &result, int64_t value)
Build a constant int op that produces an index.
Definition ArithOps.cpp:392
static bool classof(Operation *op)
Definition ArithOps.cpp:413
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
Specialization of arith.constant op that returns an integer value.
Definition Arith.h:34
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
Definition ArithOps.cpp:297
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.
Definition ArithOps.cpp:290
static bool classof(Operation *op)
Definition ArithOps.cpp:357
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.
Definition ArithOps.cpp:95
Value getZeroConstant(OpBuilder &builder, Location loc, Type type)
Creates an arith.constant operation with a zero value of type type.
Definition ArithOps.cpp:419
auto m_Val(Value v)
Definition Matchers.h:539
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
Definition UBMatchers.h:46
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
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...
Definition Matchers.h:527
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.
Definition Matchers.h:421
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.
Definition Utils.cpp:311
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...
Definition Matchers.h:471
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.
Definition Matchers.h:404
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Definition Matchers.h:442
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.
Definition Matchers.h:399
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition Matchers.h:478
detail::constant_float_predicate_matcher m_NegInfFloat()
Matches a constant scalar / vector splat / tensor splat float negative infinity.
Definition Matchers.h:435
detail::constant_float_predicate_matcher m_NegZeroFloat()
Matches a constant scalar / vector splat / tensor splat float negative zero.
Definition Matchers.h:409
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...
Definition Matchers.h:462
detail::op_matcher< OpClass > m_Op()
Matches the given OpClass.
Definition Matchers.h:484
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
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.
Definition Matchers.h:427
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
detail::constant_float_predicate_matcher m_OneFloat()
Matches a constant scalar / vector splat / tensor splat float ones.
Definition Matchers.h:414
detail::constant_int_range_predicate_matcher m_IntRangeWithoutZeroU()
Matches a constant scalar / vector splat / tensor splat integer or a unsigned integer range that does...
Definition Matchers.h:455
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.