This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] move add after min/max intrinsic
ClosedPublic

Authored by spatel on Sep 19 2021, 7:51 AM.

Details

Summary

This is another regression noted with the proposal to canonicalize to the min/max intrinsics in D98152.

Here are Alive2 attempts to show correctness without specifying exact constants:
https://alive2.llvm.org/ce/z/bvfCwh (smax)
https://alive2.llvm.org/ce/z/of7eqy (smin)
https://alive2.llvm.org/ce/z/2Xtxoh (umax)
https://alive2.llvm.org/ce/z/Rm4Ad8 (umin)
(if you comment out the assume and/or no-wrap, you should see failures)

The different output for the umin test is due to a fold added with c4fc2cb5b2d98125 :

// umin(x, 1) == zext(x != 0)

We probably want to adjust that, so it applies more generally (umax --> sext? or patterns where we can fold to select-of-constants). Some folds that were ok when starting with cmp+select may increase instruction count for the equivalent intrinsic, so we have to decide if it's worth altering a min/max.

Diff Detail

Event Timeline

spatel created this revision.Sep 19 2021, 7:51 AM
spatel requested review of this revision.Sep 19 2021, 7:51 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 19 2021, 7:51 AM
lebedev.ri added inline comments.Sep 19 2021, 8:12 AM
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
766

Even without undef/poison elts, can this not support constant vectors from the beginning?

790

I'm not sure what this assertion is doing.
Perhaps you want to initialize Overflow to true?

spatel marked 2 inline comments as done.Sep 19 2021, 8:36 AM
spatel added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
766

It's more work because we have to check that no element overflows its subtract op. We can't rely on instsimplify to fold that for us either (see next comment).

I want to make sure that we have the base case correct first, and it's really only that (the scalar case) that shows up currently in D98152 if I'm seeing it properly.

790

This is verifying that this min/max has been analyzed by -instsimplify already. If the constant math overflows, we should not be here. Not sure if we can make that clearer on the comment at line 775. Or could just move that down here?

We expect APInt to set Overflow either way, so initializing it here could hide a bug in APInt if that implementation ever broke.

spatel marked 2 inline comments as done.Sep 20 2021, 7:21 AM
spatel added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
790

Not sure if this was clear, but the transform does not hold if the sub-of-constants overflows:
https://alive2.llvm.org/ce/z/3TScp5

So if we don't want to rely on instsimplify handling that, we would have to bail out on overflow.

And if we want to extend this transform to handle arbitrary vector constants, then we have to evaluate each element of the vector, check for overflow on each one, and bail out if any of the subtract ops overflows.

lebedev.ri added inline comments.Sep 20 2021, 7:26 AM
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
790

No, all that was obvious to me.
The thing i was pointing at, if you happen to not early-return,
and not call *sub_ov, then Overflow is uninitialized,
and the assertion becomes useless.
But if Overflow was init'd to true, then it would just fire.

spatel added inline comments.Sep 20 2021, 7:49 AM
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
790

Ah, I see - so it's a trade-off whether the initialization provides some more guarantees about the code here vs. in the called code.

We could restructure it so there's no room for things to go wrong in this code between the Overflow declaration and assert:

// Check for necessary no-wrap and overflow constraints.
bool IsSigned = MinMaxID == Intrinsic::smax || MinMaxID == Intrinsic::smin;
auto *Add = cast<BinaryOperator>(Op0);
if ((IsSigned && !Add->hasNoSignedWrap()) ||
    (!IsSigned && !Add->hasNoUnsignedWrap()))
  return nullptr;

// If the constant difference overflows, then instsimplify should reduce the
// min/max to the add or C1.
bool Overflow;
APInt CDiff =
    IsSigned ? C1->ssub_ov(*C0, Overflow) : C1->usub_ov(*C0, Overflow);
assert(!Overflow && "Expected simplify of min/max");
spatel updated this revision to Diff 374533.Sep 23 2021, 6:52 AM

Patch updated:
Refactored to make assert clearer (I hope).

lebedev.ri accepted this revision.Sep 23 2021, 7:41 AM

LG under assumption that support for vectors/non-splat vectors/vectors w. undef
will be added in followups.

This revision is now accepted and ready to land.Sep 23 2021, 7:41 AM
This revision was landed with ongoing or failed builds.Sep 26 2021, 6:49 AM
This revision was automatically updated to reflect the committed changes.