-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[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
5a58774
c52d811
d2b1a09
592633b
f728ddc
dc0784a
d5a1b09
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()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it impossible to represent There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
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
&&
?