[llvm] [ValueTracking] isNonZero trunc of sub of ptr2int's with recursive GEP where pointers are limited to a 32bit alloc. (PR #84933)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 24 13:59:34 PDT 2024


https://github.com/bipmis updated https://github.com/llvm/llvm-project/pull/84933

>From 6b14ef9ee06c5f001b3c5089e702360b2760b884 Mon Sep 17 00:00:00 2001
From: bipmis <biplob.mishra at arm.com>
Date: Tue, 12 Mar 2024 15:00:10 +0000
Subject: [PATCH 1/3] Add tests where trunc operand can be a KnownNonZero

---
 .../ValueTracking/phi-known-bits-truncflag.ll | 389 ++++++++++++++++++
 1 file changed, 389 insertions(+)
 create mode 100644 llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll

diff --git a/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
new file mode 100644
index 00000000000000..b9b9bcefc3ca98
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
@@ -0,0 +1,389 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=instcombine < %s -S | FileCheck %s
+
+define i1 @recursiveGEP_withPtrSubTrunc(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[VAL1]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %val1, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %a.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i8 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %val1 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %conv4 = trunc i64 %sub.ptr.sub.i to i32
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i32 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i32 %retval.0.i, 0
+  ret i1 %bool
+}
+
+define i1 @recursiveGEP_withPtrSubAshrTrunc(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubAshrTrunc(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[VAL1]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 2
+; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i16 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[SUB_PTR_DIV:%.*]] = lshr exact i64 [[SUB_PTR_SUB_I]], 1
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_DIV]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %val1, %entry ]
+  %test.0.i = getelementptr inbounds i16, ptr %a.pn.i, i64 1
+  %0 = load i16, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i16 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %val1 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %sub.ptr.div = ashr exact i64 %sub.ptr.sub.i, 1
+  %conv4 = trunc i64 %sub.ptr.div to i32
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i32 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i32 %retval.0.i, 0
+  ret i1 %bool
+}
+
+define i1 @recursiveGEP_withPtrSubTrunc_StartNotEqualtoB(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc_StartNotEqualtoB(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[VAL1]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[TEST_1_I:%.*]] = getelementptr inbounds i8, ptr [[VAL1]], i64 -5
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_1_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %val1, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %a.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i8 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %test.1.i = getelementptr inbounds i8, ptr %val1, i64 -5
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %test.1.i to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %conv4 = trunc i64 %sub.ptr.sub.i to i32
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i32 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i32 %retval.0.i, 0
+  ret i1 %bool
+}
+
+define i1 @recursiveGEP_withPtrSubTrunc_PhiOperandsCommuted(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc_PhiOperandsCommuted(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[VAL1]], [[ENTRY:%.*]] ], [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %val1, %entry ], [ %test.0.i, %while.cond.i ]
+  %test.0.i = getelementptr inbounds i8, ptr %a.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i8 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %val1 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %conv4 = trunc i64 %sub.ptr.sub.i to i32
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i32 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i32 %retval.0.i, 0
+  ret i1 %bool
+}
+
+define i1 @recursiveGEP_withPtrSubTrunc_SubOperandsCommuted(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc_SubOperandsCommuted(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[VAL1]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_RHS_CAST_I]], [[SUB_PTR_LHS_CAST_I]]
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %val1, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %a.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i8 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %val1 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.rhs.cast.i, %sub.ptr.lhs.cast.i
+  %conv4 = trunc i64 %sub.ptr.sub.i to i32
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i32 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i32 %retval.0.i, 0
+  ret i1 %bool
+}
+
+define i1 @recursiveGEP_withPtrSubTrunc64to16(ptr noundef %val1) {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc64to16(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp eq ptr [[VAL1:%.*]], null
+; CHECK-NEXT:    br i1 [[CMP_I]], label [[_Z9STRINGLENPKS_EXIT:%.*]], label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[A_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[VAL1]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[A_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 2
+; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
+; CHECK:       while.end.i:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i16
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i16 [[CONV4]], 0
+; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
+; CHECK:       _Z9stringlenPKs.exit:
+; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
+; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+;
+entry:
+  %cmp.i = icmp eq ptr %val1, null
+  br i1 %cmp.i, label %_Z9stringlenPKs.exit, label %while.cond.i
+
+while.cond.i:
+  %a.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %val1, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %a.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 2
+  %cmp3.not.i = icmp eq i8 %0, 0
+  br i1 %cmp3.not.i, label %while.end.i, label %while.cond.i
+
+while.end.i:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %val1 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %conv4 = trunc i64 %sub.ptr.sub.i to i16
+  br label %_Z9stringlenPKs.exit
+
+_Z9stringlenPKs.exit:
+  %retval.0.i = phi i16 [ %conv4, %while.end.i ], [ 0, %entry ]
+  %bool = icmp eq i16 %retval.0.i, 0
+  ret i1 %bool
+}
+
+%struct.Foo = type { [256 x i8] }
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
+
+define i1 @recursiveGEP_withPtrSubTrunc_knownObject() {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc_knownObject(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[FOO:%.*]] = alloca [[STRUCT_FOO:%.*]], align 1
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 256, ptr nonnull [[FOO]])
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(256) [[FOO]], i8 97, i64 255, i1 false)
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 255
+; CHECK-NEXT:    store i8 0, ptr [[ARRAYIDX]], align 1
+; CHECK-NEXT:    br label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[F_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[FOO]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[F_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 1
+; CHECK-NEXT:    [[CMP5_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP5_NOT_I]], label [[LEN_EXIT:%.*]], label [[WHILE_COND_I]]
+; CHECK:       len.exit:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[FOO]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 256, ptr nonnull [[FOO]])
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %foo = alloca %struct.Foo, align 1
+  call void @llvm.lifetime.start.p0(i64 256, ptr nonnull %foo)
+  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(256) %foo, i8 97, i64 255, i1 false)
+  %arrayidx = getelementptr inbounds [256 x i8], ptr %foo, i64 0, i64 255
+  store i8 0, ptr %arrayidx, align 1
+  br label %while.cond.i
+
+while.cond.i:
+  %f.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %foo, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %f.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 1
+  %cmp5.not.i = icmp eq i8 %0, 0
+  br i1 %cmp5.not.i, label %len.exit, label %while.cond.i
+
+len.exit:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %foo to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %1 = trunc i64 %sub.ptr.sub.i to i32
+  %cmp = icmp eq i32 %1, 0
+  call void @llvm.lifetime.end.p0(i64 256, ptr nonnull %foo)
+  ret i1 %cmp
+}
+
+define i1 @recursiveGEP_withPtrSubTrunc_knownObject_ObjSize0() {
+; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc_knownObject_ObjSize0(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[FOO:%.*]] = alloca [[STRUCT_FOO:%.*]], align 1
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 256, ptr nonnull [[FOO]])
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(256) [[FOO]], i8 97, i64 255, i1 false)
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 255
+; CHECK-NEXT:    store i8 0, ptr [[ARRAYIDX]], align 1
+; CHECK-NEXT:    [[FOO2:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 256
+; CHECK-NEXT:    br label [[WHILE_COND_I:%.*]]
+; CHECK:       while.cond.i:
+; CHECK-NEXT:    [[F_PN_I:%.*]] = phi ptr [ [[TEST_0_I:%.*]], [[WHILE_COND_I]] ], [ [[FOO2]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[TEST_0_I]] = getelementptr inbounds i8, ptr [[F_PN_I]], i64 1
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[TEST_0_I]], align 1
+; CHECK-NEXT:    [[CMP5_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP5_NOT_I]], label [[LEN_EXIT:%.*]], label [[WHILE_COND_I]]
+; CHECK:       len.exit:
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[FOO2]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 256, ptr nonnull [[FOO]])
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %foo = alloca %struct.Foo, align 1
+  call void @llvm.lifetime.start.p0(i64 256, ptr nonnull %foo)
+  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(256) %foo, i8 97, i64 255, i1 false)
+  %arrayidx = getelementptr inbounds [256 x i8], ptr %foo, i64 0, i64 255
+  store i8 0, ptr %arrayidx, align 1
+  %foo2 = getelementptr inbounds i8, ptr %foo, i64 256
+  br label %while.cond.i
+
+while.cond.i:
+  %f.pn.i = phi ptr [ %test.0.i, %while.cond.i ], [ %foo2, %entry ]
+  %test.0.i = getelementptr inbounds i8, ptr %f.pn.i, i64 1
+  %0 = load i8, ptr %test.0.i, align 1
+  %cmp5.not.i = icmp eq i8 %0, 0
+  br i1 %cmp5.not.i, label %len.exit, label %while.cond.i
+
+len.exit:
+  %sub.ptr.lhs.cast.i = ptrtoint ptr %test.0.i to i64
+  %sub.ptr.rhs.cast.i = ptrtoint ptr %foo2 to i64
+  %sub.ptr.sub.i = sub i64 %sub.ptr.lhs.cast.i, %sub.ptr.rhs.cast.i
+  %1 = trunc i64 %sub.ptr.sub.i to i32
+  %cmp = icmp eq i32 %1, 0
+  call void @llvm.lifetime.end.p0(i64 256, ptr nonnull %foo)
+  ret i1 %cmp
+}
+
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+

>From 48a08c42debd9a8e981690dd5cb44d87db573c9c Mon Sep 17 00:00:00 2001
From: bipmis <biplob.mishra at arm.com>
Date: Wed, 24 Apr 2024 20:53:54 +0000
Subject: [PATCH 2/3] Add nuw to trunc with sub of ptr2int operands which
 suggests a recursion when objSize is known

---
 .../InstCombine/InstCombineCasts.cpp          | 67 +++++++++++++++++++
 .../ValueTracking/phi-known-bits-truncflag.ll |  7 +-
 2 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 6ce39be59bda98..e78015abaa6449 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -13,6 +13,7 @@
 #include "InstCombineInternal.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Analysis/ConstantFolding.h"
+#include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/PatternMatch.h"
@@ -25,6 +26,11 @@ using namespace PatternMatch;
 
 #define DEBUG_TYPE "instcombine"
 
+// Controls the maximum memory allocation to 32-bit or 4GB.
+static cl::opt<bool> PointerAllocationsLimitedto32bit(
+    "alloc-limit-32bit", cl::init(false),
+    cl::desc("A Pointer with max 32bit allocs"));
+
 /// Given an expression that CanEvaluateTruncated or CanEvaluateSExtd returns
 /// true for, actually insert the code to evaluate the expression.
 Value *InstCombinerImpl::EvaluateInDifferentType(Value *V, Type *Ty,
@@ -910,6 +916,67 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
     Changed = true;
   }
 
+  // Handle scenarios like trunc i64 (sub(ptr2int, ptr2int)) to i32 / trunc i64
+  // (lshr(sub(ptr2int, ptr2int))) to i32, where the ptrs are from same object
+  // and one of the pointers is a recursive GEP. And either the objectSize is
+  // known OR is indicative via a compiler flag, which suggests objectSize<4G.
+
+  // Check if trunc src type is same as Ptr type and dest is a 32bit.
+  if (!Trunc.hasNoUnsignedWrap() && SrcWidth == DL.getPointerSizeInBits() &&
+      DestTy == Type::getInt32Ty(Trunc.getContext())) {
+    Value *A, *B;
+    if (match(Src, m_Sub(m_PtrToIntSameSize(DL, m_Value(A)),
+                         m_PtrToIntSameSize(DL, m_Value(B)))) ||
+        match(Src, m_LShr(m_Sub(m_PtrToIntSameSize(DL, m_Value(A)),
+                                m_PtrToIntSameSize(DL, m_Value(B))),
+                          m_ConstantInt()))) {
+      // Check for a specific pattern where A is recursive GEP with a PHI ptr
+      // with incoming values as (Start,Step) and Start==B.
+      auto *GEPA = dyn_cast<GEPOperator>(A);
+      if (!GEPA) {
+        GEPA = dyn_cast<GEPOperator>(B);
+        std::swap(A, B);
+      }
+      if (GEPA && GEPA->getNumIndices() == 1 &&
+          isa<Constant>(GEPA->idx_begin())) {
+        // Handle 2 incoming PHI values with one being a recursive GEP.
+        auto *PN = dyn_cast<PHINode>(GEPA->getPointerOperand());
+        if (PN && PN->getNumIncomingValues() == 2) {
+          // Search for the recursive GEP as an incoming operand, and record
+          // that as Step.
+          Value *Start = nullptr;
+          Value *Step = const_cast<Value *>(A);
+          if (PN->getIncomingValue(0) == Step)
+            Start = PN->getIncomingValue(1);
+          else if (PN->getIncomingValue(1) == Step)
+            Start = PN->getIncomingValue(0);
+          // Check if the pointers Start and B are same and ObjectSize can be
+          // determined/PointerAllocationsLimitedto32bit flag set.
+          if (Start && Start == B) {
+            ObjectSizeOpts Opts;
+            Opts.RoundToAlign = false;
+            Opts.NullIsUnknownSize = true;
+            uint64_t ObjSize = 0;
+            // Can we get the ObjectSize and ObjectSize is within range.
+            // There is possibly more that we can do when ObjSize if known, and
+            // extend the same for other truncates. Restricting it for a 32bit
+            // truncate result.
+            if (getObjectSize(Start, ObjSize, DL, &TLI, Opts)) {
+              if (ObjSize != 0 && ObjSize <= (uint64_t)0xFFFFFFFF)
+                Changed = true;
+            }
+            // Does the compiler flag specify Object allocs within 4G.
+            else if (PointerAllocationsLimitedto32bit)
+              Changed = true;
+
+            if (Changed)
+              Trunc.setHasNoUnsignedWrap(true);
+          }
+        }
+      }
+    }
+  }
+
   return Changed ? &Trunc : nullptr;
 }
 
diff --git a/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
index b9b9bcefc3ca98..56e4372d4b9d16 100644
--- a/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
+++ b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
@@ -301,13 +301,8 @@ define i1 @recursiveGEP_withPtrSubTrunc_knownObject() {
 ; CHECK-NEXT:    [[CMP5_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
 ; CHECK-NEXT:    br i1 [[CMP5_NOT_I]], label [[LEN_EXIT:%.*]], label [[WHILE_COND_I]]
 ; CHECK:       len.exit:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[FOO]] to i64
-; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
-; CHECK-NEXT:    [[TMP1:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0
 ; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 256, ptr nonnull [[FOO]])
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
 entry:
   %foo = alloca %struct.Foo, align 1

>From 7b21620f7f58abd0655fcd40163b00f073fa74a6 Mon Sep 17 00:00:00 2001
From: bipmis <biplob.mishra at arm.com>
Date: Wed, 24 Apr 2024 20:57:48 +0000
Subject: [PATCH 3/3] Add nuw to trunc with sub of ptr2int operands which
 suggests a recursion when allocs are restricted to 32-bit

---
 .../ValueTracking/phi-known-bits-truncflag.ll | 35 +++----------------
 1 file changed, 5 insertions(+), 30 deletions(-)

diff --git a/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
index 56e4372d4b9d16..01c6e35e3fbdd2 100644
--- a/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
+++ b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=instcombine < %s -S | FileCheck %s
+; RUN: opt -alloc-limit-32bit=true -passes=instcombine < %s -S | FileCheck %s
 
 define i1 @recursiveGEP_withPtrSubTrunc(ptr noundef %val1) {
 ; CHECK-LABEL: @recursiveGEP_withPtrSubTrunc(
@@ -13,15 +13,9 @@ define i1 @recursiveGEP_withPtrSubTrunc(ptr noundef %val1) {
 ; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
 ; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
 ; CHECK:       while.end.i:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
-; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
-; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
 ; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
 ; CHECK:       _Z9stringlenPKs.exit:
-; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
-; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+; CHECK-NEXT:    ret i1 [[CMP_I]]
 ;
 entry:
   %cmp.i = icmp eq ptr %val1, null
@@ -59,16 +53,9 @@ define i1 @recursiveGEP_withPtrSubAshrTrunc(ptr noundef %val1) {
 ; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i16 [[TMP0]], 0
 ; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
 ; CHECK:       while.end.i:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
-; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
-; CHECK-NEXT:    [[SUB_PTR_DIV:%.*]] = lshr exact i64 [[SUB_PTR_SUB_I]], 1
-; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_DIV]] to i32
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
 ; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
 ; CHECK:       _Z9stringlenPKs.exit:
-; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
-; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+; CHECK-NEXT:    ret i1 [[CMP_I]]
 ;
 entry:
   %cmp.i = icmp eq ptr %val1, null
@@ -155,15 +142,9 @@ define i1 @recursiveGEP_withPtrSubTrunc_PhiOperandsCommuted(ptr noundef %val1) {
 ; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
 ; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
 ; CHECK:       while.end.i:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
-; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_LHS_CAST_I]], [[SUB_PTR_RHS_CAST_I]]
-; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
 ; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
 ; CHECK:       _Z9stringlenPKs.exit:
-; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
-; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+; CHECK-NEXT:    ret i1 [[CMP_I]]
 ;
 entry:
   %cmp.i = icmp eq ptr %val1, null
@@ -201,15 +182,9 @@ define i1 @recursiveGEP_withPtrSubTrunc_SubOperandsCommuted(ptr noundef %val1) {
 ; CHECK-NEXT:    [[CMP3_NOT_I:%.*]] = icmp eq i8 [[TMP0]], 0
 ; CHECK-NEXT:    br i1 [[CMP3_NOT_I]], label [[WHILE_END_I:%.*]], label [[WHILE_COND_I]]
 ; CHECK:       while.end.i:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST_I:%.*]] = ptrtoint ptr [[TEST_0_I]] to i64
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST_I:%.*]] = ptrtoint ptr [[VAL1]] to i64
-; CHECK-NEXT:    [[SUB_PTR_SUB_I:%.*]] = sub i64 [[SUB_PTR_RHS_CAST_I]], [[SUB_PTR_LHS_CAST_I]]
-; CHECK-NEXT:    [[CONV4:%.*]] = trunc i64 [[SUB_PTR_SUB_I]] to i32
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[CONV4]], 0
 ; CHECK-NEXT:    br label [[_Z9STRINGLENPKS_EXIT]]
 ; CHECK:       _Z9stringlenPKs.exit:
-; CHECK-NEXT:    [[RETVAL_0_I:%.*]] = phi i1 [ [[TMP1]], [[WHILE_END_I]] ], [ true, [[ENTRY]] ]
-; CHECK-NEXT:    ret i1 [[RETVAL_0_I]]
+; CHECK-NEXT:    ret i1 [[CMP_I]]
 ;
 entry:
   %cmp.i = icmp eq ptr %val1, null



More information about the llvm-commits mailing list