diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h index fcd09744de2f6..ee1de77fc206d 100644 --- a/llvm/include/llvm/Analysis/MemoryBuiltins.h +++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h @@ -114,6 +114,16 @@ inline CallInst *isFreeCall(Value *I, const TargetLibraryInfo *TLI) { // Properties of allocation functions // +/// Return false if the allocation can have side effects on the program state +/// we are required to preserve beyond the effect of allocating a new object. +/// Ex: If our allocation routine has a counter for the number of objects +/// allocated, and the program prints it on exit, can the value change due +/// to optimization? Answer is highly language dependent. +/// Note: *Removable* really does mean removable; it does not mean observable. +/// A language (e.g. C++) can allow removing allocations without allowing +/// insertion or speculative execution of allocation routines. +bool isAllocRemovable(const CallBase *V, const TargetLibraryInfo *TLI); + /// Gets the alignment argument for an aligned_alloc-like function Value *getAllocAlignment(const CallBase *V, const TargetLibraryInfo *TLI); diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 213fdc5aacee9..48c01fbae11b4 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -298,6 +298,17 @@ bool llvm::isStrdupLikeFn(const Value *V, const TargetLibraryInfo *TLI) { return getAllocationData(V, StrDupLike, TLI).hasValue(); } +bool llvm::isAllocRemovable(const CallBase *CB, const TargetLibraryInfo *TLI) { + assert(isAllocationFn(CB, TLI)); + + // Note: Removability is highly dependent on the source language. For + // example, recent C++ requires direct calls to the global allocation + // [basic.stc.dynamic.allocation] to be observable unless part of a new + // expression [expr.new paragraph 13]. + + // Historically we've treated the C family allocation routines as removable + return isAllocLikeFn(CB, TLI); +} Value *llvm::getAllocAlignment(const CallBase *V, const TargetLibraryInfo *TLI) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 790424298b6ed..22c736f423f07 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2761,7 +2761,8 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) { Call, Builder.CreateBitOrPointerCast(ReturnedArg, CallTy)); } - if (isAllocLikeFn(&Call, &TLI)) + if (isAllocationFn(&Call, &TLI) && + isAllocRemovable(&cast(Call), &TLI)) return visitAllocSite(Call); // Handle intrinsics which can be used in both call and invoke context. diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 31329ae3fc8a1..6d5548d144145 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2696,6 +2696,8 @@ static bool isAllocSiteRemovable(Instruction *AI, } Instruction *InstCombinerImpl::visitAllocSite(Instruction &MI) { + assert(isa(MI) || isAllocRemovable(&cast(MI), &TLI)); + // If we have a malloc call which is only used in any amount of comparisons to // null and free calls, delete the calls and replace the comparisons with true // or false as appropriate. diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index efc53968b1e94..3ea82e10ba93d 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -492,7 +492,7 @@ bool llvm::wouldInstructionBeTriviallyDead(Instruction *I, } } - if (isAllocLikeFn(I, TLI)) + if (isAllocationFn(I, TLI) && isAllocRemovable(cast(I), TLI)) return true; if (CallInst *CI = isFreeCall(I, TLI))