From 6629210aaf9d2e4fcbecc80b35f72108304da4b4 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 24 Sep 2013 16:37:51 +0000 Subject: [PATCH] Teach MemoryBuiltins and InstructionSimplify that operator new never returns NULL. This is safe per C++11 18.6.1.1p3: [operator new returns] a non-null pointer to suitably aligned storage (3.7.4), or else throw a bad_alloc exception. This requirement is binding on a replacement version of this function. Brings us a tiny bit closer to eliminating more vector push_backs. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191310 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/MemoryBuiltins.h | 4 ++++ include/llvm/Analysis/ValueTracking.h | 3 ++- lib/Analysis/InstructionSimplify.cpp | 2 +- lib/Analysis/MemoryBuiltins.cpp | 16 ++++++++++++---- lib/Analysis/ValueTracking.cpp | 8 +++++++- test/Transforms/InstSimplify/call.ll | 20 ++++++++++++++++++++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/include/llvm/Analysis/MemoryBuiltins.h b/include/llvm/Analysis/MemoryBuiltins.h index 0d1ae8c48ee..587b87f6521 100644 --- a/include/llvm/Analysis/MemoryBuiltins.h +++ b/include/llvm/Analysis/MemoryBuiltins.h @@ -64,6 +64,10 @@ bool isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI, bool isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, bool LookThroughBitCast = false); +/// \brief Tests if a value is a call or invoke to a library function that +/// allocates memory and never returns null (such as operator new). +bool isOperatorNewLikeFn(const Value *V, const TargetLibraryInfo *TLI, + bool LookThroughBitCast = false); //===----------------------------------------------------------------------===// // malloc Call Utility Functions. diff --git a/include/llvm/Analysis/ValueTracking.h b/include/llvm/Analysis/ValueTracking.h index 3775ec9f07a..0392f98f075 100644 --- a/include/llvm/Analysis/ValueTracking.h +++ b/include/llvm/Analysis/ValueTracking.h @@ -25,6 +25,7 @@ namespace llvm { class DataLayout; class StringRef; class MDNode; + class TargetLibraryInfo; /// ComputeMaskedBits - Determine which of the bits specified in Mask are /// known to be either zero or one and return them in the KnownZero/KnownOne @@ -186,7 +187,7 @@ namespace llvm { /// isKnownNonNull - Return true if this pointer couldn't possibly be null by /// its definition. This returns true for allocas, non-extern-weak globals /// and byval arguments. - bool isKnownNonNull(const Value *V); + bool isKnownNonNull(const Value *V, const TargetLibraryInfo *TLI = 0); } // end namespace llvm diff --git a/lib/Analysis/InstructionSimplify.cpp b/lib/Analysis/InstructionSimplify.cpp index af65cb74083..b867af1dc3d 100644 --- a/lib/Analysis/InstructionSimplify.cpp +++ b/lib/Analysis/InstructionSimplify.cpp @@ -1739,7 +1739,7 @@ static Constant *computePointerICmp(const DataLayout *TD, RHS = RHS->stripPointerCasts(); // A non-null pointer is not equal to a null pointer. - if (llvm::isKnownNonNull(LHS) && isa(RHS) && + if (llvm::isKnownNonNull(LHS, TLI) && isa(RHS) && (Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE)) return ConstantInt::get(GetCompareTy(LHS), !CmpInst::isTrueWhenEqual(Pred)); diff --git a/lib/Analysis/MemoryBuiltins.cpp b/lib/Analysis/MemoryBuiltins.cpp index 0f0a1c98d0d..2ec2aec4aa0 100644 --- a/lib/Analysis/MemoryBuiltins.cpp +++ b/lib/Analysis/MemoryBuiltins.cpp @@ -35,6 +35,7 @@ enum AllocType { CallocLike = 1<<1, // allocates + bzero ReallocLike = 1<<2, // reallocates StrDupLike = 1<<3, + OpNewLike = MallocLike | (1<<4), // allocates; never returns null AllocLike = MallocLike | CallocLike | StrDupLike, AnyAlloc = MallocLike | CallocLike | ReallocLike | StrDupLike }; @@ -52,13 +53,13 @@ struct AllocFnsTy { static const AllocFnsTy AllocationFnData[] = { {LibFunc::malloc, MallocLike, 1, 0, -1}, {LibFunc::valloc, MallocLike, 1, 0, -1}, - {LibFunc::Znwj, MallocLike, 1, 0, -1}, // new(unsigned int) + {LibFunc::Znwj, OpNewLike, 1, 0, -1}, // new(unsigned int) {LibFunc::ZnwjRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new(unsigned int, nothrow) - {LibFunc::Znwm, MallocLike, 1, 0, -1}, // new(unsigned long) + {LibFunc::Znwm, OpNewLike, 1, 0, -1}, // new(unsigned long) {LibFunc::ZnwmRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new(unsigned long, nothrow) - {LibFunc::Znaj, MallocLike, 1, 0, -1}, // new[](unsigned int) + {LibFunc::Znaj, OpNewLike, 1, 0, -1}, // new[](unsigned int) {LibFunc::ZnajRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new[](unsigned int, nothrow) - {LibFunc::Znam, MallocLike, 1, 0, -1}, // new[](unsigned long) + {LibFunc::Znam, OpNewLike, 1, 0, -1}, // new[](unsigned long) {LibFunc::ZnamRKSt9nothrow_t, MallocLike, 2, 0, -1}, // new[](unsigned long, nothrow) {LibFunc::posix_memalign, MallocLike, 3, 2, -1}, {LibFunc::calloc, CallocLike, 2, 0, 1}, @@ -189,6 +190,13 @@ bool llvm::isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, return getAllocationData(V, ReallocLike, TLI, LookThroughBitCast); } +/// \brief Tests if a value is a call or invoke to a library function that +/// allocates memory and never returns null (such as operator new). +bool llvm::isOperatorNewLikeFn(const Value *V, const TargetLibraryInfo *TLI, + bool LookThroughBitCast) { + return getAllocationData(V, OpNewLike, TLI, LookThroughBitCast); +} + /// extractMallocCall - Returns the corresponding CallInst if the instruction /// is a malloc call. Since CallInst::CreateMalloc() only creates calls, we /// ignore InvokeInst here. diff --git a/lib/Analysis/ValueTracking.cpp b/lib/Analysis/ValueTracking.cpp index c81537543f5..e7a512ce0fd 100644 --- a/lib/Analysis/ValueTracking.cpp +++ b/lib/Analysis/ValueTracking.cpp @@ -15,6 +15,7 @@ #include "llvm/Analysis/ValueTracking.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/MemoryBuiltins.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalAlias.h" @@ -2064,7 +2065,7 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V, /// isKnownNonNull - Return true if we know that the specified value is never /// null. -bool llvm::isKnownNonNull(const Value *V) { +bool llvm::isKnownNonNull(const Value *V, const TargetLibraryInfo *TLI) { // Alloca never returns null, malloc might. if (isa(V)) return true; @@ -2075,5 +2076,10 @@ bool llvm::isKnownNonNull(const Value *V) { // Global values are not null unless extern weak. if (const GlobalValue *GV = dyn_cast(V)) return !GV->hasExternalWeakLinkage(); + + // operator new never returns null. + if (isOperatorNewLikeFn(V, TLI, /*LookThroughBitCast=*/true)) + return true; + return false; } diff --git a/test/Transforms/InstSimplify/call.ll b/test/Transforms/InstSimplify/call.ll index 3e1621c8388..aa3b08653ad 100644 --- a/test/Transforms/InstSimplify/call.ll +++ b/test/Transforms/InstSimplify/call.ll @@ -101,3 +101,23 @@ define float @test_idempotence(float %a) { ret float %r4 } + +define i8* @operator_new() { +entry: + %call = tail call noalias i8* @_Znwm(i64 8) + %cmp = icmp eq i8* %call, null + br i1 %cmp, label %cast.end, label %cast.notnull + +cast.notnull: ; preds = %entry + %add.ptr = getelementptr inbounds i8* %call, i64 4 + br label %cast.end + +cast.end: ; preds = %cast.notnull, %entry + %cast.result = phi i8* [ %add.ptr, %cast.notnull ], [ null, %entry ] + ret i8* %cast.result + +; CHECK-LABEL: @operator_new +; CHECK: br i1 false, label %cast.end, label %cast.notnull +} + +declare noalias i8* @_Znwm(i64) -- 2.34.1