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
29using namespace llvm;
30
31#define DEBUG_TYPE "elim-avail-extern"
32
33static 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.
42static 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
48STATISTIC(NumRemovals, "Number of functions removed");
49STATISTIC(NumFunctionsConverted, "Number of functions converted");
50STATISTIC(NumGlobalVariablesConverted, "Number of global variables converted");
51STATISTIC(NumVariables, "Number of global variables removed");
52
53void deleteFunction(Function &F) {
54 // This will set the linkage to external
55 F.deleteBody();
56 ++NumRemovals;
57}
58
59static 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).
71static 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.
108static 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
115static 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
159PreservedAnalyses
160EliminateAvailableExternallyPass::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