-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[mlir][IntRange] Poison support in int-range analysis #152932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5410e01
2bd89e6
1d804f3
e3aad00
04da1b8
09b67c9
aedf37c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,8 @@ const APInt &ConstantIntRanges::smin() const { return sminVal; } | |
|
|
||
| const APInt &ConstantIntRanges::smax() const { return smaxVal; } | ||
|
|
||
| unsigned ConstantIntRanges::getBitWidth() const { return umin().getBitWidth(); } | ||
|
|
||
| unsigned ConstantIntRanges::getStorageBitwidth(Type type) { | ||
| type = getElementTypeOrSelf(type); | ||
| if (type.isIndex()) | ||
|
|
@@ -42,6 +44,21 @@ ConstantIntRanges ConstantIntRanges::maxRange(unsigned bitwidth) { | |
| return fromUnsigned(APInt::getZero(bitwidth), APInt::getMaxValue(bitwidth)); | ||
| } | ||
|
|
||
| ConstantIntRanges ConstantIntRanges::poison(unsigned bitwidth) { | ||
| if (bitwidth == 0) { | ||
| auto zero = APInt::getZero(0); | ||
| return {zero, zero, zero, zero}; | ||
| } | ||
|
|
||
| // Poison is represented by an empty range. | ||
| auto zero = APInt::getZero(bitwidth); | ||
| auto one = zero + 1; | ||
| auto onem = zero - 1; | ||
| // For i1 the valid unsigned range is [0, 1] and the valid signed range | ||
| // is [-1, 0]. | ||
| return {one, zero, zero, onem}; | ||
| } | ||
|
|
||
| ConstantIntRanges ConstantIntRanges::constant(const APInt &value) { | ||
| return {value, value, value, value}; | ||
| } | ||
|
|
@@ -85,15 +102,44 @@ ConstantIntRanges | |
| ConstantIntRanges::rangeUnion(const ConstantIntRanges &other) const { | ||
| // "Not an integer" poisons everything and also cannot be fed to comparison | ||
| // operators. | ||
| if (umin().getBitWidth() == 0) | ||
| if (getBitWidth() == 0) | ||
| return *this; | ||
| if (other.umin().getBitWidth() == 0) | ||
| if (other.getBitWidth() == 0) | ||
| return other; | ||
|
|
||
| const APInt &uminUnion = umin().ult(other.umin()) ? umin() : other.umin(); | ||
| const APInt &umaxUnion = umax().ugt(other.umax()) ? umax() : other.umax(); | ||
| const APInt &sminUnion = smin().slt(other.smin()) ? smin() : other.smin(); | ||
| const APInt &smaxUnion = smax().sgt(other.smax()) ? smax() : other.smax(); | ||
| APInt uminUnion; | ||
| APInt umaxUnion; | ||
| APInt sminUnion; | ||
| APInt smaxUnion; | ||
|
|
||
| // Union of poisoned range with any other range is the other range. | ||
| // Union is used when we need to merge ranges from multiple indepdenent | ||
| // sources, e.g. in `arith.select` or CFG merge. "Observing" a poisoned | ||
| // value (using it in side-effecting operation) will cause the immediate UB. | ||
| // Well-formed programs should never observe the immediate UB so we assume | ||
| // result is either unused or only used in circumstances when it received the | ||
| // non-poisoned argument. | ||
| if (isUnsignedPoison()) { | ||
| uminUnion = other.umin(); | ||
| umaxUnion = other.umax(); | ||
| } else if (other.isUnsignedPoison()) { | ||
| uminUnion = umin(); | ||
| umaxUnion = umax(); | ||
| } else { | ||
| uminUnion = umin().ult(other.umin()) ? umin() : other.umin(); | ||
| umaxUnion = umax().ugt(other.umax()) ? umax() : other.umax(); | ||
| } | ||
|
|
||
| if (isSignedPoison()) { | ||
| sminUnion = other.smin(); | ||
| smaxUnion = other.smax(); | ||
| } else if (other.isSignedPoison()) { | ||
| sminUnion = smin(); | ||
| smaxUnion = smax(); | ||
| } else { | ||
| sminUnion = smin().slt(other.smin()) ? smin() : other.smin(); | ||
| smaxUnion = smax().sgt(other.smax()) ? smax() : other.smax(); | ||
| } | ||
|
|
||
| return {uminUnion, umaxUnion, sminUnion, smaxUnion}; | ||
| } | ||
|
|
@@ -102,15 +148,38 @@ ConstantIntRanges | |
| ConstantIntRanges::intersection(const ConstantIntRanges &other) const { | ||
| // "Not an integer" poisons everything and also cannot be fed to comparison | ||
| // operators. | ||
| if (umin().getBitWidth() == 0) | ||
| if (getBitWidth() == 0) | ||
| return *this; | ||
| if (other.umin().getBitWidth() == 0) | ||
| if (other.getBitWidth() == 0) | ||
| return other; | ||
|
|
||
| const APInt &uminIntersect = umin().ugt(other.umin()) ? umin() : other.umin(); | ||
| const APInt &umaxIntersect = umax().ult(other.umax()) ? umax() : other.umax(); | ||
| const APInt &sminIntersect = smin().sgt(other.smin()) ? smin() : other.smin(); | ||
| const APInt &smaxIntersect = smax().slt(other.smax()) ? smax() : other.smax(); | ||
| APInt uminIntersect; | ||
| APInt umaxIntersect; | ||
| APInt sminIntersect; | ||
| APInt smaxIntersect; | ||
|
|
||
| // Intersection of poisoned range with any other range is poisoned. | ||
| if (isUnsignedPoison()) { | ||
| uminIntersect = umin(); | ||
| umaxIntersect = umax(); | ||
| } else if (other.isUnsignedPoison()) { | ||
| uminIntersect = other.umin(); | ||
| umaxIntersect = other.umax(); | ||
| } else { | ||
| uminIntersect = umin().ugt(other.umin()) ? umin() : other.umin(); | ||
| umaxIntersect = umax().ult(other.umax()) ? umax() : other.umax(); | ||
| } | ||
|
|
||
| if (isSignedPoison()) { | ||
| sminIntersect = smin(); | ||
| smaxIntersect = smax(); | ||
| } else if (other.isSignedPoison()) { | ||
| sminIntersect = other.smin(); | ||
| smaxIntersect = other.smax(); | ||
| } else { | ||
| sminIntersect = smin().sgt(other.smin()) ? smin() : other.smin(); | ||
| smaxIntersect = smax().slt(other.smax()) ? smax() : other.smax(); | ||
| } | ||
|
|
||
| return {uminIntersect, umaxIntersect, sminIntersect, smaxIntersect}; | ||
| } | ||
|
|
@@ -124,6 +193,14 @@ std::optional<APInt> ConstantIntRanges::getConstantValue() const { | |
| return std::nullopt; | ||
| } | ||
|
|
||
| bool ConstantIntRanges::isSignedPoison() const { | ||
| return getBitWidth() > 0 && smin().sgt(smax()); | ||
|
||
| } | ||
|
|
||
| bool ConstantIntRanges::isUnsignedPoison() const { | ||
| return getBitWidth() > 0 && umin().ugt(umax()); | ||
| } | ||
|
|
||
| raw_ostream &mlir::operator<<(raw_ostream &os, const ConstantIntRanges &range) { | ||
| os << "unsigned : ["; | ||
| range.umin().print(os, /*isSigned*/ false); | ||
|
|
@@ -152,17 +229,32 @@ void mlir::intrange::detail::defaultInferResultRanges( | |
| llvm::SmallVector<ConstantIntRanges> unpacked; | ||
| unpacked.reserve(argRanges.size()); | ||
|
|
||
| bool signedPoison = false; | ||
| bool unsignedPoison = false; | ||
| for (const IntegerValueRange &range : argRanges) { | ||
| if (range.isUninitialized()) | ||
| return; | ||
| unpacked.push_back(range.getValue()); | ||
|
|
||
| const ConstantIntRanges &value = range.getValue(); | ||
| unpacked.push_back(value); | ||
| signedPoison = signedPoison || value.isSignedPoison(); | ||
| unsignedPoison = unsignedPoison || value.isUnsignedPoison(); | ||
| } | ||
|
|
||
| interface.inferResultRanges( | ||
| unpacked, | ||
| [&setResultRanges](Value value, const ConstantIntRanges &argRanges) { | ||
| setResultRanges(value, IntegerValueRange{argRanges}); | ||
| }); | ||
| auto visitor = [&](Value value, const ConstantIntRanges &range) { | ||
| if (!signedPoison && !unsignedPoison) | ||
| return setResultRanges(value, range); | ||
|
|
||
| auto poison = ConstantIntRanges::poison(range.getBitWidth()); | ||
| APInt umin = unsignedPoison ? poison.umin() : range.umin(); | ||
| APInt umax = unsignedPoison ? poison.umax() : range.umax(); | ||
| APInt smin = signedPoison ? poison.smin() : range.smin(); | ||
| APInt smax = signedPoison ? poison.smax() : range.smax(); | ||
|
|
||
| setResultRanges(value, ConstantIntRanges(umin, umax, smin, smax)); | ||
| }; | ||
|
|
||
| interface.inferResultRanges(unpacked, visitor); | ||
| } | ||
|
|
||
| void mlir::intrange::detail::defaultInferResultRangesFromOptional( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment on why this uses
&&?