diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -4020,7 +4020,26 @@ 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(); + auto EIt = Explorer.begin(I.getNextNode()), + EEnd = Explorer.end(I.getNextNode()); + bool Found = EIt.count(UniqueFree); + while (!Found && ++EIt != EEnd) + Found = EIt.getCurrentInst() == UniqueFree; + return Found; + }; + auto UsesCheck = [&](Instruction &I) { + bool ValidUsesOnly = true; + bool MustUse = true; + SmallPtrSet Visited; SmallVector Worklist; @@ -4039,9 +4058,10 @@ if (auto *SI = dyn_cast(UserI)) { if (SI->getValueOperand() == U->get()) { LLVM_DEBUG(dbgs() << "[H2S] escaping store to memory: " << *UserI << "\n"); - return false; + ValidUsesOnly = false; + } else { + // A store into the malloc'ed memory is fine. } - // A store into the malloc'ed memory is fine. continue; } @@ -4059,8 +4079,14 @@ // Record malloc. if (isFreeCall(UserI, TLI)) { - FreesForMalloc[&I].insert( - cast(const_cast(UserI))); + if (MustUse) { + FreesForMalloc[&I].insert( + cast(const_cast(UserI))); + } else { + LLVM_DEBUG(dbgs() << "[H2S] free potentially on different mallocs: " + << *UserI << "\n"); + ValidUsesOnly = false; + } continue; } @@ -4074,22 +4100,25 @@ if (!NoCaptureAA.isAssumedNoCapture() || !NoFreeAA.isAssumedNoFree()) { LLVM_DEBUG(dbgs() << "[H2S] Bad user: " << *UserI << "\n"); - return false; + ValidUsesOnly = false; } continue; } - if (isa(UserI) || isa(UserI)) { + if (isa(UserI) || isa(UserI) || + isa(UserI) || isa(UserI)) { + MustUse &= !(isa(UserI) || isa(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) { @@ -4106,7 +4135,7 @@ if (IsMalloc) { if (auto *Size = dyn_cast(I.getOperand(0))) if (Size->getValue().sle(MaxHeapToStackSize)) - if (UsesCheck(I)) { + if (UsesCheck(I) || FreeCheck(I)) { MallocCalls.insert(&I); return true; } @@ -4116,7 +4145,7 @@ if (auto *Size = dyn_cast(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; } @@ -4142,8 +4171,10 @@ /// 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 --- a/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll +++ b/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll @@ -8,7 +8,7 @@ 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 @@ } ; 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 @@ %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 +}