Skip to content

Commit c0b268f

Browse files
author
Max Kazantsev
committed
[IRCE] Fix miscompile with range checks against negative values
In the patch rL329547, we have lifted the over-restrictive limitation on collected range checks, allowing to work with range checks with the end of their range not being provably non-negative. However it appeared that the non-negativity of this value was assumed in the utility function `ClampedSubtract`. In particular, its reasoning is based on the fact that `0 <= SINT_MAX - X`, which is not true if `X` is negative. The function `ClampedSubtract` is only called twice, once with `X = 0` (which is OK) and the second time with `X = IRC.getEnd()`, where we may now see the problem if the end is actually a negative value. In this case, we may sometimes miscompile. This patch is the conservative fix of the miscompile problem. Rather than rejecting non-provably non-negative `getEnd()` values, we will check it for non-negativity in runtime. For this, we use function `smax(smin(X, 0), -1) + 1` that is equal to `1` if `X` is non-negative and is equal to 0 if `X` is negative. If we multiply `Begin, End` of safe iteration space by this function calculated for `X = IRC.getEnd()`, we will get the original `[Begin, End)` if `IRC.getEnd()` was non-negative (and, thus, `ClampedSubtract` worked correctly) and the empty range `[0, 0)` in case if ` IRC.getEnd()` was negative. So we in fact prohibit execution of the main loop if at least one of range checks was made against a negative value (and we figured it out in runtime). It is still better than what we have before (non-negativity had to be proved in compile time) and prevents us from miscompile, however it is sometiles too restrictive for unsigned range checks against a negative value (which in fact can be eliminated). Once we re-implement `ClampedSubtract` in a way that it handles negative `X` correctly, this limitation can be lifted, too. Differential Revision: https://reviews.llvm.org/D46860 Reviewed By: samparker llvm-svn: 332809
1 parent a76b64f commit c0b268f

File tree

2 files changed

+639
-2
lines changed

2 files changed

+639
-2
lines changed

llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,13 @@ static bool isKnownNonNegativeInLoop(const SCEV *BoundSCEV, const Loop *L,
813813
SE.isLoopEntryGuardedByCond(L, ICmpInst::ICMP_SGE, BoundSCEV, Zero);
814814
}
815815

816+
static bool isKnownNegativeInLoop(const SCEV *BoundSCEV, const Loop *L,
817+
ScalarEvolution &SE) {
818+
const SCEV *Zero = SE.getZero(BoundSCEV->getType());
819+
return SE.isAvailableAtLoopEntry(BoundSCEV, L) &&
820+
SE.isLoopEntryGuardedByCond(L, ICmpInst::ICMP_SLT, BoundSCEV, Zero);
821+
}
822+
816823
Optional<LoopStructure>
817824
LoopStructure::parseLoopStructure(ScalarEvolution &SE,
818825
BranchProbabilityInfo *BPI, Loop &L,
@@ -1700,6 +1707,10 @@ InductiveRangeCheck::computeSafeIterationSpace(
17001707
// values, depending on type of latch condition that defines IV iteration
17011708
// space.
17021709
auto ClampedSubtract = [&](const SCEV *X, const SCEV *Y) {
1710+
// FIXME: The current implementation assumes that X is in [0, SINT_MAX].
1711+
// This is required to ensure that SINT_MAX - X does not overflow signed and
1712+
// that X - Y does not overflow unsigned if Y is negative. Can we lift this
1713+
// restriction and make it work for negative X either?
17031714
if (IsLatchSigned) {
17041715
// X is a number from signed range, Y is interpreted as signed.
17051716
// Even if Y is SINT_MAX, (X - Y) does not reach SINT_MIN. So the only
@@ -1729,8 +1740,34 @@ InductiveRangeCheck::computeSafeIterationSpace(
17291740
};
17301741
const SCEV *M = SE.getMinusSCEV(C, A);
17311742
const SCEV *Zero = SE.getZero(M->getType());
1732-
const SCEV *Begin = ClampedSubtract(Zero, M);
1733-
const SCEV *End = ClampedSubtract(getEnd(), M);
1743+
1744+
// This function returns SCEV equal to 1 if X is non-negative 0 otherwise.
1745+
auto SCEVCheckNonNegative = [&](const SCEV *X) {
1746+
const Loop *L = IndVar->getLoop();
1747+
const SCEV *One = SE.getOne(X->getType());
1748+
// Can we trivially prove that X is a non-negative or negative value?
1749+
if (isKnownNonNegativeInLoop(X, L, SE))
1750+
return One;
1751+
else if (isKnownNegativeInLoop(X, L, SE))
1752+
return Zero;
1753+
// If not, we will have to figure it out during the execution.
1754+
// Function smax(smin(X, 0), -1) + 1 equals to 1 if X >= 0 and 0 if X < 0.
1755+
const SCEV *NegOne = SE.getNegativeSCEV(One);
1756+
return SE.getAddExpr(SE.getSMaxExpr(SE.getSMinExpr(X, Zero), NegOne), One);
1757+
};
1758+
// FIXME: Current implementation of ClampedSubtract implicitly assumes that
1759+
// X is non-negative (in sense of a signed value). We need to re-implement
1760+
// this function in a way that it will correctly handle negative X as well.
1761+
// We use it twice: for X = 0 everything is fine, but for X = getEnd() we can
1762+
// end up with a negative X and produce wrong results. So currently we ensure
1763+
// that if getEnd() is negative then both ends of the safe range are zero.
1764+
// Note that this may pessimize elimination of unsigned range checks against
1765+
// negative values.
1766+
const SCEV *REnd = getEnd();
1767+
const SCEV *EndIsNonNegative = SCEVCheckNonNegative(REnd);
1768+
1769+
const SCEV *Begin = SE.getMulExpr(ClampedSubtract(Zero, M), EndIsNonNegative);
1770+
const SCEV *End = SE.getMulExpr(ClampedSubtract(REnd, M), EndIsNonNegative);
17341771
return InductiveRangeCheck::Range(Begin, End);
17351772
}
17361773

0 commit comments

Comments
 (0)