summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Doerfert <johannes@jdoerfert.de>2019-10-14 17:29:05 -0500
committerJohannes Doerfert <johannes@jdoerfert.de>2019-10-30 20:57:57 -0500
commit0be9cf2da9c1400eea720f0c6bead3df07c98a9c (patch)
treeb167c09aef7de64ee195f56869983a646c9210aa
parented7bcb2cb1575d26bd9161103fae01d1a5fa4b07 (diff)
downloadbcm5719-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.h24
-rw-r--r--llvm/lib/Transforms/IPO/Attributor.cpp61
-rw-r--r--llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll28
-rw-r--r--llvm/test/Transforms/FunctionAttrs/noalias_returned.ll20
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
}
OpenPOWER on IntegriCloud