This is an archive of the discontinued LLVM Phabricator instance.

[IRCE] Fix intersection between signed and unsigned ranges
ClosedPublic

Authored by mkazantsev on Oct 5 2017, 4:39 AM.

Details

Summary

IRCE for unsigned latch conditions was temporarily disabled by rL314881. The motivating
example contained an unsigned latch condition and a signed range check. One of the safe
iteration ranges was [1, SINT_MAX + 1]. Its right border was incorrectly interpreted as a negative
value in IntersectRange function, this lead to a miscompile under which we deleted a range check
without inserting a postloop where it was needed.

This patch brings back IRCE for unsigned latch conditions. Now we treat range intersection more
carefully. If the latch condition was unsigned, we only try to consider a range check for deletion if:

  1. The range check is also unsigned, or
  2. Safe iteration range of the range check lies within [0, SINT_MAX].

The same is done for signed latch.

Values from [0, SINT_MAX] are unambiguous, these values are non-negative under any interpretation,
and all values of a range intersected with such range are also non-negative.

We also use signed/unsigned min/max functions for range intersection depending on type of the
latch condition.

Diff Detail

Repository
rL LLVM

Event Timeline

mkazantsev created this revision.Oct 5 2017, 4:39 AM
anna added inline comments.Oct 10 2017, 7:38 AM
lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
900 ↗(On Diff #117797)

How about leaving this here to quickly find any problems in the future?

984 ↗(On Diff #117797)

Ditto here.

1753 ↗(On Diff #117797)

Is this enough for proving range is non-negative?

Looking at this lambda alone with no context here, we can have [5, 2] and still have range wrap around.
Is there a way to add an assert that getBegin < getEnd?

mkazantsev planned changes to this revision.Oct 13 2017, 12:56 AM
mkazantsev added inline comments.
lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
900 ↗(On Diff #117797)

Ok, I'll update the patch preserving the option (and set it to false by default).

1753 ↗(On Diff #117797)

In parseLoopStructure we assert that the IV HasNoSignedWrap. Interesting detail is that currently it only checks for nsw. My guess is that we should check nuw for unsigned IVs instead. I will check with that and possibly add some tests.

But generally, we assume that the IV does not have wraparound, and ranges for range checks are also in canonical form from a smaller value to a greater value.

mkazantsev added inline comments.Oct 19 2017, 1:42 AM
lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
1753 ↗(On Diff #117797)

Thanks for pointing this situation out! Indeed, it wasn't handled before. As result, sometimes IRCE applied to safe ranges like [5, 2) ending up with making completely unprofitable transformations.

We cannot assert that Begin < End because in the most common situation we don't know it (for example, iteration from 0 to N where N is non-negative is potentially an empty loop, as well as iteration from A to B; we might be not able to say anything from SCEVs, however if we knew that we iterated from A to B with nsw flag, that would give us this piece of information). So this check is over-conservative.

Rather than asserting that Begin < End which is not always known without context (about the indvar and so on), I've prepared a patch https://reviews.llvm.org/D39082 that trats ranges for which we know that Begin >= End as empty.

mkazantsev marked 3 inline comments as done.

Added corner tests to unsigned_comparisons_ult.ll, rebased on the patch that improves work with empty ranges.

anna requested changes to this revision.Oct 19 2017, 8:09 AM

Some clarification questions and comments inline.

lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
1753 ↗(On Diff #117797)

thanks for the clarification! I have added comments on the other review thread about detecting empty ranges.

test/Transforms/IRCE/unsigned_comparisons_ult.ll
345 ↗(On Diff #119556)

Am I right that because of the ult for the compare, -100 is actually UINT_MAX - 99, i.e. the 2's complement of the number.
So, the iteration space is within [0, SINT_MAX].

360 ↗(On Diff #119556)

Please add checks here as CHECK-NOT for pre and post loops.

This revision now requires changes to proceed.Oct 19 2017, 8:09 AM
mkazantsev added inline comments.Oct 19 2017, 9:27 PM
test/Transforms/IRCE/unsigned_comparisons_ult.ll
345 ↗(On Diff #119556)

The iteration space here is [0, UINT_MAX - 100). SINT_MAX is within the iteration range. The comment before test about [100; -100) is misleading, i'll fix it.

360 ↗(On Diff #119556)

On line 11, we had a check that IRCE didn't apply to the test:
; CHECK-NOT: irce: in function test_09: constrained Loop at depth 1 containing: %loop<header><exiting>,%in.bounds<latch><exiting>
But OK, I will add these asserts as well. :)

mkazantsev added inline comments.Oct 19 2017, 9:28 PM
test/Transforms/IRCE/unsigned_comparisons_ult.ll
345 ↗(On Diff #119556)

[0, UINT_MAX - 99) fixed

mkazantsev edited edge metadata.
mkazantsev marked 2 inline comments as done.

Fixed misleading comments in test, added some checks.

fhahn added a subscriber: fhahn.Oct 19 2017, 11:35 PM
anna accepted this revision.Oct 23 2017, 9:29 AM

LGTM . Since context is not here but the actual logic was correct and unchanged wrt the previous review, I went by that.

test/Transforms/IRCE/unsigned_comparisons_ult.ll
360 ↗(On Diff #119556)

ah yes, missed that. Thanks for making it clearer here!

This revision is now accepted and ready to land.Oct 23 2017, 9:29 AM
reames added inline comments.Oct 23 2017, 1:43 PM
lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
1810 ↗(On Diff #119633)

This is correct, but a likely cleaner strategy overall would be normalize ranges at the point of construction. If, for instance, all strictly positive ranges were always considered unsigned, then this check would disappear.

Might be a good standalone cleanup change after this goes in.

1753 ↗(On Diff #117797)

One possibility, you could assert that we don't know that End <= Begin. As before, we might not be able to confirm that, but then at least if we do, we know we have a problem.

I suspect SCEV already has a function which takes a Predicate and two SCEVs, and returns an optional bool. If it doesn't, adding one might be reasonable and easily extractable.

This would be a good stand alone change to land separately.

This revision was automatically updated to reflect the committed changes.