Skip to content

Commit bc35510

Browse files
[InstSimplify] Fold X * C >= X to true (#129352)
Proof: https://alive2.llvm.org/ce/z/T_ocLy Discovered in: rust-lang/rust#114386 This PR folds `X * C >= X` to `true` when `C` is known to be non-zero and `mul` is `nuw`. Folds for other math operators exist already: https://llvm-ir.godbolt.org/z/GKcYEf5Kb
1 parent 5ddf40f commit bc35510

File tree

2 files changed

+166
-12
lines changed

2 files changed

+166
-12
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,7 +3032,9 @@ enum class MonotonicType { GreaterEq, LowerEq };
30323032

30333033
/// Get values V_i such that V uge V_i (GreaterEq) or V ule V_i (LowerEq).
30343034
static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
3035-
MonotonicType Type, unsigned Depth = 0) {
3035+
MonotonicType Type,
3036+
const SimplifyQuery &Q,
3037+
unsigned Depth = 0) {
30363038
if (!Res.insert(V).second)
30373039
return;
30383040

@@ -3048,24 +3050,31 @@ static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
30483050
if (Type == MonotonicType::GreaterEq) {
30493051
if (match(I, m_Or(m_Value(X), m_Value(Y))) ||
30503052
match(I, m_Intrinsic<Intrinsic::uadd_sat>(m_Value(X), m_Value(Y)))) {
3051-
getUnsignedMonotonicValues(Res, X, Type, Depth);
3052-
getUnsignedMonotonicValues(Res, Y, Type, Depth);
3053+
getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
3054+
getUnsignedMonotonicValues(Res, Y, Type, Q, Depth);
3055+
}
3056+
// X * Y >= X --> true
3057+
if (match(I, m_NUWMul(m_Value(X), m_Value(Y)))) {
3058+
if (isKnownNonZero(X, Q))
3059+
getUnsignedMonotonicValues(Res, Y, Type, Q, Depth);
3060+
if (isKnownNonZero(Y, Q))
3061+
getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
30533062
}
30543063
} else {
30553064
assert(Type == MonotonicType::LowerEq);
30563065
switch (I->getOpcode()) {
30573066
case Instruction::And:
3058-
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
3059-
getUnsignedMonotonicValues(Res, I->getOperand(1), Type, Depth);
3067+
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Q, Depth);
3068+
getUnsignedMonotonicValues(Res, I->getOperand(1), Type, Q, Depth);
30603069
break;
30613070
case Instruction::URem:
30623071
case Instruction::UDiv:
30633072
case Instruction::LShr:
3064-
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
3073+
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Q, Depth);
30653074
break;
30663075
case Instruction::Call:
30673076
if (match(I, m_Intrinsic<Intrinsic::usub_sat>(m_Value(X))))
3068-
getUnsignedMonotonicValues(Res, X, Type, Depth);
3077+
getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
30693078
break;
30703079
default:
30713080
break;
@@ -3074,16 +3083,17 @@ static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
30743083
}
30753084

30763085
static Value *simplifyICmpUsingMonotonicValues(CmpPredicate Pred, Value *LHS,
3077-
Value *RHS) {
3086+
Value *RHS,
3087+
const SimplifyQuery &Q) {
30783088
if (Pred != ICmpInst::ICMP_UGE && Pred != ICmpInst::ICMP_ULT)
30793089
return nullptr;
30803090

30813091
// We have LHS uge GreaterValues and LowerValues uge RHS. If any of the
30823092
// GreaterValues and LowerValues are the same, it follows that LHS uge RHS.
30833093
SmallPtrSet<Value *, 4> GreaterValues;
30843094
SmallPtrSet<Value *, 4> LowerValues;
3085-
getUnsignedMonotonicValues(GreaterValues, LHS, MonotonicType::GreaterEq);
3086-
getUnsignedMonotonicValues(LowerValues, RHS, MonotonicType::LowerEq);
3095+
getUnsignedMonotonicValues(GreaterValues, LHS, MonotonicType::GreaterEq, Q);
3096+
getUnsignedMonotonicValues(LowerValues, RHS, MonotonicType::LowerEq, Q);
30873097
for (Value *GV : GreaterValues)
30883098
if (LowerValues.contains(GV))
30893099
return ConstantInt::getBool(getCompareTy(LHS),
@@ -3999,10 +4009,10 @@ static Value *simplifyICmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
39994009
ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
40004010
return V;
40014011

4002-
if (Value *V = simplifyICmpUsingMonotonicValues(Pred, LHS, RHS))
4012+
if (Value *V = simplifyICmpUsingMonotonicValues(Pred, LHS, RHS, Q))
40034013
return V;
40044014
if (Value *V = simplifyICmpUsingMonotonicValues(
4005-
ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
4015+
ICmpInst::getSwappedPredicate(Pred), RHS, LHS, Q))
40064016
return V;
40074017

40084018
if (Value *V = simplifyICmpWithDominatingAssume(Pred, LHS, RHS, Q))

llvm/test/Transforms/InstSimplify/icmp-monotonic.ll

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,147 @@ define i1 @lshr_add_ule_non_monotonic(i32 %x, i32 %y, i32 %z) {
216216
%cmp = icmp ule i32 %op1, %op2
217217
ret i1 %cmp
218218
}
219+
220+
define i1 @mul_nuw_nonzero_rhs_monotonic(i8 %x, i8 %c) {
221+
; CHECK-LABEL: define i1 @mul_nuw_nonzero_rhs_monotonic(
222+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
223+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
224+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
225+
; CHECK-NEXT: ret i1 true
226+
;
227+
%c_nonzero = icmp ne i8 %c, 0
228+
call void @llvm.assume(i1 %c_nonzero)
229+
230+
%prod = mul nuw i8 %x, %c
231+
%cmp = icmp uge i8 %prod, %x
232+
ret i1 %cmp
233+
}
234+
235+
define i1 @mul_nuw_nonzero_rhs_commuted_monotonic(i8 %x, i8 %c) {
236+
; CHECK-LABEL: define i1 @mul_nuw_nonzero_rhs_commuted_monotonic(
237+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
238+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
239+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
240+
; CHECK-NEXT: ret i1 true
241+
;
242+
%c_nonzero = icmp ne i8 %c, 0
243+
call void @llvm.assume(i1 %c_nonzero)
244+
245+
%prod = mul nuw i8 %c, %x
246+
%cmp = icmp uge i8 %prod, %x
247+
ret i1 %cmp
248+
}
249+
250+
define i1 @mul_nuw_nonzero_rhs_monotonic_inverse_predicate(i8 %x, i8 %c) {
251+
; CHECK-LABEL: define i1 @mul_nuw_nonzero_rhs_monotonic_inverse_predicate(
252+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
253+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
254+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
255+
; CHECK-NEXT: ret i1 false
256+
;
257+
%c_nonzero = icmp ne i8 %c, 0
258+
call void @llvm.assume(i1 %c_nonzero)
259+
260+
%prod = mul nuw i8 %x, %c
261+
%cmp = icmp ult i8 %prod, %x
262+
ret i1 %cmp
263+
}
264+
265+
define i1 @mul_nuw_nonzero_lhs_monotonic(i8 %x, i8 %c) {
266+
; CHECK-LABEL: define i1 @mul_nuw_nonzero_lhs_monotonic(
267+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
268+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[X]], 0
269+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
270+
; CHECK-NEXT: ret i1 true
271+
;
272+
%c_nonzero = icmp ne i8 %x, 0
273+
call void @llvm.assume(i1 %c_nonzero)
274+
275+
%prod = mul nuw i8 %x, %c
276+
%cmp = icmp uge i8 %prod, %c
277+
ret i1 %cmp
278+
}
279+
280+
define i1 @mul_nuw_nonzero_lhs_rhs_monotonic(i8 %x, i8 %c) {
281+
; CHECK-LABEL: define i1 @mul_nuw_nonzero_lhs_rhs_monotonic(
282+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
283+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
284+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
285+
; CHECK-NEXT: [[X_NONZERO:%.*]] = icmp ne i8 [[X]], 0
286+
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NONZERO]])
287+
; CHECK-NEXT: ret i1 true
288+
;
289+
%c_nonzero = icmp ne i8 %c, 0
290+
call void @llvm.assume(i1 %c_nonzero)
291+
292+
%x_nonzero = icmp ne i8 %x, 0
293+
call void @llvm.assume(i1 %x_nonzero)
294+
295+
%prod = mul nuw i8 %x, %c
296+
%cmp = icmp uge i8 %prod, %x
297+
ret i1 %cmp
298+
}
299+
300+
301+
define i1 @negative_mul_non_nsw(i8 %x, i8 %c) {
302+
; CHECK-LABEL: define i1 @negative_mul_non_nsw(
303+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
304+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
305+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
306+
; CHECK-NEXT: [[X3:%.*]] = mul i8 [[X]], [[C]]
307+
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
308+
; CHECK-NEXT: ret i1 [[CMP]]
309+
;
310+
%c_nonzero = icmp ne i8 %c, 0
311+
call void @llvm.assume(i1 %c_nonzero)
312+
313+
%prod = mul i8 %x, %c
314+
%cmp = icmp uge i8 %prod, %x
315+
ret i1 %cmp
316+
}
317+
318+
define i1 @negative_mul_no_nonzero(i8 %x, i8 %c) {
319+
; CHECK-LABEL: define i1 @negative_mul_no_nonzero(
320+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
321+
; CHECK-NEXT: [[X3:%.*]] = mul nsw i8 [[X]], [[C]]
322+
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
323+
; CHECK-NEXT: ret i1 [[CMP]]
324+
;
325+
%prod = mul nsw i8 %x, %c
326+
%cmp = icmp uge i8 %prod, %x
327+
ret i1 %cmp
328+
}
329+
330+
define i1 @negative_mul_lhs_maybe_zero(i8 %x, i8 %c) {
331+
; CHECK-LABEL: define i1 @negative_mul_lhs_maybe_zero(
332+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
333+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
334+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
335+
; CHECK-NEXT: [[X3:%.*]] = mul nuw i8 [[X]], [[C]]
336+
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[C]]
337+
; CHECK-NEXT: ret i1 [[CMP]]
338+
;
339+
%c_nonzero = icmp ne i8 %c, 0
340+
call void @llvm.assume(i1 %c_nonzero)
341+
342+
%prod = mul nuw i8 %x, %c
343+
%cmp = icmp uge i8 %prod, %c
344+
ret i1 %cmp
345+
}
346+
347+
define i1 @negative_mul_rhs_maybe_zero(i8 %x, i8 %c) {
348+
; CHECK-LABEL: define i1 @negative_mul_rhs_maybe_zero(
349+
; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
350+
; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[X]], 0
351+
; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
352+
; CHECK-NEXT: [[X3:%.*]] = mul nuw i8 [[X]], [[C]]
353+
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
354+
; CHECK-NEXT: ret i1 [[CMP]]
355+
;
356+
%c_nonzero = icmp ne i8 %x, 0
357+
call void @llvm.assume(i1 %c_nonzero)
358+
359+
%prod = mul nuw i8 %x, %c
360+
%cmp = icmp uge i8 %prod, %x
361+
ret i1 %cmp
362+
}

0 commit comments

Comments
 (0)