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