1 //===----- CompileOnDemandLayer.cpp - Lazily emit IR on first call --------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
10 #include "llvm/ADT/Hashing.h"
11 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
12 #include "llvm/IR/Mangler.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/Support/FormatVariadic.h"
18 using namespace llvm::orc
;
20 static ThreadSafeModule
extractSubModule(ThreadSafeModule
&TSM
,
22 GVPredicate ShouldExtract
) {
24 auto DeleteExtractedDefs
= [](GlobalValue
&GV
) {
25 // Bump the linkage: this global will be provided by the external module.
26 GV
.setLinkage(GlobalValue::ExternalLinkage
);
28 // Delete the definition in the source module.
29 if (isa
<Function
>(GV
)) {
30 auto &F
= cast
<Function
>(GV
);
32 F
.setPersonalityFn(nullptr);
33 } else if (isa
<GlobalVariable
>(GV
)) {
34 cast
<GlobalVariable
>(GV
).setInitializer(nullptr);
35 } else if (isa
<GlobalAlias
>(GV
)) {
36 // We need to turn deleted aliases into function or variable decls based
37 // on the type of their aliasee.
38 auto &A
= cast
<GlobalAlias
>(GV
);
39 Constant
*Aliasee
= A
.getAliasee();
40 assert(A
.hasName() && "Anonymous alias?");
41 assert(Aliasee
->hasName() && "Anonymous aliasee");
42 std::string AliasName
= std::string(A
.getName());
44 if (isa
<Function
>(Aliasee
)) {
45 auto *F
= cloneFunctionDecl(*A
.getParent(), *cast
<Function
>(Aliasee
));
46 A
.replaceAllUsesWith(F
);
48 F
->setName(AliasName
);
49 } else if (isa
<GlobalVariable
>(Aliasee
)) {
50 auto *G
= cloneGlobalVariableDecl(*A
.getParent(),
51 *cast
<GlobalVariable
>(Aliasee
));
52 A
.replaceAllUsesWith(G
);
54 G
->setName(AliasName
);
56 llvm_unreachable("Alias to unsupported type");
58 llvm_unreachable("Unsupported global type");
61 auto NewTSM
= cloneToNewContext(TSM
, ShouldExtract
, DeleteExtractedDefs
);
62 NewTSM
.withModuleDo([&](Module
&M
) {
63 M
.setModuleIdentifier((M
.getModuleIdentifier() + Suffix
).str());
72 class PartitioningIRMaterializationUnit
: public IRMaterializationUnit
{
74 PartitioningIRMaterializationUnit(ExecutionSession
&ES
,
75 const IRSymbolMapper::ManglingOptions
&MO
,
77 CompileOnDemandLayer
&Parent
)
78 : IRMaterializationUnit(ES
, MO
, std::move(TSM
)), Parent(Parent
) {}
80 PartitioningIRMaterializationUnit(
81 ThreadSafeModule TSM
, SymbolFlagsMap SymbolFlags
,
82 SymbolStringPtr InitSymbol
, SymbolNameToDefinitionMap SymbolToDefinition
,
83 CompileOnDemandLayer
&Parent
)
84 : IRMaterializationUnit(std::move(TSM
), std::move(SymbolFlags
),
85 std::move(InitSymbol
),
86 std::move(SymbolToDefinition
)),
90 void materialize(std::unique_ptr
<MaterializationResponsibility
> R
) override
{
91 Parent
.emitPartition(std::move(R
), std::move(TSM
),
92 std::move(SymbolToDefinition
));
95 void discard(const JITDylib
&V
, const SymbolStringPtr
&Name
) override
{
96 // All original symbols were materialized by the CODLayer and should be
97 // final. The function bodies provided by M should never be overridden.
98 llvm_unreachable("Discard should never be called on an "
99 "ExtractingIRMaterializationUnit");
102 mutable std::mutex SourceModuleMutex
;
103 CompileOnDemandLayer
&Parent
;
106 Optional
<CompileOnDemandLayer::GlobalValueSet
>
107 CompileOnDemandLayer::compileRequested(GlobalValueSet Requested
) {
108 return std::move(Requested
);
111 Optional
<CompileOnDemandLayer::GlobalValueSet
>
112 CompileOnDemandLayer::compileWholeModule(GlobalValueSet Requested
) {
116 CompileOnDemandLayer::CompileOnDemandLayer(
117 ExecutionSession
&ES
, IRLayer
&BaseLayer
, LazyCallThroughManager
&LCTMgr
,
118 IndirectStubsManagerBuilder BuildIndirectStubsManager
)
119 : IRLayer(ES
, BaseLayer
.getManglingOptions()), BaseLayer(BaseLayer
),
121 BuildIndirectStubsManager(std::move(BuildIndirectStubsManager
)) {}
123 void CompileOnDemandLayer::setPartitionFunction(PartitionFunction Partition
) {
124 this->Partition
= std::move(Partition
);
127 void CompileOnDemandLayer::setImplMap(ImplSymbolMap
*Imp
) {
128 this->AliaseeImpls
= Imp
;
130 void CompileOnDemandLayer::emit(
131 std::unique_ptr
<MaterializationResponsibility
> R
, ThreadSafeModule TSM
) {
132 assert(TSM
&& "Null module");
134 auto &ES
= getExecutionSession();
136 // Sort the callables and non-callables, build re-exports and lodge the
137 // actual module with the implementation dylib.
138 auto &PDR
= getPerDylibResources(R
->getTargetJITDylib());
140 SymbolAliasMap NonCallables
;
141 SymbolAliasMap Callables
;
142 TSM
.withModuleDo([&](Module
&M
) {
143 // First, do some cleanup on the module:
147 for (auto &KV
: R
->getSymbols()) {
148 auto &Name
= KV
.first
;
149 auto &Flags
= KV
.second
;
150 if (Flags
.isCallable())
151 Callables
[Name
] = SymbolAliasMapEntry(Name
, Flags
);
153 NonCallables
[Name
] = SymbolAliasMapEntry(Name
, Flags
);
156 // Create a partitioning materialization unit and lodge it with the
157 // implementation dylib.
158 if (auto Err
= PDR
.getImplDylib().define(
159 std::make_unique
<PartitioningIRMaterializationUnit
>(
160 ES
, *getManglingOptions(), std::move(TSM
), *this))) {
161 ES
.reportError(std::move(Err
));
162 R
->failMaterialization();
166 if (!NonCallables
.empty())
168 R
->replace(reexports(PDR
.getImplDylib(), std::move(NonCallables
),
169 JITDylibLookupFlags::MatchAllSymbols
))) {
170 getExecutionSession().reportError(std::move(Err
));
171 R
->failMaterialization();
174 if (!Callables
.empty()) {
175 if (auto Err
= R
->replace(
176 lazyReexports(LCTMgr
, PDR
.getISManager(), PDR
.getImplDylib(),
177 std::move(Callables
), AliaseeImpls
))) {
178 getExecutionSession().reportError(std::move(Err
));
179 R
->failMaterialization();
185 CompileOnDemandLayer::PerDylibResources
&
186 CompileOnDemandLayer::getPerDylibResources(JITDylib
&TargetD
) {
187 auto I
= DylibResources
.find(&TargetD
);
188 if (I
== DylibResources
.end()) {
190 getExecutionSession().createBareJITDylib(TargetD
.getName() + ".impl");
191 JITDylibSearchOrder NewLinkOrder
;
192 TargetD
.withLinkOrderDo([&](const JITDylibSearchOrder
&TargetLinkOrder
) {
193 NewLinkOrder
= TargetLinkOrder
;
196 assert(!NewLinkOrder
.empty() && NewLinkOrder
.front().first
== &TargetD
&&
197 NewLinkOrder
.front().second
==
198 JITDylibLookupFlags::MatchAllSymbols
&&
199 "TargetD must be at the front of its own search order and match "
200 "non-exported symbol");
201 NewLinkOrder
.insert(std::next(NewLinkOrder
.begin()),
202 {&ImplD
, JITDylibLookupFlags::MatchAllSymbols
});
203 ImplD
.setLinkOrder(NewLinkOrder
, false);
204 TargetD
.setLinkOrder(std::move(NewLinkOrder
), false);
206 PerDylibResources
PDR(ImplD
, BuildIndirectStubsManager());
207 I
= DylibResources
.insert(std::make_pair(&TargetD
, std::move(PDR
))).first
;
213 void CompileOnDemandLayer::cleanUpModule(Module
&M
) {
214 for (auto &F
: M
.functions()) {
215 if (F
.isDeclaration())
218 if (F
.hasAvailableExternallyLinkage()) {
220 F
.setPersonalityFn(nullptr);
226 void CompileOnDemandLayer::expandPartition(GlobalValueSet
&Partition
) {
227 // Expands the partition to ensure the following rules hold:
228 // (1) If any alias is in the partition, its aliasee is also in the partition.
229 // (2) If any aliasee is in the partition, its aliases are also in the
231 // (3) If any global variable is in the partition then all global variables
232 // are in the partition.
233 assert(!Partition
.empty() && "Unexpected empty partition");
235 const Module
&M
= *(*Partition
.begin())->getParent();
236 bool ContainsGlobalVariables
= false;
237 std::vector
<const GlobalValue
*> GVsToAdd
;
239 for (auto *GV
: Partition
)
240 if (isa
<GlobalAlias
>(GV
))
242 cast
<GlobalValue
>(cast
<GlobalAlias
>(GV
)->getAliasee()));
243 else if (isa
<GlobalVariable
>(GV
))
244 ContainsGlobalVariables
= true;
246 for (auto &A
: M
.aliases())
247 if (Partition
.count(cast
<GlobalValue
>(A
.getAliasee())))
248 GVsToAdd
.push_back(&A
);
250 if (ContainsGlobalVariables
)
251 for (auto &G
: M
.globals())
252 GVsToAdd
.push_back(&G
);
254 for (auto *GV
: GVsToAdd
)
255 Partition
.insert(GV
);
258 void CompileOnDemandLayer::emitPartition(
259 std::unique_ptr
<MaterializationResponsibility
> R
, ThreadSafeModule TSM
,
260 IRMaterializationUnit::SymbolNameToDefinitionMap Defs
) {
262 // FIXME: Need a 'notify lazy-extracting/emitting' callback to tie the
263 // extracted module key, extracted module, and source module key
264 // together. This could be used, for example, to provide a specific
265 // memory manager instance to the linking layer.
267 auto &ES
= getExecutionSession();
268 GlobalValueSet RequestedGVs
;
269 for (auto &Name
: R
->getRequestedSymbols()) {
270 if (Name
== R
->getInitializerSymbol())
271 TSM
.withModuleDo([&](Module
&M
) {
272 for (auto &GV
: getStaticInitGVs(M
))
273 RequestedGVs
.insert(&GV
);
276 assert(Defs
.count(Name
) && "No definition for symbol");
277 RequestedGVs
.insert(Defs
[Name
]);
281 /// Perform partitioning with the context lock held, since the partition
282 /// function is allowed to access the globals to compute the partition.
284 TSM
.withModuleDo([&](Module
&M
) { return Partition(RequestedGVs
); });
286 // Take a 'None' partition to mean the whole module (as opposed to an empty
287 // partition, which means "materialize nothing"). Emit the whole module
288 // unmodified to the base layer.
289 if (GVsToExtract
== None
) {
291 BaseLayer
.emit(std::move(R
), std::move(TSM
));
295 // If the partition is empty, return the whole module to the symbol table.
296 if (GVsToExtract
->empty()) {
298 R
->replace(std::make_unique
<PartitioningIRMaterializationUnit
>(
299 std::move(TSM
), R
->getSymbols(), R
->getInitializerSymbol(),
300 std::move(Defs
), *this))) {
301 getExecutionSession().reportError(std::move(Err
));
302 R
->failMaterialization();
308 // Ok -- we actually need to partition the symbols. Promote the symbol
309 // linkages/names, expand the partition to include any required symbols
310 // (i.e. symbols that can't be separated from our partition), and
311 // then extract the partition.
313 // FIXME: We apply this promotion once per partitioning. It's safe, but
316 TSM
.withModuleDo([&](Module
&M
) -> Expected
<ThreadSafeModule
> {
317 auto PromotedGlobals
= PromoteSymbols(M
);
318 if (!PromotedGlobals
.empty()) {
320 MangleAndInterner
Mangle(ES
, M
.getDataLayout());
321 SymbolFlagsMap SymbolFlags
;
322 IRSymbolMapper::add(ES
, *getManglingOptions(),
323 PromotedGlobals
, SymbolFlags
);
325 if (auto Err
= R
->defineMaterializing(SymbolFlags
))
326 return std::move(Err
);
329 expandPartition(*GVsToExtract
);
331 // Submodule name is given by hashing the names of the globals.
332 std::string SubModuleName
;
334 std::vector
<const GlobalValue
*> HashGVs
;
335 HashGVs
.reserve(GVsToExtract
->size());
336 for (auto *GV
: *GVsToExtract
)
337 HashGVs
.push_back(GV
);
338 llvm::sort(HashGVs
, [](const GlobalValue
*LHS
, const GlobalValue
*RHS
) {
339 return LHS
->getName() < RHS
->getName();
342 for (auto *GV
: HashGVs
) {
343 assert(GV
->hasName() && "All GVs to extract should be named by now");
344 auto GVName
= GV
->getName();
345 HC
= hash_combine(HC
, hash_combine_range(GVName
.begin(), GVName
.end()));
347 raw_string_ostream(SubModuleName
)
349 << formatv(sizeof(size_t) == 8 ? "{0:x16}" : "{0:x8}",
350 static_cast<size_t>(HC
))
354 // Extract the requested partiton (plus any necessary aliases) and
355 // put the rest back into the impl dylib.
356 auto ShouldExtract
= [&](const GlobalValue
&GV
) -> bool {
357 return GVsToExtract
->count(&GV
);
360 return extractSubModule(TSM
, SubModuleName
, ShouldExtract
);
364 ES
.reportError(ExtractedTSM
.takeError());
365 R
->failMaterialization();
369 if (auto Err
= R
->replace(std::make_unique
<PartitioningIRMaterializationUnit
>(
370 ES
, *getManglingOptions(), std::move(TSM
), *this))) {
371 ES
.reportError(std::move(Err
));
372 R
->failMaterialization();
375 BaseLayer
.emit(std::move(R
), std::move(*ExtractedTSM
));
378 } // end namespace orc
379 } // end namespace llvm