This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Fold `select` of `srem` and conditional add
ClosedPublic

Authored by antoniofrighetto on Aug 1 2023, 10:14 AM.

Details

Summary

Simplify a pattern that may show up when computing the remainder of euclidean division.

Fixes: #64305

Proofs: https://alive2.llvm.org/ce/z/9_KG6c.

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptAug 1 2023, 10:14 AM
antoniofrighetto requested review of this revision.Aug 1 2023, 10:14 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 1 2023, 10:14 AM

Didn't manage to generalize the specific pattern out of it, not sure if this can be done here.

A note about the proofs. You proved this when the remander is 8, not generically for any power of 2. Proofs with constants should generally look more like: https://alive2.llvm.org/ce/z/9_KG6c
i.e instead of declaring with the constant is, declare the constraints you have on it.

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Can you remove the NSW from the comment / all the alive2 links. It makes it seem like the nsw is necessary for correctness.

2608

This also works for urem, unless that already gets properly folded, can you also support that here?

2609

Just use m_SpecificInt(I0) instead of I1 and drop this equality comparison.

Although really, this doesn't need to be an integer at all, it could be:

if (!(match(TrueVal, m_Add(m_Value(RemRes), m_Value(Remainder))) &&
        match(RemRes, m_SRem(m_Value(Op), m_Specific(m_Value(Remainder))) && FalseVal == RemRes &&
        && isKnownToBeAPowerOfTwo(Remainder, ...)))
2616

Can you move this check to before the other match. 1) think its clearer, 2) if you add the generic isKnownToBeAPowerOfTwo approach is good to exhaust all the cheap checks before the recursive call.

2618

getZExtValue is really okay here, what if its an i128?
You can build a ConstantInt directly from an APInt, so just *I0 should be okay here.

llvm/test/Transforms/InstCombine/select-divrem.ll
239

The tests should be precommit,.

They are also insufficient. Can you add some negative cases for: incorrect sign test, add with different constant, wrong operand positions in select.

Can you also add a test that use vecs, and add one where the datatype is i128 (as your current code seems to be buggy there).

goldstein.w.n added inline comments.Aug 1 2023, 10:55 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2608

If urem already gets properly folded, then if its in some clear place can you just update that codes to work for the srem case as well (instead of adding an entirely new sequence for srem).

Thanks for reviewing it, and for the note! Updated proofs & changes, and rebased.

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Dropped, thanks.

2608

urem is directly folded into an and upon entering visitURem as it can only take positive values. srem requires dedicated handling instead.

2616

Moved above, and moved from using m_Power2 to the generic isKnownToBeAPowerOfTwo.

2618

Right, getZExtValue does not really look good when handling i128. Moved to using getAllOnesValue, thanks.

llvm/test/Transforms/InstCombine/select-divrem.ll
239

Tests updated, I'll be pre-comitting them should they look OK.

antoniofrighetto edited the summary of this revision. (Show Details)

Separated tests & added diff.

goldstein.w.n added inline comments.Aug 2 2023, 9:16 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

Please update you proofs to replace 8 with a variable C and use assume to provide its constraints (in your case pow2 or zero).

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

I had tried with something similar, but i128 seems to lead Alive2 to timeout, and there's no assume for vectors. Updated proofs with this, if it suffices.

antoniofrighetto edited the summary of this revision. (Show Details)Aug 2 2023, 9:50 AM
craig.topper added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Use InstCombiner::isSignBitCheck so you can handle the case where the condition is inverted and the add and srem operands are swapped.

craig.topper added inline comments.Aug 2 2023, 9:59 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Or does something already canonicalize to have the compare be ICMP SLT?

craig.topper added inline comments.Aug 2 2023, 10:07 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Ok there is a canonicalization inside InstCombinerImpl::foldSelectInstWithICmp to prefer the SLT form. But it only triggers if the select is the only use of the condition.

This srem transform doesn't have any one use checks. So we might not want to rely on that canonicalization and should use InstCombiner::isSignBitCheck to handle both cases.

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

I suppose this would require to have something like:

ConstantInt *RHS;
bool TrueIfSigned = false;

if (!(match(CondVal, m_ICmp(Pred, m_Value(RemRes), m_ConstantInt(RHS))) &&
      IC.isSignBitCheck(Pred, RHS->getValue(), TrueIfSigned)))

And possibly also continue checking that RHS->getZExtValue() == 0. Wouldn't this make the code a bit slightly less readable? Considering the canonicalization, maybe we could use m_OneUse instead?

craig.topper added inline comments.Aug 2 2023, 11:01 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Should be m_APInt instead of m_ConstantInt to support vector splats. See foldSelectToCopysign for similar. I don't think you need RHS->getZExtValue() == 0, since the SGT form uses -1 instead of 0.

2616

This uses isKnownToBeAPowerOfTwo, but the tests only check for constants. isKnownToBeAPowerOfTwo handles more than that.

goldstein.w.n added inline comments.Aug 2 2023, 11:09 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

I had tried with something similar, but i128 seems to lead Alive2 to timeout, and there's no assume for vectors. Updated proofs with this, if it suffices.

Yeah, there is noneed to use i128 in the alive2 proofs, just having a test for it is fine.

2616

This uses isKnownToBeAPowerOfTwo, but the tests only check for constants. isKnownToBeAPowerOfTwo handles more than that.

+1

goldstein.w.n added inline comments.Aug 2 2023, 11:11 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2604

Personally think with a comment the readability will be fine. If its that big a concern we should add m_ICmpSignTest is its a fairly common match req.

Updated & rebased!

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

Good point, updated tests with rem_euclid_non_const_pow2, thanks!

goldstein.w.n added inline comments.Aug 3 2023, 9:12 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

This needs to be TrueIfSigned ? (FalseVal == Rem) : (TrueVal == Rem)

craig.topper added inline comments.Aug 3 2023, 9:35 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

Wouldn't we need this right after we find the compare?

if (!TrueIfSigned)
  std::swap(TrueVal, FalseVal)
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

As per rem_euclid_2 test, I think that if we are handling a SGT, we should check that the srem is in the true arm.

Updated & rebased.

@craig.topper, we are still dealing with a SGT as it has not been canonicalized into a SLT, right?

craig.topper added inline comments.Aug 3 2023, 10:04 AM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

This line match(TrueVal, m_Add(m_Value(RemRes), m_Value(Remainder))) is also incorrect when TrueIfSigned is false. Which is why I suggested swapping the True and False value.

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2616

Right, ifdef'ing out the call to foldSelectInstWithICmp exhibits this clearly. Should be good now.

Fixed support of inverse operands.

Rebased & further commented.

This revision is now accepted and ready to land.Aug 7 2023, 2:55 PM
nikic added inline comments.Aug 8 2023, 12:47 PM
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
2618

I think this fails to handle the case where the add operands are commuted? Can't happen for constant remainder, but I don't think anything would guarantee the remainder is on the RHS if it's variable.