[llvm] c329a47 - [CVP] @llvm.abs() handling
Roman Lebedev via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 10 06:47:53 PDT 2021
Author: Roman Lebedev
Date: 2021-04-10T16:47:31+03:00
New Revision: c329a47d9ed77512493f787520317e3f51be3387
URL: https://github.com/llvm/llvm-project/commit/c329a47d9ed77512493f787520317e3f51be3387
DIFF: https://github.com/llvm/llvm-project/commit/c329a47d9ed77512493f787520317e3f51be3387.diff
LOG: [CVP] @llvm.abs() handling
Iff we know the sigdness domain of the argument,
we can either skip @llvm.abs, or do negation directly.
Notably, INT_MIN can belong to either domain:
* X u<= INT_MIN --> X is always fine
https://alive2.llvm.org/ce/z/QB8j-C https://alive2.llvm.org/ce/z/7sFKpS
* X s<= 0 --> -X is always fine
https://alive2.llvm.org/ce/z/QbGSyq https://alive2.llvm.org/ce/z/APsN84
If all else fails, try to inferr NSW flag:
https://alive2.llvm.org/ce/z/qCJfYm
Added:
Modified:
llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
llvm/test/Transforms/CorrelatedValuePropagation/abs.ll
llvm/test/Transforms/CorrelatedValuePropagation/minmaxabs.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 02d2bacabecb3..e84f6eb63da5c 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -82,6 +82,7 @@ STATISTIC(NumMulNUW, "Number of no-unsigned-wrap deductions for mul");
STATISTIC(NumShlNW, "Number of no-wrap deductions for shl");
STATISTIC(NumShlNSW, "Number of no-signed-wrap deductions for shl");
STATISTIC(NumShlNUW, "Number of no-unsigned-wrap deductions for shl");
+STATISTIC(NumAbs, "Number of llvm.abs intrinsics removed");
STATISTIC(NumOverflows, "Number of overflow checks removed");
STATISTIC(NumSaturating,
"Number of saturating arithmetics converted to normal arithmetics");
@@ -442,6 +443,61 @@ static void setDeducedOverflowingFlags(Value *V, Instruction::BinaryOps Opcode,
static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI);
+// See if @llvm.abs argument is alays positive/negative, and simplify.
+// Notably, INT_MIN can belong to either range, regardless of the NSW,
+// because it is negation-invariant.
+static void 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;
+
+ // 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) {
+ ++NumAbs;
+ II->replaceAllUsesWith(X);
+ II->eraseFromParent();
+ return;
+ }
+
+ // 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.
+ 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()));
+ }
+ }
+ return;
+ }
+
+ 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);
+}
+
// Rewrite this with.overflow intrinsic as non-overflowing.
static void processOverflowIntrinsic(WithOverflowInst *WO, LazyValueInfo *LVI) {
IRBuilder<> B(WO);
@@ -488,6 +544,11 @@ static void processSaturatingInst(SaturatingInst *SI, LazyValueInfo *LVI) {
/// Infer nonnull attributes for the arguments at the specified callsite.
static bool processCallSite(CallBase &CB, LazyValueInfo *LVI) {
+ if (CB.getIntrinsicID() == Intrinsic::abs) {
+ processAbsIntrinsic(&cast<IntrinsicInst>(CB), LVI);
+ return true;
+ }
+
if (auto *WO = dyn_cast<WithOverflowInst>(&CB)) {
if (WO->getLHS()->getType()->isIntegerTy() && willNotOverflow(WO, LVI)) {
processOverflowIntrinsic(WO, LVI);
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll b/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll
index d534aecf7a560..c8b21a8e3ef1a 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/abs.ll
@@ -29,7 +29,7 @@ define i8 @test2(i8 %x) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: [[LIM:%.*]] = icmp sge i8 [[X:%.*]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
; CHECK-NEXT: ret i8 [[R]]
;
@@ -56,8 +56,7 @@ define i8 @test4(i8 %x) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp sge i8 %x, 0
@@ -69,8 +68,7 @@ define i8 @test5(i8 %x) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp sge i8 %x, 0
@@ -83,8 +81,7 @@ define i8 @test6(i8 %x) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp sge i8 %x, 1
@@ -96,8 +93,7 @@ define i8 @test7(i8 %x) {
; CHECK-LABEL: @test7(
; CHECK-NEXT: [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp sge i8 %x, 1
@@ -112,8 +108,7 @@ define i8 @test8(i8 %x) {
; CHECK-LABEL: @test8(
; CHECK-NEXT: [[LIM:%.*]] = icmp ule i8 [[X:%.*]], 127
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp ule i8 %x, 127
@@ -125,8 +120,7 @@ define i8 @test9(i8 %x) {
; CHECK-LABEL: @test9(
; CHECK-NEXT: [[LIM:%.*]] = icmp ule i8 [[X:%.*]], 127
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp ule i8 %x, 127
@@ -139,8 +133,7 @@ define i8 @test10(i8 %x) {
; CHECK-LABEL: @test10(
; CHECK-NEXT: [[LIM:%.*]] = icmp ule i8 [[X:%.*]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp ule i8 %x, 128
@@ -152,8 +145,7 @@ define i8 @test11(i8 %x) {
; CHECK-LABEL: @test11(
; CHECK-NEXT: [[LIM:%.*]] = icmp ule i8 [[X:%.*]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[X]]
;
%lim = icmp ule i8 %x, 128
@@ -197,8 +189,8 @@ define i8 @test14(i8 %x) {
; CHECK-LABEL: @test14(
; CHECK-NEXT: [[LIM:%.*]] = icmp sle i8 [[X:%.*]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp sle i8 %x, -1
@@ -210,8 +202,8 @@ define i8 @test15(i8 %x) {
; CHECK-LABEL: @test15(
; CHECK-NEXT: [[LIM:%.*]] = icmp sle i8 [[X:%.*]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub nsw i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp sle i8 %x, -1
@@ -224,8 +216,8 @@ define i8 @test16(i8 %x) {
; CHECK-LABEL: @test16(
; CHECK-NEXT: [[LIM:%.*]] = icmp sle i8 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp sle i8 %x, 0
@@ -237,8 +229,8 @@ define i8 @test17(i8 %x) {
; CHECK-LABEL: @test17(
; CHECK-NEXT: [[LIM:%.*]] = icmp sle i8 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub nsw i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp sle i8 %x, 0
@@ -307,8 +299,8 @@ define i8 @test22(i8 %x) {
; CHECK-LABEL: @test22(
; CHECK-NEXT: [[LIM:%.*]] = icmp uge i8 [[X:%.*]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp uge i8 %x, 128
@@ -320,8 +312,8 @@ define i8 @test23(i8 %x) {
; CHECK-LABEL: @test23(
; CHECK-NEXT: [[LIM:%.*]] = icmp uge i8 [[X:%.*]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub nsw i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp uge i8 %x, 128
@@ -334,8 +326,8 @@ define i8 @test24(i8 %x) {
; CHECK-LABEL: @test24(
; CHECK-NEXT: [[LIM:%.*]] = icmp uge i8 [[X:%.*]], -127
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub nsw i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp uge i8 %x, 129
@@ -347,8 +339,8 @@ define i8 @test25(i8 %x) {
; CHECK-LABEL: @test25(
; CHECK-NEXT: [[LIM:%.*]] = icmp uge i8 [[X:%.*]], -127
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: [[R1:%.*]] = sub nsw i8 0, [[X]]
+; CHECK-NEXT: ret i8 [[R1]]
;
%lim = icmp uge i8 %x, 129
@@ -363,7 +355,7 @@ define i8 @test26(i8 %x) {
; CHECK-LABEL: @test26(
; CHECK-NEXT: [[LIM:%.*]] = icmp ne i8 [[X:%.*]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 false)
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.abs.i8(i8 [[X]], i1 true)
; CHECK-NEXT: ret i8 [[R]]
;
%lim = icmp ne i8 %x, 128
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/minmaxabs.ll b/llvm/test/Transforms/CorrelatedValuePropagation/minmaxabs.ll
index 409503439165e..bc0a0150de76c 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/minmaxabs.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/minmaxabs.ll
@@ -74,8 +74,8 @@ define void @test_smax(i32 %x) {
define void @test_abs1(i32* %p) {
; CHECK-LABEL: @test_abs1(
-; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4, [[RNG0:!range !.*]]
-; CHECK-NEXT: [[A:%.*]] = call i32 @llvm.abs.i32(i32 [[X]], i1 false)
+; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P:%.*]], align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT: [[A:%.*]] = call i32 @llvm.abs.i32(i32 [[X]], i1 true)
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[A]], 15
; CHECK-NEXT: call void @use(i1 [[C2]])
More information about the llvm-commits
mailing list