diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index e4cb457499ef5..9d53acbe6d46a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5416,6 +5416,45 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I, if (B && D && B == D && NoOp0WrapProblem && NoOp1WrapProblem) return new ICmpInst(Pred, A, C); + // icmp (A-B), (C-B) -> icmp A, C for comparisons of pointer subtraction + // that is, if A, B and C are all ptrs converted to integers. + // + // Tricky case because pointers are effectively unsigned integers, but the + // result of their subtraction is signed. Also, these subtractions ought to + // have NSW semantics, except we cannot give that to them because the results + // are considered signed, but if we optimize away the subtraction, the + // underlying pointers need to be treated as unsigned, thus special handling + // is required. In this scenario, we must ensure that the comparison is + // unsigned after removing the subtraction operations. + if (B && D && B == D && isa(A) && + isa(B) && isa(C)) { + CmpInst::Predicate UnsignedPred; + switch (Pred) { + default: + // If already unsigned, explicit cast from ptr to unsigned, + // so cannot optimize + UnsignedPred = CmpInst::BAD_ICMP_PREDICATE; + break; + case ICmpInst::ICMP_SGT: + UnsignedPred = ICmpInst::ICMP_UGT; + break; + case ICmpInst::ICMP_SLT: + UnsignedPred = ICmpInst::ICMP_ULT; + break; + case ICmpInst::ICMP_SGE: + UnsignedPred = ICmpInst::ICMP_UGE; + break; + case ICmpInst::ICMP_SLE: + UnsignedPred = ICmpInst::ICMP_ULE; + break; + } + if (UnsignedPred != CmpInst::BAD_ICMP_PREDICATE) { + PtrToIntOperator *AOp = dyn_cast(A); + PtrToIntOperator *COp = dyn_cast(C); + return new ICmpInst(UnsignedPred, AOp->getOperand(0), COp->getOperand(0)); + } + } + // icmp (A-B), (A-D) -> icmp D, B for equalities or if there is no overflow. if (A && C && A == C && NoOp0WrapProblem && NoOp1WrapProblem) return new ICmpInst(Pred, D, B); diff --git a/llvm/test/Transforms/InstCombine/icmp-ptrdiff.ll b/llvm/test/Transforms/InstCombine/icmp-ptrdiff.ll new file mode 100644 index 0000000000000..2512135172196 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/icmp-ptrdiff.ll @@ -0,0 +1,109 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define ptr @icmp_ptrdiff_gt(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_gt( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP10:%.*]] = icmp ugt ptr [[TMP4]], [[TMP2:%.*]] +; CHECK-NEXT: [[TMP11:%.*]] = select i1 [[TMP10]], ptr [[TMP2]], ptr [[TMP4]] +; CHECK-NEXT: ret ptr [[TMP11]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp sgt i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} + +define ptr @icmp_ptrdiff_lt(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_lt( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP4]], [[TMP2:%.*]] +; CHECK-NEXT: [[TMP11:%.*]] = select i1 [[TMP10]], ptr [[TMP2]], ptr [[TMP4]] +; CHECK-NEXT: ret ptr [[TMP11]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp slt i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} + +define ptr @icmp_ptrdiff_ge(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_ge( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[TMP4]], [[TMP2:%.*]] +; CHECK-NEXT: [[TMP10:%.*]] = select i1 [[DOTNOT]], ptr [[TMP4]], ptr [[TMP2]] +; CHECK-NEXT: ret ptr [[TMP10]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp sge i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} + +define ptr @icmp_ptrdiff_le(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_le( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP4]], [[TMP2:%.*]] +; CHECK-NEXT: [[TMP10:%.*]] = select i1 [[DOTNOT]], ptr [[TMP4]], ptr [[TMP2]] +; CHECK-NEXT: ret ptr [[TMP10]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp sle i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} + +define ptr @icmp_ptrdiff_eq(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_eq( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: ret ptr [[TMP4]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp eq i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} + +define ptr @icmp_ptrdiff_ne(ptr %0, ptr %1, ptr %2) { +; CHECK-LABEL: @icmp_ptrdiff_ne( +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP4]], [[TMP2:%.*]] +; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[DOTNOT]], ptr [[TMP4]], ptr [[TMP2]] +; CHECK-NEXT: ret ptr [[TMP5]] +; + %4 = getelementptr inbounds nuw i8, ptr %0, i64 1 + %5 = ptrtoint ptr %4 to i64 + %6 = ptrtoint ptr %1 to i64 + %7 = sub i64 %5, %6 + %8 = ptrtoint ptr %2 to i64 + %9 = sub i64 %8, %6 + %10 = icmp ne i64 %7, %9 + %11 = select i1 %10, ptr %2, ptr %4 + ret ptr %11 +} +