Skip to content

Commit 12c1156

Browse files
authored
[NFC][AlwaysInliner] Reduce AlwaysInliner memory consumption. (#96958)
Refactored AlwaysInliner to remove some of inlined functions earlier. Before the change AlwaysInliner walked through all functions in the module and inlined them into calls where it is appropriate. Removing of the dead inlined functions was performed only after all of inlining. For the test case from the issue [59126](#59126) compiler consumes all of the memory on 64GB machine, so is killed. The change checks if just inlined function can be removed from the module and removes it.
1 parent b558ac0 commit 12c1156

File tree

2 files changed

+55
-69
lines changed

2 files changed

+55
-69
lines changed

llvm/lib/Transforms/IPO/AlwaysInliner.cpp

Lines changed: 55 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
#include "llvm/ADT/SetVector.h"
1616
#include "llvm/Analysis/AliasAnalysis.h"
1717
#include "llvm/Analysis/AssumptionCache.h"
18+
#include "llvm/Analysis/InlineAdvisor.h"
1819
#include "llvm/Analysis/InlineCost.h"
1920
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
2021
#include "llvm/Analysis/ProfileSummaryInfo.h"
2122
#include "llvm/IR/Module.h"
2223
#include "llvm/InitializePasses.h"
23-
#include "llvm/Transforms/IPO/Inliner.h"
2424
#include "llvm/Transforms/Utils/Cloning.h"
2525
#include "llvm/Transforms/Utils/ModuleUtils.h"
2626

@@ -37,86 +37,73 @@ bool AlwaysInlineImpl(
3737
function_ref<BlockFrequencyInfo &(Function &)> GetBFI) {
3838
SmallSetVector<CallBase *, 16> Calls;
3939
bool Changed = false;
40-
SmallVector<Function *, 16> InlinedFunctions;
41-
for (Function &F : M) {
42-
// When callee coroutine function is inlined into caller coroutine function
43-
// before coro-split pass,
44-
// coro-early pass can not handle this quiet well.
45-
// So we won't inline the coroutine function if it have not been unsplited
40+
SmallVector<Function *, 16> InlinedComdatFunctions;
41+
42+
for (Function &F : make_early_inc_range(M)) {
4643
if (F.isPresplitCoroutine())
4744
continue;
4845

49-
if (!F.isDeclaration() && isInlineViable(F).isSuccess()) {
50-
Calls.clear();
51-
52-
for (User *U : F.users())
53-
if (auto *CB = dyn_cast<CallBase>(U))
54-
if (CB->getCalledFunction() == &F &&
55-
CB->hasFnAttr(Attribute::AlwaysInline) &&
56-
!CB->getAttributes().hasFnAttr(Attribute::NoInline))
57-
Calls.insert(CB);
58-
59-
for (CallBase *CB : Calls) {
60-
Function *Caller = CB->getCaller();
61-
OptimizationRemarkEmitter ORE(Caller);
62-
DebugLoc DLoc = CB->getDebugLoc();
63-
BasicBlock *Block = CB->getParent();
64-
65-
InlineFunctionInfo IFI(GetAssumptionCache, &PSI,
66-
GetBFI ? &GetBFI(*Caller) : nullptr,
67-
GetBFI ? &GetBFI(F) : nullptr);
68-
69-
InlineResult Res = InlineFunction(*CB, IFI, /*MergeAttributes=*/true,
70-
&GetAAR(F), InsertLifetime);
71-
if (!Res.isSuccess()) {
72-
ORE.emit([&]() {
73-
return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc,
74-
Block)
75-
<< "'" << ore::NV("Callee", &F) << "' is not inlined into '"
76-
<< ore::NV("Caller", Caller)
77-
<< "': " << ore::NV("Reason", Res.getFailureReason());
78-
});
79-
continue;
80-
}
81-
82-
emitInlinedIntoBasedOnCost(
83-
ORE, DLoc, Block, F, *Caller,
84-
InlineCost::getAlways("always inline attribute"),
85-
/*ForProfileContext=*/false, DEBUG_TYPE);
46+
if (F.isDeclaration() || !isInlineViable(F).isSuccess())
47+
continue;
8648

87-
Changed = true;
49+
Calls.clear();
50+
51+
for (User *U : F.users())
52+
if (auto *CB = dyn_cast<CallBase>(U))
53+
if (CB->getCalledFunction() == &F &&
54+
CB->hasFnAttr(Attribute::AlwaysInline) &&
55+
!CB->getAttributes().hasFnAttr(Attribute::NoInline))
56+
Calls.insert(CB);
57+
58+
for (CallBase *CB : Calls) {
59+
Function *Caller = CB->getCaller();
60+
OptimizationRemarkEmitter ORE(Caller);
61+
DebugLoc DLoc = CB->getDebugLoc();
62+
BasicBlock *Block = CB->getParent();
63+
64+
InlineFunctionInfo IFI(GetAssumptionCache, &PSI,
65+
GetBFI ? &GetBFI(*Caller) : nullptr,
66+
GetBFI ? &GetBFI(F) : nullptr);
67+
68+
InlineResult Res = InlineFunction(*CB, IFI, /*MergeAttributes=*/true,
69+
&GetAAR(F), InsertLifetime);
70+
if (!Res.isSuccess()) {
71+
ORE.emit([&]() {
72+
return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block)
73+
<< "'" << ore::NV("Callee", &F) << "' is not inlined into '"
74+
<< ore::NV("Caller", Caller)
75+
<< "': " << ore::NV("Reason", Res.getFailureReason());
76+
});
77+
continue;
8878
}
8979

90-
if (F.hasFnAttribute(Attribute::AlwaysInline)) {
91-
// Remember to try and delete this function afterward. This both avoids
92-
// re-walking the rest of the module and avoids dealing with any
93-
// iterator invalidation issues while deleting functions.
94-
InlinedFunctions.push_back(&F);
95-
}
80+
emitInlinedIntoBasedOnCost(
81+
ORE, DLoc, Block, F, *Caller,
82+
InlineCost::getAlways("always inline attribute"),
83+
/*ForProfileContext=*/false, DEBUG_TYPE);
84+
85+
Changed = true;
9686
}
97-
}
9887

99-
// Remove any live functions.
100-
erase_if(InlinedFunctions, [&](Function *F) {
101-
F->removeDeadConstantUsers();
102-
return !F->isDefTriviallyDead();
103-
});
104-
105-
// Delete the non-comdat ones from the module and also from our vector.
106-
auto NonComdatBegin = partition(
107-
InlinedFunctions, [&](Function *F) { return F->hasComdat(); });
108-
for (Function *F : make_range(NonComdatBegin, InlinedFunctions.end())) {
109-
M.getFunctionList().erase(F);
110-
Changed = true;
88+
F.removeDeadConstantUsers();
89+
if (F.hasFnAttribute(Attribute::AlwaysInline) && F.isDefTriviallyDead()) {
90+
// Remember to try and delete this function afterward. This allows to call
91+
// filterDeadComdatFunctions() only once.
92+
if (F.hasComdat()) {
93+
InlinedComdatFunctions.push_back(&F);
94+
} else {
95+
M.getFunctionList().erase(F);
96+
Changed = true;
97+
}
98+
}
11199
}
112-
InlinedFunctions.erase(NonComdatBegin, InlinedFunctions.end());
113100

114-
if (!InlinedFunctions.empty()) {
101+
if (!InlinedComdatFunctions.empty()) {
115102
// Now we just have the comdat functions. Filter out the ones whose comdats
116103
// are not actually dead.
117-
filterDeadComdatFunctions(InlinedFunctions);
104+
filterDeadComdatFunctions(InlinedComdatFunctions);
118105
// The remaining functions are actually dead.
119-
for (Function *F : InlinedFunctions) {
106+
for (Function *F : InlinedComdatFunctions) {
120107
M.getFunctionList().erase(F);
121108
Changed = true;
122109
}

llvm/test/Transforms/Inline/always-inline-phase-ordering.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ target triple = "arm64e-apple-macosx13"
66
; CHECK: remark: <unknown>:0:0: 'wibble' inlined into 'pluto' with (cost=always): always inline attribute
77
; CHECK: remark: <unknown>:0:0: 'snork' inlined into 'blam' with (cost=always): always inline attribute
88
; CHECK: remark: <unknown>:0:0: 'wobble' inlined into 'blam' with (cost=always): always inline attribute
9-
; CHECK: remark: <unknown>:0:0: 'wobble' inlined into 'snork' with (cost=always): always inline attribute
109
; CHECK: remark: <unknown>:0:0: 'spam' inlined into 'blam' with (cost=65, threshold=75)
1110
; CHECK: remark: <unknown>:0:0: 'wibble.1' inlined into 'widget' with (cost=30, threshold=75)
1211
; CHECK: remark: <unknown>:0:0: 'widget' inlined into 'bar.8' with (cost=30, threshold=75)

0 commit comments

Comments
 (0)