1 | //===- AlwaysInliner.cpp - Code to inline always_inline functions ----------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements a custom inliner that handles only functions that |
10 | // are marked as "always inline". |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Transforms/IPO/AlwaysInliner.h" |
15 | #include "llvm/ADT/SetVector.h" |
16 | #include "llvm/Analysis/AliasAnalysis.h" |
17 | #include "llvm/Analysis/AssumptionCache.h" |
18 | #include "llvm/Analysis/InlineAdvisor.h" |
19 | #include "llvm/Analysis/InlineCost.h" |
20 | #include "llvm/Analysis/OptimizationRemarkEmitter.h" |
21 | #include "llvm/Analysis/ProfileSummaryInfo.h" |
22 | #include "llvm/IR/Module.h" |
23 | #include "llvm/InitializePasses.h" |
24 | #include "llvm/Transforms/Utils/Cloning.h" |
25 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
26 | |
27 | using namespace llvm; |
28 | |
29 | #define DEBUG_TYPE "inline" |
30 | |
31 | namespace { |
32 | |
33 | bool AlwaysInlineImpl( |
34 | Module &M, bool InsertLifetime, ProfileSummaryInfo &PSI, |
35 | FunctionAnalysisManager *FAM, |
36 | function_ref<AssumptionCache &(Function &)> GetAssumptionCache, |
37 | function_ref<AAResults &(Function &)> GetAAR) { |
38 | SmallSetVector<CallBase *, 16> Calls; |
39 | bool Changed = false; |
40 | SmallVector<Function *, 16> InlinedComdatFunctions; |
41 | |
42 | for (Function &F : make_early_inc_range(Range&: M)) { |
43 | if (F.isPresplitCoroutine()) |
44 | continue; |
45 | |
46 | if (F.isDeclaration() || !isInlineViable(Callee&: F).isSuccess()) |
47 | continue; |
48 | |
49 | Calls.clear(); |
50 | |
51 | for (User *U : F.users()) |
52 | if (auto *CB = dyn_cast<CallBase>(Val: U)) |
53 | if (CB->getCalledFunction() == &F && |
54 | CB->hasFnAttr(Kind: Attribute::AlwaysInline) && |
55 | !CB->getAttributes().hasFnAttr(Kind: Attribute::NoInline)) |
56 | Calls.insert(X: 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, nullptr, nullptr); |
65 | InlineResult Res = InlineFunction(CB&: *CB, IFI, /*MergeAttributes=*/true, |
66 | CalleeAAR: &GetAAR(F), InsertLifetime); |
67 | if (!Res.isSuccess()) { |
68 | ORE.emit(RemarkBuilder: [&]() { |
69 | return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined" , DLoc, Block) |
70 | << "'" << ore::NV("Callee" , &F) << "' is not inlined into '" |
71 | << ore::NV("Caller" , Caller) |
72 | << "': " << ore::NV("Reason" , Res.getFailureReason()); |
73 | }); |
74 | continue; |
75 | } |
76 | |
77 | emitInlinedIntoBasedOnCost( |
78 | ORE, DLoc, Block, Callee: F, Caller: *Caller, |
79 | IC: InlineCost::getAlways(Reason: "always inline attribute" ), |
80 | /*ForProfileContext=*/false, DEBUG_TYPE); |
81 | |
82 | Changed = true; |
83 | if (FAM) |
84 | FAM->invalidate(IR&: *Caller, PA: PreservedAnalyses::none()); |
85 | } |
86 | |
87 | F.removeDeadConstantUsers(); |
88 | if (F.hasFnAttribute(Kind: Attribute::AlwaysInline) && F.isDefTriviallyDead()) { |
89 | // Remember to try and delete this function afterward. This allows to call |
90 | // filterDeadComdatFunctions() only once. |
91 | if (F.hasComdat()) { |
92 | InlinedComdatFunctions.push_back(Elt: &F); |
93 | } else { |
94 | if (FAM) |
95 | FAM->clear(IR&: F, Name: F.getName()); |
96 | M.getFunctionList().erase(IT&: F); |
97 | Changed = true; |
98 | } |
99 | } |
100 | } |
101 | |
102 | if (!InlinedComdatFunctions.empty()) { |
103 | // Now we just have the comdat functions. Filter out the ones whose comdats |
104 | // are not actually dead. |
105 | filterDeadComdatFunctions(DeadComdatFunctions&: InlinedComdatFunctions); |
106 | // The remaining functions are actually dead. |
107 | for (Function *F : InlinedComdatFunctions) { |
108 | if (FAM) |
109 | FAM->clear(IR&: *F, Name: F->getName()); |
110 | M.getFunctionList().erase(IT: F); |
111 | Changed = true; |
112 | } |
113 | } |
114 | |
115 | return Changed; |
116 | } |
117 | |
118 | struct AlwaysInlinerLegacyPass : public ModulePass { |
119 | bool InsertLifetime; |
120 | |
121 | AlwaysInlinerLegacyPass() |
122 | : AlwaysInlinerLegacyPass(/*InsertLifetime*/ true) {} |
123 | |
124 | AlwaysInlinerLegacyPass(bool InsertLifetime) |
125 | : ModulePass(ID), InsertLifetime(InsertLifetime) { |
126 | initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry()); |
127 | } |
128 | |
129 | /// Main run interface method. |
130 | bool runOnModule(Module &M) override { |
131 | |
132 | auto &PSI = getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); |
133 | auto GetAAR = [&](Function &F) -> AAResults & { |
134 | return getAnalysis<AAResultsWrapperPass>(F).getAAResults(); |
135 | }; |
136 | auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & { |
137 | return getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F); |
138 | }; |
139 | |
140 | return AlwaysInlineImpl(M, InsertLifetime, PSI, /*FAM=*/nullptr, |
141 | GetAssumptionCache, GetAAR); |
142 | } |
143 | |
144 | static char ID; // Pass identification, replacement for typeid |
145 | |
146 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
147 | AU.addRequired<AssumptionCacheTracker>(); |
148 | AU.addRequired<AAResultsWrapperPass>(); |
149 | AU.addRequired<ProfileSummaryInfoWrapperPass>(); |
150 | } |
151 | }; |
152 | |
153 | } // namespace |
154 | |
155 | char AlwaysInlinerLegacyPass::ID = 0; |
156 | INITIALIZE_PASS_BEGIN(AlwaysInlinerLegacyPass, "always-inline" , |
157 | "Inliner for always_inline functions" , false, false) |
158 | INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass) |
159 | INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) |
160 | INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) |
161 | INITIALIZE_PASS_END(AlwaysInlinerLegacyPass, "always-inline" , |
162 | "Inliner for always_inline functions" , false, false) |
163 | |
164 | Pass *llvm::createAlwaysInlinerLegacyPass(bool InsertLifetime) { |
165 | return new AlwaysInlinerLegacyPass(InsertLifetime); |
166 | } |
167 | |
168 | PreservedAnalyses AlwaysInlinerPass::run(Module &M, |
169 | ModuleAnalysisManager &MAM) { |
170 | FunctionAnalysisManager &FAM = |
171 | MAM.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager(); |
172 | auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & { |
173 | return FAM.getResult<AssumptionAnalysis>(IR&: F); |
174 | }; |
175 | auto GetAAR = [&](Function &F) -> AAResults & { |
176 | return FAM.getResult<AAManager>(IR&: F); |
177 | }; |
178 | auto &PSI = MAM.getResult<ProfileSummaryAnalysis>(IR&: M); |
179 | |
180 | bool Changed = AlwaysInlineImpl(M, InsertLifetime, PSI, FAM: &FAM, |
181 | GetAssumptionCache, GetAAR); |
182 | if (!Changed) |
183 | return PreservedAnalyses::all(); |
184 | |
185 | PreservedAnalyses PA; |
186 | // We have already invalidated all analyses on modified functions. |
187 | PA.preserveSet<AllAnalysesOn<Function>>(); |
188 | return PA; |
189 | } |
190 | |