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