Skip to content

Commit

Permalink
[LVI] Support urem in implied conditions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nikic committed Nov 20, 2021
1 parent 25a9ee5 commit cd84cab
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 14 deletions.
16 changes: 14 additions & 2 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Expand Up @@ -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);

Expand All @@ -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();
}

Expand Down
25 changes: 13 additions & 12 deletions llvm/test/Transforms/CorrelatedValuePropagation/urem.ll
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit cd84cab

Please sign in to comment.