diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 2d78aa4be3455..5aaed70f5895f 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -499,8 +499,9 @@ static void computeKnownBitsAddSub(bool Add, const Value *Op0, const Value *Op1, KnownOut = KnownBits::computeForAddSub(Add, NSW, NUW, Known2, KnownOut); if (!Add && NSW && !KnownOut.isNonNegative() && - isImpliedByDomCondition(ICmpInst::ICMP_SLE, Op1, Op0, Q.CxtI, Q.DL) - .value_or(false)) + (isImpliedByDomCondition(ICmpInst::ICMP_SLE, Op1, Op0, Q.CxtI, Q.DL) + .value_or(false) || + match(Op1, m_c_SMin(m_Specific(Op0), m_Value())))) KnownOut.makeNonNegative(); if (Add) diff --git a/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll b/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll new file mode 100644 index 0000000000000..b1121637c83b4 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/sext-nonneg-sub.ll @@ -0,0 +1,63 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +; Test that b - smin(b, a) is recognized as non-negative +define i64 @func1(i32 %a, i32 %b) { +; CHECK-LABEL: define i64 @func1( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = tail call i32 @llvm.smin.i32(i32 [[B]], i32 [[A]]) +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[SPEC_SELECT]] +; CHECK-NEXT: [[CONV:%.*]] = zext nneg i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[CONV]] +; +entry: + %spec.select = tail call i32 @llvm.smin.i32(i32 %b, i32 %a) + %sub = sub nsw i32 %b, %spec.select + %conv = sext i32 %sub to i64 + ret i64 %conv +} + +; Test commutative smin pattern: b - smin(a, b) should also optimize +define i64 @smin_commutative(i32 %a, i32 %b) { +; CHECK-LABEL: define i64 @smin_commutative( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.smin.i32(i32 [[A]], i32 [[B]]) +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[MIN]] +; CHECK-NEXT: [[EXT:%.*]] = zext nneg i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; + %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) + %sub = sub nsw i32 %b, %min + %ext = sext i32 %sub to i64 + ret i64 %ext +} + +; NEGATIVE TEST: mismatched operands should not optimize +define i64 @smin_mismatch(i32 %a, i32 %b, i32 %c) { +; CHECK-LABEL: define i64 @smin_mismatch( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) { +; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.smin.i32(i32 [[B]], i32 [[C]]) +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], [[MIN]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; + %min = call i32 @llvm.smin.i32(i32 %b, i32 %c) + %sub = sub nsw i32 %a, %min + %ext = sext i32 %sub to i64 + ret i64 %ext +} + + +; NEGATIVE TEST: unguarded subtraction should NOT optimize +define i64 @neg_unguarded_sub(i32 %a, i32 %b) { +; CHECK-LABEL: define i64 @neg_unguarded_sub( +; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[B]], [[A]] +; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; + %sub = sub nsw i32 %b, %a + %ext = sext i32 %sub to i64 + ret i64 %ext +}