This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine]: foldSelectICmpAndAnd(): and is commutative
ClosedPublic

Authored by lebedev.ri on Apr 11 2018, 3:22 PM.

Details

Summary

The fold added in D45108 did not account for the fact that
the and instruction is commutative, and if the mask is a variable,
the mask variable and the fold variable may be swapped.

I have noticed this by accident when looking into PR6773

This extends/generalizes that fold, so it is handled too.

Diff Detail

Repository
rL LLVM

Event Timeline

I think the patch is correct, but it's hard to keep all of the values straight (because there are a lot!). What do you think about rearranging it a bit like this and using m_c_And():

/// We want to turn:
///   (select (icmp eq (and X, Y), 0), (and (lshr X, Z), 1), 1)
/// into:
///   zext (icmp ne i32 (and X, (or Y, (shl 1, Z))), 0)
/// Note:
///   Z may be 0 if lshr is missing.
/// Worst-case scenario is that we will replace 5 instructions with 5 different
/// instructions, but we got rid of select.
static Instruction *foldSelectICmpAndAnd(Type *SelType, const ICmpInst *Cmp,
                                         Value *TVal, Value *FVal,
                                         InstCombiner::BuilderTy &Builder) {
  if (!Cmp->hasOneUse() || !Cmp->getOperand(0)->hasOneUse() ||
      Cmp->getPredicate() != ICmpInst::ICMP_EQ ||
      !match(Cmp->getOperand(1), m_Zero()) || !match(FVal, m_One()))
    return nullptr;

  // The TrueVal has general form of:
  //   and %B, 1
  // where %B may be optionally shifted: lshr %X, %Z.
  Value *B;
  if (!match(TVal, m_OneUse(m_And(m_Value(B), m_One()))))
    return nullptr;

  Value *X, *Y, *Z;
  bool HasShift = match(B, m_OneUse(m_LShr(m_Value(X), m_Value(Z))));
  if (!HasShift)
    X = B;

  if (!match(Cmp->getOperand(0), m_c_And(m_Specific(X), m_Value(Y))))
    return nullptr;

  // ((X & Y) == 0) ? ((X >> Z) & 1) : 1 --> (X & (Y | (1 << Z))) != 0
  // ((X & Y) == 0) ? (X & 1) : 1 --> (X & (Y | 1)) != 0
  Constant *One = ConstantInt::get(SelType, 1);
  Value *MaskB = HasShift ? Builder.CreateShl(One, Z) : One;
  Value *FullMask = Builder.CreateOr(Y, MaskB);
  Value *MaskedX = Builder.CreateAnd(X, FullMask);
  Value *ICmpNeZero = Builder.CreateIsNotNull(MaskedX);
  return new ZExtInst(ICmpNeZero, SelType);
}

Side note: this patch / commit should not be labeled 'NFC'.

lebedev.ri retitled this revision from [InstCombine][NFC]: foldSelectICmpAndAnd(): and is commutative to [InstCombine]: foldSelectICmpAndAnd(): and is commutative.Apr 12 2018, 1:45 PM

Side note: this patch / commit should not be labeled 'NFC'.

Whoops, too greedy copypasting from the D45538 subject, sorry!

Make foldSelectICmpAndAnd() much smaller, as suggested by @spatel.

spatel accepted this revision.Apr 12 2018, 3:51 PM

LGTM.

This revision is now accepted and ready to land.Apr 12 2018, 3:51 PM
This revision was automatically updated to reflect the committed changes.