18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/Support/Debug.h"
28 #define DEBUG_TYPE "int-range-analysis"
39 std::function<std::optional<APInt>(
const APInt &,
const APInt &)>;
44 const APInt &minRight,
46 const APInt &maxRight,
bool isSigned) {
47 std::optional<APInt> maybeMin = op(minLeft, minRight);
48 std::optional<APInt> maybeMax = op(maxLeft, maxRight);
49 if (maybeMin && maybeMax)
58 unsigned width = lhs[0].getBitWidth();
60 isSigned ? APInt::getSignedMaxValue(width) : APInt::getMaxValue(width);
62 isSigned ? APInt::getSignedMinValue(width) :
APInt::getZero(width);
63 for (
const APInt &left : lhs) {
64 for (
const APInt &right : rhs) {
65 std::optional<APInt> maybeThisResult = op(left, right);
68 APInt result = std::move(*maybeThisResult);
69 min = (isSigned ? result.slt(
min) : result.ult(
min)) ? result :
min;
70 max = (isSigned ? result.sgt(
max) : result.ugt(
max)) ? result :
max;
86 llvm::transform(argRanges, std::back_inserter(truncated),
96 LLVM_DEBUG(llvm::dbgs() <<
"Index handling: 64-bit result = " << sixtyFour
97 <<
" 32-bit = " << thirtyTwo <<
"\n");
98 bool truncEqual =
false;
101 truncEqual = (thirtyTwo == sixtyFourAsThirtyTwo);
104 truncEqual = (thirtyTwo.smin() == sixtyFourAsThirtyTwo.
smin() &&
105 thirtyTwo.smax() == sixtyFourAsThirtyTwo.
smax());
108 truncEqual = (thirtyTwo.umin() == sixtyFourAsThirtyTwo.
umin() &&
109 thirtyTwo.umax() == sixtyFourAsThirtyTwo.
umax());
120 unsigned int destWidth) {
121 APInt umin = range.
umin().zext(destWidth);
122 APInt umax = range.
umax().zext(destWidth);
123 APInt smin = range.
smin().sext(destWidth);
124 APInt smax = range.
smax().sext(destWidth);
125 return {umin, umax, smin, smax};
129 unsigned destWidth) {
130 APInt umin = range.
umin().zext(destWidth);
131 APInt umax = range.
umax().zext(destWidth);
136 unsigned destWidth) {
137 APInt smin = range.
smin().sext(destWidth);
138 APInt smax = range.
smax().sext(destWidth);
143 unsigned int destWidth) {
148 bool hasUnsignedRollover =
149 range.
umin().lshr(destWidth) != range.
umax().lshr(destWidth);
151 : range.umin().trunc(destWidth);
152 APInt umax = hasUnsignedRollover ? APInt::getMaxValue(destWidth)
153 : range.umax().trunc(destWidth);
164 APInt sminHighPart = range.
smin().ashr(destWidth - 1);
165 APInt smaxHighPart = range.
smax().ashr(destWidth - 1);
166 bool hasSignedOverflow =
167 (sminHighPart != smaxHighPart) &&
168 !(sminHighPart.isAllOnes() &&
169 (smaxHighPart.isAllOnes() || smaxHighPart.isZero())) &&
170 !(sminHighPart.isZero() && smaxHighPart.isZero());
171 APInt smin = hasSignedOverflow ? APInt::getSignedMinValue(destWidth)
172 : range.smin().trunc(destWidth);
173 APInt smax = hasSignedOverflow ? APInt::getSignedMaxValue(destWidth)
174 : range.smax().trunc(destWidth);
175 return {umin, umax, smin, smax};
188 const APInt &b) -> std::optional<APInt> {
189 bool overflowed =
false;
190 APInt result = any(ovfFlags & OverflowFlags::Nuw)
192 : a.uadd_ov(b, overflowed);
193 return overflowed ? std::optional<APInt>() : result;
196 const APInt &b) -> std::optional<APInt> {
197 bool overflowed =
false;
198 APInt result = any(ovfFlags & OverflowFlags::Nsw)
200 : a.sadd_ov(b, overflowed);
201 return overflowed ? std::optional<APInt>() : result;
205 uadd, lhs.
umin(), rhs.umin(), lhs.
umax(), rhs.umax(),
false);
207 sadd, lhs.
smin(), rhs.smin(), lhs.
smax(), rhs.smax(),
true);
221 const APInt &b) -> std::optional<APInt> {
222 bool overflowed =
false;
223 APInt result = any(ovfFlags & OverflowFlags::Nuw)
225 : a.usub_ov(b, overflowed);
226 return overflowed ? std::optional<APInt>() : result;
229 const APInt &b) -> std::optional<APInt> {
230 bool overflowed =
false;
231 APInt result = any(ovfFlags & OverflowFlags::Nsw)
233 : a.ssub_ov(b, overflowed);
234 return overflowed ? std::optional<APInt>() : result;
237 usub, lhs.
umin(), rhs.umax(), lhs.
umax(), rhs.umin(),
false);
239 ssub, lhs.
smin(), rhs.smax(), lhs.
smax(), rhs.smin(),
true);
253 const APInt &b) -> std::optional<APInt> {
254 bool overflowed =
false;
255 APInt result = any(ovfFlags & OverflowFlags::Nuw)
257 : a.umul_ov(b, overflowed);
258 return overflowed ? std::optional<APInt>() : result;
261 const APInt &b) -> std::optional<APInt> {
262 bool overflowed =
false;
263 APInt result = any(ovfFlags & OverflowFlags::Nsw)
265 : a.smul_ov(b, overflowed);
266 return overflowed ? std::optional<APInt>() : result;
285 const APInt &lhs,
const APInt &rhs,
const APInt &result)>;
290 const APInt &lhsMin = lhs.
umin(), &lhsMax = lhs.
umax(), &rhsMin = rhs.
umin(),
291 &rhsMax = rhs.
umax();
293 if (!rhsMin.isZero()) {
294 auto udiv = [&fixup](
const APInt &a,
295 const APInt &b) -> std::optional<APInt> {
296 return fixup(a, b, a.udiv(b));
298 return minMaxBy(udiv, {lhsMin, lhsMax}, {rhsMin, rhsMax},
303 if (lhsMin.uge(rhsMax) && !rhsMax.isZero())
304 umin = lhsMin.udiv(rhsMax);
314 [](
const APInt &lhs,
const APInt &rhs,
315 const APInt &result) {
return result; });
322 auto ceilDivUIFix = [](
const APInt &lhs,
const APInt &rhs,
323 const APInt &result) -> std::optional<APInt> {
324 if (!lhs.urem(rhs).isZero()) {
325 bool overflowed =
false;
327 result.uadd_ov(APInt(result.getBitWidth(), 1), overflowed);
328 return overflowed ? std::optional<APInt>() : corrected;
342 const APInt &lhsMin = lhs.
smin(), &lhsMax = lhs.
smax(), &rhsMin = rhs.
smin(),
343 &rhsMax = rhs.
smax();
344 bool canDivide = rhsMin.isStrictlyPositive() || rhsMax.isNegative();
347 auto sdiv = [&fixup](
const APInt &a,
348 const APInt &b) -> std::optional<APInt> {
349 bool overflowed =
false;
350 APInt result = a.sdiv_ov(b, overflowed);
351 return overflowed ? std::optional<APInt>() : fixup(a, b, result);
353 return minMaxBy(sdiv, {lhsMin, lhsMax}, {rhsMin, rhsMax},
362 [](
const APInt &lhs,
const APInt &rhs,
363 const APInt &result) {
return result; });
370 auto ceilDivSIFix = [](
const APInt &lhs,
const APInt &rhs,
371 const APInt &result) -> std::optional<APInt> {
372 if (!lhs.srem(rhs).isZero() && lhs.isNonNegative() == rhs.isNonNegative()) {
373 bool overflowed =
false;
375 result.sadd_ov(APInt(result.getBitWidth(), 1), overflowed);
376 return overflowed ? std::optional<APInt>() : corrected;
384 if (lhs.isMinSignedValue() && rhs.sgt(1)) {
396 auto floorDivSIFix = [](
const APInt &lhs,
const APInt &rhs,
397 const APInt &result) -> std::optional<APInt> {
398 if (!lhs.srem(rhs).isZero() && lhs.isNonNegative() != rhs.isNonNegative()) {
399 bool overflowed =
false;
401 result.ssub_ov(APInt(result.getBitWidth(), 1), overflowed);
402 return overflowed ? std::optional<APInt>() : corrected;
416 const APInt &lhsMin = lhs.
smin(), &lhsMax = lhs.
smax(), &rhsMin = rhs.smin(),
417 &rhsMax = rhs.smax();
419 unsigned width = rhsMax.getBitWidth();
420 APInt smin = APInt::getSignedMinValue(width);
421 APInt smax = APInt::getSignedMaxValue(width);
423 bool canBound = (rhsMin.isStrictlyPositive() || rhsMax.isNegative());
425 APInt maxDivisor = rhsMin.isStrictlyPositive() ? rhsMax : rhsMin.abs();
426 bool canNegativeDividend = lhsMin.isNegative();
427 bool canPositiveDividend = lhsMax.isStrictlyPositive();
429 APInt maxPositiveResult = maxDivisor - 1;
430 APInt minNegativeResult = -maxPositiveResult;
431 smin = canNegativeDividend ? minNegativeResult : zero;
432 smax = canPositiveDividend ? maxPositiveResult : zero;
434 if (rhsMin == rhsMax) {
435 if ((lhsMax - lhsMin).
ult(maxDivisor)) {
436 APInt minRem = lhsMin.srem(maxDivisor);
437 APInt maxRem = lhsMax.srem(maxDivisor);
438 if (minRem.sle(maxRem)) {
455 const APInt &rhsMin = rhs.umin(), &rhsMax = rhs.umax();
457 unsigned width = rhsMin.getBitWidth();
460 APInt umax = llvm::APIntOps::umin((rhsMax - 1), lhs.
umax());
462 if (!rhsMin.isZero()) {
464 if (rhsMin == rhsMax) {
465 const APInt &lhsMin = lhs.
umin(), &lhsMax = lhs.
umax();
466 if ((lhsMax - lhsMin).
ult(rhsMax)) {
467 APInt minRem = lhsMin.urem(rhsMax);
468 APInt maxRem = lhsMax.urem(rhsMax);
469 if (minRem.ule(maxRem)) {
487 const APInt &smin = lhs.
smin().sgt(rhs.smin()) ? lhs.
smin() : rhs.smin();
488 const APInt &smax = lhs.
smax().sgt(rhs.smax()) ? lhs.
smax() : rhs.smax();
496 const APInt &umin = lhs.
umin().ugt(rhs.umin()) ? lhs.
umin() : rhs.umin();
497 const APInt &umax = lhs.
umax().ugt(rhs.umax()) ? lhs.
umax() : rhs.umax();
505 const APInt &smin = lhs.
smin().slt(rhs.smin()) ? lhs.
smin() : rhs.smin();
506 const APInt &smax = lhs.
smax().slt(rhs.smax()) ? lhs.
smax() : rhs.smax();
514 const APInt &umin = lhs.
umin().ult(rhs.umin()) ? lhs.
umin() : rhs.umin();
515 const APInt &umax = lhs.
umax().ult(rhs.umax()) ? lhs.
umax() : rhs.umax();
527 static std::tuple<APInt, APInt>
529 APInt leftVal = bound.
umin(), rightVal = bound.
umax();
530 unsigned bitwidth = leftVal.getBitWidth();
531 unsigned differingBits = bitwidth - (leftVal ^ rightVal).countl_zero();
532 leftVal.clearLowBits(differingBits);
533 rightVal.setLowBits(differingBits);
534 return std::make_tuple(std::move(leftVal), std::move(rightVal));
541 auto andi = [](
const APInt &a,
const APInt &b) -> std::optional<APInt> {
544 return minMaxBy(andi, {lhsZeros, lhsOnes}, {rhsZeros, rhsOnes},
552 auto ori = [](
const APInt &a,
const APInt &b) -> std::optional<APInt> {
555 return minMaxBy(ori, {lhsZeros, lhsOnes}, {rhsZeros, rhsOnes},
562 APInt leftVal = bound.
umin(), rightVal = bound.
umax();
563 unsigned bitwidth = leftVal.getBitWidth();
564 unsigned differingBits = bitwidth - (leftVal ^ rightVal).countl_zero();
565 return APInt::getLowBitsSet(bitwidth, differingBits);
574 APInt res = lhs.
umin() ^ rhs.umin();
575 APInt
min = res & ~mask;
576 APInt
max = res | mask;
588 const APInt &rhsUMin = rhs.umin(), &rhsUMax = rhs.umax();
593 const APInt &r) -> std::optional<APInt> {
594 bool overflowed =
false;
595 APInt result = any(ovfFlags & OverflowFlags::Nuw)
597 : l.ushl_ov(r, overflowed);
598 return overflowed ? std::optional<APInt>() : result;
601 const APInt &r) -> std::optional<APInt> {
602 bool overflowed =
false;
603 APInt result = any(ovfFlags & OverflowFlags::Nsw)
605 : l.sshl_ov(r, overflowed);
606 return overflowed ? std::optional<APInt>() : result;
622 auto ashr = [](
const APInt &l,
const APInt &r) -> std::optional<APInt> {
623 return r.uge(r.getBitWidth()) ? std::optional<APInt>() : l.ashr(r);
634 auto lshr = [](
const APInt &l,
const APInt &r) -> std::optional<APInt> {
635 return r.uge(r.getBitWidth()) ? std::optional<APInt>() : l.lshr(r);
668 llvm_unreachable(
"unknown cmp predicate value");
694 return lhsConst && rhsConst && lhsConst == rhsConst;
static Value getZero(OpBuilder &b, Location loc, Type elementType)
Get zero value for an element type.
static ConstantIntRanges inferDivSRange(const ConstantIntRanges &lhs, const ConstantIntRanges &rhs, DivisionFixupFn fixup)
static bool isStaticallyTrue(intrange::CmpPredicate pred, const ConstantIntRanges &lhs, const ConstantIntRanges &rhs)
static intrange::CmpPredicate invertPredicate(intrange::CmpPredicate pred)
static ConstantIntRanges minMaxBy(ConstArithFn op, ArrayRef< APInt > lhs, ArrayRef< APInt > rhs, bool isSigned)
Compute the minimum and maximum of (op(l, r) for l in lhs for r in rhs), ignoring unbounded values.
static std::tuple< APInt, APInt > widenBitwiseBounds(const ConstantIntRanges &bound)
"Widen" bounds - if 0bvvvvv??? <= a <= 0bvvvvv???, relax the bounds to 0bvvvvv000 <= a <= 0bvvvvv111,...
static ConstantIntRanges inferDivURange(const ConstantIntRanges &lhs, const ConstantIntRanges &rhs, DivisionFixupFn fixup)
std::function< std::optional< APInt >(const APInt &, const APInt &)> ConstArithStdFn
static ConstantIntRanges computeBoundsBy(ConstArithFn op, const APInt &minLeft, const APInt &minRight, const APInt &maxLeft, const APInt &maxRight, bool isSigned)
Compute op(minLeft, minRight) and op(maxLeft, maxRight) if possible, If either computation overflows,...
static APInt getVaryingBitsMask(const ConstantIntRanges &bound)
Get bitmask of all bits which can change while iterating in [bound.umin(), bound.umax()].
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
A set of arbitrary-precision integers representing bounds on a given integer value.
static ConstantIntRanges maxRange(unsigned bitwidth)
Create a ConstantIntRanges with the maximum bounds for the width bitwidth, that is - [0,...
const APInt & smax() const
The maximum value of an integer when it is interpreted as signed.
static ConstantIntRanges fromUnsigned(const APInt &umin, const APInt &umax)
Create an ConstantIntRanges with the unsigned minimum and maximum equal to umin and umax and the sign...
const APInt & smin() const
The minimum value of an integer when it is interpreted as signed.
static ConstantIntRanges range(const APInt &min, const APInt &max, bool isSigned)
Create a ConstantIntRanges whose minimum is min and maximum is max with isSigned specifying if the mi...
ConstantIntRanges intersection(const ConstantIntRanges &other) const
Returns the intersection (computed separately for signed and unsigned bounds) of this range and other...
static ConstantIntRanges fromSigned(const APInt &smin, const APInt &smax)
Create an ConstantIntRanges with the signed minimum and maximum equal to smin and smax,...
std::optional< APInt > getConstantValue() const
If either the signed or unsigned interpretations of the range indicate that the value it bounds is a ...
const APInt & umax() const
The maximum value of an integer when it is interpreted as unsigned.
const APInt & umin() const
The minimum value of an integer when it is interpreted as unsigned.
ConstantIntRanges rangeUnion(const ConstantIntRanges &other) const
Returns the union (computed separately for signed and unsigned bounds) of this range and other.
ConstantIntRanges inferAnd(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferShl(ArrayRef< ConstantIntRanges > argRanges, OverflowFlags ovfFlags=OverflowFlags::None)
ConstantIntRanges inferIndexOp(const InferRangeFn &inferFn, ArrayRef< ConstantIntRanges > argRanges, CmpMode mode)
Compute inferFn on ranges, whose size should be the index storage bitwidth.
ConstantIntRanges inferShrS(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges extSIRange(const ConstantIntRanges &range, unsigned destWidth)
Use the signed values in range to sign-extend it to destWidth.
std::optional< bool > evaluatePred(CmpPredicate pred, const ConstantIntRanges &lhs, const ConstantIntRanges &rhs)
Returns a boolean value if pred is statically true or false for anypossible inputs falling within lhs...
ConstantIntRanges inferMinS(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferMaxU(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferRemS(ArrayRef< ConstantIntRanges > argRanges)
CmpPredicate
Copy of the enum from arith and index to allow the common integer range infrastructure to not depend ...
ConstantIntRanges inferOr(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges extRange(const ConstantIntRanges &range, unsigned destWidth)
Independently zero-extend the unsigned values and sign-extend the signed values in range to destWidth...
ConstantIntRanges inferSub(ArrayRef< ConstantIntRanges > argRanges, OverflowFlags ovfFlags=OverflowFlags::None)
ConstantIntRanges inferAdd(ArrayRef< ConstantIntRanges > argRanges, OverflowFlags ovfFlags=OverflowFlags::None)
ConstantIntRanges truncRange(const ConstantIntRanges &range, unsigned destWidth)
Truncate range to destWidth bits, taking care to handle cases such as the truncation of [255,...
ConstantIntRanges inferDivU(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferCeilDivS(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferMinU(ArrayRef< ConstantIntRanges > argRanges)
std::function< ConstantIntRanges(ArrayRef< ConstantIntRanges >)> InferRangeFn
Function that performs inference on an array of ConstantIntRanges, abstracted away here to permit wri...
ConstantIntRanges inferMul(ArrayRef< ConstantIntRanges > argRanges, OverflowFlags ovfFlags=OverflowFlags::None)
ConstantIntRanges inferXor(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferDivS(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferShrU(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges inferRemU(ArrayRef< ConstantIntRanges > argRanges)
static constexpr unsigned indexMinWidth
ConstantIntRanges inferFloorDivS(ArrayRef< ConstantIntRanges > argRanges)
static constexpr unsigned indexMaxWidth
ConstantIntRanges inferCeilDivU(ArrayRef< ConstantIntRanges > argRanges)
ConstantIntRanges extUIRange(const ConstantIntRanges &range, unsigned destWidth)
Use the unsigned values in range to zero-extend it to destWidth.
ConstantIntRanges inferMaxS(ArrayRef< ConstantIntRanges > argRanges)
Include the generated interface declarations.