Skip to content

Commit

Permalink
[LVI][CVP] Treat undef like a full range on abs(x, false) (#68711)
Browse files Browse the repository at this point in the history
Fixes #68682.

(cherry picked from commit 2ad9a65)
  • Loading branch information
DianQK authored and tru committed Oct 24, 2023
1 parent aa03fba commit a710137
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 41 deletions.
68 changes: 29 additions & 39 deletions llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,58 +470,48 @@ 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<ConstantInt>(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<ConstantInt>(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();
return true;
}

// 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<BinaryOperator>(NegX))
processBinOp(BO, LVI);

// See if we can infer some no-wrap flags.
if (auto *BO = dyn_cast<BinaryOperator>(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.
Expand Down
133 changes: 131 additions & 2 deletions llvm/test/Transforms/CorrelatedValuePropagation/abs.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
}

0 comments on commit a710137

Please sign in to comment.