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