diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 3df9af4bc95fe..b226eaf310f3b 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -362,6 +362,11 @@ static void computeKnownBitsAddSub(bool Add, const Value *Op0, const Value *Op1, computeKnownBits(Op0, DemandedElts, Known2, Q, Depth + 1); 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)) + KnownOut.makeNonNegative(); } static void computeKnownBitsMul(const Value *Op0, const Value *Op1, bool NSW, diff --git a/llvm/test/Transforms/InstCombine/sub-after-sle-is-non-negative.ll b/llvm/test/Transforms/InstCombine/sub-after-sle-is-non-negative.ll new file mode 100644 index 0000000000000..8395895b1af65 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/sub-after-sle-is-non-negative.ll @@ -0,0 +1,153 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s +declare void @subroutine(i16) + +define void @test_as_arg(i8 %a, i8 %b) { +; CHECK-LABEL: define void @test_as_arg( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_END:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = zext nneg i8 [[SUB]] to i16 +; CHECK-NEXT: call void @subroutine(i16 [[CONV]]) +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: ret void +; + %cmp = icmp sgt i8 %a, %b + br i1 %cmp, label %cond.end, label %cond.false + +cond.false: + %sub = sub nsw i8 %b, %a + %conv = sext i8 %sub to i16 + call void @subroutine(i16 %conv) + br label %cond.end + +cond.end: + ret void +} + +define i16 @test_as_retval(i8 %a, i8 %b) { +; CHECK-LABEL: define i16 @test_as_retval( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: ret i16 0 +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = zext nneg i8 [[SUB]] to i16 +; CHECK-NEXT: ret i16 [[CONV]] +; + %cmp = icmp sgt i8 %a, %b + br i1 %cmp, label %cond.true, label %cond.false + +cond.true: + ret i16 0 + +cond.false: + %sub = sub nsw i8 %b, %a + %conv = sext i8 %sub to i16 + ret i16 %conv +} + +define void @test_as_arg_wrong_icmp(i8 %a, i8 %b) { +; CHECK-LABEL: define void @test_as_arg_wrong_icmp( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_END:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[SUB]] to i16 +; CHECK-NEXT: call void @subroutine(i16 [[CONV]]) +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: ret void +; + %cmp = icmp slt i8 %a, %b + br i1 %cmp, label %cond.end, label %cond.false + +cond.false: + %sub = sub nsw i8 %b, %a + %conv = sext i8 %sub to i16 + call void @subroutine(i16 %conv) + br label %cond.end + +cond.end: + ret void +} + +define void @test_as_arg_missing_nsw(i8 %a, i8 %b) { +; CHECK-LABEL: define void @test_as_arg_missing_nsw( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_END:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[SUB]] to i16 +; CHECK-NEXT: call void @subroutine(i16 [[CONV]]) +; CHECK-NEXT: br label %[[COND_END]] +; CHECK: [[COND_END]]: +; CHECK-NEXT: ret void +; + %cmp = icmp sgt i8 %a, %b + br i1 %cmp, label %cond.end, label %cond.false + +cond.false: + %sub = sub i8 %b, %a + %conv = sext i8 %sub to i16 + call void @subroutine(i16 %conv) + br label %cond.end + +cond.end: + ret void +} + +define i16 @test_as_retval_wrong_icmp(i8 %a, i8 %b) { +; CHECK-LABEL: define i16 @test_as_retval_wrong_icmp( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: ret i16 0 +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[SUB]] to i16 +; CHECK-NEXT: ret i16 [[CONV]] +; + %cmp = icmp slt i8 %a, %b + br i1 %cmp, label %cond.true, label %cond.false + +cond.true: + ret i16 0 + +cond.false: + %sub = sub nsw i8 %b, %a + %conv = sext i8 %sub to i16 + ret i16 %conv +} + +define i16 @test_as_retval_missing_nsw(i8 %a, i8 %b) { +; CHECK-LABEL: define i16 @test_as_retval_missing_nsw( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]] +; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]] +; CHECK: [[COND_TRUE]]: +; CHECK-NEXT: ret i16 0 +; CHECK: [[COND_FALSE]]: +; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[B]], [[A]] +; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[SUB]] to i16 +; CHECK-NEXT: ret i16 [[CONV]] +; + %cmp = icmp sgt i8 %a, %b + br i1 %cmp, label %cond.true, label %cond.false + +cond.true: + ret i16 0 + +cond.false: + %sub = sub i8 %b, %a + %conv = sext i8 %sub to i16 + ret i16 %conv +}