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 `and(a, and(a, b))` to `and(a, b)`
1136static Value foldAndIofAndI(arith::AndIOp op) {
1137 for (bool reversePrev : {false, true}) {
1138 auto prev = (reversePrev ? op.getRhs() : op.getLhs())
1139 .getDefiningOp<arith::AndIOp>();
1140 if (!prev)
1141 continue;
1142
1143 Value other = (reversePrev ? op.getLhs() : op.getRhs());
1144 if (other != prev.getLhs() && other != prev.getRhs())
1145 continue;
1146
1147 return prev.getResult();
1148 }
1149 return {};
1150}
1151
1152OpFoldResult arith::AndIOp::fold(FoldAdaptor adaptor) {
1153 /// and(x, 0) -> 0
1154 if (matchPattern(adaptor.getRhs(), m_Zero()))
1155 return getRhs();
1156 /// and(x, allOnes) -> x
1157 APInt intValue;
1158 if (matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue)) &&
1159 intValue.isAllOnes())
1160 return getLhs();
1161 /// and(x, not(x)) -> 0
1162 if (matchPattern(getRhs(), m_Op<XOrIOp>(matchers::m_Val(getLhs()),
1163 m_ConstantInt(&intValue))) &&
1164 intValue.isAllOnes())
1165 return Builder(getContext()).getZeroAttr(getType());
1166 /// and(not(x), x) -> 0
1167 if (matchPattern(getLhs(), m_Op<XOrIOp>(matchers::m_Val(getRhs()),
1168 m_ConstantInt(&intValue))) &&
1169 intValue.isAllOnes())
1170 return Builder(getContext()).getZeroAttr(getType());
1171
1172 /// and(a, and(a, b)) -> and(a, b)
1173 if (Value result = foldAndIofAndI(*this))
1174 return result;
1175
1177 adaptor.getOperands(),
1178 [](APInt a, const APInt &b) { return std::move(a) & b; });
1179}
1180
1181//===----------------------------------------------------------------------===//
1182// OrIOp
1183//===----------------------------------------------------------------------===//
1184
1185OpFoldResult arith::OrIOp::fold(FoldAdaptor adaptor) {
1186 if (APInt rhsVal; matchPattern(adaptor.getRhs(), m_ConstantInt(&rhsVal))) {
1187 /// or(x, 0) -> x
1188 if (rhsVal.isZero())
1189 return getLhs();
1190 /// or(x, <all ones>) -> <all ones>
1191 if (rhsVal.isAllOnes())
1192 return adaptor.getRhs();
1193 }
1194
1195 APInt intValue;
1196 /// or(x, xor(x, 1)) -> 1
1197 if (matchPattern(getRhs(), m_Op<XOrIOp>(matchers::m_Val(getLhs()),
1198 m_ConstantInt(&intValue))) &&
1199 intValue.isAllOnes())
1200 return getRhs().getDefiningOp<XOrIOp>().getRhs();
1201 /// or(xor(x, 1), x) -> 1
1202 if (matchPattern(getLhs(), m_Op<XOrIOp>(matchers::m_Val(getRhs()),
1203 m_ConstantInt(&intValue))) &&
1204 intValue.isAllOnes())
1205 return getLhs().getDefiningOp<XOrIOp>().getRhs();
1206
1208 adaptor.getOperands(),
1209 [](APInt a, const APInt &b) { return std::move(a) | b; });
1210}
1211
1212//===----------------------------------------------------------------------===//
1213// XOrIOp
1214//===----------------------------------------------------------------------===//
1215
1216OpFoldResult arith::XOrIOp::fold(FoldAdaptor adaptor) {
1217 /// xor(x, 0) -> x
1218 if (matchPattern(adaptor.getRhs(), m_Zero()))
1219 return getLhs();
1220 /// xor(x, x) -> 0
1221 if (getLhs() == getRhs()) {
1222 // A dynamically-shaped result cannot be a constant; bail before
1223 // getZeroAttr, which would assert on a non-static shape.
1224 auto shapedType = dyn_cast<ShapedType>(getType());
1225 if (!shapedType || shapedType.hasStaticShape())
1226 return Builder(getContext()).getZeroAttr(getType());
1227 }
1228 /// xor(xor(x, a), a) -> x
1229 /// xor(xor(a, x), a) -> x
1230 if (arith::XOrIOp prev = getLhs().getDefiningOp<arith::XOrIOp>()) {
1231 if (prev.getRhs() == getRhs())
1232 return prev.getLhs();
1233 if (prev.getLhs() == getRhs())
1234 return prev.getRhs();
1235 }
1236 /// xor(a, xor(x, a)) -> x
1237 /// xor(a, xor(a, x)) -> x
1238 if (arith::XOrIOp prev = getRhs().getDefiningOp<arith::XOrIOp>()) {
1239 if (prev.getRhs() == getLhs())
1240 return prev.getLhs();
1241 if (prev.getLhs() == getLhs())
1242 return prev.getRhs();
1243 }
1244
1246 adaptor.getOperands(),
1247 [](APInt a, const APInt &b) { return std::move(a) ^ b; });
1248}
1249
1250void arith::XOrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1251 MLIRContext *context) {
1252 patterns.add<XOrIXOrIConstant, XOrINotCmpI, XOrIOfExtUI, XOrIOfExtSI>(
1253 context);
1254}
1255
1256//===----------------------------------------------------------------------===//
1257// NegFOp
1258//===----------------------------------------------------------------------===//
1259
1260OpFoldResult arith::NegFOp::fold(FoldAdaptor adaptor) {
1261 /// negf(negf(x)) -> x
1262 if (auto op = this->getOperand().getDefiningOp<arith::NegFOp>())
1263 return op.getOperand();
1264 return constFoldUnaryOp<FloatAttr>(adaptor.getOperands(),
1265 [](const APFloat &a) { return -a; });
1266}
1267
1268//===----------------------------------------------------------------------===//
1269// FlushDenormalsOp
1270//===----------------------------------------------------------------------===//
1271
1272OpFoldResult arith::FlushDenormalsOp::fold(FoldAdaptor adaptor) {
1273 // TODO: Fold flush_denormals if the floating-point type does not support
1274 // denormals. There is currently no API to query this information from
1275 // APFloat.
1276
1277 // flush_denormals(flush_denormals(x)) -> flush_denormals(x)
1278 if (auto op = this->getOperand().getDefiningOp<arith::FlushDenormalsOp>())
1279 return op.getResult();
1280
1281 // Constant-fold flush_denormals if the operand is a constant.
1283 adaptor.getOperands(), [](const APFloat &a) {
1284 if (a.isDenormal())
1285 return APFloat::getZero(a.getSemantics(), a.isNegative());
1286 return a;
1287 });
1288}
1289
1290//===----------------------------------------------------------------------===//
1291// AddFOp
1292//===----------------------------------------------------------------------===//
1293
1294OpFoldResult arith::AddFOp::fold(FoldAdaptor adaptor) {
1295 // addf(x, -0) -> x
1296 if (matchPattern(adaptor.getRhs(), m_NegZeroFloat()))
1297 return getLhs();
1298 if (matchPattern(adaptor.getRhs(), m_PosZeroFloat()) &&
1299 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1300 return getLhs();
1301
1302 auto rm = getRoundingmode();
1304 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1305 APFloat result(a);
1306 result.add(b, convertArithRoundingModeToLLVMIR(rm));
1307 return result;
1308 });
1309}
1310
1311void arith::AddFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1312 MLIRContext *context) {
1313 patterns.add<AddFOfNegFLhs, AddFOfNegFRhs>(context);
1314}
1315
1316//===----------------------------------------------------------------------===//
1317// SubFOp
1318//===----------------------------------------------------------------------===//
1319
1320OpFoldResult arith::SubFOp::fold(FoldAdaptor adaptor) {
1321 // subf(x, +0) -> x
1322 if (matchPattern(adaptor.getRhs(), m_PosZeroFloat()))
1323 return getLhs();
1324 if (matchPattern(adaptor.getRhs(), m_NegZeroFloat()) &&
1325 bitEnumContainsAll(adaptor.getFastmath(), FastMathFlags::nsz))
1326 return getLhs();
1327
1328 auto rm = getRoundingmode();
1330 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1331 APFloat result(a);
1332 result.subtract(b, convertArithRoundingModeToLLVMIR(rm));
1333 return result;
1334 });
1335}
1336
1337void arith::SubFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1338 MLIRContext *context) {
1339 patterns.add<SubFOfNegZero>(context);
1340}
1341
1342namespace {
1343
1344/// Narrow an extremum whose operands were extended from the result type:
1345///
1346/// trunc(extremum(ext(lhs), ext(rhs))) -> extremum(lhs, rhs)
1347///
1348/// The concrete extension is part of the pattern so each extremum is only
1349/// registered with extensions that preserve its ordering.
1350/// For floating-point types, also require the extension to preserve every
1351/// source value relevant under the extremum's fast-math flags.
1352template <typename TruncOp, typename ExtOp, typename ExtremumOp>
1353struct NarrowExtremum final : OpRewritePattern<TruncOp> {
1354 using OpRewritePattern<TruncOp>::OpRewritePattern;
1355
1356 LogicalResult matchAndRewrite(TruncOp truncOp,
1357 PatternRewriter &rewriter) const override {
1358 auto extremumOp = truncOp.getIn().template getDefiningOp<ExtremumOp>();
1359 if (!extremumOp || !extremumOp->hasOneUse())
1360 return failure();
1361
1362 auto lhsExt = extremumOp.getLhs().template getDefiningOp<ExtOp>();
1363 auto rhsExt = extremumOp.getRhs().template getDefiningOp<ExtOp>();
1364 if (!lhsExt || !rhsExt)
1365 return failure();
1366
1367 Value lhs = lhsExt.getIn();
1368 Value rhs = rhsExt.getIn();
1369 Type narrowType = truncOp.getType();
1370 if (lhs.getType() != narrowType || rhs.getType() != narrowType)
1371 return failure();
1372
1373 // A floating-point extension is not necessarily lossless between arbitrary
1374 // floating-point semantics, even when the destination has a larger bit
1375 // width. In particular, it may lose the sign of zero or quiet a signaling
1376 // NaN, either of which can change an extremum's result. `nnan` lets us
1377 // disregard NaN representation differences, but all other relevant source
1378 // values must be preserved.
1379 if (auto narrowFloatType =
1380 dyn_cast<FloatType>(getElementTypeOrSelf(narrowType))) {
1381 auto wideFloatType =
1382 dyn_cast<FloatType>(getElementTypeOrSelf(extremumOp.getType()));
1383 if (!wideFloatType)
1384 return failure();
1385
1386 const llvm::fltSemantics &narrowSemantics =
1387 narrowFloatType.getFloatSemantics();
1388 const llvm::fltSemantics &wideSemantics =
1389 wideFloatType.getFloatSemantics();
1390 bool ignoreNaNs = false;
1391 if constexpr (std::is_same_v<TruncOp, TruncFOp>)
1392 ignoreNaNs =
1393 bitEnumContainsAll(extremumOp.getFastmath(), FastMathFlags::nnan);
1394 if (!llvm::APFloatBase::isLosslesslyConvertibleTo(
1395 narrowSemantics, wideSemantics, ignoreNaNs))
1396 return failure();
1397 }
1398
1399 rewriter.replaceOpWithNewOp<ExtremumOp>(
1400 truncOp, TypeRange{narrowType}, ValueRange{lhs, rhs},
1401 extremumOp.getProperties(),
1402 extremumOp->getDiscardableAttrDictionary().getValue());
1403 return success();
1404 }
1405};
1406
1407} // namespace
1408
1409//===----------------------------------------------------------------------===//
1410// MaximumFOp
1411//===----------------------------------------------------------------------===//
1412
1413OpFoldResult arith::MaximumFOp::fold(FoldAdaptor adaptor) {
1414 // maximumf(x,x) -> x
1415 if (getLhs() == getRhs())
1416 return getRhs();
1417
1418 // maximumf(x, -inf) -> x
1419 if (matchPattern(adaptor.getRhs(), m_NegInfFloat()))
1420 return getLhs();
1421
1422 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::maximum);
1423}
1424
1425//===----------------------------------------------------------------------===//
1426// MaxNumFOp
1427//===----------------------------------------------------------------------===//
1428
1429OpFoldResult arith::MaxNumFOp::fold(FoldAdaptor adaptor) {
1430 // maxnumf(x,x) -> x
1431 if (getLhs() == getRhs())
1432 return getRhs();
1433
1434 // maxnumf(x, NaN) -> x
1435 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1436 return getLhs();
1437
1438 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::maxnum);
1439}
1440
1441//===----------------------------------------------------------------------===//
1442// MaxSIOp
1443//===----------------------------------------------------------------------===//
1444
1445OpFoldResult MaxSIOp::fold(FoldAdaptor adaptor) {
1446 // maxsi(x,x) -> x
1447 if (getLhs() == getRhs())
1448 return getRhs();
1449
1450 if (APInt intValue;
1451 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1452 // maxsi(x,MAX_INT) -> MAX_INT
1453 if (intValue.isMaxSignedValue())
1454 return getRhs();
1455 // maxsi(x, MIN_INT) -> x
1456 if (intValue.isMinSignedValue())
1457 return getLhs();
1458 }
1459
1460 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1461 llvm::APIntOps::smax);
1462}
1463
1464//===----------------------------------------------------------------------===//
1465// MaxUIOp
1466//===----------------------------------------------------------------------===//
1467
1468OpFoldResult MaxUIOp::fold(FoldAdaptor adaptor) {
1469 // maxui(x,x) -> x
1470 if (getLhs() == getRhs())
1471 return getRhs();
1472
1473 if (APInt intValue;
1474 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1475 // maxui(x,MAX_INT) -> MAX_INT
1476 if (intValue.isMaxValue())
1477 return getRhs();
1478 // maxui(x, MIN_INT) -> x
1479 if (intValue.isMinValue())
1480 return getLhs();
1481 }
1482
1483 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1484 llvm::APIntOps::umax);
1485}
1486
1487//===----------------------------------------------------------------------===//
1488// MinimumFOp
1489//===----------------------------------------------------------------------===//
1490
1491OpFoldResult arith::MinimumFOp::fold(FoldAdaptor adaptor) {
1492 // minimumf(x,x) -> x
1493 if (getLhs() == getRhs())
1494 return getRhs();
1495
1496 // minimumf(x, +inf) -> x
1497 if (matchPattern(adaptor.getRhs(), m_PosInfFloat()))
1498 return getLhs();
1499
1500 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::minimum);
1501}
1502
1503//===----------------------------------------------------------------------===//
1504// MinNumFOp
1505//===----------------------------------------------------------------------===//
1506
1507OpFoldResult arith::MinNumFOp::fold(FoldAdaptor adaptor) {
1508 // minnumf(x,x) -> x
1509 if (getLhs() == getRhs())
1510 return getRhs();
1511
1512 // minnumf(x, NaN) -> x
1513 if (matchPattern(adaptor.getRhs(), m_NaNFloat()))
1514 return getLhs();
1515
1516 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(), llvm::minnum);
1517}
1518
1519//===----------------------------------------------------------------------===//
1520// MinSIOp
1521//===----------------------------------------------------------------------===//
1522
1523OpFoldResult MinSIOp::fold(FoldAdaptor adaptor) {
1524 // minsi(x,x) -> x
1525 if (getLhs() == getRhs())
1526 return getRhs();
1527
1528 if (APInt intValue;
1529 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1530 // minsi(x,MIN_INT) -> MIN_INT
1531 if (intValue.isMinSignedValue())
1532 return getRhs();
1533 // minsi(x, MAX_INT) -> x
1534 if (intValue.isMaxSignedValue())
1535 return getLhs();
1536 }
1537
1538 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1539 llvm::APIntOps::smin);
1540}
1541
1542//===----------------------------------------------------------------------===//
1543// MinUIOp
1544//===----------------------------------------------------------------------===//
1545
1546OpFoldResult MinUIOp::fold(FoldAdaptor adaptor) {
1547 // minui(x,x) -> x
1548 if (getLhs() == getRhs())
1549 return getRhs();
1550
1551 if (APInt intValue;
1552 matchPattern(adaptor.getRhs(), m_ConstantInt(&intValue))) {
1553 // minui(x,MIN_INT) -> MIN_INT
1554 if (intValue.isMinValue())
1555 return getRhs();
1556 // minui(x, MAX_INT) -> x
1557 if (intValue.isMaxValue())
1558 return getLhs();
1559 }
1560
1561 return constFoldBinaryOp<IntegerAttr>(adaptor.getOperands(),
1562 llvm::APIntOps::umin);
1563}
1564
1565//===----------------------------------------------------------------------===//
1566// MulFOp
1567//===----------------------------------------------------------------------===//
1568
1569OpFoldResult arith::MulFOp::fold(FoldAdaptor adaptor) {
1570 // mulf(x, 1) -> x
1571 if (matchPattern(adaptor.getRhs(), m_OneFloat()))
1572 return getLhs();
1573
1574 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::nnan |
1575 arith::FastMathFlags::nsz)) {
1576 // mulf(x, 0) -> 0
1577 if (matchPattern(adaptor.getRhs(), m_AnyZeroFloat()))
1578 return getRhs();
1579 }
1580
1581 auto rm = getRoundingmode();
1583 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1584 APFloat result(a);
1585 result.multiply(b, convertArithRoundingModeToLLVMIR(rm));
1586 return result;
1587 });
1588}
1589
1590void arith::MulFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1591 MLIRContext *context) {
1592 patterns.add<MulFOfNegF>(context);
1593}
1594
1595//===----------------------------------------------------------------------===//
1596// DivFOp
1597//===----------------------------------------------------------------------===//
1598
1599OpFoldResult arith::DivFOp::fold(FoldAdaptor adaptor) {
1600 // divf(x, 1) -> x
1601 if (matchPattern(adaptor.getRhs(), m_OneFloat()))
1602 return getLhs();
1603
1604 auto rm = getRoundingmode();
1606 adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
1607 APFloat result(a);
1608 result.divide(b, convertArithRoundingModeToLLVMIR(rm));
1609 return result;
1610 });
1611}
1612
1613void arith::DivFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1614 MLIRContext *context) {
1615 patterns.add<DivFOfNegF>(context);
1616}
1617
1618//===----------------------------------------------------------------------===//
1619// RemFOp
1620//===----------------------------------------------------------------------===//
1621
1622OpFoldResult arith::RemFOp::fold(FoldAdaptor adaptor) {
1623 return constFoldBinaryOp<FloatAttr>(adaptor.getOperands(),
1624 [](const APFloat &a, const APFloat &b) {
1625 APFloat result(a);
1626 // APFloat::mod() offers the remainder
1627 // behavior we want, i.e. the result has
1628 // the sign of LHS operand.
1629 (void)result.mod(b);
1630 return result;
1631 });
1632}
1633
1634//===----------------------------------------------------------------------===//
1635// Utility functions for verifying cast ops
1636//===----------------------------------------------------------------------===//
1637
1638template <typename... Types>
1639using type_list = std::tuple<Types...> *;
1640
1641/// Returns a non-null type only if the provided type is one of the allowed
1642/// types or one of the allowed shaped types of the allowed types. Returns the
1643/// element type if a valid shaped type is provided.
1644template <typename... ShapedTypes, typename... ElementTypes>
1647 if (llvm::isa<ShapedType>(type) && !llvm::isa<ShapedTypes...>(type))
1648 return {};
1649
1650 auto underlyingType = getElementTypeOrSelf(type);
1651 if (!llvm::isa<ElementTypes...>(underlyingType))
1652 return {};
1653
1654 return underlyingType;
1655}
1656
1657/// Get allowed underlying types for vectors and tensors.
1658template <typename... ElementTypes>
1663
1664/// Get allowed underlying types for vectors, tensors, and memrefs.
1665template <typename... ElementTypes>
1671
1672/// Return false if both types are ranked tensor with mismatching encoding.
1673static bool hasSameEncoding(Type typeA, Type typeB) {
1674 auto rankedTensorA = dyn_cast<RankedTensorType>(typeA);
1675 auto rankedTensorB = dyn_cast<RankedTensorType>(typeB);
1676 if (!rankedTensorA || !rankedTensorB)
1677 return true;
1678 return rankedTensorA.getEncoding() == rankedTensorB.getEncoding();
1679}
1680
1682 if (inputs.size() != 1 || outputs.size() != 1)
1683 return false;
1684 if (!hasSameEncoding(inputs.front(), outputs.front()))
1685 return false;
1686 return succeeded(verifyCompatibleShapes(inputs.front(), outputs.front()));
1687}
1688
1689//===----------------------------------------------------------------------===//
1690// Verifiers for integer and floating point extension/truncation ops
1691//===----------------------------------------------------------------------===//
1692
1693// Extend ops can only extend to a wider type.
1694template <typename ValType, typename Op>
1695static LogicalResult verifyExtOp(Op op) {
1696 Type srcType = getElementTypeOrSelf(op.getIn().getType());
1697 Type dstType = getElementTypeOrSelf(op.getType());
1698
1699 if (llvm::cast<ValType>(srcType).getWidth() >=
1700 llvm::cast<ValType>(dstType).getWidth())
1701 return op.emitError("result type ")
1702 << dstType << " must be wider than operand type " << srcType;
1703
1704 return success();
1705}
1706
1707// Truncate ops can only truncate to a shorter type.
1708template <typename ValType, typename Op>
1709static LogicalResult verifyTruncateOp(Op op) {
1710 Type srcType = getElementTypeOrSelf(op.getIn().getType());
1711 Type dstType = getElementTypeOrSelf(op.getType());
1712
1713 if (llvm::cast<ValType>(srcType).getWidth() <=
1714 llvm::cast<ValType>(dstType).getWidth())
1715 return op.emitError("result type ")
1716 << dstType << " must be shorter than operand type " << srcType;
1717
1718 return success();
1719}
1720
1721/// Validate a cast that changes the width of a type.
1722template <template <typename> class WidthComparator, typename... ElementTypes>
1723static bool checkWidthChangeCast(TypeRange inputs, TypeRange outputs) {
1724 if (!areValidCastInputsAndOutputs(inputs, outputs))
1725 return false;
1726
1727 auto srcType = getTypeIfLike<ElementTypes...>(inputs.front());
1728 auto dstType = getTypeIfLike<ElementTypes...>(outputs.front());
1729 if (!srcType || !dstType)
1730 return false;
1731
1732 return WidthComparator<unsigned>()(dstType.getIntOrFloatBitWidth(),
1733 srcType.getIntOrFloatBitWidth());
1734}
1735
1736/// Attempts to convert `sourceValue` to an APFloat value with
1737/// `targetSemantics` and `roundingMode`, without any information loss.
1738static FailureOr<APFloat>
1739convertFloatValue(APFloat sourceValue,
1740 const llvm::fltSemantics &targetSemantics,
1741 llvm::RoundingMode roundingMode = kDefaultRoundingMode) {
1742 // Reject special values that are not representable in the target type before
1743 // calling APFloat::convert, which would llvm_unreachable on them.
1744 using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
1745 if (sourceValue.isInfinity() &&
1746 (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
1747 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
1748 return failure();
1749 if (sourceValue.isNaN() &&
1750 targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
1751 return failure();
1752
1753 bool losesInfo = false;
1754 auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
1755 if (losesInfo || status != APFloat::opOK)
1756 return failure();
1757
1758 return sourceValue;
1759}
1760
1761//===----------------------------------------------------------------------===//
1762// ExtUIOp
1763//===----------------------------------------------------------------------===//
1764
1765OpFoldResult arith::ExtUIOp::fold(FoldAdaptor adaptor) {
1766 if (auto lhs = getIn().getDefiningOp<ExtUIOp>()) {
1767 // Only the inner extension's nneg speaks about the surviving source; the
1768 // outer flag described the already-extended value.
1769 setNonNeg(lhs.getNonNeg());
1770 getInMutable().assign(lhs.getIn());
1771 return getResult();
1772 }
1773
1774 Type resType = getElementTypeOrSelf(getType());
1775 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1777 adaptor.getOperands(), getType(),
1778 [bitWidth](const APInt &a, bool &castStatus) {
1779 return a.zext(bitWidth);
1780 });
1781}
1782
1783bool arith::ExtUIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1785}
1786
1787LogicalResult arith::ExtUIOp::verify() {
1788 return verifyExtOp<IntegerType>(*this);
1789}
1790
1791//===----------------------------------------------------------------------===//
1792// ExtSIOp
1793//===----------------------------------------------------------------------===//
1794
1795OpFoldResult arith::ExtSIOp::fold(FoldAdaptor adaptor) {
1796 if (auto lhs = getIn().getDefiningOp<ExtSIOp>()) {
1797 getInMutable().assign(lhs.getIn());
1798 return getResult();
1799 }
1800
1801 Type resType = getElementTypeOrSelf(getType());
1802 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1804 adaptor.getOperands(), getType(),
1805 [bitWidth](const APInt &a, bool &castStatus) {
1806 return a.sext(bitWidth);
1807 });
1808}
1809
1810bool arith::ExtSIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1812}
1813
1814void arith::ExtSIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1815 MLIRContext *context) {
1816 patterns.add<ExtSIOfExtUI>(context);
1817}
1818
1819LogicalResult arith::ExtSIOp::verify() {
1820 return verifyExtOp<IntegerType>(*this);
1821}
1822
1823//===----------------------------------------------------------------------===//
1824// ExtFOp
1825//===----------------------------------------------------------------------===//
1826
1827/// Fold extension of float constants when there is no information loss due the
1828/// difference in fp semantics.
1829OpFoldResult arith::ExtFOp::fold(FoldAdaptor adaptor) {
1830 if (auto truncFOp = getOperand().getDefiningOp<TruncFOp>()) {
1831 if (truncFOp.getOperand().getType() == getType()) {
1832 arith::FastMathFlags truncFMF =
1833 truncFOp.getFastmath().value_or(arith::FastMathFlags::none);
1834 bool isTruncContract =
1835 bitEnumContainsAll(truncFMF, arith::FastMathFlags::contract);
1836 arith::FastMathFlags extFMF =
1837 getFastmath().value_or(arith::FastMathFlags::none);
1838 bool isExtContract =
1839 bitEnumContainsAll(extFMF, arith::FastMathFlags::contract);
1840 if (isTruncContract && isExtContract) {
1841 return truncFOp.getOperand();
1842 }
1843 }
1844 }
1845
1846 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
1847 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
1849 adaptor.getOperands(), getType(),
1850 [&targetSemantics](const APFloat &a, bool &castStatus) {
1851 FailureOr<APFloat> result = convertFloatValue(a, targetSemantics);
1852 if (failed(result)) {
1853 castStatus = false;
1854 return a;
1855 }
1856 return *result;
1857 });
1858}
1859
1860bool arith::ExtFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
1861 return checkWidthChangeCast<std::greater, FloatType>(inputs, outputs);
1862}
1863
1864LogicalResult arith::ExtFOp::verify() { return verifyExtOp<FloatType>(*this); }
1865
1866//===----------------------------------------------------------------------===//
1867// ScalingExtFOp
1868//===----------------------------------------------------------------------===//
1869
1870/// Fold `calculate` element-wise over the operands of a scaling cast op. The
1871/// `constFoldBinaryOp` helpers cannot be used: they bail out unless both
1872/// operands have the same type, and `in` and `scale` never do.
1874 Attribute inAttr, Attribute scaleAttr, Type resultType,
1875 function_ref<std::optional<APFloat>(const APFloat &, const APFloat &)>
1876 calculate) {
1877 // Poison propagates, as it does in the generic constant folders.
1878 if (isa_and_nonnull<ub::PoisonAttr>(inAttr))
1879 return inAttr;
1880 if (isa_and_nonnull<ub::PoisonAttr>(scaleAttr))
1881 return scaleAttr;
1882
1883 if (!inAttr || !scaleAttr || !resultType)
1884 return {};
1885
1886 if (auto inFloat = dyn_cast<FloatAttr>(inAttr)) {
1887 auto scaleFloat = dyn_cast<FloatAttr>(scaleAttr);
1888 if (!scaleFloat)
1889 return {};
1890 std::optional<APFloat> result =
1891 calculate(inFloat.getValue(), scaleFloat.getValue());
1892 if (!result)
1893 return {};
1894 return FloatAttr::get(resultType, *result);
1895 }
1896
1897 auto inElements = dyn_cast<DenseFPElementsAttr>(inAttr);
1898 auto scaleElements = dyn_cast<DenseFPElementsAttr>(scaleAttr);
1899 auto shapedResultType = dyn_cast<ShapedType>(resultType);
1900 if (!inElements || !scaleElements || !shapedResultType ||
1901 !shapedResultType.hasStaticShape() ||
1902 inElements.getNumElements() != scaleElements.getNumElements())
1903 return {};
1904
1905 // Both operands are splats, so avoid expanding the elements out.
1906 if (inElements.isSplat() && scaleElements.isSplat()) {
1907 std::optional<APFloat> result =
1908 calculate(inElements.getSplatValue<APFloat>(),
1909 scaleElements.getSplatValue<APFloat>());
1910 if (!result)
1911 return {};
1912 return DenseElementsAttr::get(shapedResultType, *result);
1913 }
1914
1915 SmallVector<APFloat> results;
1916 results.reserve(inElements.getNumElements());
1917 for (const auto &[in, scale] : llvm::zip_equal(inElements, scaleElements)) {
1918 std::optional<APFloat> result = calculate(in, scale);
1919 if (!result)
1920 return {};
1921 results.push_back(*result);
1922 }
1923 return DenseElementsAttr::get(shapedResultType, results);
1924}
1925
1926/// Only scales that already are f8E8M0FNU fold. What a wider scale means is
1927/// unsettled -- the tree does not say whether truncating one to f8E8M0FNU
1928/// rounds or takes its exponent -- so a folder should not settle it, see
1929/// https://github.com/llvm/llvm-project/issues/215295.
1930static bool isFoldableScalingScale(Value scale) {
1931 return isa<Float8E8M0FNUType>(getElementTypeOrSelf(scale.getType()));
1932}
1933
1934OpFoldResult arith::ScalingExtFOp::fold(FoldAdaptor adaptor) {
1935 // scaling_extf(in, scale) -> mulf(extf(in), extf(scale)), matching the
1936 // expansion in ExpandOps.cpp. As in arith.extf, the widening steps only fold
1937 // when they are lossless.
1938 if (!isFoldableScalingScale(getScale()))
1939 return {};
1940
1941 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
1942 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
1943 return foldScalingCastOp(
1944 adaptor.getIn(), adaptor.getScale(), getType(),
1945 [&resSemantics](const APFloat &in,
1946 const APFloat &scale) -> std::optional<APFloat> {
1947 FailureOr<APFloat> inExt = convertFloatValue(in, resSemantics);
1948 FailureOr<APFloat> scaleExt = convertFloatValue(scale, resSemantics);
1949 if (failed(inExt) || failed(scaleExt))
1950 return std::nullopt;
1951 APFloat result(*inExt);
1952 result.multiply(*scaleExt, kDefaultRoundingMode);
1953 return result;
1954 });
1955}
1956
1957bool arith::ScalingExtFOp::areCastCompatible(TypeRange inputs,
1958 TypeRange outputs) {
1959 return checkWidthChangeCast<std::greater, FloatType>(inputs.front(), outputs);
1960}
1961
1962LogicalResult arith::ScalingExtFOp::verify() {
1963 return verifyExtOp<FloatType>(*this);
1964}
1965
1966//===----------------------------------------------------------------------===//
1967// TruncIOp
1968//===----------------------------------------------------------------------===//
1969
1970OpFoldResult arith::TruncIOp::fold(FoldAdaptor adaptor) {
1971 if (matchPattern(getOperand(), m_Op<arith::ExtUIOp>()) ||
1972 matchPattern(getOperand(), m_Op<arith::ExtSIOp>())) {
1973 Value src = getOperand().getDefiningOp()->getOperand(0);
1974 Type srcType = getElementTypeOrSelf(src.getType());
1975 Type dstType = getElementTypeOrSelf(getType());
1976 // trunci(zexti(a)) -> trunci(a)
1977 // trunci(sexti(a)) -> trunci(a)
1978 if (llvm::cast<IntegerType>(srcType).getWidth() >
1979 llvm::cast<IntegerType>(dstType).getWidth()) {
1980 setOperand(src);
1981 return getResult();
1982 }
1983
1984 // trunci(zexti(a)) -> a
1985 // trunci(sexti(a)) -> a
1986 if (srcType == dstType)
1987 return src;
1988 }
1989
1990 // trunci(trunci(a)) -> trunci(a))
1991 if (matchPattern(getOperand(), m_Op<arith::TruncIOp>())) {
1992 setOperand(getOperand().getDefiningOp()->getOperand(0));
1993 return getResult();
1994 }
1995
1996 Type resType = getElementTypeOrSelf(getType());
1997 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
1999 adaptor.getOperands(), getType(),
2000 [bitWidth](const APInt &a, bool &castStatus) {
2001 return a.trunc(bitWidth);
2002 });
2003}
2004
2005bool arith::TruncIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2006 return checkWidthChangeCast<std::less, IntegerType>(inputs, outputs);
2007}
2008
2009void arith::TruncIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2010 MLIRContext *context) {
2011 patterns.add<NarrowExtremum<TruncIOp, ExtSIOp, MaxSIOp>,
2012 NarrowExtremum<TruncIOp, ExtSIOp, MinSIOp>,
2013 NarrowExtremum<TruncIOp, ExtUIOp, MaxUIOp>,
2014 NarrowExtremum<TruncIOp, ExtUIOp, MinUIOp>, TruncIExtSIToExtSI,
2015 TruncIExtUIToExtUI, TruncIShrSIToTrunciShrUI>(context);
2016}
2017
2018LogicalResult arith::TruncIOp::verify() {
2019 return verifyTruncateOp<IntegerType>(*this);
2020}
2021
2022//===----------------------------------------------------------------------===//
2023// TruncFOp
2024//===----------------------------------------------------------------------===//
2025
2026/// Perform safe const propagation for truncf, i.e., only propagate if FP value
2027/// can be represented without precision loss.
2028OpFoldResult arith::TruncFOp::fold(FoldAdaptor adaptor) {
2029 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2030 if (auto extOp = getOperand().getDefiningOp<arith::ExtFOp>()) {
2031 Value src = extOp.getIn();
2032 auto srcType = cast<FloatType>(getElementTypeOrSelf(src.getType()));
2033 auto intermediateType =
2034 cast<FloatType>(getElementTypeOrSelf(extOp.getType()));
2035 // Check whether every source value round-trips through the intermediate
2036 // type, including signaling NaNs and signed zero.
2037 if (llvm::APFloatBase::isLosslesslyConvertibleTo(
2038 srcType.getFloatSemantics(),
2039 intermediateType.getFloatSemantics())) {
2040 // truncf(extf(a)) -> truncf(a)
2041 if (srcType.getWidth() > resElemType.getWidth()) {
2042 setOperand(src);
2043 return getResult();
2044 }
2045
2046 // truncf(extf(a)) -> a
2047 if (srcType == resElemType)
2048 return src;
2049 }
2050 }
2051
2052 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2054 adaptor.getOperands(), getType(),
2055 [this, &targetSemantics](const APFloat &a, bool &castStatus) {
2056 llvm::RoundingMode llvmRoundingMode =
2057 convertArithRoundingModeToLLVMIR(getRoundingmode());
2058 FailureOr<APFloat> result =
2059 convertFloatValue(a, targetSemantics, llvmRoundingMode);
2060 if (failed(result)) {
2061 castStatus = false;
2062 return a;
2063 }
2064 return *result;
2065 });
2066}
2067
2068void arith::TruncFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2069 MLIRContext *context) {
2070 patterns.add<NarrowExtremum<TruncFOp, ExtFOp, MaximumFOp>,
2071 NarrowExtremum<TruncFOp, ExtFOp, MaxNumFOp>,
2072 NarrowExtremum<TruncFOp, ExtFOp, MinimumFOp>,
2073 NarrowExtremum<TruncFOp, ExtFOp, MinNumFOp>,
2074 TruncFSIToFPToSIToFP, TruncFUIToFPToUIToFP>(context);
2075}
2076
2077bool arith::TruncFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2078 return checkWidthChangeCast<std::less, FloatType>(inputs, outputs);
2079}
2080
2081LogicalResult arith::TruncFOp::verify() {
2082 return verifyTruncateOp<FloatType>(*this);
2083}
2084
2085//===----------------------------------------------------------------------===//
2086// ConvertFOp
2087//===----------------------------------------------------------------------===//
2088
2089OpFoldResult arith::ConvertFOp::fold(FoldAdaptor adaptor) {
2090 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2091 const llvm::fltSemantics &targetSemantics = resElemType.getFloatSemantics();
2093 adaptor.getOperands(), getType(),
2094 [this, &targetSemantics](const APFloat &a, bool &castStatus) {
2095 llvm::RoundingMode llvmRoundingMode =
2096 convertArithRoundingModeToLLVMIR(getRoundingmode());
2097 FailureOr<APFloat> result =
2098 convertFloatValue(a, targetSemantics, llvmRoundingMode);
2099 if (failed(result)) {
2100 castStatus = false;
2101 return a;
2102 }
2103 return *result;
2104 });
2105}
2106
2107bool arith::ConvertFOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2108 if (!areValidCastInputsAndOutputs(inputs, outputs))
2109 return false;
2110 auto srcType = getTypeIfLike<FloatType>(inputs.front());
2111 auto dstType = getTypeIfLike<FloatType>(outputs.front());
2112 if (!srcType || !dstType)
2113 return false;
2114 return srcType != dstType &&
2115 srcType.getIntOrFloatBitWidth() == dstType.getIntOrFloatBitWidth();
2116}
2117
2118LogicalResult arith::ConvertFOp::verify() {
2119 auto srcType = cast<FloatType>(getElementTypeOrSelf(getIn().getType()));
2120 auto dstType = cast<FloatType>(getElementTypeOrSelf(getType()));
2121 if (srcType == dstType)
2122 return emitError("result element type ")
2123 << dstType << " must be different from operand element type "
2124 << srcType;
2125 if (srcType.getWidth() != dstType.getWidth())
2126 return emitError("result element type ")
2127 << dstType << " must have the same bitwidth as operand element type "
2128 << srcType;
2129 return success();
2130}
2131
2132//===----------------------------------------------------------------------===//
2133// ScalingTruncFOp
2134//===----------------------------------------------------------------------===//
2135
2136OpFoldResult arith::ScalingTruncFOp::fold(FoldAdaptor adaptor) {
2137 // scaling_truncf(in, scale) -> truncf(in / extf(scale)), matching the
2138 // expansion in ExpandOps.cpp. Unlike scaling_extf, the scale is widened to
2139 // the type of `in` rather than to the result type.
2140 if (!isFoldableScalingScale(getScale()))
2141 return {};
2142
2143 auto inElemType = cast<FloatType>(getElementTypeOrSelf(getIn().getType()));
2144 auto resElemType = cast<FloatType>(getElementTypeOrSelf(getType()));
2145 const llvm::fltSemantics &inSemantics = inElemType.getFloatSemantics();
2146 const llvm::fltSemantics &resSemantics = resElemType.getFloatSemantics();
2147 llvm::RoundingMode roundingMode =
2148 convertArithRoundingModeToLLVMIR(getRoundingmode());
2149 return foldScalingCastOp(
2150 adaptor.getIn(), adaptor.getScale(), getType(),
2151 [&](const APFloat &in, const APFloat &scale) -> std::optional<APFloat> {
2152 FailureOr<APFloat> scaleExt = convertFloatValue(scale, inSemantics);
2153 if (failed(scaleExt))
2154 return std::nullopt;
2155 APFloat quotient(in);
2156 quotient.divide(*scaleExt, kDefaultRoundingMode);
2157 FailureOr<APFloat> result =
2158 convertFloatValue(quotient, resSemantics, roundingMode);
2159 if (failed(result))
2160 return std::nullopt;
2161 return *result;
2162 });
2163}
2164
2165bool arith::ScalingTruncFOp::areCastCompatible(TypeRange inputs,
2166 TypeRange outputs) {
2167 return checkWidthChangeCast<std::less, FloatType>(inputs.front(), outputs);
2168}
2169
2170LogicalResult arith::ScalingTruncFOp::verify() {
2171 return verifyTruncateOp<FloatType>(*this);
2172}
2173
2174//===----------------------------------------------------------------------===//
2175// AndIOp
2176//===----------------------------------------------------------------------===//
2177
2178void arith::AndIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2179 MLIRContext *context) {
2180 patterns.add<AndIAndIConstant, AndOfExtUI, AndOfExtSI>(context);
2181}
2182
2183//===----------------------------------------------------------------------===//
2184// OrIOp
2185//===----------------------------------------------------------------------===//
2186
2187void arith::OrIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2188 MLIRContext *context) {
2189 patterns.add<OrIOrIConstant, OrOfExtUI, OrOfExtSI>(context);
2190}
2191
2192//===----------------------------------------------------------------------===//
2193// Verifiers for casts between integers and floats.
2194//===----------------------------------------------------------------------===//
2195
2196template <typename From, typename To>
2197static bool checkIntFloatCast(TypeRange inputs, TypeRange outputs) {
2198 if (!areValidCastInputsAndOutputs(inputs, outputs))
2199 return false;
2200
2201 auto srcType = getTypeIfLike<From>(inputs.front());
2202 auto dstType = getTypeIfLike<To>(outputs.back());
2203
2204 return srcType && dstType;
2205}
2206
2207//===----------------------------------------------------------------------===//
2208// UIToFPOp
2209//===----------------------------------------------------------------------===//
2210
2211bool arith::UIToFPOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2212 return checkIntFloatCast<IntegerType, FloatType>(inputs, outputs);
2213}
2214
2215OpFoldResult arith::UIToFPOp::fold(FoldAdaptor adaptor) {
2216 Type resEleType = getElementTypeOrSelf(getType());
2218 adaptor.getOperands(), getType(),
2219 [&resEleType](const APInt &a, bool &castStatus) {
2220 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2221 APFloat apf(floatTy.getFloatSemantics(),
2222 APInt::getZero(floatTy.getWidth()));
2223 apf.convertFromAPInt(a, /*IsSigned=*/false,
2224 APFloat::rmNearestTiesToEven);
2225 return apf;
2226 });
2227}
2228
2229void arith::UIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2230 MLIRContext *context) {
2231 patterns.add<UIToFPOfExtUI>(context);
2232}
2233
2234//===----------------------------------------------------------------------===//
2235// SIToFPOp
2236//===----------------------------------------------------------------------===//
2237
2238bool arith::SIToFPOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2239 return checkIntFloatCast<IntegerType, FloatType>(inputs, outputs);
2240}
2241
2242OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
2243 Type resEleType = getElementTypeOrSelf(getType());
2245 adaptor.getOperands(), getType(),
2246 [&resEleType](const APInt &a, bool &castStatus) {
2247 FloatType floatTy = llvm::cast<FloatType>(resEleType);
2248 APFloat apf(floatTy.getFloatSemantics(),
2249 APInt::getZero(floatTy.getWidth()));
2250 apf.convertFromAPInt(a, /*IsSigned=*/true,
2251 APFloat::rmNearestTiesToEven);
2252 return apf;
2253 });
2254}
2255
2256void arith::SIToFPOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2257 MLIRContext *context) {
2258 patterns.add<SIToFPOfExtSI, SIToFPOfExtUI>(context);
2259}
2260
2261//===----------------------------------------------------------------------===//
2262// FPToUIOp
2263//===----------------------------------------------------------------------===//
2264
2265bool arith::FPToUIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2266 return checkIntFloatCast<FloatType, IntegerType>(inputs, outputs);
2267}
2268
2269OpFoldResult arith::FPToUIOp::fold(FoldAdaptor adaptor) {
2270 Type resType = getElementTypeOrSelf(getType());
2271 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2273 adaptor.getOperands(), getType(),
2274 [&bitWidth](const APFloat &a, bool &castStatus) {
2275 bool ignored;
2276 APSInt api(bitWidth, /*isUnsigned=*/true);
2277 castStatus = APFloat::opInvalidOp !=
2278 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2279 return api;
2280 });
2281}
2282
2283//===----------------------------------------------------------------------===//
2284// FPToSIOp
2285//===----------------------------------------------------------------------===//
2286
2287bool arith::FPToSIOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2288 return checkIntFloatCast<FloatType, IntegerType>(inputs, outputs);
2289}
2290
2291OpFoldResult arith::FPToSIOp::fold(FoldAdaptor adaptor) {
2292 Type resType = getElementTypeOrSelf(getType());
2293 unsigned bitWidth = llvm::cast<IntegerType>(resType).getWidth();
2295 adaptor.getOperands(), getType(),
2296 [&bitWidth](const APFloat &a, bool &castStatus) {
2297 bool ignored;
2298 APSInt api(bitWidth, /*isUnsigned=*/false);
2299 castStatus = APFloat::opInvalidOp !=
2300 a.convertToInteger(api, APFloat::rmTowardZero, &ignored);
2301 return api;
2302 });
2303}
2304
2305//===----------------------------------------------------------------------===//
2306// IndexCastOp
2307//===----------------------------------------------------------------------===//
2308
2309/// Return the bit-width of \p t for the purpose of index_cast width checks.
2310/// For vector types use the element type; index maps to its internal storage
2311/// width (64 on all current targets).
2312static unsigned getIndexCastWidth(Type t) {
2313 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(t)))
2314 return intTy.getWidth();
2315 return IndexType::kInternalStorageBitWidth;
2316}
2317
2318static bool areIndexCastCompatible(TypeRange inputs, TypeRange outputs) {
2319 if (!areValidCastInputsAndOutputs(inputs, outputs))
2320 return false;
2321
2322 auto srcType = getTypeIfLikeOrMemRef<IntegerType, IndexType>(inputs.front());
2323 auto dstType = getTypeIfLikeOrMemRef<IntegerType, IndexType>(outputs.front());
2324 if (!srcType || !dstType)
2325 return false;
2326
2327 return (srcType.isIndex() && dstType.isSignlessInteger()) ||
2328 (srcType.isSignlessInteger() && dstType.isIndex());
2329}
2330
2331bool arith::IndexCastOp::areCastCompatible(TypeRange inputs,
2332 TypeRange outputs) {
2333 return areIndexCastCompatible(inputs, outputs);
2334}
2335
2336OpFoldResult arith::IndexCastOp::fold(FoldAdaptor adaptor) {
2337 // index_cast(constant) -> constant
2338 unsigned resultBitwidth = 64; // Default for index integer attributes.
2339 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(getType())))
2340 resultBitwidth = intTy.getWidth();
2341
2342 if (auto foldResult = constFoldCastOp<IntegerAttr, IntegerAttr>(
2343 adaptor.getOperands(), getType(),
2344 [resultBitwidth](const APInt &a, bool & /*castStatus*/) {
2345 return a.sextOrTrunc(resultBitwidth);
2346 }))
2347 return foldResult;
2348
2349 // index_cast(index_cast(x : A) : B) : A -> x, but only when B is at least
2350 // as wide as A. If B is narrower, the inner cast truncates and the outer
2351 // cast sign-extends, so the round-trip is lossy.
2352 if (auto inner = getOperand().getDefiningOp<arith::IndexCastOp>()) {
2353 Value x = inner.getOperand();
2354 if (x.getType() == getType()) {
2355 if (getIndexCastWidth(inner.getType()) >= getIndexCastWidth(x.getType()))
2356 return x;
2357 }
2358 }
2359 return {};
2360}
2361
2362void arith::IndexCastOp::getCanonicalizationPatterns(
2363 RewritePatternSet &patterns, MLIRContext *context) {
2364 patterns.add<IndexCastOfExtSI>(context);
2365}
2366
2367//===----------------------------------------------------------------------===//
2368// IndexCastUIOp
2369//===----------------------------------------------------------------------===//
2370
2371bool arith::IndexCastUIOp::areCastCompatible(TypeRange inputs,
2372 TypeRange outputs) {
2373 return areIndexCastCompatible(inputs, outputs);
2374}
2375
2376OpFoldResult arith::IndexCastUIOp::fold(FoldAdaptor adaptor) {
2377 // index_castui(constant) -> constant
2378 unsigned resultBitwidth = 64; // Default for index integer attributes.
2379 if (auto intTy = dyn_cast<IntegerType>(getElementTypeOrSelf(getType())))
2380 resultBitwidth = intTy.getWidth();
2381
2382 if (auto foldResult = constFoldCastOp<IntegerAttr, IntegerAttr>(
2383 adaptor.getOperands(), getType(),
2384 [resultBitwidth](const APInt &a, bool & /*castStatus*/) {
2385 return a.zextOrTrunc(resultBitwidth);
2386 }))
2387 return foldResult;
2388
2389 // index_castui(index_castui(x : A) : B) : A -> x, but only when B is at
2390 // least as wide as A. If B is narrower, the inner cast truncates and the
2391 // outer cast zero-extends, so the round-trip is lossy.
2392 if (auto inner = getOperand().getDefiningOp<arith::IndexCastUIOp>()) {
2393 Value x = inner.getOperand();
2394 if (x.getType() == getType()) {
2395 if (getIndexCastWidth(inner.getType()) >= getIndexCastWidth(x.getType()))
2396 return x;
2397 }
2398 }
2399 return {};
2400}
2401
2402void arith::IndexCastUIOp::getCanonicalizationPatterns(
2403 RewritePatternSet &patterns, MLIRContext *context) {
2404 patterns.add<IndexCastUIOfExtUI>(context);
2405}
2406
2407//===----------------------------------------------------------------------===//
2408// BitcastOp
2409//===----------------------------------------------------------------------===//
2410
2411bool arith::BitcastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
2412 if (!areValidCastInputsAndOutputs(inputs, outputs))
2413 return false;
2414
2415 auto srcType = getTypeIfLikeOrMemRef<IntegerType, FloatType>(inputs.front());
2416 auto dstType = getTypeIfLikeOrMemRef<IntegerType, FloatType>(outputs.front());
2417 if (!srcType || !dstType)
2418 return false;
2419
2420 return srcType.getIntOrFloatBitWidth() == dstType.getIntOrFloatBitWidth();
2421}
2422
2423OpFoldResult arith::BitcastOp::fold(FoldAdaptor adaptor) {
2424 auto resType = getType();
2425 auto operand = adaptor.getIn();
2426 if (!operand)
2427 return {};
2428
2429 /// Bitcast dense elements.
2430 if (auto denseAttr = dyn_cast_or_null<DenseElementsAttr>(operand))
2431 return denseAttr.bitcast(llvm::cast<ShapedType>(resType).getElementType());
2432 /// Other shaped types unhandled.
2433 if (llvm::isa<ShapedType>(resType))
2434 return {};
2435
2436 /// Bitcast poison.
2437 if (matchPattern(operand, ub::m_Poison()))
2438 return ub::PoisonAttr::get(getContext());
2439
2440 /// Bitcast integer or float to integer or float.
2441 if (!llvm::isa<FloatAttr, IntegerAttr>(operand))
2442 return {};
2443
2444 APInt bits = llvm::isa<FloatAttr>(operand)
2445 ? llvm::cast<FloatAttr>(operand).getValue().bitcastToAPInt()
2446 : llvm::cast<IntegerAttr>(operand).getValue();
2447 assert(resType.getIntOrFloatBitWidth() == bits.getBitWidth() &&
2448 "trying to fold on broken IR: operands have incompatible types");
2449
2450 if (auto resFloatType = dyn_cast<FloatType>(resType))
2451 return FloatAttr::get(resType,
2452 APFloat(resFloatType.getFloatSemantics(), bits));
2453 return IntegerAttr::get(resType, bits);
2454}
2455
2456void arith::BitcastOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2457 MLIRContext *context) {
2458 patterns.add<BitcastOfBitcast>(context);
2459}
2460
2461//===----------------------------------------------------------------------===//
2462// CmpIOp
2463//===----------------------------------------------------------------------===//
2464
2465/// Compute `lhs` `pred` `rhs`, where `pred` is one of the known integer
2466/// comparison predicates.
2467bool mlir::arith::applyCmpPredicate(arith::CmpIPredicate predicate,
2468 const APInt &lhs, const APInt &rhs) {
2469 switch (predicate) {
2470 case arith::CmpIPredicate::eq:
2471 return lhs.eq(rhs);
2472 case arith::CmpIPredicate::ne:
2473 return lhs.ne(rhs);
2474 case arith::CmpIPredicate::slt:
2475 return lhs.slt(rhs);
2476 case arith::CmpIPredicate::sle:
2477 return lhs.sle(rhs);
2478 case arith::CmpIPredicate::sgt:
2479 return lhs.sgt(rhs);
2480 case arith::CmpIPredicate::sge:
2481 return lhs.sge(rhs);
2482 case arith::CmpIPredicate::ult:
2483 return lhs.ult(rhs);
2484 case arith::CmpIPredicate::ule:
2485 return lhs.ule(rhs);
2486 case arith::CmpIPredicate::ugt:
2487 return lhs.ugt(rhs);
2488 case arith::CmpIPredicate::uge:
2489 return lhs.uge(rhs);
2490 }
2491 llvm_unreachable("unknown cmpi predicate kind");
2492}
2493
2494/// Returns true if the predicate is true for two equal operands.
2495static bool applyCmpPredicateToEqualOperands(arith::CmpIPredicate predicate) {
2496 switch (predicate) {
2497 case arith::CmpIPredicate::eq:
2498 case arith::CmpIPredicate::sle:
2499 case arith::CmpIPredicate::sge:
2500 case arith::CmpIPredicate::ule:
2501 case arith::CmpIPredicate::uge:
2502 return true;
2503 case arith::CmpIPredicate::ne:
2504 case arith::CmpIPredicate::slt:
2505 case arith::CmpIPredicate::sgt:
2506 case arith::CmpIPredicate::ult:
2507 case arith::CmpIPredicate::ugt:
2508 return false;
2509 }
2510 llvm_unreachable("unknown cmpi predicate kind");
2511}
2512
2513static std::optional<int64_t> getIntegerWidth(Type t) {
2514 if (auto intType = dyn_cast<IntegerType>(t)) {
2515 return intType.getWidth();
2516 }
2517 if (auto vectorIntType = dyn_cast<VectorType>(t)) {
2518 return llvm::cast<IntegerType>(vectorIntType.getElementType()).getWidth();
2519 }
2520 return std::nullopt;
2521}
2522
2523OpFoldResult arith::CmpIOp::fold(FoldAdaptor adaptor) {
2524 // cmpi(pred, x, x)
2525 if (getLhs() == getRhs()) {
2526 auto val = applyCmpPredicateToEqualOperands(getPredicate());
2527 return getBoolAttribute(getType(), val);
2528 }
2529
2530 if (matchPattern(adaptor.getRhs(), m_Zero())) {
2531 if (auto extOp = getLhs().getDefiningOp<ExtSIOp>()) {
2532 // extsi(%x : i1 -> iN) != 0 -> %x
2533 std::optional<int64_t> integerWidth =
2534 getIntegerWidth(extOp.getOperand().getType());
2535 if (integerWidth && integerWidth.value() == 1 &&
2536 getPredicate() == arith::CmpIPredicate::ne)
2537 return extOp.getOperand();
2538 }
2539 if (auto extOp = getLhs().getDefiningOp<ExtUIOp>()) {
2540 // extui(%x : i1 -> iN) != 0 -> %x
2541 std::optional<int64_t> integerWidth =
2542 getIntegerWidth(extOp.getOperand().getType());
2543 if (integerWidth && integerWidth.value() == 1 &&
2544 getPredicate() == arith::CmpIPredicate::ne)
2545 return extOp.getOperand();
2546 }
2547
2548 // arith.cmpi ne, %val, %zero : i1 -> %val
2549 if (getElementTypeOrSelf(getLhs().getType()).isInteger(1) &&
2550 getPredicate() == arith::CmpIPredicate::ne)
2551 return getLhs();
2552 }
2553
2554 if (matchPattern(adaptor.getRhs(), m_One())) {
2555 // arith.cmpi eq, %val, %one : i1 -> %val
2556 if (getElementTypeOrSelf(getLhs().getType()).isInteger(1) &&
2557 getPredicate() == arith::CmpIPredicate::eq)
2558 return getLhs();
2559 }
2560
2561 // Move constant to the right side.
2562 if (adaptor.getLhs() && !adaptor.getRhs()) {
2563 // Do not use invertPredicate, as it will change eq to ne and vice versa.
2564 using Pred = CmpIPredicate;
2565 const std::pair<Pred, Pred> invPreds[] = {
2566 {Pred::slt, Pred::sgt}, {Pred::sgt, Pred::slt}, {Pred::sle, Pred::sge},
2567 {Pred::sge, Pred::sle}, {Pred::ult, Pred::ugt}, {Pred::ugt, Pred::ult},
2568 {Pred::ule, Pred::uge}, {Pred::uge, Pred::ule}, {Pred::eq, Pred::eq},
2569 {Pred::ne, Pred::ne},
2570 };
2571 Pred origPred = getPredicate();
2572 for (auto pred : invPreds) {
2573 if (origPred == pred.first) {
2574 setPredicate(pred.second);
2575 Value lhs = getLhs();
2576 Value rhs = getRhs();
2577 getLhsMutable().assign(rhs);
2578 getRhsMutable().assign(lhs);
2579 return getResult();
2580 }
2581 }
2582 llvm_unreachable("unknown cmpi predicate kind");
2583 }
2584
2585 // We are moving constants to the right side; So if lhs is constant rhs is
2586 // guaranteed to be a constant.
2587 if (auto lhs = dyn_cast_if_present<TypedAttr>(adaptor.getLhs())) {
2589 adaptor.getOperands(), getI1SameShape(lhs.getType()),
2590 [pred = getPredicate()](const APInt &lhs, const APInt &rhs) {
2591 return APInt(1,
2592 static_cast<int64_t>(applyCmpPredicate(pred, lhs, rhs)));
2593 });
2594 }
2595
2596 return {};
2597}
2598
2599void arith::CmpIOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2600 MLIRContext *context) {
2601 patterns.insert<CmpIExtSI, CmpIExtUI>(context);
2602}
2603
2604//===----------------------------------------------------------------------===//
2605// CmpFOp
2606//===----------------------------------------------------------------------===//
2607
2608/// Compute `lhs` `pred` `rhs`, where `pred` is one of the known floating point
2609/// comparison predicates.
2610bool mlir::arith::applyCmpPredicate(arith::CmpFPredicate predicate,
2611 const APFloat &lhs, const APFloat &rhs) {
2612 auto cmpResult = lhs.compare(rhs);
2613 switch (predicate) {
2614 case arith::CmpFPredicate::AlwaysFalse:
2615 return false;
2616 case arith::CmpFPredicate::OEQ:
2617 return cmpResult == APFloat::cmpEqual;
2618 case arith::CmpFPredicate::OGT:
2619 return cmpResult == APFloat::cmpGreaterThan;
2620 case arith::CmpFPredicate::OGE:
2621 return cmpResult == APFloat::cmpGreaterThan ||
2622 cmpResult == APFloat::cmpEqual;
2623 case arith::CmpFPredicate::OLT:
2624 return cmpResult == APFloat::cmpLessThan;
2625 case arith::CmpFPredicate::OLE:
2626 return cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2627 case arith::CmpFPredicate::ONE:
2628 return cmpResult != APFloat::cmpUnordered && cmpResult != APFloat::cmpEqual;
2629 case arith::CmpFPredicate::ORD:
2630 return cmpResult != APFloat::cmpUnordered;
2631 case arith::CmpFPredicate::UEQ:
2632 return cmpResult == APFloat::cmpUnordered || cmpResult == APFloat::cmpEqual;
2633 case arith::CmpFPredicate::UGT:
2634 return cmpResult == APFloat::cmpUnordered ||
2635 cmpResult == APFloat::cmpGreaterThan;
2636 case arith::CmpFPredicate::UGE:
2637 return cmpResult == APFloat::cmpUnordered ||
2638 cmpResult == APFloat::cmpGreaterThan ||
2639 cmpResult == APFloat::cmpEqual;
2640 case arith::CmpFPredicate::ULT:
2641 return cmpResult == APFloat::cmpUnordered ||
2642 cmpResult == APFloat::cmpLessThan;
2643 case arith::CmpFPredicate::ULE:
2644 return cmpResult == APFloat::cmpUnordered ||
2645 cmpResult == APFloat::cmpLessThan || cmpResult == APFloat::cmpEqual;
2646 case arith::CmpFPredicate::UNE:
2647 return cmpResult != APFloat::cmpEqual;
2648 case arith::CmpFPredicate::UNO:
2649 return cmpResult == APFloat::cmpUnordered;
2650 case arith::CmpFPredicate::AlwaysTrue:
2651 return true;
2652 }
2653 llvm_unreachable("unknown cmpf predicate kind");
2654}
2655
2656OpFoldResult arith::CmpFOp::fold(FoldAdaptor adaptor) {
2657 auto lhs = dyn_cast_if_present<FloatAttr>(adaptor.getLhs());
2658 auto rhs = dyn_cast_if_present<FloatAttr>(adaptor.getRhs());
2659
2660 // If one operand is NaN, making them both NaN does not change the result.
2661 if (lhs && lhs.getValue().isNaN())
2662 rhs = lhs;
2663 if (rhs && rhs.getValue().isNaN())
2664 lhs = rhs;
2665
2666 if (!lhs || !rhs)
2667 return {};
2668
2669 auto val = applyCmpPredicate(getPredicate(), lhs.getValue(), rhs.getValue());
2670 return BoolAttr::get(getContext(), val);
2671}
2672
2673class CmpFIntToFPConst final : public OpRewritePattern<CmpFOp> {
2674public:
2675 using Base::Base;
2676
2677 static CmpIPredicate convertToIntegerPredicate(CmpFPredicate pred,
2678 bool isUnsigned) {
2679 using namespace arith;
2680 switch (pred) {
2681 case CmpFPredicate::UEQ:
2682 case CmpFPredicate::OEQ:
2683 return CmpIPredicate::eq;
2684 case CmpFPredicate::UGT:
2685 case CmpFPredicate::OGT:
2686 return isUnsigned ? CmpIPredicate::ugt : CmpIPredicate::sgt;
2687 case CmpFPredicate::UGE:
2688 case CmpFPredicate::OGE:
2689 return isUnsigned ? CmpIPredicate::uge : CmpIPredicate::sge;
2690 case CmpFPredicate::ULT:
2691 case CmpFPredicate::OLT:
2692 return isUnsigned ? CmpIPredicate::ult : CmpIPredicate::slt;
2693 case CmpFPredicate::ULE:
2694 case CmpFPredicate::OLE:
2695 return isUnsigned ? CmpIPredicate::ule : CmpIPredicate::sle;
2696 case CmpFPredicate::UNE:
2697 case CmpFPredicate::ONE:
2698 return CmpIPredicate::ne;
2699 default:
2700 llvm_unreachable("Unexpected predicate!");
2701 }
2702 }
2703
2704 LogicalResult matchAndRewrite(CmpFOp op,
2705 PatternRewriter &rewriter) const override {
2706 FloatAttr flt;
2707 if (!matchPattern(op.getRhs(), m_Constant(&flt)))
2708 return failure();
2709
2710 const APFloat &rhs = flt.getValue();
2711
2712 // Don't attempt to fold a nan.
2713 if (rhs.isNaN())
2714 return failure();
2715
2716 // Get the width of the mantissa. We don't want to hack on conversions that
2717 // might lose information from the integer, e.g. "i64 -> float"
2718 FloatType floatTy = llvm::cast<FloatType>(op.getRhs().getType());
2719 int mantissaWidth = floatTy.getFPMantissaWidth();
2720 if (mantissaWidth <= 0)
2721 return failure();
2722
2723 bool isUnsigned;
2724 Value intVal;
2725
2726 if (auto si = op.getLhs().getDefiningOp<SIToFPOp>()) {
2727 isUnsigned = false;
2728 intVal = si.getIn();
2729 } else if (auto ui = op.getLhs().getDefiningOp<UIToFPOp>()) {
2730 isUnsigned = true;
2731 intVal = ui.getIn();
2732 } else {
2733 return failure();
2734 }
2735
2736 // Check to see that the input is converted from an integer type that is
2737 // small enough that preserves all bits.
2738 auto intTy = llvm::cast<IntegerType>(intVal.getType());
2739 auto intWidth = intTy.getWidth();
2740
2741 // Number of bits representing values, as opposed to the sign
2742 auto valueBits = isUnsigned ? intWidth : (intWidth - 1);
2743
2744 // Following test does NOT adjust intWidth downwards for signed inputs,
2745 // because the most negative value still requires all the mantissa bits
2746 // to distinguish it from one less than that value.
2747 if ((int)intWidth > mantissaWidth) {
2748 // Conversion would lose accuracy. Check if loss can impact comparison.
2749 int exponent = ilogb(rhs);
2750 if (exponent == APFloat::IEK_Inf) {
2751 int maxExponent = ilogb(APFloat::getLargest(rhs.getSemantics()));
2752 if (maxExponent < (int)valueBits) {
2753 // Conversion could create infinity.
2754 return failure();
2755 }
2756 } else {
2757 // Note that if rhs is zero or NaN, then Exp is negative
2758 // and first condition is trivially false.
2759 if (mantissaWidth <= exponent && exponent <= (int)valueBits) {
2760 // Conversion could affect comparison.
2761 return failure();
2762 }
2763 }
2764 }
2765
2766 // Convert to equivalent cmpi predicate
2767 CmpIPredicate pred;
2768 switch (op.getPredicate()) {
2769 case CmpFPredicate::ORD:
2770 // Int to fp conversion doesn't create a nan (ord checks neither is a nan)
2771 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2772 /*width=*/1);
2773 return success();
2774 case CmpFPredicate::UNO:
2775 // Int to fp conversion doesn't create a nan (uno checks either is a nan)
2776 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2777 /*width=*/1);
2778 return success();
2779 default:
2780 pred = convertToIntegerPredicate(op.getPredicate(), isUnsigned);
2781 break;
2782 }
2783
2784 if (!isUnsigned) {
2785 // If the rhs value is > SignedMax, fold the comparison. This handles
2786 // +INF and large values.
2787 APFloat signedMax(rhs.getSemantics());
2788 signedMax.convertFromAPInt(APInt::getSignedMaxValue(intWidth), true,
2789 APFloat::rmNearestTiesToEven);
2790 if (signedMax < rhs) { // smax < 13123.0
2791 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::slt ||
2792 pred == CmpIPredicate::sle)
2793 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2794 /*width=*/1);
2795 else
2796 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2797 /*width=*/1);
2798 return success();
2799 }
2800 } else {
2801 // If the rhs value is > UnsignedMax, fold the comparison. This handles
2802 // +INF and large values.
2803 APFloat unsignedMax(rhs.getSemantics());
2804 unsignedMax.convertFromAPInt(APInt::getMaxValue(intWidth), false,
2805 APFloat::rmNearestTiesToEven);
2806 if (unsignedMax < rhs) { // umax < 13123.0
2807 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ult ||
2808 pred == CmpIPredicate::ule)
2809 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2810 /*width=*/1);
2811 else
2812 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2813 /*width=*/1);
2814 return success();
2815 }
2816 }
2817
2818 if (!isUnsigned) {
2819 // See if the rhs value is < SignedMin.
2820 APFloat signedMin(rhs.getSemantics());
2821 signedMin.convertFromAPInt(APInt::getSignedMinValue(intWidth), true,
2822 APFloat::rmNearestTiesToEven);
2823 if (signedMin > rhs) { // smin > 12312.0
2824 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::sgt ||
2825 pred == CmpIPredicate::sge)
2826 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2827 /*width=*/1);
2828 else
2829 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2830 /*width=*/1);
2831 return success();
2832 }
2833 } else {
2834 // See if the rhs value is < UnsignedMin.
2835 APFloat unsignedMin(rhs.getSemantics());
2836 unsignedMin.convertFromAPInt(APInt::getMinValue(intWidth), false,
2837 APFloat::rmNearestTiesToEven);
2838 if (unsignedMin > rhs) { // umin > 12312.0
2839 if (pred == CmpIPredicate::ne || pred == CmpIPredicate::ugt ||
2840 pred == CmpIPredicate::uge)
2841 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2842 /*width=*/1);
2843 else
2844 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2845 /*width=*/1);
2846 return success();
2847 }
2848 }
2849
2850 // Okay, now we know that the FP constant fits in the range [SMIN, SMAX] or
2851 // [0, UMAX], but it may still be fractional. See if it is fractional by
2852 // casting the FP value to the integer value and back, checking for
2853 // equality. Don't do this for zero, because -0.0 is not fractional.
2854 bool ignored;
2855 APSInt rhsInt(intWidth, isUnsigned);
2856 if (APFloat::opInvalidOp ==
2857 rhs.convertToInteger(rhsInt, APFloat::rmTowardZero, &ignored)) {
2858 // Undefined behavior invoked - the destination type can't represent
2859 // the input constant.
2860 return failure();
2861 }
2862
2863 if (!rhs.isZero()) {
2864 APFloat apf(floatTy.getFloatSemantics(),
2865 APInt::getZero(floatTy.getWidth()));
2866 apf.convertFromAPInt(rhsInt, !isUnsigned, APFloat::rmNearestTiesToEven);
2867
2868 bool equal = apf == rhs;
2869 if (!equal) {
2870 // If we had a comparison against a fractional value, we have to adjust
2871 // the compare predicate and sometimes the value. rhsInt is rounded
2872 // towards zero at this point.
2873 switch (pred) {
2874 case CmpIPredicate::ne: // (float)int != 4.4 --> true
2875 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2876 /*width=*/1);
2877 return success();
2878 case CmpIPredicate::eq: // (float)int == 4.4 --> false
2879 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2880 /*width=*/1);
2881 return success();
2882 case CmpIPredicate::ule:
2883 // (float)int <= 4.4 --> int <= 4
2884 // (float)int <= -4.4 --> false
2885 if (rhs.isNegative()) {
2886 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2887 /*width=*/1);
2888 return success();
2889 }
2890 break;
2891 case CmpIPredicate::sle:
2892 // (float)int <= 4.4 --> int <= 4
2893 // (float)int <= -4.4 --> int < -4
2894 if (rhs.isNegative())
2895 pred = CmpIPredicate::slt;
2896 break;
2897 case CmpIPredicate::ult:
2898 // (float)int < -4.4 --> false
2899 // (float)int < 4.4 --> int <= 4
2900 if (rhs.isNegative()) {
2901 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/false,
2902 /*width=*/1);
2903 return success();
2904 }
2905 pred = CmpIPredicate::ule;
2906 break;
2907 case CmpIPredicate::slt:
2908 // (float)int < -4.4 --> int < -4
2909 // (float)int < 4.4 --> int <= 4
2910 if (!rhs.isNegative())
2911 pred = CmpIPredicate::sle;
2912 break;
2913 case CmpIPredicate::ugt:
2914 // (float)int > 4.4 --> int > 4
2915 // (float)int > -4.4 --> true
2916 if (rhs.isNegative()) {
2917 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2918 /*width=*/1);
2919 return success();
2920 }
2921 break;
2922 case CmpIPredicate::sgt:
2923 // (float)int > 4.4 --> int > 4
2924 // (float)int > -4.4 --> int >= -4
2925 if (rhs.isNegative())
2926 pred = CmpIPredicate::sge;
2927 break;
2928 case CmpIPredicate::uge:
2929 // (float)int >= -4.4 --> true
2930 // (float)int >= 4.4 --> int > 4
2931 if (rhs.isNegative()) {
2932 rewriter.replaceOpWithNewOp<ConstantIntOp>(op, /*value=*/true,
2933 /*width=*/1);
2934 return success();
2935 }
2936 pred = CmpIPredicate::ugt;
2937 break;
2938 case CmpIPredicate::sge:
2939 // (float)int >= -4.4 --> int >= -4
2940 // (float)int >= 4.4 --> int > 4
2941 if (!rhs.isNegative())
2942 pred = CmpIPredicate::sgt;
2943 break;
2944 }
2945 }
2946 }
2947
2948 // Lower this FP comparison into an appropriate integer version of the
2949 // comparison.
2950 rewriter.replaceOpWithNewOp<CmpIOp>(
2951 op, pred, intVal,
2952 ConstantOp::create(rewriter, op.getLoc(), intVal.getType(),
2953 rewriter.getIntegerAttr(intVal.getType(), rhsInt)));
2954 return success();
2955 }
2956};
2957
2958void arith::CmpFOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2959 MLIRContext *context) {
2960 patterns.insert<CmpFIntToFPConst>(context);
2961}
2962
2963//===----------------------------------------------------------------------===//
2964// SelectOp
2965//===----------------------------------------------------------------------===//
2966
2967// select %arg, %c1, %c0 => extui %arg
2968struct SelectToExtUI : public OpRewritePattern<arith::SelectOp> {
2969 using Base::Base;
2970
2971 LogicalResult matchAndRewrite(arith::SelectOp op,
2972 PatternRewriter &rewriter) const override {
2973 // Cannot extui i1 to i1, or i1 to f32
2974 if (!llvm::isa<IntegerType>(op.getType()) || op.getType().isInteger(1))
2975 return failure();
2976
2977 // select %x, c1, %c0 => extui %arg
2978 if (matchPattern(op.getTrueValue(), m_One()) &&
2979 matchPattern(op.getFalseValue(), m_Zero())) {
2980 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(op, op.getType(),
2981 op.getCondition());
2982 return success();
2983 }
2984
2985 // select %x, c0, %c1 => extui (xor %arg, true)
2986 if (matchPattern(op.getTrueValue(), m_Zero()) &&
2987 matchPattern(op.getFalseValue(), m_One())) {
2988 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(
2989 op, op.getType(),
2990 arith::XOrIOp::create(
2991 rewriter, op.getLoc(), op.getCondition(),
2992 arith::ConstantIntOp::create(rewriter, op.getLoc(),
2993 op.getCondition().getType(), 1)));
2994 return success();
2995 }
2996
2997 return failure();
2998 }
2999};
3000
3001void arith::SelectOp::getCanonicalizationPatterns(RewritePatternSet &results,
3002 MLIRContext *context) {
3003 results.add<RedundantSelectFalse, RedundantSelectTrue, SelectNotCond,
3004 SelectI1ToNot, SelectCmpISgeToMaxSI, SelectCmpISgeToMinSI,
3005 SelectCmpISgtToMaxSI, SelectCmpISgtToMinSI, SelectCmpISleToMaxSI,
3006 SelectCmpISleToMinSI, SelectCmpISltToMaxSI, SelectCmpISltToMinSI,
3007 SelectCmpIUgeToMaxUI, SelectCmpIUgeToMinUI, SelectCmpIUgtToMaxUI,
3008 SelectCmpIUgtToMinUI, SelectCmpIUleToMaxUI, SelectCmpIUleToMinUI,
3009 SelectCmpIUltToMaxUI, SelectCmpIUltToMinUI, SelectToExtUI>(
3010 context);
3011}
3012
3013OpFoldResult arith::SelectOp::fold(FoldAdaptor adaptor) {
3014 Value trueVal = getTrueValue();
3015 Value falseVal = getFalseValue();
3016 if (trueVal == falseVal)
3017 return trueVal;
3018
3019 Value condition = getCondition();
3020
3021 // select true, %0, %1 => %0
3022 if (matchPattern(adaptor.getCondition(), m_One()))
3023 return trueVal;
3024
3025 // select false, %0, %1 => %1
3026 if (matchPattern(adaptor.getCondition(), m_Zero()))
3027 return falseVal;
3028
3029 // If either operand is fully poisoned, return the other.
3030 if (matchPattern(adaptor.getTrueValue(), ub::m_Poison()))
3031 return falseVal;
3032
3033 if (matchPattern(adaptor.getFalseValue(), ub::m_Poison()))
3034 return trueVal;
3035
3036 // select %x, true, false => %x
3037 if (getType().isSignlessInteger(1) &&
3038 matchPattern(adaptor.getTrueValue(), m_One()) &&
3039 matchPattern(adaptor.getFalseValue(), m_Zero()))
3040 return condition;
3041
3042 if (auto cmp = condition.getDefiningOp<arith::CmpIOp>()) {
3043 auto pred = cmp.getPredicate();
3044 if (pred == arith::CmpIPredicate::eq || pred == arith::CmpIPredicate::ne) {
3045 auto cmpLhs = cmp.getLhs();
3046 auto cmpRhs = cmp.getRhs();
3047
3048 // %0 = arith.cmpi eq, %arg0, %arg1
3049 // %1 = arith.select %0, %arg0, %arg1 => %arg1
3050
3051 // %0 = arith.cmpi ne, %arg0, %arg1
3052 // %1 = arith.select %0, %arg0, %arg1 => %arg0
3053
3054 if ((cmpLhs == trueVal && cmpRhs == falseVal) ||
3055 (cmpRhs == trueVal && cmpLhs == falseVal))
3056 return pred == arith::CmpIPredicate::ne ? trueVal : falseVal;
3057 }
3058 }
3059
3060 // Constant-fold constant operands over non-splat constant condition.
3061 // select %cst_vec, %cst0, %cst1 => %cst2
3062 if (auto cond =
3063 dyn_cast_if_present<DenseElementsAttr>(adaptor.getCondition())) {
3064 // DenseElementsAttr by construction always has a static shape.
3065 assert(cond.getType().hasStaticShape() &&
3066 "DenseElementsAttr must have static shape");
3067 if (auto lhs =
3068 dyn_cast_if_present<DenseElementsAttr>(adaptor.getTrueValue())) {
3069 if (auto rhs =
3070 dyn_cast_if_present<DenseElementsAttr>(adaptor.getFalseValue())) {
3071 SmallVector<Attribute> results;
3072 results.reserve(static_cast<size_t>(cond.getNumElements()));
3073 auto condVals = llvm::make_range(cond.value_begin<BoolAttr>(),
3074 cond.value_end<BoolAttr>());
3075 auto lhsVals = llvm::make_range(lhs.value_begin<Attribute>(),
3076 lhs.value_end<Attribute>());
3077 auto rhsVals = llvm::make_range(rhs.value_begin<Attribute>(),
3078 rhs.value_end<Attribute>());
3079
3080 for (auto [condVal, lhsVal, rhsVal] :
3081 llvm::zip_equal(condVals, lhsVals, rhsVals))
3082 results.push_back(condVal.getValue() ? lhsVal : rhsVal);
3083
3084 return DenseElementsAttr::get(lhs.getType(), results);
3085 }
3086 }
3087 }
3088
3089 return nullptr;
3090}
3091
3092ParseResult SelectOp::parse(OpAsmParser &parser, OperationState &result) {
3093 Type conditionType, resultType;
3094 SmallVector<OpAsmParser::UnresolvedOperand, 3> operands;
3095 if (parser.parseOperandList(operands, /*requiredOperandCount=*/3) ||
3096 parser.parseOptionalAttrDict(result.attributes) ||
3097 parser.parseColonType(resultType))
3098 return failure();
3099
3100 // Check for the explicit condition type if this is a masked tensor or vector.
3101 if (succeeded(parser.parseOptionalComma())) {
3102 conditionType = resultType;
3103 if (parser.parseType(resultType))
3104 return failure();
3105 } else {
3106 conditionType = parser.getBuilder().getI1Type();
3107 }
3108
3109 result.addTypes(resultType);
3110 return parser.resolveOperands(operands,
3111 {conditionType, resultType, resultType},
3112 parser.getNameLoc(), result.operands);
3113}
3114
3115void arith::SelectOp::print(OpAsmPrinter &p) {
3116 p << " " << getOperands();
3117 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
3118 p << " : ";
3119 if (ShapedType condType = dyn_cast<ShapedType>(getCondition().getType()))
3120 p << condType << ", ";
3121 p << getType();
3122}
3123
3124LogicalResult arith::SelectOp::verify() {
3125 Type conditionType = getCondition().getType();
3126 if (conditionType.isSignlessInteger(1))
3127 return success();
3128
3129 // If the result type is a vector or tensor, the type can be a mask with the
3130 // same elements.
3131 Type resultType = getType();
3132 if (!llvm::isa<TensorType, VectorType>(resultType))
3133 return emitOpError() << "expected condition to be a signless i1, but got "
3134 << conditionType;
3135 Type shapedConditionType = getI1SameShape(resultType);
3136 if (conditionType != shapedConditionType) {
3137 return emitOpError() << "expected condition type to have the same shape "
3138 "as the result type, expected "
3139 << shapedConditionType << ", but got "
3140 << conditionType;
3141 }
3142 return success();
3143}
3144//===----------------------------------------------------------------------===//
3145// ShLIOp
3146//===----------------------------------------------------------------------===//
3147
3148OpFoldResult arith::ShLIOp::fold(FoldAdaptor adaptor) {
3149 // TODO: shli(x, c) -> poison when c is out of range (c >= bit width). An
3150 // out-of-range shift amount is undefined behaviour and could fold to poison,
3151 // but that would make the arith dialect depend on the ub dialect to
3152 // materialize ub.poison; left out for now.
3153
3154 // shli(x, 0) -> x
3155 if (matchPattern(adaptor.getRhs(), m_Zero()))
3156 return getLhs();
3157 // shli(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3158 // it to 0 is valid.
3159 if (matchPattern(adaptor.getLhs(), m_Zero()))
3160 return getLhs();
3161 // Don't fold if shifting more or equal than the bit width.
3162 bool bounded = false;
3164 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3165 bounded = b.ult(b.getBitWidth());
3166 return a.shl(b);
3167 });
3168 return bounded ? result : Attribute();
3169}
3170
3171//===----------------------------------------------------------------------===//
3172// ShRUIOp
3173//===----------------------------------------------------------------------===//
3174
3175OpFoldResult arith::ShRUIOp::fold(FoldAdaptor adaptor) {
3176 // TODO: shrui(x, c) -> poison when c is out of range (c >= bit width). An
3177 // out-of-range shift amount is undefined behaviour and could fold to poison,
3178 // but that would make the arith dialect depend on the ub dialect to
3179 // materialize ub.poison; left out for now.
3180
3181 // shrui(x, 0) -> x
3182 if (matchPattern(adaptor.getRhs(), m_Zero()))
3183 return getLhs();
3184 // shrui(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3185 // it to 0 is valid.
3186 if (matchPattern(adaptor.getLhs(), m_Zero()))
3187 return getLhs();
3188 // shrui(x, x) -> 0. For any in-range shift amount v < bitwidth, v >> v == 0
3189 // (v < 2^v); out-of-range amounts yield poison, so 0 is a valid refinement.
3190 if (getLhs() == getRhs())
3191 return getIntegerAttrOfType(getType(), 0);
3192 // Don't fold if shifting more or equal than the bit width.
3193 bool bounded = false;
3195 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3196 bounded = b.ult(b.getBitWidth());
3197 return a.lshr(b);
3198 });
3199 return bounded ? result : Attribute();
3200}
3201
3202//===----------------------------------------------------------------------===//
3203// ShRSIOp
3204//===----------------------------------------------------------------------===//
3205
3206OpFoldResult arith::ShRSIOp::fold(FoldAdaptor adaptor) {
3207 // TODO: shrsi(x, c) -> poison when c is out of range (c >= bit width). An
3208 // out-of-range shift amount is undefined behaviour and could fold to poison,
3209 // but that would make the arith dialect depend on the ub dialect to
3210 // materialize ub.poison; left out for now.
3211
3212 // shrsi(x, 0) -> x
3213 if (matchPattern(adaptor.getRhs(), m_Zero()))
3214 return getLhs();
3215 // shrsi(0, x) -> 0. An out-of-range shift amount yields poison, so refining
3216 // it to 0 is valid.
3217 if (matchPattern(adaptor.getLhs(), m_Zero()))
3218 return getLhs();
3219 // shrsi(x, x) -> 0. For any in-range shift amount v < bitwidth, v is a small
3220 // non-negative value and v >> v == 0; out-of-range amounts yield poison.
3221 if (getLhs() == getRhs())
3222 return getIntegerAttrOfType(getType(), 0);
3223 // shrsi(-1, x) -> -1. Arithmetic shift of all-ones is all-ones for any
3224 // in-range amount; out-of-range amounts yield poison.
3225 if (APInt val;
3226 matchPattern(adaptor.getLhs(), m_ConstantInt(&val)) && val.isAllOnes())
3227 return getLhs();
3228 // Don't fold if shifting more or equal than the bit width.
3229 bool bounded = false;
3231 adaptor.getOperands(), [&](const APInt &a, const APInt &b) {
3232 bounded = b.ult(b.getBitWidth());
3233 return a.ashr(b);
3234 });
3235 return bounded ? result : Attribute();
3236}
3237
3238//===----------------------------------------------------------------------===//
3239// Atomic Enum
3240//===----------------------------------------------------------------------===//
3241
3242/// Returns the identity value attribute associated with an AtomicRMWKind op.
3243TypedAttr mlir::arith::getIdentityValueAttr(AtomicRMWKind kind, Type resultType,
3244 OpBuilder &builder, Location loc,
3245 bool useOnlyFiniteValue) {
3246 switch (kind) {
3247 case AtomicRMWKind::maximumf: {
3248 const llvm::fltSemantics &semantic =
3249 llvm::cast<FloatType>(resultType).getFloatSemantics();
3250 APFloat identity = useOnlyFiniteValue
3251 ? APFloat::getLargest(semantic, /*Negative=*/true)
3252 : APFloat::getInf(semantic, /*Negative=*/true);
3253 return builder.getFloatAttr(resultType, identity);
3254 }
3255 case AtomicRMWKind::maxnumf: {
3256 const llvm::fltSemantics &semantic =
3257 llvm::cast<FloatType>(resultType).getFloatSemantics();
3258 APFloat identity = APFloat::getNaN(semantic, /*Negative=*/true);
3259 return builder.getFloatAttr(resultType, identity);
3260 }
3261 case AtomicRMWKind::addf:
3262 case AtomicRMWKind::addi:
3263 case AtomicRMWKind::maxu:
3264 case AtomicRMWKind::ori:
3265 case AtomicRMWKind::xori:
3266 return builder.getZeroAttr(resultType);
3267 case AtomicRMWKind::andi:
3268 return builder.getIntegerAttr(
3269 resultType,
3270 APInt::getAllOnes(llvm::cast<IntegerType>(resultType).getWidth()));
3271 case AtomicRMWKind::maxs:
3272 return builder.getIntegerAttr(
3273 resultType, APInt::getSignedMinValue(
3274 llvm::cast<IntegerType>(resultType).getWidth()));
3275 case AtomicRMWKind::minimumf: {
3276 const llvm::fltSemantics &semantic =
3277 llvm::cast<FloatType>(resultType).getFloatSemantics();
3278 APFloat identity = useOnlyFiniteValue
3279 ? APFloat::getLargest(semantic, /*Negative=*/false)
3280 : APFloat::getInf(semantic, /*Negative=*/false);
3281
3282 return builder.getFloatAttr(resultType, identity);
3283 }
3284 case AtomicRMWKind::minnumf: {
3285 const llvm::fltSemantics &semantic =
3286 llvm::cast<FloatType>(resultType).getFloatSemantics();
3287 APFloat identity = APFloat::getNaN(semantic, /*Negative=*/false);
3288 return builder.getFloatAttr(resultType, identity);
3289 }
3290 case AtomicRMWKind::mins:
3291 return builder.getIntegerAttr(
3292 resultType, APInt::getSignedMaxValue(
3293 llvm::cast<IntegerType>(resultType).getWidth()));
3294 case AtomicRMWKind::minu:
3295 return builder.getIntegerAttr(
3296 resultType,
3297 APInt::getMaxValue(llvm::cast<IntegerType>(resultType).getWidth()));
3298 case AtomicRMWKind::muli:
3299 return builder.getIntegerAttr(resultType, 1);
3300 case AtomicRMWKind::mulf:
3301 return builder.getFloatAttr(resultType, 1);
3302 // `assign` is not a reduction and has no identity element.
3303 case AtomicRMWKind::assign:
3304 break;
3305 }
3306 (void)emitOptionalError(loc, "Reduction operation type not supported");
3307 return nullptr;
3308}
3309
3310/// Returns the identity numeric value of the given op.
3311std::optional<TypedAttr> mlir::arith::getNeutralElement(Operation *op) {
3312 std::optional<AtomicRMWKind> maybeKind =
3314 // Floating-point operations.
3315 .Case([](arith::AddFOp op) { return AtomicRMWKind::addf; })
3316 .Case([](arith::MulFOp op) { return AtomicRMWKind::mulf; })
3317 .Case([](arith::MaximumFOp op) { return AtomicRMWKind::maximumf; })
3318 .Case([](arith::MinimumFOp op) { return AtomicRMWKind::minimumf; })
3319 .Case([](arith::MaxNumFOp op) { return AtomicRMWKind::maxnumf; })
3320 .Case([](arith::MinNumFOp op) { return AtomicRMWKind::minnumf; })
3321 // Integer operations.
3322 .Case([](arith::AddIOp op) { return AtomicRMWKind::addi; })
3323 .Case([](arith::OrIOp op) { return AtomicRMWKind::ori; })
3324 .Case([](arith::XOrIOp op) { return AtomicRMWKind::xori; })
3325 .Case([](arith::AndIOp op) { return AtomicRMWKind::andi; })
3326 .Case([](arith::MaxUIOp op) { return AtomicRMWKind::maxu; })
3327 .Case([](arith::MinUIOp op) { return AtomicRMWKind::minu; })
3328 .Case([](arith::MaxSIOp op) { return AtomicRMWKind::maxs; })
3329 .Case([](arith::MinSIOp op) { return AtomicRMWKind::mins; })
3330 .Case([](arith::MulIOp op) { return AtomicRMWKind::muli; })
3331 .Default(std::nullopt);
3332 if (!maybeKind) {
3333 return std::nullopt;
3334 }
3335
3336 bool useOnlyFiniteValue = false;
3337 auto fmfOpInterface = dyn_cast<ArithFastMathInterface>(op);
3338 if (fmfOpInterface) {
3339 arith::FastMathFlagsAttr fmfAttr = fmfOpInterface.getFastMathFlagsAttr();
3340 useOnlyFiniteValue =
3341 bitEnumContainsAny(fmfAttr.getValue(), arith::FastMathFlags::ninf);
3342 }
3343
3344 // Builder only used as helper for attribute creation.
3345 OpBuilder b(op->getContext());
3346 Type resultType = op->getResult(0).getType();
3347
3348 return getIdentityValueAttr(*maybeKind, resultType, b, op->getLoc(),
3349 useOnlyFiniteValue);
3350}
3351
3352/// Returns the identity value associated with an AtomicRMWKind op.
3353Value mlir::arith::getIdentityValue(AtomicRMWKind op, Type resultType,
3354 OpBuilder &builder, Location loc,
3355 bool useOnlyFiniteValue) {
3356 if (auto attr = getIdentityValueAttr(op, resultType, builder, loc,
3357 useOnlyFiniteValue))
3358 return arith::ConstantOp::create(builder, loc, attr);
3359 return {};
3360}
3361
3362/// Return the value obtained by applying the reduction operation kind
3363/// associated with a binary AtomicRMWKind op to `lhs` and `rhs`.
3365 Location loc, Value lhs, Value rhs) {
3366 switch (op) {
3367 case AtomicRMWKind::addf:
3368 return arith::AddFOp::create(builder, loc, lhs, rhs);
3369 case AtomicRMWKind::addi:
3370 return arith::AddIOp::create(builder, loc, lhs, rhs);
3371 case AtomicRMWKind::mulf:
3372 return arith::MulFOp::create(builder, loc, lhs, rhs);
3373 case AtomicRMWKind::muli:
3374 return arith::MulIOp::create(builder, loc, lhs, rhs);
3375 case AtomicRMWKind::maximumf:
3376 return arith::MaximumFOp::create(builder, loc, lhs, rhs);
3377 case AtomicRMWKind::minimumf:
3378 return arith::MinimumFOp::create(builder, loc, lhs, rhs);
3379 case AtomicRMWKind::maxnumf:
3380 return arith::MaxNumFOp::create(builder, loc, lhs, rhs);
3381 case AtomicRMWKind::minnumf:
3382 return arith::MinNumFOp::create(builder, loc, lhs, rhs);
3383 case AtomicRMWKind::maxs:
3384 return arith::MaxSIOp::create(builder, loc, lhs, rhs);
3385 case AtomicRMWKind::mins:
3386 return arith::MinSIOp::create(builder, loc, lhs, rhs);
3387 case AtomicRMWKind::maxu:
3388 return arith::MaxUIOp::create(builder, loc, lhs, rhs);
3389 case AtomicRMWKind::minu:
3390 return arith::MinUIOp::create(builder, loc, lhs, rhs);
3391 case AtomicRMWKind::ori:
3392 return arith::OrIOp::create(builder, loc, lhs, rhs);
3393 case AtomicRMWKind::andi:
3394 return arith::AndIOp::create(builder, loc, lhs, rhs);
3395 case AtomicRMWKind::xori:
3396 return arith::XOrIOp::create(builder, loc, lhs, rhs);
3397 // `assign` is not a reduction and has no corresponding binary operation.
3398 case AtomicRMWKind::assign:
3399 break;
3400 }
3401 (void)emitOptionalError(loc, "Reduction operation type not supported");
3402 return nullptr;
3403}
3404
3405//===----------------------------------------------------------------------===//
3406// TableGen'd op method definitions
3407//===----------------------------------------------------------------------===//
3408
3409#define GET_OP_CLASSES
3410#include "mlir/Dialect/Arith/IR/ArithOps.cpp.inc"
3411
3412//===----------------------------------------------------------------------===//
3413// TableGen'd enum attribute definitions
3414//===----------------------------------------------------------------------===//
3415
3416#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 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 Value foldAndIofAndI(arith::AndIOp op)
Fold and(a, and(a, b)) to and(a, b)
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:63
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:732
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
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:310
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.
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:147
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.