diff options
| author | Johannes Doerfert <johannes@jdoerfert.de> | 2019-10-14 17:29:05 -0500 |
|---|---|---|
| committer | Johannes Doerfert <johannes@jdoerfert.de> | 2019-10-30 20:57:57 -0500 |
| commit | 0be9cf2da9c1400eea720f0c6bead3df07c98a9c (patch) | |
| tree | b167c09aef7de64ee195f56869983a646c9210aa | |
| parent | ed7bcb2cb1575d26bd9161103fae01d1a5fa4b07 (diff) | |
| download | bcm5719-llvm-0be9cf2da9c1400eea720f0c6bead3df07c98a9c.tar.gz bcm5719-llvm-0be9cf2da9c1400eea720f0c6bead3df07c98a9c.zip | |
[Attributor] Add "free"-based heap2stack deduction
Summary:
If there is a unique free of the allocated that has to be reached from
the malloc, we can apply the heap-2-stack transformation even if the
pointer escapes.
Reviewers: hfinkel, sstefan1, uenoku
Subscribers: hiraditya, bollu, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D68958
| -rw-r--r-- | llvm/include/llvm/Analysis/MustExecute.h | 24 | ||||
| -rw-r--r-- | llvm/lib/Transforms/IPO/Attributor.cpp | 61 | ||||
| -rw-r--r-- | llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll | 28 | ||||
| -rw-r--r-- | llvm/test/Transforms/FunctionAttrs/noalias_returned.ll | 20 |
4 files changed, 101 insertions, 32 deletions
diff --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h index 87cf9f85c7f..045171b706f 100644 --- a/llvm/include/llvm/Analysis/MustExecute.h +++ b/llvm/include/llvm/Analysis/MustExecute.h @@ -420,6 +420,30 @@ struct MustBeExecutedContextExplorer { } ///} + /// Helper to look for \p I in the context of \p PP. + /// + /// The context is expanded until \p I was found or no more expansion is + /// possible. + /// + /// \returns True, iff \p I was found. + bool findInContextOf(const Instruction *I, const Instruction *PP) { + auto EIt = begin(PP), EEnd = end(PP); + return findInContextOf(I, EIt, EEnd); + } + + /// Helper to look for \p I in the context defined by \p EIt and \p EEnd. + /// + /// The context is expanded until \p I was found or no more expansion is + /// possible. + /// + /// \returns True, iff \p I was found. + bool findInContextOf(const Instruction *I, iterator &EIt, iterator &EEnd) { + bool Found = EIt.count(I); + while (!Found && EIt != EEnd) + Found = (++EIt).getCurrentInst() == I; + return Found; + } + /// Return the next instruction that is guaranteed to be executed after \p PP. /// /// \param It The iterator that is used to traverse the must be diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index 567ec782950..11a4939da78 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -728,12 +728,10 @@ struct AAFromMustBeExecutedContext : public Base { SetVector<const Use *> NextUses; + auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI); for (const Use *U : Uses) { if (const Instruction *UserI = dyn_cast<Instruction>(U->getUser())) { - auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI); - bool Found = EIt.count(UserI); - while (!Found && ++EIt != EEnd) - Found = EIt.getCurrentInst() == UserI; + bool Found = Explorer.findInContextOf(UserI, EIt, EEnd); if (Found && Base::followUse(A, U, UserI)) for (const Use &Us : UserI->uses()) NextUses.insert(&Us); @@ -3634,7 +3632,21 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { const Function *F = getAssociatedFunction(); const auto *TLI = A.getInfoCache().getTargetLibraryInfoForFunction(*F); + MustBeExecutedContextExplorer &Explorer = + A.getInfoCache().getMustBeExecutedContextExplorer(); + + auto FreeCheck = [&](Instruction &I) { + const auto &Frees = FreesForMalloc.lookup(&I); + if (Frees.size() != 1) + return false; + Instruction *UniqueFree = *Frees.begin(); + return Explorer.findInContextOf(UniqueFree, I.getNextNode()); + }; + auto UsesCheck = [&](Instruction &I) { + bool ValidUsesOnly = true; + bool MustUse = true; + SmallPtrSet<const Use *, 8> Visited; SmallVector<const Use *, 8> Worklist; @@ -3652,10 +3664,12 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { continue; if (auto *SI = dyn_cast<StoreInst>(UserI)) { if (SI->getValueOperand() == U->get()) { - LLVM_DEBUG(dbgs() << "[H2S] escaping store to memory: " << *UserI << "\n"); - return false; + LLVM_DEBUG(dbgs() + << "[H2S] escaping store to memory: " << *UserI << "\n"); + ValidUsesOnly = false; + } else { + // A store into the malloc'ed memory is fine. } - // A store into the malloc'ed memory is fine. continue; } @@ -3673,8 +3687,14 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { // Record malloc. if (isFreeCall(UserI, TLI)) { - FreesForMalloc[&I].insert( - cast<Instruction>(const_cast<User *>(UserI))); + if (MustUse) { + FreesForMalloc[&I].insert( + cast<Instruction>(const_cast<User *>(UserI))); + } else { + LLVM_DEBUG(dbgs() << "[H2S] free potentially on different mallocs: " + << *UserI << "\n"); + ValidUsesOnly = false; + } continue; } @@ -3688,22 +3708,25 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { if (!NoCaptureAA.isAssumedNoCapture() || !NoFreeAA.isAssumedNoFree()) { LLVM_DEBUG(dbgs() << "[H2S] Bad user: " << *UserI << "\n"); - return false; + ValidUsesOnly = false; } continue; } - if (isa<GetElementPtrInst>(UserI) || isa<BitCastInst>(UserI)) { + if (isa<GetElementPtrInst>(UserI) || isa<BitCastInst>(UserI) || + isa<PHINode>(UserI) || isa<SelectInst>(UserI)) { + MustUse &= !(isa<PHINode>(UserI) || isa<SelectInst>(UserI)); for (Use &U : UserI->uses()) Worklist.push_back(&U); continue; } - // Unknown user. + // Unknown user for which we can not track uses further (in a way that + // makes sense). LLVM_DEBUG(dbgs() << "[H2S] Unknown user: " << *UserI << "\n"); - return false; + ValidUsesOnly = false; } - return true; + return ValidUsesOnly; }; auto MallocCallocCheck = [&](Instruction &I) { @@ -3720,7 +3743,7 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { if (IsMalloc) { if (auto *Size = dyn_cast<ConstantInt>(I.getOperand(0))) if (Size->getValue().sle(MaxHeapToStackSize)) - if (UsesCheck(I)) { + if (UsesCheck(I) || FreeCheck(I)) { MallocCalls.insert(&I); return true; } @@ -3730,7 +3753,7 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) { if (auto *Size = dyn_cast<ConstantInt>(I.getOperand(1))) if ((Size->getValue().umul_ov(Num->getValue(), Overflow)) .sle(MaxHeapToStackSize)) - if (!Overflow && UsesCheck(I)) { + if (!Overflow && (UsesCheck(I) || FreeCheck(I))) { MallocCalls.insert(&I); return true; } @@ -3756,8 +3779,10 @@ struct AAHeapToStackFunction final : public AAHeapToStackImpl { /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECL(MallocCalls, Function, - "Number of MallocCalls converted to allocas"); - BUILD_STAT_NAME(MallocCalls, Function) += MallocCalls.size(); + "Number of malloc calls converted to allocas"); + for (auto *C : MallocCalls) + if (!BadMallocCalls.count(C)) + ++BUILD_STAT_NAME(MallocCalls, Function); } }; diff --git a/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll b/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll index 876760a8051..04313cf0e77 100644 --- a/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll +++ b/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll @@ -8,7 +8,7 @@ declare void @func_throws(...) declare void @sync_func(i8* %p) -declare void @sync_will_return(i8* %p) willreturn +declare void @sync_will_return(i8* %p) willreturn nounwind declare void @no_sync_func(i8* nocapture %p) nofree nosync willreturn @@ -202,11 +202,11 @@ define i32 @test_lifetime() { } ; TEST 11 -; FIXME: should be ok define void @test11() { %1 = tail call noalias i8* @malloc(i64 4) - ; CHECK: @malloc(i64 4) + ; CHECK: test11 + ; CHECK-NEXT: alloc ; CHECK-NEXT: @sync_will_return(i8* %1) tail call void @sync_will_return(i8* %1) tail call void @free(i8* %1) @@ -330,9 +330,29 @@ define void @test16b(i8 %v, i8** %P) { %1 = tail call noalias i8* @malloc(i64 4) ; CHECK-NEXT: store i8* %1, i8** %P store i8* %1, i8** %P - ; CHECK-NEXT: @no_sync_func(i8* %1) + ; CHECK-NEXT: @no_sync_func(i8* nocapture %1) tail call void @no_sync_func(i8* %1) ; CHECK-NEXT: @free(i8* %1) tail call void @free(i8* %1) ret void } + +define void @test16c(i8 %v, i8** %P) { + ; CHECK: %1 = alloca + %1 = tail call noalias i8* @malloc(i64 4) + ; CHECK-NEXT: store i8* %1, i8** %P + store i8* %1, i8** %P + ; CHECK-NEXT: @no_sync_func(i8* nocapture %1) + tail call void @no_sync_func(i8* %1) nounwind + ; CHECK-NOT: @free + tail call void @free(i8* %1) + ret void +} + +define void @test16d(i8 %v, i8** %P) { + ; CHECK: %1 = tail call noalias i8* @malloc(i64 4) + %1 = tail call noalias i8* @malloc(i64 4) + ; CHECK-NEXT: store i8* %1, i8** %P + store i8* %1, i8** %P + ret void +} diff --git a/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll b/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll index 72de6fb282b..8cb1ec2eb4e 100644 --- a/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll +++ b/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll @@ -204,7 +204,7 @@ define void @test12_1() { ; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nonnull align 4 dereferenceable(1) [[A]]) ; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nonnull align 4 dereferenceable(1) [[A]]) ; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nocapture [[B]]) -; CHECK-NEXT: tail call void @use_nocapture(i8* noalias [[B]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nocapture [[B]]) ; CHECK-NEXT: ret void ; %A = alloca i8, align 4 @@ -221,10 +221,10 @@ define void @test12_2(){ ; CHECK-NEXT: [[A:%.*]] = tail call noalias i8* @malloc(i64 4) ; FIXME: This should be @use_nocapture(i8* noalias [[A]]) ; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) -; FIXME: This should be @use_nocapture(i8* noalias [[A]]) -; CHECK-NEXT: tail call void @use_nocapture(i8* [[A]]) +; FIXME: This should be @use_nocapture(i8* noalias nocapture [[A]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) ; CHECK-NEXT: tail call void @use(i8* [[A]]) -; CHECK-NEXT: tail call void @use_nocapture(i8* [[A]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) ; CHECK-NEXT: ret void ; %A = tail call noalias i8* @malloc(i64 4) @@ -239,7 +239,7 @@ declare void @two_args(i8* nocapture , i8* nocapture) define void @test12_3(){ ; CHECK-LABEL: @test12_3( %A = tail call noalias i8* @malloc(i64 4) -; CHECK: tail call void @two_args(i8* nocapture %A, i8* %A) +; CHECK: tail call void @two_args(i8* nocapture %A, i8* nocapture %A) tail call void @two_args(i8* %A, i8* %A) ret void } @@ -252,17 +252,17 @@ define void @test12_4(){ %A_1 = getelementptr i8, i8* %A, i64 1 %B_0 = getelementptr i8, i8* %B, i64 0 -; CHECK: tail call void @two_args(i8* noalias %A, i8* noalias %B) +; CHECK: tail call void @two_args(i8* noalias nocapture %A, i8* noalias nocapture %B) tail call void @two_args(i8* %A, i8* %B) -; CHECK: tail call void @two_args(i8* %A, i8* nocapture %A_0) +; CHECK: tail call void @two_args(i8* nocapture %A, i8* nocapture %A_0) tail call void @two_args(i8* %A, i8* %A_0) -; CHECK: tail call void @two_args(i8* %A, i8* %A_1) +; CHECK: tail call void @two_args(i8* nocapture %A, i8* nocapture %A_1) tail call void @two_args(i8* %A, i8* %A_1) -; FIXME: This should be @two_args(i8* noalias %A_0, i8* noalias %B_0) -; CHECK: tail call void @two_args(i8* %A_0, i8* nocapture %B_0) +; FIXME: This should be @two_args(i8* noalias nocapture %A_0, i8* noalias nocapture %B_0) +; CHECK: tail call void @two_args(i8* nocapture %A_0, i8* nocapture %B_0) tail call void @two_args(i8* %A_0, i8* %B_0) ret void } |

