LLVM 20.0.0git
IRPartitionLayer.cpp
Go to the documentation of this file.
1//===----- IRPartitionLayer.cpp - Partition IR module into submodules -----===//
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
12
13using namespace llvm;
14using namespace llvm::orc;
15
17 StringRef Suffix,
18 GVPredicate ShouldExtract) {
19
20 auto DeleteExtractedDefs = [](GlobalValue &GV) {
21 // Bump the linkage: this global will be provided by the external module.
22 GV.setLinkage(GlobalValue::ExternalLinkage);
23
24 // Delete the definition in the source module.
25 if (isa<Function>(GV)) {
26 auto &F = cast<Function>(GV);
27 F.deleteBody();
28 F.setPersonalityFn(nullptr);
29 } else if (isa<GlobalVariable>(GV)) {
30 cast<GlobalVariable>(GV).setInitializer(nullptr);
31 } else if (isa<GlobalAlias>(GV)) {
32 // We need to turn deleted aliases into function or variable decls based
33 // on the type of their aliasee.
34 auto &A = cast<GlobalAlias>(GV);
35 Constant *Aliasee = A.getAliasee();
36 assert(A.hasName() && "Anonymous alias?");
37 assert(Aliasee->hasName() && "Anonymous aliasee");
38 std::string AliasName = std::string(A.getName());
39
40 if (isa<Function>(Aliasee)) {
41 auto *F = cloneFunctionDecl(*A.getParent(), *cast<Function>(Aliasee));
42 A.replaceAllUsesWith(F);
43 A.eraseFromParent();
44 F->setName(AliasName);
45 } else if (isa<GlobalVariable>(Aliasee)) {
46 auto *G = cloneGlobalVariableDecl(*A.getParent(),
47 *cast<GlobalVariable>(Aliasee));
48 A.replaceAllUsesWith(G);
49 A.eraseFromParent();
50 G->setName(AliasName);
51 } else
52 llvm_unreachable("Alias to unsupported type");
53 } else
54 llvm_unreachable("Unsupported global type");
55 };
56
57 auto NewTSM = cloneToNewContext(TSM, ShouldExtract, DeleteExtractedDefs);
58 NewTSM.withModuleDo([&](Module &M) {
59 M.setModuleIdentifier((M.getModuleIdentifier() + Suffix).str());
60 });
61
62 return NewTSM;
63}
64
65namespace llvm {
66namespace orc {
67
69public:
73 IRPartitionLayer &Parent)
74 : IRMaterializationUnit(ES, MO, std::move(TSM)), Parent(Parent) {}
75
81 Parent(Parent) {}
82
83private:
84 void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
85 Parent.emitPartition(std::move(R), std::move(TSM),
86 std::move(SymbolToDefinition));
87 }
88
89 void discard(const JITDylib &V, const SymbolStringPtr &Name) override {
90 // All original symbols were materialized by the CODLayer and should be
91 // final. The function bodies provided by M should never be overridden.
92 llvm_unreachable("Discard should never be called on an "
93 "ExtractingIRMaterializationUnit");
94 }
95
96 IRPartitionLayer &Parent;
97};
98
99} // namespace orc
100} // namespace llvm
101
103 : IRLayer(ES, BaseLayer.getManglingOptions()), BaseLayer(BaseLayer) {}
104
106 this->Partition = Partition;
107}
108
109std::optional<IRPartitionLayer::GlobalValueSet>
111 return std::move(Requested);
112}
113
114std::optional<IRPartitionLayer::GlobalValueSet>
116 return std::nullopt;
117}
118
119void IRPartitionLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
120 ThreadSafeModule TSM) {
121 assert(TSM && "Null module");
122
123 auto &ES = getExecutionSession();
124 TSM.withModuleDo([&](Module &M) {
125 // First, do some cleanup on the module:
126 cleanUpModule(M);
127 });
128
129 // Create a partitioning materialization unit and pass the responsibility.
130 if (auto Err = R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
131 ES, *getManglingOptions(), std::move(TSM), *this))) {
132 ES.reportError(std::move(Err));
133 R->failMaterialization();
134 return;
135 }
136}
137
138void IRPartitionLayer::cleanUpModule(Module &M) {
139 for (auto &F : M.functions()) {
140 if (F.isDeclaration())
141 continue;
142
143 if (F.hasAvailableExternallyLinkage()) {
144 F.deleteBody();
145 F.setPersonalityFn(nullptr);
146 continue;
147 }
148 }
149}
150
151void IRPartitionLayer::expandPartition(GlobalValueSet &Partition) {
152 // Expands the partition to ensure the following rules hold:
153 // (1) If any alias is in the partition, its aliasee is also in the partition.
154 // (2) If any aliasee is in the partition, its aliases are also in the
155 // partiton.
156 // (3) If any global variable is in the partition then all global variables
157 // are in the partition.
158 assert(!Partition.empty() && "Unexpected empty partition");
159
160 const Module &M = *(*Partition.begin())->getParent();
161 bool ContainsGlobalVariables = false;
162 std::vector<const GlobalValue *> GVsToAdd;
163
164 for (const auto *GV : Partition)
165 if (isa<GlobalAlias>(GV))
166 GVsToAdd.push_back(
167 cast<GlobalValue>(cast<GlobalAlias>(GV)->getAliasee()));
168 else if (isa<GlobalVariable>(GV))
169 ContainsGlobalVariables = true;
170
171 for (auto &A : M.aliases())
172 if (Partition.count(cast<GlobalValue>(A.getAliasee())))
173 GVsToAdd.push_back(&A);
174
175 if (ContainsGlobalVariables)
176 for (auto &G : M.globals())
177 GVsToAdd.push_back(&G);
178
179 for (const auto *GV : GVsToAdd)
180 Partition.insert(GV);
181}
182
183void IRPartitionLayer::emitPartition(
184 std::unique_ptr<MaterializationResponsibility> R, ThreadSafeModule TSM,
186
187 // FIXME: Need a 'notify lazy-extracting/emitting' callback to tie the
188 // extracted module key, extracted module, and source module key
189 // together. This could be used, for example, to provide a specific
190 // memory manager instance to the linking layer.
191
192 auto &ES = getExecutionSession();
193 GlobalValueSet RequestedGVs;
194 for (auto &Name : R->getRequestedSymbols()) {
195 if (Name == R->getInitializerSymbol())
196 TSM.withModuleDo([&](Module &M) {
197 for (auto &GV : getStaticInitGVs(M))
198 RequestedGVs.insert(&GV);
199 });
200 else {
201 assert(Defs.count(Name) && "No definition for symbol");
202 RequestedGVs.insert(Defs[Name]);
203 }
204 }
205
206 /// Perform partitioning with the context lock held, since the partition
207 /// function is allowed to access the globals to compute the partition.
208 auto GVsToExtract =
209 TSM.withModuleDo([&](Module &M) { return Partition(RequestedGVs); });
210
211 // Take a 'None' partition to mean the whole module (as opposed to an empty
212 // partition, which means "materialize nothing"). Emit the whole module
213 // unmodified to the base layer.
214 if (GVsToExtract == std::nullopt) {
215 Defs.clear();
216 BaseLayer.emit(std::move(R), std::move(TSM));
217 return;
218 }
219
220 // If the partition is empty, return the whole module to the symbol table.
221 if (GVsToExtract->empty()) {
222 if (auto Err =
223 R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
224 std::move(TSM),
225 MaterializationUnit::Interface(R->getSymbols(),
226 R->getInitializerSymbol()),
227 std::move(Defs), *this))) {
228 getExecutionSession().reportError(std::move(Err));
229 R->failMaterialization();
230 return;
231 }
232 return;
233 }
234
235 // Ok -- we actually need to partition the symbols. Promote the symbol
236 // linkages/names, expand the partition to include any required symbols
237 // (i.e. symbols that can't be separated from our partition), and
238 // then extract the partition.
239 //
240 // FIXME: We apply this promotion once per partitioning. It's safe, but
241 // overkill.
242 auto ExtractedTSM = TSM.withModuleDo([&](Module &M)
244 auto PromotedGlobals = PromoteSymbols(M);
245 if (!PromotedGlobals.empty()) {
246
247 MangleAndInterner Mangle(ES, M.getDataLayout());
249 IRSymbolMapper::add(ES, *getManglingOptions(), PromotedGlobals,
250 SymbolFlags);
251
252 if (auto Err = R->defineMaterializing(SymbolFlags))
253 return std::move(Err);
254 }
255
256 expandPartition(*GVsToExtract);
257
258 // Submodule name is given by hashing the names of the globals.
259 std::string SubModuleName;
260 {
261 std::vector<const GlobalValue *> HashGVs;
262 HashGVs.reserve(GVsToExtract->size());
263 for (const auto *GV : *GVsToExtract)
264 HashGVs.push_back(GV);
265 llvm::sort(HashGVs, [](const GlobalValue *LHS, const GlobalValue *RHS) {
266 return LHS->getName() < RHS->getName();
267 });
268 hash_code HC(0);
269 for (const auto *GV : HashGVs) {
270 assert(GV->hasName() && "All GVs to extract should be named by now");
271 auto GVName = GV->getName();
272 HC = hash_combine(HC, hash_combine_range(GVName.begin(), GVName.end()));
273 }
274 raw_string_ostream(SubModuleName)
275 << ".submodule."
276 << formatv(sizeof(size_t) == 8 ? "{0:x16}" : "{0:x8}",
277 static_cast<size_t>(HC))
278 << ".ll";
279 }
280
281 // Extract the requested partiton (plus any necessary aliases) and
282 // put the rest back into the impl dylib.
283 auto ShouldExtract = [&](const GlobalValue &GV) -> bool {
284 return GVsToExtract->count(&GV);
285 };
286
287 return extractSubModule(TSM, SubModuleName, ShouldExtract);
288 });
289
290 if (!ExtractedTSM) {
291 ES.reportError(ExtractedTSM.takeError());
292 R->failMaterialization();
293 return;
294 }
295
296 if (auto Err = R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
297 ES, *getManglingOptions(), std::move(TSM), *this))) {
298 ES.reportError(std::move(Err));
299 R->failMaterialization();
300 return;
301 }
302 BaseLayer.emit(std::move(R), std::move(*ExtractedTSM));
303}
static const Function * getParent(const Value *V)
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
std::string Name
static ThreadSafeModule extractSubModule(ThreadSafeModule &TSM, StringRef Suffix, GVPredicate ShouldExtract)
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Value * RHS
Value * LHS
This is an important base class in LLVM.
Definition: Constant.h:42
Tagged union holding either a T or a Error.
Definition: Error.h:481
Function::iterator insert(Function::iterator Position, BasicBlock *BB)
Insert BB in the basic block list at Position.
Definition: Function.h:754
@ ExternalLinkage
Externally visible function.
Definition: GlobalValue.h:52
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
bool hasName() const
Definition: Value.h:261
StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:309
An opaque object representing a hash code.
Definition: Hashing.h:75
An ExecutionSession represents a running JIT program.
Definition: Core.h:1339
void reportError(Error Err)
Report a error for this execution session.
Definition: Core.h:1474
Interface for layers that accept LLVM IR.
Definition: Layer.h:67
virtual void emit(std::unique_ptr< MaterializationResponsibility > R, ThreadSafeModule TSM)=0
Emit should materialize the given IR.
ExecutionSession & getExecutionSession()
Returns the ExecutionSession for this layer.
Definition: Layer.h:75
const IRSymbolMapper::ManglingOptions *& getManglingOptions() const
Get the mangling options for this layer.
Definition: Layer.h:78
IRMaterializationUnit is a convenient base class for MaterializationUnits wrapping LLVM IR.
Definition: Layer.h:31
SymbolNameToDefinitionMap SymbolToDefinition
Definition: Layer.h:57
ThreadSafeModule TSM
Definition: Layer.h:56
std::map< SymbolStringPtr, GlobalValue * > SymbolNameToDefinitionMap
Definition: Layer.h:33
A layer that breaks up IR modules into smaller submodules that only contains looked up symbols.
void emit(std::unique_ptr< MaterializationResponsibility > R, ThreadSafeModule TSM) override
Emits the given module.
static std::optional< GlobalValueSet > compileWholeModule(GlobalValueSet Requested)
Off-the-shelf partitioning which compiles whole modules whenever any symbol in them is requested.
std::function< std::optional< GlobalValueSet >(GlobalValueSet Requested)> PartitionFunction
Partitioning function.
std::set< const GlobalValue * > GlobalValueSet
IRPartitionLayer(ExecutionSession &ES, IRLayer &BaseLayer)
Construct a IRPartitionLayer.
void setPartitionFunction(PartitionFunction Partition)
Sets the partition function.
static std::optional< GlobalValueSet > compileRequested(GlobalValueSet Requested)
Off-the-shelf partitioning which compiles all requested symbols (usually a single function at a time)...
static void add(ExecutionSession &ES, const ManglingOptions &MO, ArrayRef< GlobalValue * > GVs, SymbolFlagsMap &SymbolFlags, SymbolNameToDefinitionMap *SymbolToDefinition=nullptr)
Add mangled symbols for the given GlobalValues to SymbolFlags.
Definition: Mangling.cpp:30
Represents a JIT'd dynamic library.
Definition: Core.h:897
Mangles symbol names then uniques them in the context of an ExecutionSession.
Definition: Mangling.h:26
PartitioningIRMaterializationUnit(ThreadSafeModule TSM, Interface I, SymbolNameToDefinitionMap SymbolToDefinition, IRPartitionLayer &Parent)
PartitioningIRMaterializationUnit(ExecutionSession &ES, const IRSymbolMapper::ManglingOptions &MO, ThreadSafeModule TSM, IRPartitionLayer &Parent)
Pointer to a pooled string representing a symbol name.
An LLVM Module together with a shared ThreadSafeContext.
decltype(auto) withModuleDo(Func &&F)
Locks the associated ThreadSafeContext and calls the given function on the contained Module.
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:661
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
SymbolFlags
Symbol flags.
Definition: Symbol.h:24
iterator_range< StaticInitGVIterator > getStaticInitGVs(Module &M)
Create an iterator range over the GlobalValues that contribute to static initialization.
std::function< bool(const GlobalValue &)> GVPredicate
GlobalVariable * cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV, ValueToValueMapTy *VMap=nullptr)
Clone a global variable declaration into a new module.
Function * cloneFunctionDecl(Module &Dst, const Function &F, ValueToValueMapTy *VMap=nullptr)
Clone a function declaration into a new module.
ThreadSafeModule cloneToNewContext(const ThreadSafeModule &TSMW, GVPredicate ShouldCloneDef=GVPredicate(), GVModifier UpdateClonedDefSource=GVModifier())
Clones the given module on to a new context.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void sort(IteratorTy Start, IteratorTy End)
Definition: STLExtras.h:1664
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1873
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition: Hashing.h:590
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last)
Compute a hash_code for a sequence of values.
Definition: Hashing.h:468
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858