This is an archive of the discontinued LLVM Phabricator instance.

[CVP] Canonicalize signed relational comparisons of scalar integers to unsigned comparison predicates
ClosedPublic

Authored by lebedev.ri on Oct 31 2021, 1:27 PM.

Details

Summary

Now that the reasoning was added to ConstantRange in D90924,
this replicates IndVars variant of this transform (D111836)
in a pass that uses value range reasoning for the transform.

Diff Detail

Event Timeline

lebedev.ri created this revision.Oct 31 2021, 1:27 PM
lebedev.ri requested review of this revision.Oct 31 2021, 1:27 PM
nikic added inline comments.Oct 31 2021, 2:23 PM
llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
322

Could just setPredicate() here?

326

This code also folds local icmps (nowadays). I'd just drop the "NonLocal" part from the name.

lebedev.ri marked 2 inline comments as done.

Nits.

nikic accepted this revision.Oct 31 2021, 3:28 PM

LGTM. Might want to regenerate the tests again, as the changes to instruction names should be gone now.

llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
302

Second condition is probably redundant, can't have anything else in a scalar icmp.

This revision is now accepted and ready to land.Oct 31 2021, 3:28 PM
lebedev.ri marked an inline comment as done.Oct 31 2021, 3:30 PM
lebedev.ri added inline comments.
llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
302

While scalar icmp does produce i1 result, it may have pointer operands,
and LVI->getConstantRange() asserts when that happens.

nikic added inline comments.Oct 31 2021, 3:31 PM
llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
302

Oooh right, forgot about pointers!

lebedev.ri marked an inline comment as done.

Regenerate checklines once more.

lebedev.ri marked an inline comment as done.Oct 31 2021, 3:32 PM
This revision was landed with ongoing or failed builds.Nov 1 2021, 2:16 AM
This revision was automatically updated to reflect the committed changes.

This change broke the ability to discard checks in some cases. Consider this example:

define i1 @test1(i32 %a, i32* %b_ptr) {
  %b = load i32, i32* %b_ptr, align 8, !range !{i32 0, i32 2147483646}
  %c1 = icmp slt i32 %a, %b
  br i1 %c1, label %exit, label %cont.1

cont.1:
  %c2 = icmp slt i32 %a, 0
  br i1 %c2, label %exit, label %cont.2

cont.2:
  %c3 = icmp sgt i32 %b, %a
  ret i1 %c3

exit:
  ret i1 false
}

In this form %c3 check is trivially constant foldable (from the dominating %c1 check). For example, EarlyCSE can discard it. With this change, CVP would rewrite %c3 to an unsigned form.

define i1 @test1(i32 %a, i32* %b_ptr) {
  %b = load i32, i32* %b_ptr, align 8, !range !0
  %c1 = icmp slt i32 %a, %b
  br i1 %c1, label %exit, label %cont.1

cont.1:                                           ; preds = %0
  br i1 false, label %exit, label %cont.2

cont.2:                                           ; preds = %cont.1
  %c3 = icmp ugt i32 %b, %a
  ret i1 %c3

exit:                                             ; preds = %cont.1, %0
  ret i1 false
}

!0 = !{i32 0, i32 2147483646}

The check %c is still optimizable, but currently, we don't have anything in O3 which eliminates it.

Any suggestions for the best place to handle it?

Any suggestions for the best place to handle it?

Urgh, this is an annoying case. We need a mixture of dominating facts relating two variables, and range facts about those same variables. We don't really have any single place which has both.

I don't think we should revert the patch. We can construct a similar case where before this patch we'd fail to prove a condition based on a dominating unsigned compare, and now we do. I think the canonicalization is still warranted and worthwhile.

We almost need a way to remember that we could use either signed or unsigned forms here. I'm going to give this a bit more thought and see if anything occurs to me.

We need to enable @fhahn's constraint elimination pass to solve that kind of problems once and for all.
(though, it currently does not help for that snippet, since support for signed isn't there yet)

apilipenko added a comment.EditedNov 24 2021, 10:27 AM

Do you mind if I put this change under an on by default cl::opt flag? This way we can temporarily turn it off downstream.

Do you mind if I put this change under an on by default cl::opt flag? This way we can temporarily turn it off downstream.

Seems reasonable to me.

fhahn added a comment.Feb 4 2022, 10:06 AM

It looks like this may cause a regression where we fail to remove some dead code https://github.com/llvm/llvm-project/issues/53320