This is an archive of the discontinued LLVM Phabricator instance.

[EarlyCSE] detect equivalence of selects with inverse conditions and commuted operands (PR41101)
ClosedPublic

Authored by spatel on Apr 15 2019, 10:06 AM.

Details

Summary

This is 1 of the problems discussed in the post-commit thread for:
rL355741 / http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20190311/635516.html
and filed as:
https://bugs.llvm.org/show_bug.cgi?id=41101

Instcombine tries to canonicalize some of these cases (and there's room for improvement there independently of this patch), but it can't always do that because of extra uses. So we need to recognize these commuted operand patterns here in EarlyCSE. This is similar to how we detect commuted compares and commuted min/max/abs.

The 2 patterns (inverted pred and 'not' cond) are independent, so I can commit those separately if this looks ok.

Diff Detail

Repository
rL LLVM

Event Timeline

spatel created this revision.Apr 15 2019, 10:06 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 15 2019, 10:06 AM

https://rise4fun.com/Alive/NJl

Name: Inverted predicate
%cond = icmp sgt i8 %x, 42
%invcond = icmp sle i8 %x, 42
%m1 = select i1 %cond, i32 %t, i32 %f
%m2 = select i1 %invcond, i32 %f, i32 %t
%r = xor i32 %m1, %m2
=>
%r = i32 0

Name: 'not' predicate
%invcond = xor i1 %cond, -1
%m1 = select i1 %cond, i32 %t, i32 %f
%m2 = select i1 %invcond, i32 %f, i32 %t
%r = xor i32 %m1, %m2
=>
%r = i32 0
nikic added a subscriber: nikic.Apr 15 2019, 10:17 AM
nikic added inline comments.Apr 15 2019, 10:24 AM
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
185 ↗(On Diff #195212)

Shouldn't the cmp operands be hashed as well?

spatel marked an inline comment as done.Apr 15 2019, 11:11 AM
spatel added inline comments.
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
185 ↗(On Diff #195212)

Good question. IIUC, this is not logically wrong, but using the cmp operands would improve the hashing (reduce chances of collision). Does that sound right?

nikic added inline comments.Apr 15 2019, 12:38 PM
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
185 ↗(On Diff #195212)

That would be my understanding as well.

195 ↗(On Diff #195212)

I think this check should be moved above the cmp handling: You can have a select(not(cmp(pred x, y)), t, f) where canonicalization is prevented both on the select and the not due to multiple uses. In this case we'd presumably still want to treat it exactly the same way as a select(cmp(pred x, y), f, t), including the predicate canonicalization above.

spatel marked 2 inline comments as done.Apr 15 2019, 3:04 PM
spatel added inline comments.
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
195 ↗(On Diff #195212)

That seems like a good enhancement, but now I definitely will want to split things up when committing because we will have 3 independent changes in this review.

spatel updated this revision to Diff 195255.Apr 15 2019, 3:09 PM
spatel marked an inline comment as done.

Patch updated:

  1. Improve hashing as suggested (I don't know how to expose that in a test, so no extra tests for that).
  2. Add a "double-negation" matcher to recognize selects with same true/false ops but different conditions (positive and negative tests added).
nikic added inline comments.Apr 16 2019, 12:47 AM
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
311 ↗(On Diff #195255)

This *looks* correct to me, but it seems like some pretty tricky code where it's easy to miss something. I'd suggest to add a helper to get rid of the not (which can be used both in the hashing and comparison code):

static bool matchSelect(Value *V, Value *&Cond, Value *&T, Value *&F) {
  if (match(V, m_Select(m_Value(Cond), m_Value(T), m_Value(F)))) {
    Value *CondNot;
    if (match(Cond, m_Not(m_Value(CondNot)))) {
      Cond = CondNot;
      std::swap(T, F);
    }
    return true;
  }
  return false;
}

And then only leave the handling of the comparison here:

Value *CondL, *CondR, *TL, *TR, *FL, *FR;
if (matchSelect(LHSI, CondL, TL, FL) && matchSelect(RHSI, CondR, TR, FR)) {
  if (TL == TR && FL == FR)
    return CondL == CondR;

  if (TL == FR && FL == TR) {
    CmpInst::Predicate PredL, PredR;
    Value *X, *Y;
    if (match(CondL, m_Cmp(PredL, m_Value(X), m_Value(Y))) &&
        match(CondR, m_Cmp(PredR, m_Specific(X), m_Specific(Y))) &&
        CmpInst::getInversePredicate(PredL) == PredR)
      return true;
  }
}

I think that would go a way towards making this more "obviously correct".

spatel updated this revision to Diff 195398.Apr 16 2019, 9:02 AM
spatel marked an inline comment as done.

Patch updated:
Redo equivalency matching to reduce code.

nikic accepted this revision.Apr 16 2019, 11:48 AM

LGTM

llvm/lib/Transforms/Scalar/EarlyCSE.cpp
279 ↗(On Diff #195398)

I'd still suggest to move this into a static function and use it in the hashing code as well. (But as-is is also fine.)

This revision is now accepted and ready to land.Apr 16 2019, 11:48 AM
spatel marked an inline comment as done.Apr 16 2019, 1:29 PM
spatel added inline comments.
llvm/lib/Transforms/Scalar/EarlyCSE.cpp
279 ↗(On Diff #195398)

Yes, good point. I missed the symmetry.

This revision was automatically updated to reflect the committed changes.