[LVI] Extend select handling to catch min/max/clamp idioms

Most of this is fairly straight forward. Add handling for min/max via existing matcher utility and ConstantRange routines.  Add handling for clamp by exploiting condition constraints on inputs.  

Note that I'm only handling two constant ranges at this point. It would be reasonable to consider treating overdefined as a full range if the instruction is typed as an integer, but that should be a separate change.

Differential Revision: http://reviews.llvm.org/D17184



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@262085 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Philip Reames 2016-02-26 22:53:59 +00:00
parent 86b1b67565
commit edac10e31e
2 changed files with 264 additions and 3 deletions

View File

@ -910,6 +910,37 @@ bool LazyValueInfoCache::solveBlockValueSelect(LVILatticeVal &BBLV,
return true;
}
if (TrueVal.isConstantRange() && FalseVal.isConstantRange()) {
ConstantRange TrueCR = TrueVal.getConstantRange();
ConstantRange FalseCR = FalseVal.getConstantRange();
Value *LHS = nullptr;
Value *RHS = nullptr;
SelectPatternResult SPR = matchSelectPattern(SI, LHS, RHS);
// Is this a min specifically of our two inputs? (Avoid the risk of
// ValueTracking getting smarter looking back past our immediate inputs.)
if (SelectPatternResult::isMinOrMax(SPR.Flavor) &&
LHS == SI->getTrueValue() && RHS == SI->getFalseValue()) {
switch (SPR.Flavor) {
default:
llvm_unreachable("unexpected minmax type!");
case SPF_SMIN: /// Signed minimum
BBLV.markConstantRange(TrueCR.smin(FalseCR));
return true;
case SPF_UMIN: /// Unsigned minimum
BBLV.markConstantRange(TrueCR.umin(FalseCR));
return true;
case SPF_SMAX: /// Signed maximum
BBLV.markConstantRange(TrueCR.smax(FalseCR));
return true;
case SPF_UMAX: /// Unsigned maximum
BBLV.markConstantRange(TrueCR.umax(FalseCR));
return true;
};
}
// TODO: ABS, NABS from the SelectPatternResult
}
// Can we constrain the facts about the true and false values by using the
// condition itself? This shows up with idioms like e.g. select(a > 5, a, 5).
// TODO: We could potentially refine an overdefined true value above.
@ -924,10 +955,47 @@ bool LazyValueInfoCache::solveBlockValueSelect(LVILatticeVal &BBLV,
TrueVal = intersect(TrueVal, TrueValTaken);
FalseVal = intersect(FalseVal, FalseValTaken);
}
// TODO: handle idioms like min & max where we can use a more precise merge
// when our inputs are constant ranges.
// Handle clamp idioms such as:
// %24 = constantrange<0, 17>
// %39 = icmp eq i32 %24, 0
// %40 = add i32 %24, -1
// %siv.next = select i1 %39, i32 16, i32 %40
// %siv.next = constantrange<0, 17> not <-1, 17>
// In general, this can handle any clamp idiom which tests the edge
// condition via an equality or inequality.
ICmpInst::Predicate Pred = ICI->getPredicate();
Value *A = ICI->getOperand(0);
if (ConstantInt *CIBase = dyn_cast<ConstantInt>(ICI->getOperand(1))) {
auto addConstants = [](ConstantInt *A, ConstantInt *B) {
assert(A->getType() == B->getType());
return ConstantInt::get(A->getType(), A->getValue() + B->getValue());
};
// See if either input is A + C2, subject to the constraint from the
// condition that A != C when that input is used. We can assume that
// that input doesn't include C + C2.
ConstantInt *CIAdded;
switch (Pred) {
case ICmpInst::ICMP_EQ:
if (match(SI->getFalseValue(), m_Add(m_Specific(A),
m_ConstantInt(CIAdded)))) {
auto ResNot = addConstants(CIBase, CIAdded);
FalseVal = intersect(FalseVal,
LVILatticeVal::getNot(ResNot));
}
break;
case ICmpInst::ICMP_NE:
if (match(SI->getTrueValue(), m_Add(m_Specific(A),
m_ConstantInt(CIAdded)))) {
auto ResNot = addConstants(CIBase, CIAdded);
TrueVal = intersect(TrueVal,
LVILatticeVal::getNot(ResNot));
}
break;
};
}
}
LVILatticeVal Result; // Start Undefined.
Result.mergeIn(TrueVal, DL);

View File

@ -199,3 +199,196 @@ out:
next:
ret void
}
define i1 @umin(i32 %a, i32 %b) {
; CHECK-LABEL: @umin(
entry:
%cmp = icmp ult i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%cmp2 = icmp ult i32 %b, 20
br i1 %cmp2, label %b_guard, label %out
b_guard:
%sel_cmp = icmp ult i32 %a, %b
%min = select i1 %sel_cmp, i32 %a, i32 %b
%res = icmp eq i32 %min, 7
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @smin(i32 %a, i32 %b) {
; CHECK-LABEL: @smin(
entry:
%cmp = icmp ult i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%cmp2 = icmp ult i32 %b, 20
br i1 %cmp2, label %b_guard, label %out
b_guard:
%sel_cmp = icmp sle i32 %a, %b
%min = select i1 %sel_cmp, i32 %a, i32 %b
%res = icmp eq i32 %min, 7
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @smax(i32 %a, i32 %b) {
; CHECK-LABEL: @smax(
entry:
%cmp = icmp sgt i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%cmp2 = icmp sgt i32 %b, 20
br i1 %cmp2, label %b_guard, label %out
b_guard:
%sel_cmp = icmp sge i32 %a, %b
%max = select i1 %sel_cmp, i32 %a, i32 %b
%res = icmp eq i32 %max, 7
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @umax(i32 %a, i32 %b) {
; CHECK-LABEL: @umax(
entry:
%cmp = icmp sgt i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%cmp2 = icmp sgt i32 %b, 20
br i1 %cmp2, label %b_guard, label %out
b_guard:
%sel_cmp = icmp uge i32 %a, %b
%max = select i1 %sel_cmp, i32 %a, i32 %b
%res = icmp eq i32 %max, 7
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @clamp_low1(i32 %a) {
; CHECK-LABEL: @clamp_low1(
entry:
%cmp = icmp sge i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%sel_cmp = icmp eq i32 %a, 5
%add = add i32 %a, -1
%sel = select i1 %sel_cmp, i32 5, i32 %a
%res = icmp eq i32 %sel, 4
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @clamp_low2(i32 %a) {
; CHECK-LABEL: @clamp_low2(
entry:
%cmp = icmp sge i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%sel_cmp = icmp ne i32 %a, 5
%add = add i32 %a, -1
%sel = select i1 %sel_cmp, i32 %a, i32 5
%res = icmp eq i32 %sel, 4
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @clamp_high1(i32 %a) {
; CHECK-LABEL: @clamp_high1(
entry:
%cmp = icmp sle i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%sel_cmp = icmp eq i32 %a, 5
%add = add i32 %a, 1
%sel = select i1 %sel_cmp, i32 5, i32 %a
%res = icmp eq i32 %sel, 6
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
define i1 @clamp_high2(i32 %a) {
; CHECK-LABEL: @clamp_high2(
entry:
%cmp = icmp sle i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%sel_cmp = icmp ne i32 %a, 5
%add = add i32 %a, 1
%sel = select i1 %sel_cmp, i32 %a, i32 5
%res = icmp eq i32 %sel, 6
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}
; Just showing arbitrary constants work, not really a clamp
define i1 @clamp_high3(i32 %a) {
; CHECK-LABEL: @clamp_high3(
entry:
%cmp = icmp sle i32 %a, 5
br i1 %cmp, label %a_guard, label %out
a_guard:
%sel_cmp = icmp ne i32 %a, 5
%add = add i32 %a, 100
%sel = select i1 %sel_cmp, i32 %a, i32 5
%res = icmp eq i32 %sel, 105
br label %next
next:
; CHECK: next:
; CHECK: ret i1 false
ret i1 %res
out:
ret i1 false
}