xref: /llvm-project/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp (revision 9ab16d49c99966f33900d68ed5370f19927ca52c)
15af9d16dSKrzysztof Drewniak //===- InferIntRangeCommon.cpp - Inference for common ops ------------===//
25af9d16dSKrzysztof Drewniak //
35af9d16dSKrzysztof Drewniak // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45af9d16dSKrzysztof Drewniak // See https://llvm.org/LICENSE.txt for license information.
55af9d16dSKrzysztof Drewniak // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65af9d16dSKrzysztof Drewniak //
75af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
85af9d16dSKrzysztof Drewniak //
95af9d16dSKrzysztof Drewniak // This file contains implementations of range inference for operations that are
105af9d16dSKrzysztof Drewniak // common to both the `arith` and `index` dialects to facilitate reuse.
115af9d16dSKrzysztof Drewniak //
125af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
135af9d16dSKrzysztof Drewniak 
145af9d16dSKrzysztof Drewniak #include "mlir/Interfaces/Utils/InferIntRangeCommon.h"
155af9d16dSKrzysztof Drewniak 
165af9d16dSKrzysztof Drewniak #include "mlir/Interfaces/InferIntRangeInterface.h"
175af9d16dSKrzysztof Drewniak 
185af9d16dSKrzysztof Drewniak #include "llvm/ADT/ArrayRef.h"
195af9d16dSKrzysztof Drewniak #include "llvm/ADT/STLExtras.h"
205af9d16dSKrzysztof Drewniak 
215af9d16dSKrzysztof Drewniak #include "llvm/Support/Debug.h"
225af9d16dSKrzysztof Drewniak 
235af9d16dSKrzysztof Drewniak #include <iterator>
245af9d16dSKrzysztof Drewniak #include <optional>
255af9d16dSKrzysztof Drewniak 
265af9d16dSKrzysztof Drewniak using namespace mlir;
275af9d16dSKrzysztof Drewniak 
285af9d16dSKrzysztof Drewniak #define DEBUG_TYPE "int-range-analysis"
295af9d16dSKrzysztof Drewniak 
305af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
315af9d16dSKrzysztof Drewniak // General utilities
325af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
335af9d16dSKrzysztof Drewniak 
345af9d16dSKrzysztof Drewniak /// Function that evaluates the result of doing something on arithmetic
355af9d16dSKrzysztof Drewniak /// constants and returns std::nullopt on overflow.
365af9d16dSKrzysztof Drewniak using ConstArithFn =
375af9d16dSKrzysztof Drewniak     function_ref<std::optional<APInt>(const APInt &, const APInt &)>;
3893ce8e10SThomas Preud'homme using ConstArithStdFn =
3993ce8e10SThomas Preud'homme     std::function<std::optional<APInt>(const APInt &, const APInt &)>;
405af9d16dSKrzysztof Drewniak 
415af9d16dSKrzysztof Drewniak /// Compute op(minLeft, minRight) and op(maxLeft, maxRight) if possible,
425af9d16dSKrzysztof Drewniak /// If either computation overflows, make the result unbounded.
435af9d16dSKrzysztof Drewniak static ConstantIntRanges computeBoundsBy(ConstArithFn op, const APInt &minLeft,
445af9d16dSKrzysztof Drewniak                                          const APInt &minRight,
455af9d16dSKrzysztof Drewniak                                          const APInt &maxLeft,
465af9d16dSKrzysztof Drewniak                                          const APInt &maxRight, bool isSigned) {
475af9d16dSKrzysztof Drewniak   std::optional<APInt> maybeMin = op(minLeft, minRight);
485af9d16dSKrzysztof Drewniak   std::optional<APInt> maybeMax = op(maxLeft, maxRight);
495af9d16dSKrzysztof Drewniak   if (maybeMin && maybeMax)
505af9d16dSKrzysztof Drewniak     return ConstantIntRanges::range(*maybeMin, *maybeMax, isSigned);
515af9d16dSKrzysztof Drewniak   return ConstantIntRanges::maxRange(minLeft.getBitWidth());
525af9d16dSKrzysztof Drewniak }
535af9d16dSKrzysztof Drewniak 
545af9d16dSKrzysztof Drewniak /// Compute the minimum and maximum of `(op(l, r) for l in lhs for r in rhs)`,
555af9d16dSKrzysztof Drewniak /// ignoring unbounded values. Returns the maximal range if `op` overflows.
565af9d16dSKrzysztof Drewniak static ConstantIntRanges minMaxBy(ConstArithFn op, ArrayRef<APInt> lhs,
575af9d16dSKrzysztof Drewniak                                   ArrayRef<APInt> rhs, bool isSigned) {
585af9d16dSKrzysztof Drewniak   unsigned width = lhs[0].getBitWidth();
595af9d16dSKrzysztof Drewniak   APInt min =
605af9d16dSKrzysztof Drewniak       isSigned ? APInt::getSignedMaxValue(width) : APInt::getMaxValue(width);
615af9d16dSKrzysztof Drewniak   APInt max =
625af9d16dSKrzysztof Drewniak       isSigned ? APInt::getSignedMinValue(width) : APInt::getZero(width);
635af9d16dSKrzysztof Drewniak   for (const APInt &left : lhs) {
645af9d16dSKrzysztof Drewniak     for (const APInt &right : rhs) {
655af9d16dSKrzysztof Drewniak       std::optional<APInt> maybeThisResult = op(left, right);
665af9d16dSKrzysztof Drewniak       if (!maybeThisResult)
675af9d16dSKrzysztof Drewniak         return ConstantIntRanges::maxRange(width);
685af9d16dSKrzysztof Drewniak       APInt result = std::move(*maybeThisResult);
695af9d16dSKrzysztof Drewniak       min = (isSigned ? result.slt(min) : result.ult(min)) ? result : min;
705af9d16dSKrzysztof Drewniak       max = (isSigned ? result.sgt(max) : result.ugt(max)) ? result : max;
715af9d16dSKrzysztof Drewniak     }
725af9d16dSKrzysztof Drewniak   }
735af9d16dSKrzysztof Drewniak   return ConstantIntRanges::range(min, max, isSigned);
745af9d16dSKrzysztof Drewniak }
755af9d16dSKrzysztof Drewniak 
765af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
775af9d16dSKrzysztof Drewniak // Ext, trunc, index op handling
785af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
795af9d16dSKrzysztof Drewniak 
805af9d16dSKrzysztof Drewniak ConstantIntRanges
816aeea700SSpenser Bauman mlir::intrange::inferIndexOp(const InferRangeFn &inferFn,
825af9d16dSKrzysztof Drewniak                              ArrayRef<ConstantIntRanges> argRanges,
835af9d16dSKrzysztof Drewniak                              intrange::CmpMode mode) {
845af9d16dSKrzysztof Drewniak   ConstantIntRanges sixtyFour = inferFn(argRanges);
855af9d16dSKrzysztof Drewniak   SmallVector<ConstantIntRanges, 2> truncated;
865af9d16dSKrzysztof Drewniak   llvm::transform(argRanges, std::back_inserter(truncated),
875af9d16dSKrzysztof Drewniak                   [](const ConstantIntRanges &range) {
885af9d16dSKrzysztof Drewniak                     return truncRange(range, /*destWidth=*/indexMinWidth);
895af9d16dSKrzysztof Drewniak                   });
905af9d16dSKrzysztof Drewniak   ConstantIntRanges thirtyTwo = inferFn(truncated);
915af9d16dSKrzysztof Drewniak   ConstantIntRanges thirtyTwoAsSixtyFour =
925af9d16dSKrzysztof Drewniak       extRange(thirtyTwo, /*destWidth=*/indexMaxWidth);
935af9d16dSKrzysztof Drewniak   ConstantIntRanges sixtyFourAsThirtyTwo =
945af9d16dSKrzysztof Drewniak       truncRange(sixtyFour, /*destWidth=*/indexMinWidth);
955af9d16dSKrzysztof Drewniak 
965af9d16dSKrzysztof Drewniak   LLVM_DEBUG(llvm::dbgs() << "Index handling: 64-bit result = " << sixtyFour
975af9d16dSKrzysztof Drewniak                           << " 32-bit = " << thirtyTwo << "\n");
985af9d16dSKrzysztof Drewniak   bool truncEqual = false;
995af9d16dSKrzysztof Drewniak   switch (mode) {
1005af9d16dSKrzysztof Drewniak   case intrange::CmpMode::Both:
1015af9d16dSKrzysztof Drewniak     truncEqual = (thirtyTwo == sixtyFourAsThirtyTwo);
1025af9d16dSKrzysztof Drewniak     break;
1035af9d16dSKrzysztof Drewniak   case intrange::CmpMode::Signed:
1045af9d16dSKrzysztof Drewniak     truncEqual = (thirtyTwo.smin() == sixtyFourAsThirtyTwo.smin() &&
1055af9d16dSKrzysztof Drewniak                   thirtyTwo.smax() == sixtyFourAsThirtyTwo.smax());
1065af9d16dSKrzysztof Drewniak     break;
1075af9d16dSKrzysztof Drewniak   case intrange::CmpMode::Unsigned:
1085af9d16dSKrzysztof Drewniak     truncEqual = (thirtyTwo.umin() == sixtyFourAsThirtyTwo.umin() &&
1095af9d16dSKrzysztof Drewniak                   thirtyTwo.umax() == sixtyFourAsThirtyTwo.umax());
1105af9d16dSKrzysztof Drewniak     break;
1115af9d16dSKrzysztof Drewniak   }
1125af9d16dSKrzysztof Drewniak   if (truncEqual)
1135af9d16dSKrzysztof Drewniak     // Returing the 64-bit result preserves more information.
1145af9d16dSKrzysztof Drewniak     return sixtyFour;
1155af9d16dSKrzysztof Drewniak   ConstantIntRanges merged = sixtyFour.rangeUnion(thirtyTwoAsSixtyFour);
1165af9d16dSKrzysztof Drewniak   return merged;
1175af9d16dSKrzysztof Drewniak }
1185af9d16dSKrzysztof Drewniak 
1195af9d16dSKrzysztof Drewniak ConstantIntRanges mlir::intrange::extRange(const ConstantIntRanges &range,
1205af9d16dSKrzysztof Drewniak                                            unsigned int destWidth) {
1215af9d16dSKrzysztof Drewniak   APInt umin = range.umin().zext(destWidth);
1225af9d16dSKrzysztof Drewniak   APInt umax = range.umax().zext(destWidth);
1235af9d16dSKrzysztof Drewniak   APInt smin = range.smin().sext(destWidth);
1245af9d16dSKrzysztof Drewniak   APInt smax = range.smax().sext(destWidth);
1255af9d16dSKrzysztof Drewniak   return {umin, umax, smin, smax};
1265af9d16dSKrzysztof Drewniak }
1275af9d16dSKrzysztof Drewniak 
1285af9d16dSKrzysztof Drewniak ConstantIntRanges mlir::intrange::extUIRange(const ConstantIntRanges &range,
1295af9d16dSKrzysztof Drewniak                                              unsigned destWidth) {
1305af9d16dSKrzysztof Drewniak   APInt umin = range.umin().zext(destWidth);
1315af9d16dSKrzysztof Drewniak   APInt umax = range.umax().zext(destWidth);
1325af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromUnsigned(umin, umax);
1335af9d16dSKrzysztof Drewniak }
1345af9d16dSKrzysztof Drewniak 
1355af9d16dSKrzysztof Drewniak ConstantIntRanges mlir::intrange::extSIRange(const ConstantIntRanges &range,
1365af9d16dSKrzysztof Drewniak                                              unsigned destWidth) {
1375af9d16dSKrzysztof Drewniak   APInt smin = range.smin().sext(destWidth);
1385af9d16dSKrzysztof Drewniak   APInt smax = range.smax().sext(destWidth);
1395af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromSigned(smin, smax);
1405af9d16dSKrzysztof Drewniak }
1415af9d16dSKrzysztof Drewniak 
1425af9d16dSKrzysztof Drewniak ConstantIntRanges mlir::intrange::truncRange(const ConstantIntRanges &range,
1435af9d16dSKrzysztof Drewniak                                              unsigned int destWidth) {
1445af9d16dSKrzysztof Drewniak   // If you truncate the first four bytes in [0xaaaabbbb, 0xccccbbbb],
1455af9d16dSKrzysztof Drewniak   // the range of the resulting value is not contiguous ind includes 0.
1465af9d16dSKrzysztof Drewniak   // Ex. If you truncate [256, 258] from i16 to i8, you validly get [0, 2],
1475af9d16dSKrzysztof Drewniak   // but you can't truncate [255, 257] similarly.
1485af9d16dSKrzysztof Drewniak   bool hasUnsignedRollover =
1495af9d16dSKrzysztof Drewniak       range.umin().lshr(destWidth) != range.umax().lshr(destWidth);
1505af9d16dSKrzysztof Drewniak   APInt umin = hasUnsignedRollover ? APInt::getZero(destWidth)
1515af9d16dSKrzysztof Drewniak                                    : range.umin().trunc(destWidth);
1525af9d16dSKrzysztof Drewniak   APInt umax = hasUnsignedRollover ? APInt::getMaxValue(destWidth)
1535af9d16dSKrzysztof Drewniak                                    : range.umax().trunc(destWidth);
1545af9d16dSKrzysztof Drewniak 
1555af9d16dSKrzysztof Drewniak   // Signed post-truncation rollover will not occur when either:
1565af9d16dSKrzysztof Drewniak   // - The high parts of the min and max, plus the sign bit, are the same
1575af9d16dSKrzysztof Drewniak   // - The high halves + sign bit of the min and max are either all 1s or all 0s
1585af9d16dSKrzysztof Drewniak   //  and you won't create a [positive, negative] range by truncating.
1595af9d16dSKrzysztof Drewniak   // For example, you can truncate the ranges [256, 258]_i16 to [0, 2]_i8
1605af9d16dSKrzysztof Drewniak   // but not [255, 257]_i16 to a range of i8s. You can also truncate
1615af9d16dSKrzysztof Drewniak   // [-256, -256]_i16 to [-2, 0]_i8, but not [-257, -255]_i16.
1625af9d16dSKrzysztof Drewniak   // You can also truncate [-130, 0]_i16 to i8 because -130_i16 (0xff7e)
1635af9d16dSKrzysztof Drewniak   // will truncate to 0x7e, which is greater than 0
1645af9d16dSKrzysztof Drewniak   APInt sminHighPart = range.smin().ashr(destWidth - 1);
1655af9d16dSKrzysztof Drewniak   APInt smaxHighPart = range.smax().ashr(destWidth - 1);
1665af9d16dSKrzysztof Drewniak   bool hasSignedOverflow =
1675af9d16dSKrzysztof Drewniak       (sminHighPart != smaxHighPart) &&
1685af9d16dSKrzysztof Drewniak       !(sminHighPart.isAllOnes() &&
1695af9d16dSKrzysztof Drewniak         (smaxHighPart.isAllOnes() || smaxHighPart.isZero())) &&
1705af9d16dSKrzysztof Drewniak       !(sminHighPart.isZero() && smaxHighPart.isZero());
1715af9d16dSKrzysztof Drewniak   APInt smin = hasSignedOverflow ? APInt::getSignedMinValue(destWidth)
1725af9d16dSKrzysztof Drewniak                                  : range.smin().trunc(destWidth);
1735af9d16dSKrzysztof Drewniak   APInt smax = hasSignedOverflow ? APInt::getSignedMaxValue(destWidth)
1745af9d16dSKrzysztof Drewniak                                  : range.smax().trunc(destWidth);
1755af9d16dSKrzysztof Drewniak   return {umin, umax, smin, smax};
1765af9d16dSKrzysztof Drewniak }
1775af9d16dSKrzysztof Drewniak 
1785af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
1795af9d16dSKrzysztof Drewniak // Addition
1805af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
1815af9d16dSKrzysztof Drewniak 
1825af9d16dSKrzysztof Drewniak ConstantIntRanges
1832034f2fcSFelix Schneider mlir::intrange::inferAdd(ArrayRef<ConstantIntRanges> argRanges,
1842034f2fcSFelix Schneider                          OverflowFlags ovfFlags) {
1855af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
1862034f2fcSFelix Schneider 
18793ce8e10SThomas Preud'homme   ConstArithStdFn uadd = [=](const APInt &a,
1885af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
1895af9d16dSKrzysztof Drewniak     bool overflowed = false;
1902034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nuw)
1912034f2fcSFelix Schneider                        ? a.uadd_sat(b)
1922034f2fcSFelix Schneider                        : a.uadd_ov(b, overflowed);
1935af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
1945af9d16dSKrzysztof Drewniak   };
19593ce8e10SThomas Preud'homme   ConstArithStdFn sadd = [=](const APInt &a,
1965af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
1975af9d16dSKrzysztof Drewniak     bool overflowed = false;
1982034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nsw)
1992034f2fcSFelix Schneider                        ? a.sadd_sat(b)
2002034f2fcSFelix Schneider                        : a.sadd_ov(b, overflowed);
2015af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
2025af9d16dSKrzysztof Drewniak   };
2035af9d16dSKrzysztof Drewniak 
2045af9d16dSKrzysztof Drewniak   ConstantIntRanges urange = computeBoundsBy(
2055af9d16dSKrzysztof Drewniak       uadd, lhs.umin(), rhs.umin(), lhs.umax(), rhs.umax(), /*isSigned=*/false);
2065af9d16dSKrzysztof Drewniak   ConstantIntRanges srange = computeBoundsBy(
2075af9d16dSKrzysztof Drewniak       sadd, lhs.smin(), rhs.smin(), lhs.smax(), rhs.smax(), /*isSigned=*/true);
2085af9d16dSKrzysztof Drewniak   return urange.intersection(srange);
2095af9d16dSKrzysztof Drewniak }
2105af9d16dSKrzysztof Drewniak 
2115af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2125af9d16dSKrzysztof Drewniak // Subtraction
2135af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2145af9d16dSKrzysztof Drewniak 
2155af9d16dSKrzysztof Drewniak ConstantIntRanges
2162034f2fcSFelix Schneider mlir::intrange::inferSub(ArrayRef<ConstantIntRanges> argRanges,
2172034f2fcSFelix Schneider                          OverflowFlags ovfFlags) {
2185af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
2195af9d16dSKrzysztof Drewniak 
22093ce8e10SThomas Preud'homme   ConstArithStdFn usub = [=](const APInt &a,
2215af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
2225af9d16dSKrzysztof Drewniak     bool overflowed = false;
2232034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nuw)
2242034f2fcSFelix Schneider                        ? a.usub_sat(b)
2252034f2fcSFelix Schneider                        : a.usub_ov(b, overflowed);
2265af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
2275af9d16dSKrzysztof Drewniak   };
22893ce8e10SThomas Preud'homme   ConstArithStdFn ssub = [=](const APInt &a,
2295af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
2305af9d16dSKrzysztof Drewniak     bool overflowed = false;
2312034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nsw)
2322034f2fcSFelix Schneider                        ? a.ssub_sat(b)
2332034f2fcSFelix Schneider                        : a.ssub_ov(b, overflowed);
2345af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
2355af9d16dSKrzysztof Drewniak   };
2365af9d16dSKrzysztof Drewniak   ConstantIntRanges urange = computeBoundsBy(
2375af9d16dSKrzysztof Drewniak       usub, lhs.umin(), rhs.umax(), lhs.umax(), rhs.umin(), /*isSigned=*/false);
2385af9d16dSKrzysztof Drewniak   ConstantIntRanges srange = computeBoundsBy(
2395af9d16dSKrzysztof Drewniak       ssub, lhs.smin(), rhs.smax(), lhs.smax(), rhs.smin(), /*isSigned=*/true);
2405af9d16dSKrzysztof Drewniak   return urange.intersection(srange);
2415af9d16dSKrzysztof Drewniak }
2425af9d16dSKrzysztof Drewniak 
2435af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2445af9d16dSKrzysztof Drewniak // Multiplication
2455af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2465af9d16dSKrzysztof Drewniak 
2475af9d16dSKrzysztof Drewniak ConstantIntRanges
2482034f2fcSFelix Schneider mlir::intrange::inferMul(ArrayRef<ConstantIntRanges> argRanges,
2492034f2fcSFelix Schneider                          OverflowFlags ovfFlags) {
2505af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
2515af9d16dSKrzysztof Drewniak 
25293ce8e10SThomas Preud'homme   ConstArithStdFn umul = [=](const APInt &a,
2535af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
2545af9d16dSKrzysztof Drewniak     bool overflowed = false;
2552034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nuw)
2562034f2fcSFelix Schneider                        ? a.umul_sat(b)
2572034f2fcSFelix Schneider                        : a.umul_ov(b, overflowed);
2585af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
2595af9d16dSKrzysztof Drewniak   };
26093ce8e10SThomas Preud'homme   ConstArithStdFn smul = [=](const APInt &a,
2615af9d16dSKrzysztof Drewniak                              const APInt &b) -> std::optional<APInt> {
2625af9d16dSKrzysztof Drewniak     bool overflowed = false;
2632034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nsw)
2642034f2fcSFelix Schneider                        ? a.smul_sat(b)
2652034f2fcSFelix Schneider                        : a.smul_ov(b, overflowed);
2665af9d16dSKrzysztof Drewniak     return overflowed ? std::optional<APInt>() : result;
2675af9d16dSKrzysztof Drewniak   };
2685af9d16dSKrzysztof Drewniak 
2695af9d16dSKrzysztof Drewniak   ConstantIntRanges urange =
2705af9d16dSKrzysztof Drewniak       minMaxBy(umul, {lhs.umin(), lhs.umax()}, {rhs.umin(), rhs.umax()},
2715af9d16dSKrzysztof Drewniak                /*isSigned=*/false);
2725af9d16dSKrzysztof Drewniak   ConstantIntRanges srange =
2735af9d16dSKrzysztof Drewniak       minMaxBy(smul, {lhs.smin(), lhs.smax()}, {rhs.smin(), rhs.smax()},
2745af9d16dSKrzysztof Drewniak                /*isSigned=*/true);
2755af9d16dSKrzysztof Drewniak   return urange.intersection(srange);
2765af9d16dSKrzysztof Drewniak }
2775af9d16dSKrzysztof Drewniak 
2785af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2795af9d16dSKrzysztof Drewniak // DivU, CeilDivU (Unsigned division)
2805af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
2815af9d16dSKrzysztof Drewniak 
2825af9d16dSKrzysztof Drewniak /// Fix up division results (ex. for ceiling and floor), returning an APInt
2835af9d16dSKrzysztof Drewniak /// if there has been no overflow
2845af9d16dSKrzysztof Drewniak using DivisionFixupFn = function_ref<std::optional<APInt>(
2855af9d16dSKrzysztof Drewniak     const APInt &lhs, const APInt &rhs, const APInt &result)>;
2865af9d16dSKrzysztof Drewniak 
2875af9d16dSKrzysztof Drewniak static ConstantIntRanges inferDivURange(const ConstantIntRanges &lhs,
2885af9d16dSKrzysztof Drewniak                                         const ConstantIntRanges &rhs,
2895af9d16dSKrzysztof Drewniak                                         DivisionFixupFn fixup) {
2905af9d16dSKrzysztof Drewniak   const APInt &lhsMin = lhs.umin(), &lhsMax = lhs.umax(), &rhsMin = rhs.umin(),
2915af9d16dSKrzysztof Drewniak               &rhsMax = rhs.umax();
2925af9d16dSKrzysztof Drewniak 
2935af9d16dSKrzysztof Drewniak   if (!rhsMin.isZero()) {
2945af9d16dSKrzysztof Drewniak     auto udiv = [&fixup](const APInt &a,
2955af9d16dSKrzysztof Drewniak                          const APInt &b) -> std::optional<APInt> {
2965af9d16dSKrzysztof Drewniak       return fixup(a, b, a.udiv(b));
2975af9d16dSKrzysztof Drewniak     };
2985af9d16dSKrzysztof Drewniak     return minMaxBy(udiv, {lhsMin, lhsMax}, {rhsMin, rhsMax},
2995af9d16dSKrzysztof Drewniak                     /*isSigned=*/false);
3005af9d16dSKrzysztof Drewniak   }
3012e612f8dSgoldsteinn 
3022e612f8dSgoldsteinn   APInt umin = APInt::getZero(rhsMin.getBitWidth());
3032e612f8dSgoldsteinn   if (lhsMin.uge(rhsMax) && !rhsMax.isZero())
3042e612f8dSgoldsteinn     umin = lhsMin.udiv(rhsMax);
3052e612f8dSgoldsteinn 
3062e612f8dSgoldsteinn   // X u/ Y u<= X.
3072e612f8dSgoldsteinn   APInt umax = lhsMax;
3082e612f8dSgoldsteinn   return ConstantIntRanges::fromUnsigned(umin, umax);
3095af9d16dSKrzysztof Drewniak }
3105af9d16dSKrzysztof Drewniak 
3115af9d16dSKrzysztof Drewniak ConstantIntRanges
3125af9d16dSKrzysztof Drewniak mlir::intrange::inferDivU(ArrayRef<ConstantIntRanges> argRanges) {
3135af9d16dSKrzysztof Drewniak   return inferDivURange(argRanges[0], argRanges[1],
3145af9d16dSKrzysztof Drewniak                         [](const APInt &lhs, const APInt &rhs,
3155af9d16dSKrzysztof Drewniak                            const APInt &result) { return result; });
3165af9d16dSKrzysztof Drewniak }
3175af9d16dSKrzysztof Drewniak 
3185af9d16dSKrzysztof Drewniak ConstantIntRanges
3195af9d16dSKrzysztof Drewniak mlir::intrange::inferCeilDivU(ArrayRef<ConstantIntRanges> argRanges) {
3205af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
3215af9d16dSKrzysztof Drewniak 
3224a88b904SHaojian Wu   auto ceilDivUIFix = [](const APInt &lhs, const APInt &rhs,
3235af9d16dSKrzysztof Drewniak                          const APInt &result) -> std::optional<APInt> {
3245af9d16dSKrzysztof Drewniak     if (!lhs.urem(rhs).isZero()) {
3255af9d16dSKrzysztof Drewniak       bool overflowed = false;
3265af9d16dSKrzysztof Drewniak       APInt corrected =
3275af9d16dSKrzysztof Drewniak           result.uadd_ov(APInt(result.getBitWidth(), 1), overflowed);
3285af9d16dSKrzysztof Drewniak       return overflowed ? std::optional<APInt>() : corrected;
3295af9d16dSKrzysztof Drewniak     }
3305af9d16dSKrzysztof Drewniak     return result;
3315af9d16dSKrzysztof Drewniak   };
3325af9d16dSKrzysztof Drewniak   return inferDivURange(lhs, rhs, ceilDivUIFix);
3335af9d16dSKrzysztof Drewniak }
3345af9d16dSKrzysztof Drewniak 
3355af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
3365af9d16dSKrzysztof Drewniak // DivS, CeilDivS, FloorDivS (Signed division)
3375af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
3385af9d16dSKrzysztof Drewniak 
3395af9d16dSKrzysztof Drewniak static ConstantIntRanges inferDivSRange(const ConstantIntRanges &lhs,
3405af9d16dSKrzysztof Drewniak                                         const ConstantIntRanges &rhs,
3415af9d16dSKrzysztof Drewniak                                         DivisionFixupFn fixup) {
3425af9d16dSKrzysztof Drewniak   const APInt &lhsMin = lhs.smin(), &lhsMax = lhs.smax(), &rhsMin = rhs.smin(),
3435af9d16dSKrzysztof Drewniak               &rhsMax = rhs.smax();
3445af9d16dSKrzysztof Drewniak   bool canDivide = rhsMin.isStrictlyPositive() || rhsMax.isNegative();
3455af9d16dSKrzysztof Drewniak 
3465af9d16dSKrzysztof Drewniak   if (canDivide) {
3475af9d16dSKrzysztof Drewniak     auto sdiv = [&fixup](const APInt &a,
3485af9d16dSKrzysztof Drewniak                          const APInt &b) -> std::optional<APInt> {
3495af9d16dSKrzysztof Drewniak       bool overflowed = false;
3505af9d16dSKrzysztof Drewniak       APInt result = a.sdiv_ov(b, overflowed);
3515af9d16dSKrzysztof Drewniak       return overflowed ? std::optional<APInt>() : fixup(a, b, result);
3525af9d16dSKrzysztof Drewniak     };
3535af9d16dSKrzysztof Drewniak     return minMaxBy(sdiv, {lhsMin, lhsMax}, {rhsMin, rhsMax},
3545af9d16dSKrzysztof Drewniak                     /*isSigned=*/true);
3555af9d16dSKrzysztof Drewniak   }
3565af9d16dSKrzysztof Drewniak   return ConstantIntRanges::maxRange(rhsMin.getBitWidth());
3575af9d16dSKrzysztof Drewniak }
3585af9d16dSKrzysztof Drewniak 
3595af9d16dSKrzysztof Drewniak ConstantIntRanges
3605af9d16dSKrzysztof Drewniak mlir::intrange::inferDivS(ArrayRef<ConstantIntRanges> argRanges) {
3615af9d16dSKrzysztof Drewniak   return inferDivSRange(argRanges[0], argRanges[1],
3625af9d16dSKrzysztof Drewniak                         [](const APInt &lhs, const APInt &rhs,
3635af9d16dSKrzysztof Drewniak                            const APInt &result) { return result; });
3645af9d16dSKrzysztof Drewniak }
3655af9d16dSKrzysztof Drewniak 
3665af9d16dSKrzysztof Drewniak ConstantIntRanges
3675af9d16dSKrzysztof Drewniak mlir::intrange::inferCeilDivS(ArrayRef<ConstantIntRanges> argRanges) {
3685af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
3695af9d16dSKrzysztof Drewniak 
3704a88b904SHaojian Wu   auto ceilDivSIFix = [](const APInt &lhs, const APInt &rhs,
3715af9d16dSKrzysztof Drewniak                          const APInt &result) -> std::optional<APInt> {
3725af9d16dSKrzysztof Drewniak     if (!lhs.srem(rhs).isZero() && lhs.isNonNegative() == rhs.isNonNegative()) {
3735af9d16dSKrzysztof Drewniak       bool overflowed = false;
3745af9d16dSKrzysztof Drewniak       APInt corrected =
3755af9d16dSKrzysztof Drewniak           result.sadd_ov(APInt(result.getBitWidth(), 1), overflowed);
3765af9d16dSKrzysztof Drewniak       return overflowed ? std::optional<APInt>() : corrected;
3775af9d16dSKrzysztof Drewniak     }
378f2e42d93SKrzysztof Drewniak     // Special case where the usual implementation of ceilDiv causes
379f2e42d93SKrzysztof Drewniak     // INT_MIN / [positive number] to be positive. This doesn't match the
380f2e42d93SKrzysztof Drewniak     // definition of signed ceiling division mathematically, but it prevents
381f2e42d93SKrzysztof Drewniak     // inconsistent constant-folding results. This arises because (-int_min) is
382f2e42d93SKrzysztof Drewniak     // still negative, so -(-int_min / b) is -(int_min / b), which is
383f2e42d93SKrzysztof Drewniak     // positive See #115293.
384f2e42d93SKrzysztof Drewniak     if (lhs.isMinSignedValue() && rhs.sgt(1)) {
385f2e42d93SKrzysztof Drewniak       return -result;
386f2e42d93SKrzysztof Drewniak     }
3875af9d16dSKrzysztof Drewniak     return result;
3885af9d16dSKrzysztof Drewniak   };
389*9ab16d49SIvan Butygin   ConstantIntRanges result = inferDivSRange(lhs, rhs, ceilDivSIFix);
390*9ab16d49SIvan Butygin   if (lhs.smin().isMinSignedValue() && lhs.smax().sgt(lhs.smin())) {
391*9ab16d49SIvan Butygin     // If lhs range includes INT_MIN and lhs is not a single value, we can
392*9ab16d49SIvan Butygin     // suddenly wrap to positive val, skipping entire negative range, add
393*9ab16d49SIvan Butygin     // [INT_MIN + 1, smax()] range to the result to handle this.
394*9ab16d49SIvan Butygin     auto newLhs = ConstantIntRanges::fromSigned(lhs.smin() + 1, lhs.smax());
395*9ab16d49SIvan Butygin     result = result.rangeUnion(inferDivSRange(newLhs, rhs, ceilDivSIFix));
396*9ab16d49SIvan Butygin   }
397*9ab16d49SIvan Butygin   return result;
3985af9d16dSKrzysztof Drewniak }
3995af9d16dSKrzysztof Drewniak 
4005af9d16dSKrzysztof Drewniak ConstantIntRanges
4015af9d16dSKrzysztof Drewniak mlir::intrange::inferFloorDivS(ArrayRef<ConstantIntRanges> argRanges) {
4025af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
4035af9d16dSKrzysztof Drewniak 
4044a88b904SHaojian Wu   auto floorDivSIFix = [](const APInt &lhs, const APInt &rhs,
4055af9d16dSKrzysztof Drewniak                           const APInt &result) -> std::optional<APInt> {
4065af9d16dSKrzysztof Drewniak     if (!lhs.srem(rhs).isZero() && lhs.isNonNegative() != rhs.isNonNegative()) {
4075af9d16dSKrzysztof Drewniak       bool overflowed = false;
4085af9d16dSKrzysztof Drewniak       APInt corrected =
4095af9d16dSKrzysztof Drewniak           result.ssub_ov(APInt(result.getBitWidth(), 1), overflowed);
4105af9d16dSKrzysztof Drewniak       return overflowed ? std::optional<APInt>() : corrected;
4115af9d16dSKrzysztof Drewniak     }
4125af9d16dSKrzysztof Drewniak     return result;
4135af9d16dSKrzysztof Drewniak   };
4145af9d16dSKrzysztof Drewniak   return inferDivSRange(lhs, rhs, floorDivSIFix);
4155af9d16dSKrzysztof Drewniak }
4165af9d16dSKrzysztof Drewniak 
4175af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4185af9d16dSKrzysztof Drewniak // Signed remainder (RemS)
4195af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4205af9d16dSKrzysztof Drewniak 
4215af9d16dSKrzysztof Drewniak ConstantIntRanges
4225af9d16dSKrzysztof Drewniak mlir::intrange::inferRemS(ArrayRef<ConstantIntRanges> argRanges) {
4235af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
4245af9d16dSKrzysztof Drewniak   const APInt &lhsMin = lhs.smin(), &lhsMax = lhs.smax(), &rhsMin = rhs.smin(),
4255af9d16dSKrzysztof Drewniak               &rhsMax = rhs.smax();
4265af9d16dSKrzysztof Drewniak 
4275af9d16dSKrzysztof Drewniak   unsigned width = rhsMax.getBitWidth();
4285af9d16dSKrzysztof Drewniak   APInt smin = APInt::getSignedMinValue(width);
4295af9d16dSKrzysztof Drewniak   APInt smax = APInt::getSignedMaxValue(width);
4305af9d16dSKrzysztof Drewniak   // No bounds if zero could be a divisor.
4315af9d16dSKrzysztof Drewniak   bool canBound = (rhsMin.isStrictlyPositive() || rhsMax.isNegative());
4325af9d16dSKrzysztof Drewniak   if (canBound) {
4335af9d16dSKrzysztof Drewniak     APInt maxDivisor = rhsMin.isStrictlyPositive() ? rhsMax : rhsMin.abs();
4345af9d16dSKrzysztof Drewniak     bool canNegativeDividend = lhsMin.isNegative();
4355af9d16dSKrzysztof Drewniak     bool canPositiveDividend = lhsMax.isStrictlyPositive();
4365af9d16dSKrzysztof Drewniak     APInt zero = APInt::getZero(maxDivisor.getBitWidth());
4375af9d16dSKrzysztof Drewniak     APInt maxPositiveResult = maxDivisor - 1;
4385af9d16dSKrzysztof Drewniak     APInt minNegativeResult = -maxPositiveResult;
4395af9d16dSKrzysztof Drewniak     smin = canNegativeDividend ? minNegativeResult : zero;
4405af9d16dSKrzysztof Drewniak     smax = canPositiveDividend ? maxPositiveResult : zero;
4415af9d16dSKrzysztof Drewniak     // Special case: sweeping out a contiguous range in N/[modulus].
4425af9d16dSKrzysztof Drewniak     if (rhsMin == rhsMax) {
4435af9d16dSKrzysztof Drewniak       if ((lhsMax - lhsMin).ult(maxDivisor)) {
4445af9d16dSKrzysztof Drewniak         APInt minRem = lhsMin.srem(maxDivisor);
4455af9d16dSKrzysztof Drewniak         APInt maxRem = lhsMax.srem(maxDivisor);
4465af9d16dSKrzysztof Drewniak         if (minRem.sle(maxRem)) {
4475af9d16dSKrzysztof Drewniak           smin = minRem;
4485af9d16dSKrzysztof Drewniak           smax = maxRem;
4495af9d16dSKrzysztof Drewniak         }
4505af9d16dSKrzysztof Drewniak       }
4515af9d16dSKrzysztof Drewniak     }
4525af9d16dSKrzysztof Drewniak   }
4535af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromSigned(smin, smax);
4545af9d16dSKrzysztof Drewniak }
4555af9d16dSKrzysztof Drewniak 
4565af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4575af9d16dSKrzysztof Drewniak // Unsigned remainder (RemU)
4585af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4595af9d16dSKrzysztof Drewniak 
4605af9d16dSKrzysztof Drewniak ConstantIntRanges
4615af9d16dSKrzysztof Drewniak mlir::intrange::inferRemU(ArrayRef<ConstantIntRanges> argRanges) {
4625af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
4635af9d16dSKrzysztof Drewniak   const APInt &rhsMin = rhs.umin(), &rhsMax = rhs.umax();
4645af9d16dSKrzysztof Drewniak 
4655af9d16dSKrzysztof Drewniak   unsigned width = rhsMin.getBitWidth();
4665af9d16dSKrzysztof Drewniak   APInt umin = APInt::getZero(width);
467e9b7a093Sgoldsteinn   // Remainder can't be larger than either of its arguments.
468e9b7a093Sgoldsteinn   APInt umax = llvm::APIntOps::umin((rhsMax - 1), lhs.umax());
4695af9d16dSKrzysztof Drewniak 
4705af9d16dSKrzysztof Drewniak   if (!rhsMin.isZero()) {
4715af9d16dSKrzysztof Drewniak     // Special case: sweeping out a contiguous range in N/[modulus]
4725af9d16dSKrzysztof Drewniak     if (rhsMin == rhsMax) {
4735af9d16dSKrzysztof Drewniak       const APInt &lhsMin = lhs.umin(), &lhsMax = lhs.umax();
4745af9d16dSKrzysztof Drewniak       if ((lhsMax - lhsMin).ult(rhsMax)) {
4755af9d16dSKrzysztof Drewniak         APInt minRem = lhsMin.urem(rhsMax);
4765af9d16dSKrzysztof Drewniak         APInt maxRem = lhsMax.urem(rhsMax);
4775af9d16dSKrzysztof Drewniak         if (minRem.ule(maxRem)) {
4785af9d16dSKrzysztof Drewniak           umin = minRem;
4795af9d16dSKrzysztof Drewniak           umax = maxRem;
4805af9d16dSKrzysztof Drewniak         }
4815af9d16dSKrzysztof Drewniak       }
4825af9d16dSKrzysztof Drewniak     }
4835af9d16dSKrzysztof Drewniak   }
4845af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromUnsigned(umin, umax);
4855af9d16dSKrzysztof Drewniak }
4865af9d16dSKrzysztof Drewniak 
4875af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4885af9d16dSKrzysztof Drewniak // Max and min (MaxS, MaxU, MinS, MinU)
4895af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
4905af9d16dSKrzysztof Drewniak 
4915af9d16dSKrzysztof Drewniak ConstantIntRanges
4925af9d16dSKrzysztof Drewniak mlir::intrange::inferMaxS(ArrayRef<ConstantIntRanges> argRanges) {
4935af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
4945af9d16dSKrzysztof Drewniak 
4955af9d16dSKrzysztof Drewniak   const APInt &smin = lhs.smin().sgt(rhs.smin()) ? lhs.smin() : rhs.smin();
4965af9d16dSKrzysztof Drewniak   const APInt &smax = lhs.smax().sgt(rhs.smax()) ? lhs.smax() : rhs.smax();
4975af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromSigned(smin, smax);
4985af9d16dSKrzysztof Drewniak }
4995af9d16dSKrzysztof Drewniak 
5005af9d16dSKrzysztof Drewniak ConstantIntRanges
5015af9d16dSKrzysztof Drewniak mlir::intrange::inferMaxU(ArrayRef<ConstantIntRanges> argRanges) {
5025af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
5035af9d16dSKrzysztof Drewniak 
5045af9d16dSKrzysztof Drewniak   const APInt &umin = lhs.umin().ugt(rhs.umin()) ? lhs.umin() : rhs.umin();
5055af9d16dSKrzysztof Drewniak   const APInt &umax = lhs.umax().ugt(rhs.umax()) ? lhs.umax() : rhs.umax();
5065af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromUnsigned(umin, umax);
5075af9d16dSKrzysztof Drewniak }
5085af9d16dSKrzysztof Drewniak 
5095af9d16dSKrzysztof Drewniak ConstantIntRanges
5105af9d16dSKrzysztof Drewniak mlir::intrange::inferMinS(ArrayRef<ConstantIntRanges> argRanges) {
5115af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
5125af9d16dSKrzysztof Drewniak 
5135af9d16dSKrzysztof Drewniak   const APInt &smin = lhs.smin().slt(rhs.smin()) ? lhs.smin() : rhs.smin();
5145af9d16dSKrzysztof Drewniak   const APInt &smax = lhs.smax().slt(rhs.smax()) ? lhs.smax() : rhs.smax();
5155af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromSigned(smin, smax);
5165af9d16dSKrzysztof Drewniak }
5175af9d16dSKrzysztof Drewniak 
5185af9d16dSKrzysztof Drewniak ConstantIntRanges
5195af9d16dSKrzysztof Drewniak mlir::intrange::inferMinU(ArrayRef<ConstantIntRanges> argRanges) {
5205af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
5215af9d16dSKrzysztof Drewniak 
5225af9d16dSKrzysztof Drewniak   const APInt &umin = lhs.umin().ult(rhs.umin()) ? lhs.umin() : rhs.umin();
5235af9d16dSKrzysztof Drewniak   const APInt &umax = lhs.umax().ult(rhs.umax()) ? lhs.umax() : rhs.umax();
5245af9d16dSKrzysztof Drewniak   return ConstantIntRanges::fromUnsigned(umin, umax);
5255af9d16dSKrzysztof Drewniak }
5265af9d16dSKrzysztof Drewniak 
5275af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
5285af9d16dSKrzysztof Drewniak // Bitwise operators (And, Or, Xor)
5295af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
5305af9d16dSKrzysztof Drewniak 
5315af9d16dSKrzysztof Drewniak /// "Widen" bounds - if 0bvvvvv??? <= a <= 0bvvvvv???,
5325af9d16dSKrzysztof Drewniak /// relax the bounds to 0bvvvvv000 <= a <= 0bvvvvv111, where vvvvv are the bits
5335af9d16dSKrzysztof Drewniak /// that both bonuds have in common. This gives us a consertive approximation
5345af9d16dSKrzysztof Drewniak /// for what values can be passed to bitwise operations.
5355af9d16dSKrzysztof Drewniak static std::tuple<APInt, APInt>
5365af9d16dSKrzysztof Drewniak widenBitwiseBounds(const ConstantIntRanges &bound) {
5375af9d16dSKrzysztof Drewniak   APInt leftVal = bound.umin(), rightVal = bound.umax();
5385af9d16dSKrzysztof Drewniak   unsigned bitwidth = leftVal.getBitWidth();
539f8f3db27SKazu Hirata   unsigned differingBits = bitwidth - (leftVal ^ rightVal).countl_zero();
5405af9d16dSKrzysztof Drewniak   leftVal.clearLowBits(differingBits);
5415af9d16dSKrzysztof Drewniak   rightVal.setLowBits(differingBits);
5425af9d16dSKrzysztof Drewniak   return std::make_tuple(std::move(leftVal), std::move(rightVal));
5435af9d16dSKrzysztof Drewniak }
5445af9d16dSKrzysztof Drewniak 
5455af9d16dSKrzysztof Drewniak ConstantIntRanges
5465af9d16dSKrzysztof Drewniak mlir::intrange::inferAnd(ArrayRef<ConstantIntRanges> argRanges) {
5475af9d16dSKrzysztof Drewniak   auto [lhsZeros, lhsOnes] = widenBitwiseBounds(argRanges[0]);
5485af9d16dSKrzysztof Drewniak   auto [rhsZeros, rhsOnes] = widenBitwiseBounds(argRanges[1]);
5495af9d16dSKrzysztof Drewniak   auto andi = [](const APInt &a, const APInt &b) -> std::optional<APInt> {
5505af9d16dSKrzysztof Drewniak     return a & b;
5515af9d16dSKrzysztof Drewniak   };
5525af9d16dSKrzysztof Drewniak   return minMaxBy(andi, {lhsZeros, lhsOnes}, {rhsZeros, rhsOnes},
5535af9d16dSKrzysztof Drewniak                   /*isSigned=*/false);
5545af9d16dSKrzysztof Drewniak }
5555af9d16dSKrzysztof Drewniak 
5565af9d16dSKrzysztof Drewniak ConstantIntRanges
5575af9d16dSKrzysztof Drewniak mlir::intrange::inferOr(ArrayRef<ConstantIntRanges> argRanges) {
5585af9d16dSKrzysztof Drewniak   auto [lhsZeros, lhsOnes] = widenBitwiseBounds(argRanges[0]);
5595af9d16dSKrzysztof Drewniak   auto [rhsZeros, rhsOnes] = widenBitwiseBounds(argRanges[1]);
5605af9d16dSKrzysztof Drewniak   auto ori = [](const APInt &a, const APInt &b) -> std::optional<APInt> {
5615af9d16dSKrzysztof Drewniak     return a | b;
5625af9d16dSKrzysztof Drewniak   };
5635af9d16dSKrzysztof Drewniak   return minMaxBy(ori, {lhsZeros, lhsOnes}, {rhsZeros, rhsOnes},
5645af9d16dSKrzysztof Drewniak                   /*isSigned=*/false);
5655af9d16dSKrzysztof Drewniak }
5665af9d16dSKrzysztof Drewniak 
567d471c85eSIvan Butygin /// Get bitmask of all bits which can change while iterating in
568d471c85eSIvan Butygin /// [bound.umin(), bound.umax()].
569d471c85eSIvan Butygin static APInt getVaryingBitsMask(const ConstantIntRanges &bound) {
570d471c85eSIvan Butygin   APInt leftVal = bound.umin(), rightVal = bound.umax();
571d471c85eSIvan Butygin   unsigned bitwidth = leftVal.getBitWidth();
572d471c85eSIvan Butygin   unsigned differingBits = bitwidth - (leftVal ^ rightVal).countl_zero();
573d471c85eSIvan Butygin   return APInt::getLowBitsSet(bitwidth, differingBits);
574d471c85eSIvan Butygin }
575d471c85eSIvan Butygin 
5765af9d16dSKrzysztof Drewniak ConstantIntRanges
5775af9d16dSKrzysztof Drewniak mlir::intrange::inferXor(ArrayRef<ConstantIntRanges> argRanges) {
578d471c85eSIvan Butygin   // Construct mask of varying bits for both ranges, xor values and then replace
579d471c85eSIvan Butygin   // masked bits with 0s and 1s to get min and max values respectively.
580d471c85eSIvan Butygin   ConstantIntRanges lhs = argRanges[0], rhs = argRanges[1];
581d471c85eSIvan Butygin   APInt mask = getVaryingBitsMask(lhs) | getVaryingBitsMask(rhs);
582d471c85eSIvan Butygin   APInt res = lhs.umin() ^ rhs.umin();
583d471c85eSIvan Butygin   APInt min = res & ~mask;
584d471c85eSIvan Butygin   APInt max = res | mask;
585d471c85eSIvan Butygin   return ConstantIntRanges::fromUnsigned(min, max);
5865af9d16dSKrzysztof Drewniak }
5875af9d16dSKrzysztof Drewniak 
5885af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
5895af9d16dSKrzysztof Drewniak // Shifts (Shl, ShrS, ShrU)
5905af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
5915af9d16dSKrzysztof Drewniak 
5925af9d16dSKrzysztof Drewniak ConstantIntRanges
5932034f2fcSFelix Schneider mlir::intrange::inferShl(ArrayRef<ConstantIntRanges> argRanges,
5942034f2fcSFelix Schneider                          OverflowFlags ovfFlags) {
5955af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
5962034f2fcSFelix Schneider   const APInt &rhsUMin = rhs.umin(), &rhsUMax = rhs.umax();
5970f790664SFelix Schneider 
5982034f2fcSFelix Schneider   // The signed/unsigned overflow behavior of shl by `rhs` matches a mul with
5992034f2fcSFelix Schneider   // 2^rhs.
60093ce8e10SThomas Preud'homme   ConstArithStdFn ushl = [=](const APInt &l,
6015af9d16dSKrzysztof Drewniak                              const APInt &r) -> std::optional<APInt> {
6022034f2fcSFelix Schneider     bool overflowed = false;
6032034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nuw)
6042034f2fcSFelix Schneider                        ? l.ushl_sat(r)
6052034f2fcSFelix Schneider                        : l.ushl_ov(r, overflowed);
6062034f2fcSFelix Schneider     return overflowed ? std::optional<APInt>() : result;
6072034f2fcSFelix Schneider   };
60893ce8e10SThomas Preud'homme   ConstArithStdFn sshl = [=](const APInt &l,
6092034f2fcSFelix Schneider                              const APInt &r) -> std::optional<APInt> {
6102034f2fcSFelix Schneider     bool overflowed = false;
6112034f2fcSFelix Schneider     APInt result = any(ovfFlags & OverflowFlags::Nsw)
6122034f2fcSFelix Schneider                        ? l.sshl_sat(r)
6132034f2fcSFelix Schneider                        : l.sshl_ov(r, overflowed);
6142034f2fcSFelix Schneider     return overflowed ? std::optional<APInt>() : result;
6155af9d16dSKrzysztof Drewniak   };
6160f790664SFelix Schneider 
6175af9d16dSKrzysztof Drewniak   ConstantIntRanges urange =
6182034f2fcSFelix Schneider       minMaxBy(ushl, {lhs.umin(), lhs.umax()}, {rhsUMin, rhsUMax},
6195af9d16dSKrzysztof Drewniak                /*isSigned=*/false);
6205af9d16dSKrzysztof Drewniak   ConstantIntRanges srange =
6212034f2fcSFelix Schneider       minMaxBy(sshl, {lhs.smin(), lhs.smax()}, {rhsUMin, rhsUMax},
6225af9d16dSKrzysztof Drewniak                /*isSigned=*/true);
6235af9d16dSKrzysztof Drewniak   return urange.intersection(srange);
6245af9d16dSKrzysztof Drewniak }
6255af9d16dSKrzysztof Drewniak 
6265af9d16dSKrzysztof Drewniak ConstantIntRanges
6275af9d16dSKrzysztof Drewniak mlir::intrange::inferShrS(ArrayRef<ConstantIntRanges> argRanges) {
6285af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
6295af9d16dSKrzysztof Drewniak 
6304a88b904SHaojian Wu   auto ashr = [](const APInt &l, const APInt &r) -> std::optional<APInt> {
6315af9d16dSKrzysztof Drewniak     return r.uge(r.getBitWidth()) ? std::optional<APInt>() : l.ashr(r);
6325af9d16dSKrzysztof Drewniak   };
6335af9d16dSKrzysztof Drewniak 
6345af9d16dSKrzysztof Drewniak   return minMaxBy(ashr, {lhs.smin(), lhs.smax()}, {rhs.umin(), rhs.umax()},
6355af9d16dSKrzysztof Drewniak                   /*isSigned=*/true);
6365af9d16dSKrzysztof Drewniak }
6375af9d16dSKrzysztof Drewniak 
6385af9d16dSKrzysztof Drewniak ConstantIntRanges
6395af9d16dSKrzysztof Drewniak mlir::intrange::inferShrU(ArrayRef<ConstantIntRanges> argRanges) {
6405af9d16dSKrzysztof Drewniak   const ConstantIntRanges &lhs = argRanges[0], &rhs = argRanges[1];
6415af9d16dSKrzysztof Drewniak 
6424a88b904SHaojian Wu   auto lshr = [](const APInt &l, const APInt &r) -> std::optional<APInt> {
6435af9d16dSKrzysztof Drewniak     return r.uge(r.getBitWidth()) ? std::optional<APInt>() : l.lshr(r);
6445af9d16dSKrzysztof Drewniak   };
6455af9d16dSKrzysztof Drewniak   return minMaxBy(lshr, {lhs.umin(), lhs.umax()}, {rhs.umin(), rhs.umax()},
6465af9d16dSKrzysztof Drewniak                   /*isSigned=*/false);
6475af9d16dSKrzysztof Drewniak }
6485af9d16dSKrzysztof Drewniak 
6495af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
6505af9d16dSKrzysztof Drewniak // Comparisons (Cmp)
6515af9d16dSKrzysztof Drewniak //===----------------------------------------------------------------------===//
6525af9d16dSKrzysztof Drewniak 
6535af9d16dSKrzysztof Drewniak static intrange::CmpPredicate invertPredicate(intrange::CmpPredicate pred) {
6545af9d16dSKrzysztof Drewniak   switch (pred) {
6555af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::eq:
6565af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::ne;
6575af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ne:
6585af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::eq;
6595af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::slt:
6605af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::sge;
6615af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sle:
6625af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::sgt;
6635af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sgt:
6645af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::sle;
6655af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sge:
6665af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::slt;
6675af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ult:
6685af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::uge;
6695af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ule:
6705af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::ugt;
6715af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ugt:
6725af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::ule;
6735af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::uge:
6745af9d16dSKrzysztof Drewniak     return intrange::CmpPredicate::ult;
6755af9d16dSKrzysztof Drewniak   }
6765af9d16dSKrzysztof Drewniak   llvm_unreachable("unknown cmp predicate value");
6775af9d16dSKrzysztof Drewniak }
6785af9d16dSKrzysztof Drewniak 
6795af9d16dSKrzysztof Drewniak static bool isStaticallyTrue(intrange::CmpPredicate pred,
6805af9d16dSKrzysztof Drewniak                              const ConstantIntRanges &lhs,
6815af9d16dSKrzysztof Drewniak                              const ConstantIntRanges &rhs) {
6825af9d16dSKrzysztof Drewniak   switch (pred) {
6835af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sle:
6845af9d16dSKrzysztof Drewniak     return lhs.smax().sle(rhs.smin());
6855af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::slt:
6865af9d16dSKrzysztof Drewniak     return lhs.smax().slt(rhs.smin());
6875af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ule:
6885af9d16dSKrzysztof Drewniak     return lhs.umax().ule(rhs.umin());
6895af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ult:
6905af9d16dSKrzysztof Drewniak     return lhs.umax().ult(rhs.umin());
6915af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sge:
6925af9d16dSKrzysztof Drewniak     return lhs.smin().sge(rhs.smax());
6935af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::sgt:
6945af9d16dSKrzysztof Drewniak     return lhs.smin().sgt(rhs.smax());
6955af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::uge:
6965af9d16dSKrzysztof Drewniak     return lhs.umin().uge(rhs.umax());
6975af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ugt:
6985af9d16dSKrzysztof Drewniak     return lhs.umin().ugt(rhs.umax());
6995af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::eq: {
7005af9d16dSKrzysztof Drewniak     std::optional<APInt> lhsConst = lhs.getConstantValue();
7015af9d16dSKrzysztof Drewniak     std::optional<APInt> rhsConst = rhs.getConstantValue();
7025af9d16dSKrzysztof Drewniak     return lhsConst && rhsConst && lhsConst == rhsConst;
7035af9d16dSKrzysztof Drewniak   }
7045af9d16dSKrzysztof Drewniak   case intrange::CmpPredicate::ne: {
7055af9d16dSKrzysztof Drewniak     // While equality requires that there is an interpration of the preceeding
7065af9d16dSKrzysztof Drewniak     // computations that produces equal constants, whether that be signed or
7075af9d16dSKrzysztof Drewniak     // unsigned, statically determining inequality requires that neither
7085af9d16dSKrzysztof Drewniak     // interpretation produce potentially overlapping ranges.
7095af9d16dSKrzysztof Drewniak     bool sne = isStaticallyTrue(intrange::CmpPredicate::slt, lhs, rhs) ||
7105af9d16dSKrzysztof Drewniak                isStaticallyTrue(intrange::CmpPredicate::sgt, lhs, rhs);
7115af9d16dSKrzysztof Drewniak     bool une = isStaticallyTrue(intrange::CmpPredicate::ult, lhs, rhs) ||
7125af9d16dSKrzysztof Drewniak                isStaticallyTrue(intrange::CmpPredicate::ugt, lhs, rhs);
7135af9d16dSKrzysztof Drewniak     return sne && une;
7145af9d16dSKrzysztof Drewniak   }
7155af9d16dSKrzysztof Drewniak   }
7165af9d16dSKrzysztof Drewniak   return false;
7175af9d16dSKrzysztof Drewniak }
7185af9d16dSKrzysztof Drewniak 
7195af9d16dSKrzysztof Drewniak std::optional<bool> mlir::intrange::evaluatePred(CmpPredicate pred,
7205af9d16dSKrzysztof Drewniak                                                  const ConstantIntRanges &lhs,
7215af9d16dSKrzysztof Drewniak                                                  const ConstantIntRanges &rhs) {
7225af9d16dSKrzysztof Drewniak   if (isStaticallyTrue(pred, lhs, rhs))
7235af9d16dSKrzysztof Drewniak     return true;
7245af9d16dSKrzysztof Drewniak   if (isStaticallyTrue(invertPredicate(pred), lhs, rhs))
7255af9d16dSKrzysztof Drewniak     return false;
7265af9d16dSKrzysztof Drewniak   return std::nullopt;
7275af9d16dSKrzysztof Drewniak }
728