[llvm] [ValueTracking] Handle intrinsics in `computeConstantRange` (PR #100870)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 27 05:48:35 PDT 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/100870

This patch is needed by https://github.com/llvm/llvm-project/issues/95255.


>From 164a57805bd06f8857730b104fe4e4f11ed83ba1 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 27 Jul 2024 20:39:19 +0800
Subject: [PATCH 1/2] [ValueTracking] Add pre-commit tests. NFC.

---
 .../Analysis/ValueTracking/constant-ranges.ll | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/llvm/test/Analysis/ValueTracking/constant-ranges.ll b/llvm/test/Analysis/ValueTracking/constant-ranges.ll
index c440cfad889d3..2926ca1e27822 100644
--- a/llvm/test/Analysis/ValueTracking/constant-ranges.ll
+++ b/llvm/test/Analysis/ValueTracking/constant-ranges.ll
@@ -230,3 +230,82 @@ define i1 @srem_negC_fail1(i8 %x) {
   %r = icmp sge i8 %val, -33
   ret i1 %r
 }
+
+define i1 @intrinsic_test1(i64 %x, i32 %y, i64 %z) {
+; CHECK-LABEL: @intrinsic_test1(
+; CHECK-NEXT:    [[SH_PROM:%.*]] = zext nneg i32 [[Y:%.*]] to i64
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 1, [[SH_PROM]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[Z:%.*]], 0
+; CHECK-NEXT:    [[UMIN1:%.*]] = call i64 @llvm.umin.i64(i64 [[SHL]], i64 [[Z]])
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP1]], i64 1, i64 [[UMIN1]]
+; CHECK-NEXT:    [[UMIN2:%.*]] = call i64 @llvm.umin.i64(i64 [[X:%.*]], i64 [[SEL]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[UMIN2]], -71777214294589697
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sh_prom = zext nneg i32 %y to i64
+  %shl = shl nuw i64 1, %sh_prom
+  %cmp1 = icmp eq i64 %z, 0
+  %umin1 = call i64 @llvm.umin.i64(i64 %shl, i64 %z)
+  %sel = select i1 %cmp1, i64 1, i64 %umin1
+  %umin2 = call i64 @llvm.umin.i64(i64 %x, i64 %sel)
+  %cmp = icmp ugt i64 %umin2, -71777214294589697
+  ret i1 %cmp
+}
+
+define i1 @intrinsic_test2(i32 %x, i32 %y) {
+; CHECK-LABEL: @intrinsic_test2(
+; CHECK-NEXT:    [[SH:%.*]] = shl nuw nsw i32 16, [[X:%.*]]
+; CHECK-NEXT:    [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[SH]], i32 64)
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[UMIN]], i32 1)
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[Y:%.*]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ADD]], [[UMAX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sh = shl nuw nsw i32 16, %x
+  %umin = call i32 @llvm.umin.i32(i32 %sh, i32 64)
+  %umax = call i32 @llvm.umax.i32(i32 %umin, i32 1)
+  %add = add nuw nsw i32 %y, 1
+  %cmp = icmp eq i32 %add, %umax
+  ret i1 %cmp
+}
+
+define i1 @constant_test(i64 %x, i1 %cond) {
+; CHECK-LABEL: @constant_test(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], i64 2147483647, i64 18446744073709551
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ugt i64 [[X]], [[SEL]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[OR_COND]]
+;
+  %sel = select i1 %cond, i64 2147483647, i64 18446744073709551
+  %cmp1 = icmp slt i64 %x, 0
+  %cmp2 = icmp ugt i64 %x, %sel
+  %or.cond = or i1 %cmp1, %cmp2
+  ret i1 %or.cond
+}
+
+; Make sure that we don't return a full range for an imm arg.
+define i1 @immarg_test(i32 %x, i32 %y, i1 %c1, i1 %c2, i1 %c3, i1 %c4, i1 %c5, i1 %c6, i1 %c7) {
+; CHECK-LABEL: @immarg_test(
+; CHECK-NEXT:    [[ABS:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 true)
+; CHECK-NEXT:    [[M1:%.*]] = select i1 [[C1:%.*]], i32 [[ABS]], i32 0
+; CHECK-NEXT:    [[M2:%.*]] = select i1 [[C2:%.*]], i32 [[M1]], i32 1
+; CHECK-NEXT:    [[M3:%.*]] = select i1 [[C3:%.*]], i32 [[M2]], i32 2
+; CHECK-NEXT:    [[M4:%.*]] = select i1 [[C4:%.*]], i32 [[M3]], i32 3
+; CHECK-NEXT:    [[M5:%.*]] = select i1 [[C5:%.*]], i32 [[M4]], i32 4
+; CHECK-NEXT:    [[M6:%.*]] = select i1 [[C6:%.*]], i32 [[M5]], i32 5
+; CHECK-NEXT:    [[M7:%.*]] = select i1 [[C7:%.*]], i32 [[M6]], i32 6
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[M7]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %abs = call i32 @llvm.abs.i32(i32 %y, i1 true)
+  %m1 = select i1 %c1, i32 %abs, i32 0
+  %m2 = select i1 %c2, i32 %m1, i32 1
+  %m3 = select i1 %c3, i32 %m2, i32 2
+  %m4 = select i1 %c4, i32 %m3, i32 3
+  %m5 = select i1 %c5, i32 %m4, i32 4
+  %m6 = select i1 %c6, i32 %m5, i32 5
+  %m7 = select i1 %c7, i32 %m6, i32 6
+  %cmp = icmp eq i32 %m7, 1
+  ret i1 %cmp
+}

>From bec349692a0c7f667a3370d14d8fbae0d5e2ab31 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 27 Jul 2024 20:46:12 +0800
Subject: [PATCH 2/2] [ValueTracking] Handle intrinsics in
 `computeConstantRange`

---
 llvm/lib/Analysis/ValueTracking.cpp            | 17 ++++++++++++++---
 .../Analysis/ValueTracking/constant-ranges.ll  | 18 ++++--------------
 llvm/test/Transforms/InstCombine/sub.ll        |  2 +-
 llvm/test/Transforms/InstSimplify/call.ll      |  8 ++------
 4 files changed, 21 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index bfd26fadd237b..79ed66316da37 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9687,12 +9687,12 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
                                          unsigned Depth) {
   assert(V->getType()->isIntOrIntVectorTy() && "Expected integer instruction");
 
-  if (Depth == MaxAnalysisRecursionDepth)
-    return ConstantRange::getFull(V->getType()->getScalarSizeInBits());
-
   if (auto *C = dyn_cast<Constant>(V))
     return C->toConstantRange();
 
+  if (Depth == MaxAnalysisRecursionDepth)
+    return ConstantRange::getFull(V->getType()->getScalarSizeInBits());
+
   unsigned BitWidth = V->getType()->getScalarSizeInBits();
   InstrInfoQuery IIQ(UseInstrInfo);
   ConstantRange CR = ConstantRange::getFull(BitWidth);
@@ -9728,6 +9728,17 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
     if (const auto *CB = dyn_cast<CallBase>(V))
       if (std::optional<ConstantRange> Range = CB->getRange())
         CR = CR.intersectWith(*Range);
+
+    if (const auto *II = dyn_cast<IntrinsicInst>(V)) {
+      Intrinsic::ID IID = II->getIntrinsicID();
+      if (ConstantRange::isIntrinsicSupported(IID)) {
+        SmallVector<ConstantRange, 2> OpRanges;
+        for (Value *Op : II->args())
+          OpRanges.push_back(computeConstantRange(Op, ForSigned, UseInstrInfo,
+                                                  AC, CtxI, DT, Depth + 1));
+        CR = CR.intersectWith(ConstantRange::intrinsic(IID, OpRanges));
+      }
+    }
   }
 
   if (CtxI && AC) {
diff --git a/llvm/test/Analysis/ValueTracking/constant-ranges.ll b/llvm/test/Analysis/ValueTracking/constant-ranges.ll
index 2926ca1e27822..bc9d78f4473b3 100644
--- a/llvm/test/Analysis/ValueTracking/constant-ranges.ll
+++ b/llvm/test/Analysis/ValueTracking/constant-ranges.ll
@@ -233,14 +233,7 @@ define i1 @srem_negC_fail1(i8 %x) {
 
 define i1 @intrinsic_test1(i64 %x, i32 %y, i64 %z) {
 ; CHECK-LABEL: @intrinsic_test1(
-; CHECK-NEXT:    [[SH_PROM:%.*]] = zext nneg i32 [[Y:%.*]] to i64
-; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 1, [[SH_PROM]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i64 [[Z:%.*]], 0
-; CHECK-NEXT:    [[UMIN1:%.*]] = call i64 @llvm.umin.i64(i64 [[SHL]], i64 [[Z]])
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP1]], i64 1, i64 [[UMIN1]]
-; CHECK-NEXT:    [[UMIN2:%.*]] = call i64 @llvm.umin.i64(i64 [[X:%.*]], i64 [[SEL]])
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[UMIN2]], -71777214294589697
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %sh_prom = zext nneg i32 %y to i64
   %shl = shl nuw i64 1, %sh_prom
@@ -256,9 +249,8 @@ define i1 @intrinsic_test2(i32 %x, i32 %y) {
 ; CHECK-LABEL: @intrinsic_test2(
 ; CHECK-NEXT:    [[SH:%.*]] = shl nuw nsw i32 16, [[X:%.*]]
 ; CHECK-NEXT:    [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[SH]], i32 64)
-; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[UMIN]], i32 1)
 ; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[Y:%.*]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ADD]], [[UMAX]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ADD]], [[UMIN]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sh = shl nuw nsw i32 16, %x
@@ -272,10 +264,8 @@ define i1 @intrinsic_test2(i32 %x, i32 %y) {
 define i1 @constant_test(i64 %x, i1 %cond) {
 ; CHECK-LABEL: @constant_test(
 ; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], i64 2147483647, i64 18446744073709551
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i64 [[X:%.*]], 0
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp ugt i64 [[X]], [[SEL]]
-; CHECK-NEXT:    [[OR_COND:%.*]] = or i1 [[CMP1]], [[CMP2]]
-; CHECK-NEXT:    ret i1 [[OR_COND]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ugt i64 [[X:%.*]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP2]]
 ;
   %sel = select i1 %cond, i64 2147483647, i64 18446744073709551
   %cmp1 = icmp slt i64 %x, 0
diff --git a/llvm/test/Transforms/InstCombine/sub.ll b/llvm/test/Transforms/InstCombine/sub.ll
index cb308ab66b093..1070b9ff745a7 100644
--- a/llvm/test/Transforms/InstCombine/sub.ll
+++ b/llvm/test/Transforms/InstCombine/sub.ll
@@ -1329,7 +1329,7 @@ define <2 x i32> @test68(<2 x i32> %x) {
 define <2 x i32> @test69(<2 x i32> %x) {
 ; CHECK-LABEL: @test69(
 ; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[X:%.*]], <2 x i32> <i32 255, i32 127>)
-; CHECK-NEXT:    [[DOTNEG:%.*]] = add <2 x i32> [[TMP1]], <i32 1, i32 1>
+; CHECK-NEXT:    [[DOTNEG:%.*]] = add nsw <2 x i32> [[TMP1]], <i32 1, i32 1>
 ; CHECK-NEXT:    ret <2 x i32> [[DOTNEG]]
 ;
   %1 = xor <2 x i32> %x, <i32 -1, i32 -1>
diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index c6f6b65f89dc2..503be81c65645 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -1582,9 +1582,7 @@ define i1 @ctlz_i1_non_poison_eq_false(i1 %x) {
 
 define i1 @ctlz_i1_poison_eq_false(i1 %x) {
 ; CHECK-LABEL: @ctlz_i1_poison_eq_false(
-; CHECK-NEXT:    [[CT:%.*]] = call i1 @llvm.ctlz.i1(i1 [[X:%.*]], i1 true)
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i1 [[CT]], false
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %ct = call i1 @llvm.ctlz.i1(i1 %x, i1 true)
   %cmp = icmp eq i1 %ct, false
@@ -1604,9 +1602,7 @@ define i1 @cttz_i1_non_poison_eq_false(i1 %x) {
 
 define i1 @cttz_i1_poison_eq_false(i1 %x) {
 ; CHECK-LABEL: @cttz_i1_poison_eq_false(
-; CHECK-NEXT:    [[CT:%.*]] = call i1 @llvm.cttz.i1(i1 [[X:%.*]], i1 true)
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i1 [[CT]], false
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %ct = call i1 @llvm.cttz.i1(i1 %x, i1 true)
   %cmp = icmp eq i1 %ct, false



More information about the llvm-commits mailing list