diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp index 48b27a1ea0a29..523196e5e6eab 100644 --- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -470,17 +470,17 @@ static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI); // because it is negation-invariant. static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) { Value *X = II->getArgOperand(0); - bool IsIntMinPoison = cast(II->getArgOperand(1))->isOne(); - Type *Ty = X->getType(); - Constant *IntMin = - ConstantInt::get(Ty, APInt::getSignedMinValue(Ty->getScalarSizeInBits())); - LazyValueInfo::Tristate Result; + if (!Ty->isIntegerTy()) + return false; + + bool IsIntMinPoison = cast(II->getArgOperand(1))->isOne(); + APInt IntMin = APInt::getSignedMinValue(Ty->getScalarSizeInBits()); + ConstantRange Range = LVI->getConstantRangeAtUse( + II->getOperandUse(0), /*UndefAllowed*/ IsIntMinPoison); // Is X in [0, IntMin]? NOTE: INT_MIN is fine! - Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_ULE, X, IntMin, II, - /*UseBlockValue=*/true); - if (Result == LazyValueInfo::True) { + if (Range.icmp(CmpInst::ICMP_ULE, IntMin)) { ++NumAbs; II->replaceAllUsesWith(X); II->eraseFromParent(); @@ -488,40 +488,30 @@ static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) { } // Is X in [IntMin, 0]? NOTE: INT_MIN is fine! - Constant *Zero = ConstantInt::getNullValue(Ty); - Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_SLE, X, Zero, II, - /*UseBlockValue=*/true); - assert(Result != LazyValueInfo::False && "Should have been handled already."); - - if (Result == LazyValueInfo::Unknown) { - // Argument's range crosses zero. - bool Changed = false; - if (!IsIntMinPoison) { - // Can we at least tell that the argument is never INT_MIN? - Result = LVI->getPredicateAt(CmpInst::Predicate::ICMP_NE, X, IntMin, II, - /*UseBlockValue=*/true); - if (Result == LazyValueInfo::True) { - ++NumNSW; - ++NumSubNSW; - II->setArgOperand(1, ConstantInt::getTrue(II->getContext())); - Changed = true; - } - } - return Changed; - } + if (Range.getSignedMax().isNonPositive()) { + IRBuilder<> B(II); + Value *NegX = B.CreateNeg(X, II->getName(), /*HasNUW=*/false, + /*HasNSW=*/IsIntMinPoison); + ++NumAbs; + II->replaceAllUsesWith(NegX); + II->eraseFromParent(); - IRBuilder<> B(II); - Value *NegX = B.CreateNeg(X, II->getName(), /*HasNUW=*/false, - /*HasNSW=*/IsIntMinPoison); - ++NumAbs; - II->replaceAllUsesWith(NegX); - II->eraseFromParent(); + // See if we can infer some no-wrap flags. + if (auto *BO = dyn_cast(NegX)) + processBinOp(BO, LVI); - // See if we can infer some no-wrap flags. - if (auto *BO = dyn_cast(NegX)) - processBinOp(BO, LVI); + return true; + } - return true; + // Argument's range crosses zero. + // Can we at least tell that the argument is never INT_MIN? + if (!IsIntMinPoison && !Range.contains(IntMin)) { + ++NumNSW; + ++NumSubNSW; + II->setArgOperand(1, ConstantInt::getTrue(II->getContext())); + return true; + } + return false; } // See if this min/max intrinsic always picks it's one specific operand. diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll b/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll index 6231b05a851cb..7f10ce63e2fdc 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll @@ -2,6 +2,7 @@ ; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s declare void @llvm.assume(i1) +declare i32 @llvm.abs.i32(i32, i1) declare i8 @llvm.abs.i8(i8, i1) declare i1 @llvm.abs.i1(i1, i1) @@ -379,11 +380,139 @@ define i8 @test27(i8 %x) { define i1 @pr59887(i1 %x, i1 %c) { ; CHECK-LABEL: @pr59887( -; CHECK-NEXT: [[ABS:%.*]] = call i1 @llvm.abs.i1(i1 [[X:%.*]], i1 false) -; CHECK-NEXT: [[RES:%.*]] = select i1 [[C:%.*]], i1 [[ABS]], i1 false +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C:%.*]], i1 [[X:%.*]], i1 false ; CHECK-NEXT: ret i1 [[RES]] ; %abs = call i1 @llvm.abs.i1(i1 %x, i1 false) %res = select i1 %c, i1 %abs, i1 false ret i1 %res } + +; Because of `undef`, We can't delete `abs`. +; We can't replace the `abs` argument with true either. +define i32 @pr68381_undef_abs_false(i1 %c0, i1 %c1, i8 %v1) { +; CHECK-LABEL: @pr68381_undef_abs_false( +; CHECK-NEXT: start: +; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1:%.*]] to i32 +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ] +; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB0]], label [[BB2:%.*]] +; CHECK: bb2: +; CHECK-NEXT: [[Z:%.*]] = call i32 @llvm.abs.i32(i32 [[X]], i1 false) +; CHECK-NEXT: ret i32 [[Z]] +; +start: + br i1 %c0, label %bb0, label %bb1 + +bb0: + %v1_i32 = zext i8 %v1 to i32 + br label %bb1 + +bb1: + %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ] + br i1 %c1, label %bb0, label %bb2 + +bb2: + %z = call i32 @llvm.abs.i32(i32 %x, i1 false) + ret i32 %z +} + +; Because of `and`, we can delete `abs`. +define i32 @pr68381_undef_abs_false_and(i1 %c0, i1 %c1, i8 %v1) { +; CHECK-LABEL: @pr68381_undef_abs_false_and( +; CHECK-NEXT: start: +; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1:%.*]] to i32 +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ] +; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB0]], label [[BB2:%.*]] +; CHECK: bb2: +; CHECK-NEXT: [[Y:%.*]] = and i32 [[X]], 255 +; CHECK-NEXT: ret i32 [[Y]] +; +start: + br i1 %c0, label %bb0, label %bb1 + +bb0: + %v1_i32 = zext i8 %v1 to i32 + br label %bb1 + +bb1: + %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ] + br i1 %c1, label %bb0, label %bb2 + +bb2: + %y = and i32 %x, 255 + %z = call i32 @llvm.abs.i32(i32 %y, i1 false) + ret i32 %z +} + +; Because of `undef`, we can't replace `abs` with `sub`. +define i32 @pr68381_undef_abs_false_sub(i1 %c0, i1 %c1, i32 %v1, i32 %v2) { +; CHECK-LABEL: @pr68381_undef_abs_false_sub( +; CHECK-NEXT: start: +; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[V3:%.*]] = add i32 [[V1:%.*]], [[V2:%.*]] +; CHECK-NEXT: [[LIM:%.*]] = icmp sle i32 [[V3]], -1 +; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]]) +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V3]], [[BB0]] ], [ undef, [[START:%.*]] ] +; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB0]], label [[BB2:%.*]] +; CHECK: bb2: +; CHECK-NEXT: [[Z:%.*]] = call i32 @llvm.abs.i32(i32 [[X]], i1 false) +; CHECK-NEXT: ret i32 [[Z]] +; +start: + br i1 %c0, label %bb0, label %bb1 + +bb0: + %v3 = add i32 %v1, %v2 + %lim = icmp sle i32 %v3, -1 + call void @llvm.assume(i1 %lim) + br label %bb1 + +bb1: + %x = phi i32 [ %v3, %bb0 ], [ undef, %start ] + br i1 %c1, label %bb0, label %bb2 + +bb2: + %z = call i32 @llvm.abs.i32(i32 %x, i1 false) + ret i32 %z +} + +; We can delete `abs`. +define i32 @pr68381_undef_abs_true(i1 %c0, i1 %c1, i8 %v1) { +; CHECK-LABEL: @pr68381_undef_abs_true( +; CHECK-NEXT: start: +; CHECK-NEXT: br i1 [[C0:%.*]], label [[BB0:%.*]], label [[BB1:%.*]] +; CHECK: bb0: +; CHECK-NEXT: [[V1_I32:%.*]] = zext i8 [[V1:%.*]] to i32 +; CHECK-NEXT: br label [[BB1]] +; CHECK: bb1: +; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[V1_I32]], [[BB0]] ], [ undef, [[START:%.*]] ] +; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB0]], label [[BB2:%.*]] +; CHECK: bb2: +; CHECK-NEXT: ret i32 [[X]] +; +start: + br i1 %c0, label %bb0, label %bb1 + +bb0: + %v1_i32 = zext i8 %v1 to i32 + br label %bb1 + +bb1: + %x = phi i32 [ %v1_i32, %bb0 ], [ undef, %start ] + br i1 %c1, label %bb0, label %bb2 + +bb2: + %z = call i32 @llvm.abs.i32(i32 %x, i1 true) + ret i32 %z +}