[llvm] 0be9cf2 - [Attributor] Add "free"-based heap2stack deduction
Johannes Doerfert via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 30 18:58:22 PDT 2019
Author: Johannes Doerfert
Date: 2019-10-30T20:57:57-05:00
New Revision: 0be9cf2da9c1400eea720f0c6bead3df07c98a9c
URL: https://github.com/llvm/llvm-project/commit/0be9cf2da9c1400eea720f0c6bead3df07c98a9c
DIFF: https://github.com/llvm/llvm-project/commit/0be9cf2da9c1400eea720f0c6bead3df07c98a9c.diff
LOG: [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
Added:
Modified:
llvm/include/llvm/Analysis/MustExecute.h
llvm/lib/Transforms/IPO/Attributor.cpp
llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll
llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h
index 87cf9f85c7f1..045171b706f1 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 567ec7829500..11a4939da781 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
diff erent 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 876760a80515..04313cf0e774 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 72de6fb282b0..8cb1ec2eb4eb 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
}
More information about the llvm-commits
mailing list