Skip to content

Commit

Permalink
[InstCombine] Try to reuse constant from select in leading comparison
Browse files Browse the repository at this point in the history
Summary:
If we have e.g.:
```
  %t = icmp ult i32 %x, 65536
  %r = select i1 %t, i32 %y, i32 65535
```
the constants `65535` and `65536` are suspiciously close.
We could perform a transformation to deduplicate them:
```
Name: ult
%t = icmp ult i32 %x, 65536
%r = select i1 %t, i32 %y, i32 65535
  =>
%t.inv = icmp ugt i32 %x, 65535
%r = select i1 %t.inv, i32 65535, i32 %y
```
https://rise4fun.com/Alive/avb

While this may seem esoteric, this should certainly be good for vectors
(less constant pool usage) and for opt-for-size - need to have only one constant.

But the real fun part here is that it allows further transformation,
in particular it finishes cleaning up the `clamp` folding,
see e.g. `canonicalize-clamp-with-select-of-constant-threshold-pattern.ll`.
We start with e.g.
```
  %dont_need_to_clamp_positive = icmp sle i32 %X, 32767
  %dont_need_to_clamp_negative = icmp sge i32 %X, -32768
  %clamp_limit = select i1 %dont_need_to_clamp_positive, i32 -32768, i32 32767
  %dont_need_to_clamp = and i1 %dont_need_to_clamp_positive, %dont_need_to_clamp_negative
  %R = select i1 %dont_need_to_clamp, i32 %X, i32 %clamp_limit
```
without this patch we currently produce
```
  %1 = icmp slt i32 %X, 32768
  %2 = icmp sgt i32 %X, -32768
  %3 = select i1 %2, i32 %X, i32 -32768
  %R = select i1 %1, i32 %3, i32 32767
```
which isn't really a `clamp` - both comparisons are performed on the original value,
this patch changes it into
```
  %1.inv = icmp sgt i32 %X, 32767
  %2 = icmp sgt i32 %X, -32768
  %3 = select i1 %2, i32 %X, i32 -32768
  %R = select i1 %1.inv, i32 32767, i32 %3
```
and then the magic happens! Some further transform finishes polishing it and we finally get:
```
  %t1 = icmp sgt i32 %X, -32768
  %t2 = select i1 %t1, i32 %X, i32 -32768
  %t3 = icmp slt i32 %t2, 32767
  %R = select i1 %t3, i32 %t2, i32 32767
```
which is beautiful and just what we want.

Proofs for `getFlippedStrictnessPredicateAndConstant()` for de-canonicalization:
https://rise4fun.com/Alive/THl
Proofs for the fold itself: https://rise4fun.com/Alive/THl

Reviewers: spatel, dmgreen, nikic, xbolva00

Reviewed By: spatel

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D66232

llvm-svn: 369840
  • Loading branch information
LebedevRI committed Aug 24, 2019
1 parent b3eccc7 commit 2c75fe7
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 89 deletions.
31 changes: 19 additions & 12 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Expand Up @@ -4912,20 +4912,27 @@ llvm::Optional<std::pair<CmpInst::Predicate, Constant *>>
llvm::getFlippedStrictnessPredicateAndConstant(CmpInst::Predicate Pred,
Constant *C) {
assert(ICmpInst::isRelational(Pred) && ICmpInst::isIntPredicate(Pred) &&
!isCanonicalPredicate(Pred) &&
"Only for non-canonical relational integer predicates.");
"Only for relational integer predicates.");

// Check if the constant operand can be safely incremented/decremented without
// overflowing/underflowing. For scalars, SimplifyICmpInst should have already
// handled the edge cases for us, so we just assert on them.
// For vectors, we must handle the edge cases.
Type *Type = C->getType();
bool IsSigned = ICmpInst::isSigned(Pred);
bool IsLE = (Pred == ICmpInst::ICMP_SLE || Pred == ICmpInst::ICMP_ULE);
auto *CI = dyn_cast<ConstantInt>(C);
if (CI) {

CmpInst::Predicate UnsignedPred = ICmpInst::getUnsignedPredicate(Pred);
bool WillIncrement =
UnsignedPred == ICmpInst::ICMP_ULE || UnsignedPred == ICmpInst::ICMP_UGT;

// Check if the constant operand can be safely incremented/decremented
// without overflowing/underflowing.
auto ConstantIsOk = [WillIncrement, IsSigned](ConstantInt *C) {
return WillIncrement ? !C->isMaxValue(IsSigned) : !C->isMinValue(IsSigned);
};

// For scalars, SimplifyICmpInst should have already handled
// the edge cases for us, so we just assert on them.
// For vectors, we must handle the edge cases.
if (auto *CI = dyn_cast<ConstantInt>(C)) {
// A <= MAX -> TRUE ; A >= MIN -> TRUE
assert(IsLE ? !CI->isMaxValue(IsSigned) : !CI->isMinValue(IsSigned));
assert(ConstantIsOk(CI));
} else if (Type->isVectorTy()) {
// TODO? If the edge cases for vectors were guaranteed to be handled as they
// are for scalar, we could remove the min/max checks. However, to do that,
Expand All @@ -4942,7 +4949,7 @@ llvm::getFlippedStrictnessPredicateAndConstant(CmpInst::Predicate Pred,
// Bail out if we can't determine if this constant is min/max or if we
// know that this constant is min/max.
auto *CI = dyn_cast<ConstantInt>(Elt);
if (!CI || (IsLE ? CI->isMaxValue(IsSigned) : CI->isMinValue(IsSigned)))
if (!CI || !ConstantIsOk(CI))
return llvm::None;
}
} else {
Expand All @@ -4953,7 +4960,7 @@ llvm::getFlippedStrictnessPredicateAndConstant(CmpInst::Predicate Pred,
CmpInst::Predicate NewPred = CmpInst::getFlippedStrictnessPredicate(Pred);

// Increment or decrement the constant.
Constant *OneOrNegOne = ConstantInt::get(Type, IsLE ? 1 : -1, true);
Constant *OneOrNegOne = ConstantInt::get(Type, WillIncrement ? 1 : -1, true);
Constant *NewC = ConstantExpr::getAdd(C, OneOrNegOne);

return std::make_pair(NewPred, NewC);
Expand Down
93 changes: 93 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
Expand Up @@ -1261,6 +1261,95 @@ static Instruction *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
return MaybeReplacedHigh;
}

// If we have
// %cmp = icmp [canonical predicate] i32 %x, C0
// %r = select i1 %cmp, i32 %y, i32 C1
// Where C0 != C1 and %x may be different from %y, see if the constant that we
// will have if we flip the strictness of the predicate (i.e. without changing
// the result) is identical to the C1 in select. If it matches we can change
// original comparison to one with swapped predicate, reuse the constant,
// and swap the hands of select.
static Instruction *
tryToReuseConstantFromSelectInComparison(SelectInst &Sel, ICmpInst &Cmp,
InstCombiner::BuilderTy &Builder) {
ICmpInst::Predicate Pred;
Value *X;
Constant *C0;
if (!match(&Cmp, m_OneUse(m_ICmp(
Pred, m_Value(X),
m_CombineAnd(m_AnyIntegralConstant(), m_Constant(C0))))))
return nullptr;

// If comparison predicate is non-relational, we won't be able to do anything.
if (ICmpInst::isEquality(Pred))
return nullptr;

// If comparison predicate is non-canonical, then we certainly won't be able
// to make it canonical; canonicalizeCmpWithConstant() already tried.
if (!isCanonicalPredicate(Pred))
return nullptr;

// If the [input] type of comparison and select type are different, lets abort
// for now. We could try to compare constants with trunc/[zs]ext though.
if (C0->getType() != Sel.getType())
return nullptr;

// FIXME: are there any magic icmp predicate+constant pairs we must not touch?

auto ConstantsAreElementWiseEqual = [](Constant *Cx, Value *Y) {
// Are they fully identical?
if (Cx == Y)
return true;
// They may still be identical element-wise (if they have `undef`s).
auto *Cy = dyn_cast<Constant>(Y);
if (!Cy)
return false;
return match(ConstantExpr::getICmp(ICmpInst::Predicate::ICMP_EQ, Cx, Cy),
m_One());
};

Value *SelVal0, *SelVal1; // We do not care which one is from where.
match(&Sel, m_Select(m_Value(), m_Value(SelVal0), m_Value(SelVal1)));
// At least one of these values we are selecting between must be a constant
// else we'll never succeed.
if (!match(SelVal0, m_AnyIntegralConstant()) &&
!match(SelVal1, m_AnyIntegralConstant()))
return nullptr;

// Does this constant C match any of the `select` values?
auto MatchesSelectValue = [ConstantsAreElementWiseEqual, SelVal0,
SelVal1](Constant *C) {
return ConstantsAreElementWiseEqual(C, SelVal0) ||
ConstantsAreElementWiseEqual(C, SelVal1);
};

// If C0 *already* matches true/false value of select, we are done.
if (MatchesSelectValue(C0))
return nullptr;

// Check the constant we'd have with flipped-strictness predicate.
auto FlippedStrictness = getFlippedStrictnessPredicateAndConstant(Pred, C0);
if (!FlippedStrictness)
return nullptr;

// If said constant doesn't match either, then there is no hope,
if (!MatchesSelectValue(FlippedStrictness->second))
return nullptr;

// It matched! Lets insert the new comparison just before select.
InstCombiner::BuilderTy::InsertPointGuard Guard(Builder);
Builder.SetInsertPoint(&Sel);

Pred = ICmpInst::getSwappedPredicate(Pred); // Yes, swapped.
Value *NewCmp = Builder.CreateICmp(Pred, X, FlippedStrictness->second,
Cmp.getName() + ".inv");
Sel.setCondition(NewCmp);
Sel.swapValues();
Sel.swapProfMetadata();

return &Sel;
}

/// Visit a SelectInst that has an ICmpInst as its first operand.
Instruction *InstCombiner::foldSelectInstWithICmp(SelectInst &SI,
ICmpInst *ICI) {
Expand All @@ -1276,6 +1365,10 @@ Instruction *InstCombiner::foldSelectInstWithICmp(SelectInst &SI,
if (Instruction *NewAbs = canonicalizeClampLike(SI, *ICI, Builder))
return NewAbs;

if (Instruction *NewSel =
tryToReuseConstantFromSelectInComparison(SI, *ICI, Builder))
return NewSel;

bool Changed = adjustMinMax(SI, *ICI);

if (Value *V = foldSelectICmpAnd(SI, ICI, Builder))
Expand Down
Expand Up @@ -7,10 +7,10 @@

define i32 @t0_select_cond_and_v0(i32 %X) {
; CHECK-LABEL: @t0_select_cond_and_v0(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%dont_need_to_clamp_positive = icmp sle i32 %X, 32767
Expand All @@ -22,10 +22,10 @@ define i32 @t0_select_cond_and_v0(i32 %X) {
}
define i32 @t1_select_cond_and_v1(i32 %X) {
; CHECK-LABEL: @t1_select_cond_and_v1(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%dont_need_to_clamp_positive = icmp sle i32 %X, 32767
Expand All @@ -40,10 +40,10 @@ define i32 @t1_select_cond_and_v1(i32 %X) {

define i32 @t2_select_cond_or_v0(i32 %X) {
; CHECK-LABEL: @t2_select_cond_or_v0(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%need_to_clamp_positive = icmp sgt i32 %X, 32767
Expand All @@ -55,10 +55,10 @@ define i32 @t2_select_cond_or_v0(i32 %X) {
}
define i32 @t3_select_cond_or_v1(i32 %X) {
; CHECK-LABEL: @t3_select_cond_or_v1(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%need_to_clamp_positive = icmp sgt i32 %X, 32767
Expand All @@ -73,10 +73,10 @@ define i32 @t3_select_cond_or_v1(i32 %X) {

define i32 @t4_select_cond_xor_v0(i32 %X) {
; CHECK-LABEL: @t4_select_cond_xor_v0(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[DOTINV1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[TMP1]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP2]], i32 [[TMP1]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%need_to_clamp_positive = icmp sgt i32 %X, 32767
Expand All @@ -88,10 +88,10 @@ define i32 @t4_select_cond_xor_v0(i32 %X) {
}
define i32 @t4_select_cond_xor_v1(i32 %X) {
; CHECK-LABEL: @t4_select_cond_xor_v1(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[DOTINV1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[TMP1]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP2]], i32 [[TMP1]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%need_to_clamp_positive = icmp sgt i32 %X, 32767
Expand All @@ -104,10 +104,10 @@ define i32 @t4_select_cond_xor_v1(i32 %X) {

define i32 @t5_select_cond_xor_v2(i32 %X) {
; CHECK-LABEL: @t5_select_cond_xor_v2(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[DOTINV1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[TMP1]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP2]], i32 [[TMP1]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%dont_need_to_clamp_positive = icmp sle i32 %X, 32767
Expand All @@ -119,10 +119,10 @@ define i32 @t5_select_cond_xor_v2(i32 %X) {
}
define i32 @t5_select_cond_xor_v3(i32 %X) {
; CHECK-LABEL: @t5_select_cond_xor_v3(
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], -32768
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP1]], i32 [[TMP3]], i32 32767
; CHECK-NEXT: [[DOTINV1:%.*]] = icmp sgt i32 [[X:%.*]], -32768
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV1]], i32 [[X]], i32 -32768
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[TMP1]], 32767
; CHECK-NEXT: [[R:%.*]] = select i1 [[TMP2]], i32 [[TMP1]], i32 32767
; CHECK-NEXT: ret i32 [[R]]
;
%dont_need_to_clamp_positive = icmp sle i32 %X, 32767
Expand Down

0 comments on commit 2c75fe7

Please sign in to comment.