[LVI] Support urem in implied conditions

If (X urem M) >= C we know that X >= C. Make use of this fact
when computing the implied condition range.

In some cases we could also establish an upper bound, but that's
both tricker and not interesting in practice.

Alive: https://alive2.llvm.org/ce/z/R5ZGSW
This commit is contained in:
Nikita Popov 2021-11-20 19:03:45 +01:00
parent 25a9ee52f1
commit cd84cab6b3
2 changed files with 27 additions and 14 deletions

View File

@ -1095,7 +1095,8 @@ static ValueLatticeElement getValueFromICmpCondition(Value *Val, ICmpInst *ICI,
if (!Ty->isIntegerTy())
return ValueLatticeElement::getOverdefined();
APInt Offset(Ty->getScalarSizeInBits(), 0);
unsigned BitWidth = Ty->getScalarSizeInBits();
APInt Offset(BitWidth, 0);
if (matchICmpOperand(Offset, LHS, Val, EdgePred))
return getValueFromSimpleICmpCondition(EdgePred, RHS, Offset);
@ -1118,13 +1119,24 @@ static ValueLatticeElement getValueFromICmpCondition(Value *Val, ICmpInst *ICI,
// If (Val & Mask) != 0 then the value must be larger than the lowest set
// bit of Mask.
if (EdgePred == ICmpInst::ICMP_NE && !Mask->isZero() && C->isZero()) {
unsigned BitWidth = Ty->getIntegerBitWidth();
return ValueLatticeElement::getRange(ConstantRange::getNonEmpty(
APInt::getOneBitSet(BitWidth, Mask->countTrailingZeros()),
APInt::getZero(BitWidth)));
}
}
// If (X urem Modulus) >= C, then X >= C.
// TODO: An upper bound could be computed as well.
const APInt *Modulus;
if (match(LHS, m_URem(m_Specific(Val), m_APInt(Modulus))) &&
match(RHS, m_APInt(C))) {
// Use the icmp region so we don't have to deal with different predicates.
ConstantRange CR = ConstantRange::makeExactICmpRegion(EdgePred, *C);
if (!CR.isEmptySet())
return ValueLatticeElement::getRange(ConstantRange::getNonEmpty(
CR.getUnsignedMin(), APInt(BitWidth, 0)));
}
return ValueLatticeElement::getOverdefined();
}

View File

@ -161,18 +161,18 @@ define void @non_power_of_2(i24 %n) {
ret void
}
; (x urem 5) uge 2 implies x uge 2 on the true branch.
; We don't know anything about the lower bound on the false branch.
define void @urem_implied_cond_uge(i8 %x) {
; CHECK-LABEL: @urem_implied_cond_uge(
; CHECK-NEXT: [[U:%.*]] = urem i8 [[X:%.*]], 5
; CHECK-NEXT: [[C1:%.*]] = icmp uge i8 [[U]], 2
; CHECK-NEXT: br i1 [[C1]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: [[C2:%.*]] = icmp ult i8 [[X]], 2
; CHECK-NEXT: call void @use(i1 [[C2]])
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C3:%.*]] = icmp ule i8 [[X]], 2
; CHECK-NEXT: call void @use(i1 [[C3]])
; CHECK-NEXT: [[C4:%.*]] = icmp uge i8 [[X]], 2
; CHECK-NEXT: call void @use(i1 [[C4]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C5:%.*]] = icmp ugt i8 [[X]], 2
; CHECK-NEXT: call void @use(i1 [[C5]])
; CHECK-NEXT: ret void
@ -214,6 +214,8 @@ else:
ret void
}
; (x urem 5) uge 5 is always false. It ends up being folded first, but if it
; weren't, we should handle that gracefully.
define void @urem_implied_cond_uge_out_of_range(i8 %x) {
; CHECK-LABEL: @urem_implied_cond_uge_out_of_range(
; CHECK-NEXT: [[U:%.*]] = urem i8 [[X:%.*]], 5
@ -266,18 +268,17 @@ else:
ret void
}
; (x urem 5) != 0 is the same as (x urem 5) >= 1 and implies x >= 1.
define void @urem_implied_cond_ne_zero(i8 %x) {
; CHECK-LABEL: @urem_implied_cond_ne_zero(
; CHECK-NEXT: [[U:%.*]] = urem i8 [[X:%.*]], 5
; CHECK-NEXT: [[C1:%.*]] = icmp ne i8 [[U]], 0
; CHECK-NEXT: br i1 [[C1]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: [[C2:%.*]] = icmp ult i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C2]])
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C3:%.*]] = icmp ule i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C3]])
; CHECK-NEXT: [[C4:%.*]] = icmp uge i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C4]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C5:%.*]] = icmp ugt i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C5]])
; CHECK-NEXT: ret void
@ -319,6 +320,8 @@ else:
ret void
}
; (x urem 5) != 1 doesn't imply anything on the true branch. However, on the
; false branch (x urem 5) == 1 implies x >= 1.
define void @urem_implied_cond_ne_non_zero(i8 %x) {
; CHECK-LABEL: @urem_implied_cond_ne_non_zero(
; CHECK-NEXT: [[U:%.*]] = urem i8 [[X:%.*]], 5
@ -335,12 +338,10 @@ define void @urem_implied_cond_ne_non_zero(i8 %x) {
; CHECK-NEXT: call void @use(i1 [[C5]])
; CHECK-NEXT: ret void
; CHECK: else:
; CHECK-NEXT: [[C2_2:%.*]] = icmp ult i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C2_2]])
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C3_2:%.*]] = icmp ule i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C3_2]])
; CHECK-NEXT: [[C4_2:%.*]] = icmp uge i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C4_2]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C5_2:%.*]] = icmp ugt i8 [[X]], 1
; CHECK-NEXT: call void @use(i1 [[C5_2]])
; CHECK-NEXT: ret void