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/IR/Mangler.h"
11 #include "llvm/IR/Module.h"
14 using namespace llvm::orc
;
16 static ThreadSafeModule
extractSubModule(ThreadSafeModule
&TSM
,
18 GVPredicate ShouldExtract
) {
20 auto DeleteExtractedDefs
= [](GlobalValue
&GV
) {
21 // Bump the linkage: this global will be provided by the external module.
22 GV
.setLinkage(GlobalValue::ExternalLinkage
);
24 // Delete the definition in the source module.
25 if (isa
<Function
>(GV
)) {
26 auto &F
= cast
<Function
>(GV
);
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
= A
.getName();
40 if (isa
<Function
>(Aliasee
)) {
41 auto *F
= cloneFunctionDecl(*A
.getParent(), *cast
<Function
>(Aliasee
));
42 A
.replaceAllUsesWith(F
);
44 F
->setName(AliasName
);
45 } else if (isa
<GlobalVariable
>(Aliasee
)) {
46 auto *G
= cloneGlobalVariableDecl(*A
.getParent(),
47 *cast
<GlobalVariable
>(Aliasee
));
48 A
.replaceAllUsesWith(G
);
50 G
->setName(AliasName
);
52 llvm_unreachable("Alias to unsupported type");
54 llvm_unreachable("Unsupported global type");
57 auto NewTSM
= cloneToNewContext(TSM
, ShouldExtract
, DeleteExtractedDefs
);
58 NewTSM
.withModuleDo([&](Module
&M
) {
59 M
.setModuleIdentifier((M
.getModuleIdentifier() + Suffix
).str());
68 class PartitioningIRMaterializationUnit
: public IRMaterializationUnit
{
70 PartitioningIRMaterializationUnit(ExecutionSession
&ES
, ThreadSafeModule TSM
,
71 VModuleKey K
, CompileOnDemandLayer
&Parent
)
72 : IRMaterializationUnit(ES
, std::move(TSM
), std::move(K
)),
75 PartitioningIRMaterializationUnit(
76 ThreadSafeModule TSM
, SymbolFlagsMap SymbolFlags
,
77 SymbolNameToDefinitionMap SymbolToDefinition
,
78 CompileOnDemandLayer
&Parent
)
79 : IRMaterializationUnit(std::move(TSM
), std::move(K
),
80 std::move(SymbolFlags
),
81 std::move(SymbolToDefinition
)),
85 void materialize(MaterializationResponsibility R
) override
{
86 Parent
.emitPartition(std::move(R
), std::move(TSM
),
87 std::move(SymbolToDefinition
));
90 void discard(const JITDylib
&V
, const SymbolStringPtr
&Name
) override
{
91 // All original symbols were materialized by the CODLayer and should be
92 // final. The function bodies provided by M should never be overridden.
93 llvm_unreachable("Discard should never be called on an "
94 "ExtractingIRMaterializationUnit");
97 mutable std::mutex SourceModuleMutex
;
98 CompileOnDemandLayer
&Parent
;
101 Optional
<CompileOnDemandLayer::GlobalValueSet
>
102 CompileOnDemandLayer::compileRequested(GlobalValueSet Requested
) {
103 return std::move(Requested
);
106 Optional
<CompileOnDemandLayer::GlobalValueSet
>
107 CompileOnDemandLayer::compileWholeModule(GlobalValueSet Requested
) {
111 CompileOnDemandLayer::CompileOnDemandLayer(
112 ExecutionSession
&ES
, IRLayer
&BaseLayer
, LazyCallThroughManager
&LCTMgr
,
113 IndirectStubsManagerBuilder BuildIndirectStubsManager
)
114 : IRLayer(ES
), BaseLayer(BaseLayer
), LCTMgr(LCTMgr
),
115 BuildIndirectStubsManager(std::move(BuildIndirectStubsManager
)) {}
117 void CompileOnDemandLayer::setPartitionFunction(PartitionFunction Partition
) {
118 this->Partition
= std::move(Partition
);
121 void CompileOnDemandLayer::setImplMap(ImplSymbolMap
*Imp
) {
122 this->AliaseeImpls
= Imp
;
124 void CompileOnDemandLayer::emit(MaterializationResponsibility R
,
125 ThreadSafeModule TSM
) {
126 assert(TSM
&& "Null module");
128 auto &ES
= getExecutionSession();
130 // Sort the callables and non-callables, build re-exports and lodge the
131 // actual module with the implementation dylib.
132 auto &PDR
= getPerDylibResources(R
.getTargetJITDylib());
134 SymbolAliasMap NonCallables
;
135 SymbolAliasMap Callables
;
136 TSM
.withModuleDo([&](Module
&M
) {
137 // First, do some cleanup on the module:
140 MangleAndInterner
Mangle(ES
, M
.getDataLayout());
141 for (auto &GV
: M
.global_values()) {
142 if (GV
.isDeclaration() || GV
.hasLocalLinkage() ||
143 GV
.hasAppendingLinkage())
146 auto Name
= Mangle(GV
.getName());
147 auto Flags
= JITSymbolFlags::fromGlobalValue(GV
);
148 if (Flags
.isCallable())
149 Callables
[Name
] = SymbolAliasMapEntry(Name
, Flags
);
151 NonCallables
[Name
] = SymbolAliasMapEntry(Name
, Flags
);
155 // Create a partitioning materialization unit and lodge it with the
156 // implementation dylib.
157 if (auto Err
= PDR
.getImplDylib().define(
158 std::make_unique
<PartitioningIRMaterializationUnit
>(
159 ES
, std::move(TSM
), R
.getVModuleKey(), *this))) {
160 ES
.reportError(std::move(Err
));
161 R
.failMaterialization();
165 R
.replace(reexports(PDR
.getImplDylib(), std::move(NonCallables
), true));
166 R
.replace(lazyReexports(LCTMgr
, PDR
.getISManager(), PDR
.getImplDylib(),
167 std::move(Callables
), AliaseeImpls
));
170 CompileOnDemandLayer::PerDylibResources
&
171 CompileOnDemandLayer::getPerDylibResources(JITDylib
&TargetD
) {
172 auto I
= DylibResources
.find(&TargetD
);
173 if (I
== DylibResources
.end()) {
174 auto &ImplD
= getExecutionSession().createJITDylib(
175 TargetD
.getName() + ".impl", false);
176 TargetD
.withSearchOrderDo([&](const JITDylibSearchList
&TargetSearchOrder
) {
177 auto NewSearchOrder
= TargetSearchOrder
;
178 assert(!NewSearchOrder
.empty() &&
179 NewSearchOrder
.front().first
== &TargetD
&&
180 NewSearchOrder
.front().second
== true &&
181 "TargetD must be at the front of its own search order and match "
182 "non-exported symbol");
183 NewSearchOrder
.insert(std::next(NewSearchOrder
.begin()), {&ImplD
, true});
184 ImplD
.setSearchOrder(std::move(NewSearchOrder
), false);
186 PerDylibResources
PDR(ImplD
, BuildIndirectStubsManager());
187 I
= DylibResources
.insert(std::make_pair(&TargetD
, std::move(PDR
))).first
;
193 void CompileOnDemandLayer::cleanUpModule(Module
&M
) {
194 for (auto &F
: M
.functions()) {
195 if (F
.isDeclaration())
198 if (F
.hasAvailableExternallyLinkage()) {
200 F
.setPersonalityFn(nullptr);
206 void CompileOnDemandLayer::expandPartition(GlobalValueSet
&Partition
) {
207 // Expands the partition to ensure the following rules hold:
208 // (1) If any alias is in the partition, its aliasee is also in the partition.
209 // (2) If any aliasee is in the partition, its aliases are also in the
211 // (3) If any global variable is in the partition then all global variables
212 // are in the partition.
213 assert(!Partition
.empty() && "Unexpected empty partition");
215 const Module
&M
= *(*Partition
.begin())->getParent();
216 bool ContainsGlobalVariables
= false;
217 std::vector
<const GlobalValue
*> GVsToAdd
;
219 for (auto *GV
: Partition
)
220 if (isa
<GlobalAlias
>(GV
))
222 cast
<GlobalValue
>(cast
<GlobalAlias
>(GV
)->getAliasee()));
223 else if (isa
<GlobalVariable
>(GV
))
224 ContainsGlobalVariables
= true;
226 for (auto &A
: M
.aliases())
227 if (Partition
.count(cast
<GlobalValue
>(A
.getAliasee())))
228 GVsToAdd
.push_back(&A
);
230 if (ContainsGlobalVariables
)
231 for (auto &G
: M
.globals())
232 GVsToAdd
.push_back(&G
);
234 for (auto *GV
: GVsToAdd
)
235 Partition
.insert(GV
);
238 void CompileOnDemandLayer::emitPartition(
239 MaterializationResponsibility R
, ThreadSafeModule TSM
,
240 IRMaterializationUnit::SymbolNameToDefinitionMap Defs
) {
242 // FIXME: Need a 'notify lazy-extracting/emitting' callback to tie the
243 // extracted module key, extracted module, and source module key
244 // together. This could be used, for example, to provide a specific
245 // memory manager instance to the linking layer.
247 auto &ES
= getExecutionSession();
248 GlobalValueSet RequestedGVs
;
249 for (auto &Name
: R
.getRequestedSymbols()) {
250 assert(Defs
.count(Name
) && "No definition for symbol");
251 RequestedGVs
.insert(Defs
[Name
]);
254 /// Perform partitioning with the context lock held, since the partition
255 /// function is allowed to access the globals to compute the partition.
257 TSM
.withModuleDo([&](Module
&M
) { return Partition(RequestedGVs
); });
259 // Take a 'None' partition to mean the whole module (as opposed to an empty
260 // partition, which means "materialize nothing"). Emit the whole module
261 // unmodified to the base layer.
262 if (GVsToExtract
== None
) {
264 BaseLayer
.emit(std::move(R
), std::move(TSM
));
268 // If the partition is empty, return the whole module to the symbol table.
269 if (GVsToExtract
->empty()) {
270 R
.replace(std::make_unique
<PartitioningIRMaterializationUnit
>(
271 std::move(TSM
), R
.getSymbols(), std::move(Defs
), *this));
275 // Ok -- we actually need to partition the symbols. Promote the symbol
276 // linkages/names, expand the partition to include any required symbols
277 // (i.e. symbols that can't be separated from our partition), and
278 // then extract the partition.
280 // FIXME: We apply this promotion once per partitioning. It's safe, but
284 TSM
.withModuleDo([&](Module
&M
) -> Expected
<ThreadSafeModule
> {
285 auto PromotedGlobals
= PromoteSymbols(M
);
286 if (!PromotedGlobals
.empty()) {
287 MangleAndInterner
Mangle(ES
, M
.getDataLayout());
288 SymbolFlagsMap SymbolFlags
;
289 for (auto &GV
: PromotedGlobals
)
290 SymbolFlags
[Mangle(GV
->getName())] =
291 JITSymbolFlags::fromGlobalValue(*GV
);
292 if (auto Err
= R
.defineMaterializing(SymbolFlags
))
293 return std::move(Err
);
296 expandPartition(*GVsToExtract
);
298 // Extract the requested partiton (plus any necessary aliases) and
299 // put the rest back into the impl dylib.
300 auto ShouldExtract
= [&](const GlobalValue
&GV
) -> bool {
301 return GVsToExtract
->count(&GV
);
304 return extractSubModule(TSM
, ".submodule", ShouldExtract
);
308 ES
.reportError(ExtractedTSM
.takeError());
309 R
.failMaterialization();
313 R
.replace(std::make_unique
<PartitioningIRMaterializationUnit
>(
314 ES
, std::move(TSM
), R
.getVModuleKey(), *this));
315 BaseLayer
.emit(std::move(R
), std::move(*ExtractedTSM
));
318 } // end namespace orc
319 } // end namespace llvm