This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Fold (min/max ~X, Y) -> ~(max/min X, ~Y) when Y is freely invertible
ClosedPublic

Authored by craig.topper on Aug 28 2018, 6:21 PM.

Details

Summary

If the ~X wasn't able to simplify above the max/min, we might be able to simplify it by moving it below the max/min.

I had to modify the ~(min/max ~X, Y) transform to prevent getting stuck in a loop when we saw the new ~(max/min X, ~Y) before the ~Y had been folded away to remove the new not.

I've left the subtract tests from my previous patch because that's the pattern I want to get optimized. I've added an additional non splat test and dropped the multiple use of the select test since it no longer applied.

Diff Detail

Repository
rL LLVM

Event Timeline

craig.topper created this revision.Aug 28 2018, 6:21 PM
spatel added a subscriber: RKSimon.Aug 29 2018, 6:52 AM

https://rise4fun.com/Alive/pAh
Since we know this isn't limited to negate, there should be a 'TODO' about that...or just allow for that possibility in this patch? Not difficult to make the match() account for an arbitrary constant.
Also, whether we use IsFreeToInvert or m_Constant(), I think this fold will work for an arbitrary vector constant, not just a splat. Adding a test for that will make @RKSimon happier. :)

lib/Transforms/InstCombine/InstCombineAddSub.cpp
1771 ↗(On Diff #163002)

The formula suggests that we have a constant, but the code allows for a variable operand. Either the comment should change to match code (C becomes Y) and add a test for that possibility or change the code to match the comment.

Also, we're inverting min/max, so the result in the formula should read (max/min X, ~C)?

1787 ↗(On Diff #163002)

That should be CreateNot(LHS)? Guessing there's no test coverage for this clause.

test/Transforms/InstCombine/sub.ll
1061 ↗(On Diff #163002)

As usual, I'd prefer that the tests are committed first with baseline checks.

On 2nd thought...
This is a missed general canonicalization:
https://rise4fun.com/Alive/vTag

If we add that, then we see that this isn't really a subtract fold - any trailing op for the min/max could get simplified when we push the 'not' through the min/max.

Looking into the more general fold. Trying to figure out the right magic to avoid an infinite loop.

craig.topper retitled this revision from [InstCombine] Fold (neg (min/max ~X, C)) -> (add (min/max X, ~C), 1) to [InstCombine] Fold (min/max ~X, Y) -> ~(max/min X, ~Y) when Y is freely invertible.
craig.topper edited the summary of this revision. (Show Details)

Looks ok to me. A note and a remark

  • The code is now doing the same thing but either for the LHS or RHS. Is there any way to deduplicate it?
  • It looks like we want to more thoroughly (read: in all cases possible) hoist the not ops out like this? @spatel did bring that up before https://reviews.llvm.org/D50301#1191081
lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
2926–2927 ↗(On Diff #163158)

We are checking !IsFreeToInvert() because we might be called before we try to actually fold the not into X?
Should probably be a comment.

Add comment. Use existing createMinMax helper to shorten some code.

I did briefly look at always hoisting it, but it potentially changes the critical path so I'm playing it safe.

I did briefly look at always hoisting it, but it potentially changes the critical path so I'm playing it safe.

I wasn't talking about this case specifically, but more in general.

spatel added a subscriber: davidxl.Aug 29 2018, 2:56 PM
spatel added inline comments.
lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
2926 ↗(On Diff #163168)

simplied -> simplified

2934 ↗(On Diff #163168)

simplied -> simplified

lib/Transforms/InstCombine/InstCombineSelect.cpp
1821–1822 ↗(On Diff #163168)

Update this comment to match the new logic.

1842 ↗(On Diff #163168)

Extra whitespace?

test/Transforms/InstCombine/select.ll
1332 ↗(On Diff #163168)

Update comment for new form.

test/Transforms/InstCombine/select_meta.ll
197 ↗(On Diff #163168)

cc @davidxl
This is the same compare that we started with, so I think branch weight metadata should transfer as-is.
If we drop metadata in this patch, it's possible we could see perf regressions, so we should try to get this right.

test/Transforms/InstCombine/sub.ll
1061 ↗(On Diff #163002)

Given the inf-loop potential, there's even more reason to get these committed before the code patch - don't want to lose those in case of revert.

davidxl added inline comments.Aug 29 2018, 3:21 PM
test/Transforms/InstCombine/select_meta.ll
197 ↗(On Diff #163168)

correct, the profile data should not change and be transferred as is.

craig.topper marked 5 inline comments as done.

Address review comments.

Not sure about the metadata copying. I'm not sure if I can rely on LHS and RHS to be in the same order as the original select.

spatel accepted this revision.Aug 30 2018, 6:50 AM

Address review comments.

Not sure about the metadata copying. I'm not sure if I can rely on LHS and RHS to be in the same order as the original select.

I think that will always be true because LHS/RHS are set based on the original cmp, and we create the new min/max with A/B mapped to LHS/RHS...but some asserts would help (dis)prove it.

Also, repeating Roman's earlier request - we could eliminate the code duplication by making a helper/lambda and reversing the operands in the call?
Something like:

if (Instruction *Not = moveNotAfterMinMax(LHS, RHS))
  return Not;
if (Instruction *Not = moveNotAfterMinMax(RHS, LHS))
  return Not;

But I think that wouldn't preserve the A/B mapping mentioned above. If you see a way to resolve that, that would be nice. LGTM.

This revision is now accepted and ready to land.Aug 30 2018, 6:50 AM

LHS/RHS are set based on the select operands not the compare operands aren't they?

Now with lamdba

craig.topper requested review of this revision.Aug 30 2018, 11:36 AM

LHS/RHS are set based on the select operands not the compare operands aren't they?

Yes - sorry, I was looking at the start of matchSelectPattern() rather than matchMinMax(). Do you see a case where we would get the metadata wrong?

We should be canonicalizing harder and maybe that would bypass the question.
Ie, we shouldn't end up with a non-canonical min/max predicate like this:
%c = icmp ult i32 %x, %y
%max = select i1 %c, i32 %y, i32 %x

the meta data on select tracks the probability the predicate value is 'true'. As long as the predicate expression is not changed, the meta data does not change. Is the transformation doing something else?

@davidxl, this transform doesn't really know if its changing the compare predicate because the min/max matching considers (select (icmp slt X, ~C), ~X, C) to be equivalent to (select (icmp sgt ~X, C), ~X, C). And it only returns the select operands to the calling code. So this transform rewrites the icmp, but doesn't know which form the original compare took.

thanks. In this case, checking if LHS/RHS 's order is swapped is probably needed.

So does this look ok?

The meta data update code looks fine to me.

lib/Transforms/InstCombine/InstCombineSelect.cpp
1825 ↗(On Diff #163384)

Can the operands_swapped passed as an argument?

Pass a parameter to the lambda to indicate the LHS/RHS are swapped. Check this with an assert.

The MD part LGTM

spatel accepted this revision.Sep 7 2018, 7:16 AM

LGTM

This revision is now accepted and ready to land.Sep 7 2018, 7:16 AM
This revision was automatically updated to reflect the committed changes.