mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[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 llvm-svn: 262085
This commit is contained in:
parent
503e17c728
commit
a3ddb00a03
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user