[ORC] Add std::tuple support to SimplePackedSerialization.
[llvm-project.git] / llvm / lib / ExecutionEngine / Orc / CompileOnDemandLayer.cpp
blob5b73c0e2fbc81ec9325ff90c469e61fa4ab29d62
1 //===----- CompileOnDemandLayer.cpp - Lazily emit IR on first call --------===//
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 //===----------------------------------------------------------------------===//
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"
15 #include <string>
17 using namespace llvm;
18 using namespace llvm::orc;
20 static ThreadSafeModule extractSubModule(ThreadSafeModule &TSM,
21 StringRef Suffix,
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);
31 F.deleteBody();
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);
47 A.eraseFromParent();
48 F->setName(AliasName);
49 } else if (isa<GlobalVariable>(Aliasee)) {
50 auto *G = cloneGlobalVariableDecl(*A.getParent(),
51 *cast<GlobalVariable>(Aliasee));
52 A.replaceAllUsesWith(G);
53 A.eraseFromParent();
54 G->setName(AliasName);
55 } else
56 llvm_unreachable("Alias to unsupported type");
57 } else
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());
64 });
66 return NewTSM;
69 namespace llvm {
70 namespace orc {
72 class PartitioningIRMaterializationUnit : public IRMaterializationUnit {
73 public:
74 PartitioningIRMaterializationUnit(ExecutionSession &ES,
75 const IRSymbolMapper::ManglingOptions &MO,
76 ThreadSafeModule TSM,
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)),
87 Parent(Parent) {}
89 private:
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) {
113 return None;
116 CompileOnDemandLayer::CompileOnDemandLayer(
117 ExecutionSession &ES, IRLayer &BaseLayer, LazyCallThroughManager &LCTMgr,
118 IndirectStubsManagerBuilder BuildIndirectStubsManager)
119 : IRLayer(ES, BaseLayer.getManglingOptions()), BaseLayer(BaseLayer),
120 LCTMgr(LCTMgr),
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:
144 cleanUpModule(M);
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);
152 else
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();
163 return;
166 if (!NonCallables.empty())
167 if (auto Err =
168 R->replace(reexports(PDR.getImplDylib(), std::move(NonCallables),
169 JITDylibLookupFlags::MatchAllSymbols))) {
170 getExecutionSession().reportError(std::move(Err));
171 R->failMaterialization();
172 return;
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();
180 return;
185 CompileOnDemandLayer::PerDylibResources &
186 CompileOnDemandLayer::getPerDylibResources(JITDylib &TargetD) {
187 auto I = DylibResources.find(&TargetD);
188 if (I == DylibResources.end()) {
189 auto &ImplD =
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;
210 return I->second;
213 void CompileOnDemandLayer::cleanUpModule(Module &M) {
214 for (auto &F : M.functions()) {
215 if (F.isDeclaration())
216 continue;
218 if (F.hasAvailableExternallyLinkage()) {
219 F.deleteBody();
220 F.setPersonalityFn(nullptr);
221 continue;
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
230 // partiton.
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))
241 GVsToAdd.push_back(
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);
275 else {
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.
283 auto GVsToExtract =
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) {
290 Defs.clear();
291 BaseLayer.emit(std::move(R), std::move(TSM));
292 return;
295 // If the partition is empty, return the whole module to the symbol table.
296 if (GVsToExtract->empty()) {
297 if (auto Err =
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();
303 return;
305 return;
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
314 // overkill.
315 auto ExtractedTSM =
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();
341 hash_code HC(0);
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)
348 << ".submodule."
349 << formatv(sizeof(size_t) == 8 ? "{0:x16}" : "{0:x8}",
350 static_cast<size_t>(HC))
351 << ".ll";
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);
363 if (!ExtractedTSM) {
364 ES.reportError(ExtractedTSM.takeError());
365 R->failMaterialization();
366 return;
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();
373 return;
375 BaseLayer.emit(std::move(R), std::move(*ExtractedTSM));
378 } // end namespace orc
379 } // end namespace llvm