diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index c112fae351817..084e7fbaa268a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -7728,6 +7728,30 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { } } + // icmp slt (sub nsw x, y), (add nsw x, y) --> icmp sgt y, 0 + // icmp ult (sub nuw x, y), (add nuw x, y) --> icmp ugt y, 0 + // icmp eq (sub nsw/nuw x, y), (add nsw/nuw x, y) --> icmp eq y, 0 + { + Value *A, *B; + CmpPredicate CmpPred; + if (match(&I, m_c_ICmp(CmpPred, m_Sub(m_Value(A), m_Value(B)), + m_c_Add(m_Deferred(A), m_Deferred(B))))) { + auto *I0 = cast(Op0); + auto *I1 = cast(Op1); + bool I0NUW = I0->hasNoUnsignedWrap(); + bool I1NUW = I1->hasNoUnsignedWrap(); + bool I0NSW = I0->hasNoSignedWrap(); + bool I1NSW = I1->hasNoSignedWrap(); + if ((ICmpInst::isUnsigned(Pred) && I0NUW && I1NUW) || + (ICmpInst::isSigned(Pred) && I0NSW && I1NSW) || + (ICmpInst::isEquality(Pred) && + ((I0NUW || I0NSW) && (I1NUW || I1NSW)))) { + return new ICmpInst(CmpPredicate::getSwapped(CmpPred), B, + ConstantInt::get(Op0->getType(), 0)); + } + } + } + // Try to optimize equality comparisons against alloca-based pointers. if (Op0->getType()->isPointerTy() && I.isEquality()) { assert(Op1->getType()->isPointerTy() && diff --git a/llvm/test/Transforms/InstCombine/icmp-subadd.ll b/llvm/test/Transforms/InstCombine/icmp-subadd.ll new file mode 100644 index 0000000000000..fd7e1250d893f --- /dev/null +++ b/llvm/test/Transforms/InstCombine/icmp-subadd.ll @@ -0,0 +1,111 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i1 @test-same-operands-sub-add-nsw-icmp-sgt(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-sgt( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[B]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nsw i8 %a, %b + %cmp = icmp sgt i8 %sub, %add + ret i1 %cmp +} + +define i1 @test-same-operands-sub-add-nsw-icmp-slt(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-slt( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[B]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nsw i8 %a, %b + %cmp = icmp slt i8 %sub, %add + ret i1 %cmp +} + +define i1 @test-same-operands-sub-add-nsw-icmp-sle(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-sle( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[B]], -1 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nsw i8 %a, %b + %cmp = icmp sle i8 %sub, %add + ret i1 %cmp +} + +define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[B]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nuw i8 %a, %b + %cmp = icmp eq i8 %sub, %add + ret i1 %cmp +} + +define i1 @test-same-operands-sub-add-nsw-icmp-eq(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-eq( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[B]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nsw i8 %a, %b + %cmp = icmp eq i8 %sub, %add + ret i1 %cmp +} + +define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-add-sub-nsw-icmp-sgt( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[B]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nsw i8 %a, %b + %add = add nsw i8 %a, %b + %cmp = icmp sgt i8 %add, %sub + ret i1 %cmp +} + +define i1 @test-add-sub-nuw-icmp-uge(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-uge( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %sub = sub nuw i8 %a, %b + %add = add nuw i8 %a, %b + %cmp = icmp uge i8 %add, %sub + ret i1 %cmp +} + +; Check not folded +define i1 @test-add-sub-nuw-icmp-sge(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-sge( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[A]], [[B]] +; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[A]], [[B]] +; CHECK-NEXT: [[CMP:%.*]] = icmp sge i8 [[ADD]], [[SUB]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %sub = sub nuw i8 %a, %b + %add = add nuw i8 %a, %b + %cmp = icmp sge i8 %add, %sub + ret i1 %cmp +} + +define i1 @test-add-swap-sub-nuw-icmp-uge(i8 %a, i8 %b) { +; CHECK-LABEL: define i1 @test-add-swap-sub-nuw-icmp-uge( +; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %sub = sub nuw i8 %a, %b + %add = add nuw i8 %b, %a + %cmp = icmp uge i8 %add, %sub + ret i1 %cmp +}