1 | //===- ElimAvailExtern.cpp - DCE unreachable internal 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 transform is designed to eliminate available external global |
10 | // definitions from the program, turning them into declarations. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Transforms/IPO/ElimAvailExtern.h" |
15 | #include "llvm/ADT/STLExtras.h" |
16 | #include "llvm/ADT/Statistic.h" |
17 | #include "llvm/Analysis/CtxProfAnalysis.h" |
18 | #include "llvm/IR/Constant.h" |
19 | #include "llvm/IR/DebugInfoMetadata.h" |
20 | #include "llvm/IR/Function.h" |
21 | #include "llvm/IR/GlobalValue.h" |
22 | #include "llvm/IR/GlobalVariable.h" |
23 | #include "llvm/IR/MDBuilder.h" |
24 | #include "llvm/IR/Module.h" |
25 | #include "llvm/Support/CommandLine.h" |
26 | #include "llvm/Transforms/Utils/GlobalStatus.h" |
27 | #include "llvm/Transforms/Utils/ModuleUtils.h" |
28 | |
29 | using namespace llvm; |
30 | |
31 | #define DEBUG_TYPE "elim-avail-extern" |
32 | |
33 | static cl::opt<bool> ConvertToLocal( |
34 | "avail-extern-to-local" , cl::Hidden, |
35 | cl::desc("Convert available_externally into locals, renaming them " |
36 | "to avoid link-time clashes." )); |
37 | |
38 | // This option was originally introduced to correctly support the lowering of |
39 | // LDS variables for AMDGPU when ThinLTO is enabled. It can be utilized for |
40 | // other purposes, but make sure it is safe to do so, as privatizing global |
41 | // variables is generally not safe. |
42 | static cl::opt<unsigned> ConvertGlobalVariableInAddrSpace( |
43 | "avail-extern-gv-in-addrspace-to-local" , cl::Hidden, |
44 | cl::desc( |
45 | "Convert available_externally global variables into locals if they are " |
46 | "in specificed addrspace, renaming them to avoid link-time clashes." )); |
47 | |
48 | STATISTIC(NumRemovals, "Number of functions removed" ); |
49 | STATISTIC(NumFunctionsConverted, "Number of functions converted" ); |
50 | STATISTIC(NumGlobalVariablesConverted, "Number of global variables converted" ); |
51 | STATISTIC(NumVariables, "Number of global variables removed" ); |
52 | |
53 | void deleteFunction(Function &F) { |
54 | // This will set the linkage to external |
55 | F.deleteBody(); |
56 | ++NumRemovals; |
57 | } |
58 | |
59 | static std::string getNewName(Module &M, const GlobalValue &GV) { |
60 | return GV.getName().str() + ".__uniq" + getUniqueModuleId(M: &M); |
61 | } |
62 | |
63 | /// Create a copy of the thinlto import, mark it local, and redirect direct |
64 | /// calls to the copy. Only direct calls are replaced, so that e.g. indirect |
65 | /// call function pointer tests would use the global identity of the function. |
66 | /// |
67 | /// Currently, Value Profiling ("VP") MD_prof data isn't updated to refer to the |
68 | /// clone's GUID (which will be different, because the name and linkage is |
69 | /// different), under the assumption that the last consumer of this data is |
70 | /// upstream the pipeline (e.g. ICP). |
71 | static void convertToLocalCopy(Module &M, Function &F) { |
72 | assert(F.hasAvailableExternallyLinkage()); |
73 | assert(!F.isDeclaration()); |
74 | // If we can't find a single use that's a call, just delete the function. |
75 | if (F.uses().end() == llvm::find_if(Range: F.uses(), P: [&](Use &U) { |
76 | return isa<CallBase>(Val: U.getUser()); |
77 | })) |
78 | return deleteFunction(F); |
79 | |
80 | auto OrigName = F.getName().str(); |
81 | // Build a new name. We still need the old name (see below). |
82 | // We could just rely on internal linking allowing 2 modules have internal |
83 | // functions with the same name, but that just creates more trouble than |
84 | // necessary e.g. distinguishing profiles or debugging. Instead, we append the |
85 | // module identifier. |
86 | std::string NewName = getNewName(M, GV: F); |
87 | F.setName(NewName); |
88 | if (auto *SP = F.getSubprogram()) |
89 | SP->replaceLinkageName(LN: MDString::get(Context&: F.getParent()->getContext(), Str: NewName)); |
90 | |
91 | F.setLinkage(GlobalValue::InternalLinkage); |
92 | // Now make a declaration for the old name. We'll use it if there are non-call |
93 | // uses. For those, it would be incorrect to replace them with the local copy: |
94 | // for example, one such use could be taking the address of the function and |
95 | // passing it to an external function, which, in turn, might compare the |
96 | // function pointer to the original (non-local) function pointer, e.g. as part |
97 | // of indirect call promotion. |
98 | auto *Decl = |
99 | Function::Create(Ty: F.getFunctionType(), Linkage: GlobalValue::ExternalLinkage, |
100 | AddrSpace: F.getAddressSpace(), N: OrigName, M: F.getParent()); |
101 | F.replaceUsesWithIf(New: Decl, |
102 | ShouldReplace: [&](Use &U) { return !isa<CallBase>(Val: U.getUser()); }); |
103 | ++NumFunctionsConverted; |
104 | } |
105 | |
106 | /// Similar to the function above, this is to convert an externally available |
107 | /// global variable to local. |
108 | static void convertToLocalCopy(Module &M, GlobalVariable &GV) { |
109 | assert(GV.hasAvailableExternallyLinkage()); |
110 | GV.setName(getNewName(M, GV)); |
111 | GV.setLinkage(GlobalValue::InternalLinkage); |
112 | ++NumGlobalVariablesConverted; |
113 | } |
114 | |
115 | static bool eliminateAvailableExternally(Module &M, bool Convert) { |
116 | bool Changed = false; |
117 | |
118 | // If a global variable is available externally and in the specified address |
119 | // space, convert it to local linkage; otherwise, drop its initializer. |
120 | for (GlobalVariable &GV : M.globals()) { |
121 | if (!GV.hasAvailableExternallyLinkage()) |
122 | continue; |
123 | if (ConvertGlobalVariableInAddrSpace.getNumOccurrences() && |
124 | GV.getAddressSpace() == ConvertGlobalVariableInAddrSpace && |
125 | !GV.use_empty()) { |
126 | convertToLocalCopy(M, GV); |
127 | Changed = true; |
128 | continue; |
129 | } |
130 | if (GV.hasInitializer()) { |
131 | Constant *Init = GV.getInitializer(); |
132 | GV.setInitializer(nullptr); |
133 | if (isSafeToDestroyConstant(C: Init)) |
134 | Init->destroyConstant(); |
135 | } |
136 | GV.removeDeadConstantUsers(); |
137 | GV.setLinkage(GlobalValue::ExternalLinkage); |
138 | ++NumVariables; |
139 | Changed = true; |
140 | } |
141 | |
142 | // Drop the bodies of available externally functions. |
143 | for (Function &F : llvm::make_early_inc_range(Range&: M)) { |
144 | if (F.isDeclaration() || !F.hasAvailableExternallyLinkage()) |
145 | continue; |
146 | |
147 | if (Convert || ConvertToLocal) |
148 | convertToLocalCopy(M, F); |
149 | else |
150 | deleteFunction(F); |
151 | |
152 | F.removeDeadConstantUsers(); |
153 | Changed = true; |
154 | } |
155 | |
156 | return Changed; |
157 | } |
158 | |
159 | PreservedAnalyses |
160 | EliminateAvailableExternallyPass::run(Module &M, ModuleAnalysisManager &MAM) { |
161 | auto *CtxProf = MAM.getCachedResult<CtxProfAnalysis>(IR&: M); |
162 | // Convert to local instead of eliding if we use contextual profiling in this |
163 | // module. This is because the IPO decisions performed with contextual |
164 | // information will likely differ from decisions made without. For a function |
165 | // that's imported, its optimizations will, thus, differ, and be specialized |
166 | // for this contextual information. Eliding it in favor of the original would |
167 | // undo these optimizations. |
168 | if (!eliminateAvailableExternally( |
169 | M, /*Convert=*/(CtxProf && CtxProf->isInSpecializedModule()))) |
170 | return PreservedAnalyses::all(); |
171 | return PreservedAnalyses::none(); |
172 | } |
173 | |