diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h index d7eab9a0812cd..bc9613a5eb4a8 100644 --- a/llvm/include/llvm/Analysis/MemoryBuiltins.h +++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h @@ -115,6 +115,16 @@ inline CallInst *isFreeCall(Value *I, const TargetLibraryInfo *TLI) { return const_cast(isFreeCall((const Value*)I, TLI)); } +//===----------------------------------------------------------------------===// +// Properties of allocation functions +// + +/// If this allocation function initializes memory to a fixed value, return +/// said value in the requested type. Otherwise, return nullptr. +Constant *getInitialValueOfAllocation(const CallBase *Alloc, + const TargetLibraryInfo *TLI, + Type *Ty); + //===----------------------------------------------------------------------===// // Utility functions to compute size of objects. // diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 505b2a8261bc4..d5c34bb00df1f 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -312,6 +312,22 @@ bool llvm::isStrdupLikeFn(const Value *V, const TargetLibraryInfo *TLI) { return getAllocationData(V, StrDupLike, TLI).hasValue(); } +Constant *llvm::getInitialValueOfAllocation(const CallBase *Alloc, + const TargetLibraryInfo *TLI, + Type *Ty) { + assert(isAllocationFn(Alloc, TLI)); + + // malloc and aligned_alloc are uninitialized (undef) + if (isMallocLikeFn(Alloc, TLI) || isAlignedAllocLikeFn(Alloc, TLI)) + return UndefValue::get(Ty); + + // calloc zero initializes + if (isCallocLikeFn(Alloc, TLI)) + return Constant::getNullValue(Ty); + + return nullptr; +} + /// isLibFreeFunction - Returns true if the function is a builtin free() bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) { unsigned ExpectedNumParams; diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index 4b10e52d49fb4..a71c70b3fbca6 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -207,13 +207,9 @@ Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty, const TargetLibraryInfo *TLI) { if (isa(Obj)) return UndefValue::get(&Ty); - if (isNoAliasFn(&Obj, TLI)) { - if (isMallocLikeFn(&Obj, TLI) || isAlignedAllocLikeFn(&Obj, TLI)) - return UndefValue::get(&Ty); - if (isCallocLikeFn(&Obj, TLI)) - return Constant::getNullValue(&Ty); - return nullptr; - } + if (isAllocationFn(&Obj, TLI)) + return getInitialValueOfAllocation(&cast(Obj), TLI, &Ty); + auto *GV = dyn_cast(&Obj); if (!GV || !GV->hasLocalLinkage()) return nullptr; diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp index ee7a2e4aed253..398c93e8758ce 100644 --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -1104,20 +1104,19 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo, } assert(DepInfo.isDef() && "follows from above"); - // Loading the allocation -> undef. - if (isa(DepInst) || isMallocLikeFn(DepInst, TLI) || - isAlignedAllocLikeFn(DepInst, TLI) || - // Loading immediately after lifetime begin -> undef. - isLifetimeStart(DepInst)) { + // Loading the alloca -> undef. + // Loading immediately after lifetime begin -> undef. + if (isa(DepInst) || isLifetimeStart(DepInst)) { Res = AvailableValue::get(UndefValue::get(Load->getType())); return true; } - // Loading from calloc (which zero initializes memory) -> zero - if (isCallocLikeFn(DepInst, TLI)) { - Res = AvailableValue::get(Constant::getNullValue(Load->getType())); - return true; - } + if (isAllocationFn(DepInst, TLI)) + if (auto *InitVal = getInitialValueOfAllocation(cast(DepInst), + TLI, Load->getType())) { + Res = AvailableValue::get(InitVal); + return true; + } if (StoreInst *S = dyn_cast(DepInst)) { // Reject loads and stores that are to the same address but are of diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp index 3558ce3e11a41..ef5d9f1bef67d 100644 --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -1493,8 +1493,7 @@ NewGVN::performSymbolicLoadCoercion(Type *LoadType, Value *LoadPtr, // undef value. This can happen when loading for a fresh allocation with no // intervening stores, for example. Note that this is only true in the case // that the result of the allocation is pointer equal to the load ptr. - if (isa(DepInst) || isMallocLikeFn(DepInst, TLI) || - isAlignedAllocLikeFn(DepInst, TLI)) { + if (isa(DepInst)) { return createConstantExpression(UndefValue::get(LoadType)); } // If this load occurs either right after a lifetime begin, @@ -1502,12 +1501,10 @@ NewGVN::performSymbolicLoadCoercion(Type *LoadType, Value *LoadPtr, else if (auto *II = dyn_cast(DepInst)) { if (II->getIntrinsicID() == Intrinsic::lifetime_start) return createConstantExpression(UndefValue::get(LoadType)); - } - // If this load follows a calloc (which zero initializes memory), - // then the loaded value is zero - else if (isCallocLikeFn(DepInst, TLI)) { - return createConstantExpression(Constant::getNullValue(LoadType)); - } + } else if (isAllocationFn(DepInst, TLI)) + if (auto *InitVal = getInitialValueOfAllocation(cast(DepInst), + TLI, LoadType)) + return createConstantExpression(InitVal); return nullptr; } diff --git a/llvm/test/Transforms/GVN/calloc-load-removal.ll b/llvm/test/Transforms/GVN/calloc-load-removal.ll index a80c308dc88c6..e275b917e77de 100644 --- a/llvm/test/Transforms/GVN/calloc-load-removal.ll +++ b/llvm/test/Transforms/GVN/calloc-load-removal.ll @@ -22,4 +22,28 @@ define i32 @test1() { } +define i32 @as_invoke(i1 %c) personality i32 (...)* undef { +bb3: + %mem = invoke noalias i8* @calloc(i64 1, i64 4) + to label %bb4 unwind label %bb1 + +bb1: + %lp = landingpad { i8*, i32 } cleanup + ret i32 0 + +bb4: + %mem.i32 = bitcast i8* %mem to i32* + ; This load is trivially constant zero + %res = load i32, i32* %mem.i32, align 4 + ret i32 %res + +; CHECK-LABEL: @as_invoke( +; CHECK-NOT: %3 = load i32, i32* %2, align 4 +; CHECK: ret i32 0 + +; CHECK_NO_LIBCALLS-LABEL: @as_invoke( +; CHECK_NO_LIBCALLS: load +; CHECK_NO_LIBCALLS: ret i32 % +} + declare noalias i8* @calloc(i64, i64)