This is an archive of the discontinued LLVM Phabricator instance.

[SimplifyIndVars] Eliminate redundant truncs
ClosedPublic

Authored by mkazantsev on Jun 7 2018, 11:08 PM.

Details

Summary

This patch adds logic to deal with the following constructions:

%iv = phi i64 ...
%trunc = trunc i64 %iv to i32
%cmp = icmp <pred> i32 %trunc, %invariant

Replacing it with

%iv = phi i64 ...
%cmp = icmp <pred> i64 %iv, sext/zext(%invariant)

In case if it is legal. Specifically, if %iv has signed comparison users, it is
required that sext(trunc(%iv)) == %iv, and if it has unsigned comparison
uses then we require zext(trunc(%iv)) == %iv. The current implementation
bails if %trunc has other uses than icmp, but in theory we can handle more
cases here (e.g. if the user of trunc is bitcast).

Diff Detail

Repository
rL LLVM

Event Timeline

mkazantsev created this revision.Jun 7 2018, 11:08 PM
reames added inline comments.Jun 11 2018, 6:29 PM
lib/Transforms/Utils/SimplifyIndVar.cpp
514 ↗(On Diff #150450)

This appears to be treating eq/ne as unsigned? Is that intentional? If so, probably worth a comment.

580 ↗(On Diff #150450)

I'm missing something. Why do this via a new Trunc specific routine that has to look at users instead of just looking at the icmp directly? We do end up processing it don't we?

mkazantsev added inline comments.Jun 12 2018, 6:43 PM
lib/Transforms/Utils/SimplifyIndVar.cpp
514 ↗(On Diff #150450)

Yes it is, we prefer zext over sext for canonicalization needs in case if there is no difference, as well as we prefer unsigned comparison over signed.

580 ↗(On Diff #150450)

No, we never process this icmp(trunc(iv), constant).

mkazantsev added inline comments.Jun 12 2018, 6:46 PM
lib/Transforms/Utils/SimplifyIndVar.cpp
580 ↗(On Diff #150450)

Clarification: the analysis stops at this point.

CastInst *Cast = dyn_cast<CastInst>(UseInst);
if (V && Cast) {
  V->visitCast(Cast);
  continue;
}

We don't push its users here. Not sure if we should.

mkazantsev planned changes to this revision.Jun 12 2018, 8:05 PM
mkazantsev added inline comments.
lib/Transforms/Utils/SimplifyIndVar.cpp
514 ↗(On Diff #150450)

We can be smarter here.

mkazantsev marked 3 inline comments as done.
reames accepted this revision.Jun 13 2018, 10:57 AM

LGTM w/comments addressed before landing.

lib/Transforms/Utils/SimplifyIndVar.cpp
523 ↗(On Diff #151108)

Naming wise, including Legal is bit confusing. Maybe DoesSExtCollapse or IsSExtPrpfitable?

530 ↗(On Diff #151108)

Again, legal here is odd since as you say at the top, it's always legal to extend both sides.

555 ↗(On Diff #151108)

I think you're better off placing these in the loop and then letting existing code hoist. Specifically, this code hoists about a possible condition in the predecessor (i.e. when it's not a preheader) and while that's legal, it might not be always profitable.

You can handle the simple cases canonically by placing in the loop and calling L->makeLoopInvariant(I) on them.

574 ↗(On Diff #151108)

This is fine for the moment, but I want to note a couple of possible follow-ups:

  1. We could update signed users even if we can't updated unsigned users or vice versa.
  2. We could allow unrelated users.
  3. If we encounter a non-canonical signed compare (i.e. one which could be represented as a unsigned compare), maybe we should emit the zext form directly?

These are explicitly out of scope for this patch.

This revision is now accepted and ready to land.Jun 13 2018, 10:57 AM

Thinking about this further, I see another opportunity for a follow on improvement, sketched below in comments.

test/Transforms/IndVarSimplify/eliminate-trunc.ll
157 ↗(On Diff #151108)

I think the same basic logic as sketched for the other LFTR problem works here as well. Just with a [SINT_MIN, SINT_MAX-1] initial range for IV.

242 ↗(On Diff #151108)

I think this case is actually something missing in SCEV. Let me walk through the logic by which we should be able to handle this.
IV is known to be [0, UINT32_MAX-1] inclusive - since it must be less than some value %n and the trunc doesn't change behavior before that point.
Thus, IV + 1 is known to not nuw wrap in the 32 bit domain.
Thus, trunc(IV+1) doesn't loose any bits.
Thus zext(trunc(IV+1)) == IV+1.

I think we're probably missing a range check somewhere inside the getZeroExt routine.

Note this logic only works for strict less than. Doing it for ule wouldn't work.

mkazantsev marked 2 inline comments as done.Jun 18 2018, 8:12 PM
mkazantsev added inline comments.
lib/Transforms/Utils/SimplifyIndVar.cpp
555 ↗(On Diff #151108)

Ok, let us follow "don't make worse" strategy and insert new comparisons in location of old ones.

572 ↗(On Diff #151108)

We should not erase instructions here. It should be added to DeadInsts instead. Potential memory corruption (I don't have such test, though).

574 ↗(On Diff #151108)

Ok, I will add a comment on that.

mkazantsev planned changes to this revision.Jun 18 2018, 8:13 PM
mkazantsev added inline comments.Jun 18 2018, 9:22 PM
test/Transforms/IndVarSimplify/eliminate-trunc.ll
242 ↗(On Diff #151108)

We are not missing anything. narrow.iv gets removed by our transform. But LFTR manages to insert it back when replacing ult with ne. It is non-profitable behavior in LFTR.

This revision was not accepted when it landed; it landed in state Changes Planned.Jun 18 2018, 9:53 PM
This revision was automatically updated to reflect the committed changes.