diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index 6ce39be59bda9..e78015abaa644 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 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(A); + if (!GEPA) { + GEPA = dyn_cast(B); + std::swap(A, B); + } + if (GEPA && GEPA->getNumIndices() == 1 && + isa(GEPA->idx_begin())) { + // Handle 2 incoming PHI values with one being a recursive GEP. + auto *PN = dyn_cast(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(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 new file mode 100644 index 0000000000000..01c6e35e3fbdd --- /dev/null +++ b/llvm/test/Analysis/ValueTracking/phi-known-bits-truncflag.ll @@ -0,0 +1,359 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -alloc-limit-32bit=true -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: br label [[_Z9STRINGLENPKS_EXIT]] +; CHECK: _Z9stringlenPKs.exit: +; CHECK-NEXT: ret i1 [[CMP_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: br label [[_Z9STRINGLENPKS_EXIT]] +; CHECK: _Z9stringlenPKs.exit: +; CHECK-NEXT: ret i1 [[CMP_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: br label [[_Z9STRINGLENPKS_EXIT]] +; CHECK: _Z9stringlenPKs.exit: +; CHECK-NEXT: ret i1 [[CMP_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: br label [[_Z9STRINGLENPKS_EXIT]] +; CHECK: _Z9stringlenPKs.exit: +; CHECK-NEXT: ret i1 [[CMP_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: call void @llvm.lifetime.end.p0(i64 256, ptr nonnull [[FOO]]) +; CHECK-NEXT: ret i1 false +; +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) +