xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/IRPartitionLayer.cpp (revision 04af63b267c391a4b0a0fb61060f724f8b5bc2be)
1*04af63b2SSunho Kim //===----- IRPartitionLayer.cpp - Partition IR module into submodules -----===//
2*04af63b2SSunho Kim //
3*04af63b2SSunho Kim // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*04af63b2SSunho Kim // See https://llvm.org/LICENSE.txt for license information.
5*04af63b2SSunho Kim // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*04af63b2SSunho Kim //
7*04af63b2SSunho Kim //===----------------------------------------------------------------------===//
8*04af63b2SSunho Kim 
9*04af63b2SSunho Kim #include "llvm/ExecutionEngine/Orc/IRPartitionLayer.h"
10*04af63b2SSunho Kim #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
11*04af63b2SSunho Kim #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
12*04af63b2SSunho Kim 
13*04af63b2SSunho Kim using namespace llvm;
14*04af63b2SSunho Kim using namespace llvm::orc;
15*04af63b2SSunho Kim 
16*04af63b2SSunho Kim static ThreadSafeModule extractSubModule(ThreadSafeModule &TSM,
17*04af63b2SSunho Kim                                          StringRef Suffix,
18*04af63b2SSunho Kim                                          GVPredicate ShouldExtract) {
19*04af63b2SSunho Kim 
20*04af63b2SSunho Kim   auto DeleteExtractedDefs = [](GlobalValue &GV) {
21*04af63b2SSunho Kim     // Bump the linkage: this global will be provided by the external module.
22*04af63b2SSunho Kim     GV.setLinkage(GlobalValue::ExternalLinkage);
23*04af63b2SSunho Kim 
24*04af63b2SSunho Kim     // Delete the definition in the source module.
25*04af63b2SSunho Kim     if (isa<Function>(GV)) {
26*04af63b2SSunho Kim       auto &F = cast<Function>(GV);
27*04af63b2SSunho Kim       F.deleteBody();
28*04af63b2SSunho Kim       F.setPersonalityFn(nullptr);
29*04af63b2SSunho Kim     } else if (isa<GlobalVariable>(GV)) {
30*04af63b2SSunho Kim       cast<GlobalVariable>(GV).setInitializer(nullptr);
31*04af63b2SSunho Kim     } else if (isa<GlobalAlias>(GV)) {
32*04af63b2SSunho Kim       // We need to turn deleted aliases into function or variable decls based
33*04af63b2SSunho Kim       // on the type of their aliasee.
34*04af63b2SSunho Kim       auto &A = cast<GlobalAlias>(GV);
35*04af63b2SSunho Kim       Constant *Aliasee = A.getAliasee();
36*04af63b2SSunho Kim       assert(A.hasName() && "Anonymous alias?");
37*04af63b2SSunho Kim       assert(Aliasee->hasName() && "Anonymous aliasee");
38*04af63b2SSunho Kim       std::string AliasName = std::string(A.getName());
39*04af63b2SSunho Kim 
40*04af63b2SSunho Kim       if (isa<Function>(Aliasee)) {
41*04af63b2SSunho Kim         auto *F = cloneFunctionDecl(*A.getParent(), *cast<Function>(Aliasee));
42*04af63b2SSunho Kim         A.replaceAllUsesWith(F);
43*04af63b2SSunho Kim         A.eraseFromParent();
44*04af63b2SSunho Kim         F->setName(AliasName);
45*04af63b2SSunho Kim       } else if (isa<GlobalVariable>(Aliasee)) {
46*04af63b2SSunho Kim         auto *G = cloneGlobalVariableDecl(*A.getParent(),
47*04af63b2SSunho Kim                                           *cast<GlobalVariable>(Aliasee));
48*04af63b2SSunho Kim         A.replaceAllUsesWith(G);
49*04af63b2SSunho Kim         A.eraseFromParent();
50*04af63b2SSunho Kim         G->setName(AliasName);
51*04af63b2SSunho Kim       } else
52*04af63b2SSunho Kim         llvm_unreachable("Alias to unsupported type");
53*04af63b2SSunho Kim     } else
54*04af63b2SSunho Kim       llvm_unreachable("Unsupported global type");
55*04af63b2SSunho Kim   };
56*04af63b2SSunho Kim 
57*04af63b2SSunho Kim   auto NewTSM = cloneToNewContext(TSM, ShouldExtract, DeleteExtractedDefs);
58*04af63b2SSunho Kim   NewTSM.withModuleDo([&](Module &M) {
59*04af63b2SSunho Kim     M.setModuleIdentifier((M.getModuleIdentifier() + Suffix).str());
60*04af63b2SSunho Kim   });
61*04af63b2SSunho Kim 
62*04af63b2SSunho Kim   return NewTSM;
63*04af63b2SSunho Kim }
64*04af63b2SSunho Kim 
65*04af63b2SSunho Kim namespace llvm {
66*04af63b2SSunho Kim namespace orc {
67*04af63b2SSunho Kim 
68*04af63b2SSunho Kim class PartitioningIRMaterializationUnit : public IRMaterializationUnit {
69*04af63b2SSunho Kim public:
70*04af63b2SSunho Kim   PartitioningIRMaterializationUnit(ExecutionSession &ES,
71*04af63b2SSunho Kim                                     const IRSymbolMapper::ManglingOptions &MO,
72*04af63b2SSunho Kim                                     ThreadSafeModule TSM,
73*04af63b2SSunho Kim                                     IRPartitionLayer &Parent)
74*04af63b2SSunho Kim       : IRMaterializationUnit(ES, MO, std::move(TSM)), Parent(Parent) {}
75*04af63b2SSunho Kim 
76*04af63b2SSunho Kim   PartitioningIRMaterializationUnit(
77*04af63b2SSunho Kim       ThreadSafeModule TSM, Interface I,
78*04af63b2SSunho Kim       SymbolNameToDefinitionMap SymbolToDefinition, IRPartitionLayer &Parent)
79*04af63b2SSunho Kim       : IRMaterializationUnit(std::move(TSM), std::move(I),
80*04af63b2SSunho Kim                               std::move(SymbolToDefinition)),
81*04af63b2SSunho Kim         Parent(Parent) {}
82*04af63b2SSunho Kim 
83*04af63b2SSunho Kim private:
84*04af63b2SSunho Kim   void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
85*04af63b2SSunho Kim     Parent.emitPartition(std::move(R), std::move(TSM),
86*04af63b2SSunho Kim                          std::move(SymbolToDefinition));
87*04af63b2SSunho Kim   }
88*04af63b2SSunho Kim 
89*04af63b2SSunho Kim   void discard(const JITDylib &V, const SymbolStringPtr &Name) override {
90*04af63b2SSunho Kim     // All original symbols were materialized by the CODLayer and should be
91*04af63b2SSunho Kim     // final. The function bodies provided by M should never be overridden.
92*04af63b2SSunho Kim     llvm_unreachable("Discard should never be called on an "
93*04af63b2SSunho Kim                      "ExtractingIRMaterializationUnit");
94*04af63b2SSunho Kim   }
95*04af63b2SSunho Kim 
96*04af63b2SSunho Kim   IRPartitionLayer &Parent;
97*04af63b2SSunho Kim };
98*04af63b2SSunho Kim 
99*04af63b2SSunho Kim } // namespace orc
100*04af63b2SSunho Kim } // namespace llvm
101*04af63b2SSunho Kim 
102*04af63b2SSunho Kim IRPartitionLayer::IRPartitionLayer(ExecutionSession &ES, IRLayer &BaseLayer)
103*04af63b2SSunho Kim     : IRLayer(ES, BaseLayer.getManglingOptions()), BaseLayer(BaseLayer) {}
104*04af63b2SSunho Kim 
105*04af63b2SSunho Kim void IRPartitionLayer::setPartitionFunction(PartitionFunction Partition) {
106*04af63b2SSunho Kim   this->Partition = Partition;
107*04af63b2SSunho Kim }
108*04af63b2SSunho Kim 
109*04af63b2SSunho Kim std::optional<IRPartitionLayer::GlobalValueSet>
110*04af63b2SSunho Kim IRPartitionLayer::compileRequested(GlobalValueSet Requested) {
111*04af63b2SSunho Kim   return std::move(Requested);
112*04af63b2SSunho Kim }
113*04af63b2SSunho Kim 
114*04af63b2SSunho Kim std::optional<IRPartitionLayer::GlobalValueSet>
115*04af63b2SSunho Kim IRPartitionLayer::compileWholeModule(GlobalValueSet Requested) {
116*04af63b2SSunho Kim   return std::nullopt;
117*04af63b2SSunho Kim }
118*04af63b2SSunho Kim 
119*04af63b2SSunho Kim void IRPartitionLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
120*04af63b2SSunho Kim                             ThreadSafeModule TSM) {
121*04af63b2SSunho Kim   assert(TSM && "Null module");
122*04af63b2SSunho Kim 
123*04af63b2SSunho Kim   auto &ES = getExecutionSession();
124*04af63b2SSunho Kim   TSM.withModuleDo([&](Module &M) {
125*04af63b2SSunho Kim     // First, do some cleanup on the module:
126*04af63b2SSunho Kim     cleanUpModule(M);
127*04af63b2SSunho Kim   });
128*04af63b2SSunho Kim 
129*04af63b2SSunho Kim   // Create a partitioning materialization unit and pass the responsibility.
130*04af63b2SSunho Kim   if (auto Err = R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
131*04af63b2SSunho Kim           ES, *getManglingOptions(), std::move(TSM), *this))) {
132*04af63b2SSunho Kim     ES.reportError(std::move(Err));
133*04af63b2SSunho Kim     R->failMaterialization();
134*04af63b2SSunho Kim     return;
135*04af63b2SSunho Kim   }
136*04af63b2SSunho Kim }
137*04af63b2SSunho Kim 
138*04af63b2SSunho Kim void IRPartitionLayer::cleanUpModule(Module &M) {
139*04af63b2SSunho Kim   for (auto &F : M.functions()) {
140*04af63b2SSunho Kim     if (F.isDeclaration())
141*04af63b2SSunho Kim       continue;
142*04af63b2SSunho Kim 
143*04af63b2SSunho Kim     if (F.hasAvailableExternallyLinkage()) {
144*04af63b2SSunho Kim       F.deleteBody();
145*04af63b2SSunho Kim       F.setPersonalityFn(nullptr);
146*04af63b2SSunho Kim       continue;
147*04af63b2SSunho Kim     }
148*04af63b2SSunho Kim   }
149*04af63b2SSunho Kim }
150*04af63b2SSunho Kim 
151*04af63b2SSunho Kim void IRPartitionLayer::expandPartition(GlobalValueSet &Partition) {
152*04af63b2SSunho Kim   // Expands the partition to ensure the following rules hold:
153*04af63b2SSunho Kim   // (1) If any alias is in the partition, its aliasee is also in the partition.
154*04af63b2SSunho Kim   // (2) If any aliasee is in the partition, its aliases are also in the
155*04af63b2SSunho Kim   //     partiton.
156*04af63b2SSunho Kim   // (3) If any global variable is in the partition then all global variables
157*04af63b2SSunho Kim   //     are in the partition.
158*04af63b2SSunho Kim   assert(!Partition.empty() && "Unexpected empty partition");
159*04af63b2SSunho Kim 
160*04af63b2SSunho Kim   const Module &M = *(*Partition.begin())->getParent();
161*04af63b2SSunho Kim   bool ContainsGlobalVariables = false;
162*04af63b2SSunho Kim   std::vector<const GlobalValue *> GVsToAdd;
163*04af63b2SSunho Kim 
164*04af63b2SSunho Kim   for (const auto *GV : Partition)
165*04af63b2SSunho Kim     if (isa<GlobalAlias>(GV))
166*04af63b2SSunho Kim       GVsToAdd.push_back(
167*04af63b2SSunho Kim           cast<GlobalValue>(cast<GlobalAlias>(GV)->getAliasee()));
168*04af63b2SSunho Kim     else if (isa<GlobalVariable>(GV))
169*04af63b2SSunho Kim       ContainsGlobalVariables = true;
170*04af63b2SSunho Kim 
171*04af63b2SSunho Kim   for (auto &A : M.aliases())
172*04af63b2SSunho Kim     if (Partition.count(cast<GlobalValue>(A.getAliasee())))
173*04af63b2SSunho Kim       GVsToAdd.push_back(&A);
174*04af63b2SSunho Kim 
175*04af63b2SSunho Kim   if (ContainsGlobalVariables)
176*04af63b2SSunho Kim     for (auto &G : M.globals())
177*04af63b2SSunho Kim       GVsToAdd.push_back(&G);
178*04af63b2SSunho Kim 
179*04af63b2SSunho Kim   for (const auto *GV : GVsToAdd)
180*04af63b2SSunho Kim     Partition.insert(GV);
181*04af63b2SSunho Kim }
182*04af63b2SSunho Kim 
183*04af63b2SSunho Kim void IRPartitionLayer::emitPartition(
184*04af63b2SSunho Kim     std::unique_ptr<MaterializationResponsibility> R, ThreadSafeModule TSM,
185*04af63b2SSunho Kim     IRMaterializationUnit::SymbolNameToDefinitionMap Defs) {
186*04af63b2SSunho Kim 
187*04af63b2SSunho Kim   // FIXME: Need a 'notify lazy-extracting/emitting' callback to tie the
188*04af63b2SSunho Kim   //        extracted module key, extracted module, and source module key
189*04af63b2SSunho Kim   //        together. This could be used, for example, to provide a specific
190*04af63b2SSunho Kim   //        memory manager instance to the linking layer.
191*04af63b2SSunho Kim 
192*04af63b2SSunho Kim   auto &ES = getExecutionSession();
193*04af63b2SSunho Kim   GlobalValueSet RequestedGVs;
194*04af63b2SSunho Kim   for (auto &Name : R->getRequestedSymbols()) {
195*04af63b2SSunho Kim     if (Name == R->getInitializerSymbol())
196*04af63b2SSunho Kim       TSM.withModuleDo([&](Module &M) {
197*04af63b2SSunho Kim         for (auto &GV : getStaticInitGVs(M))
198*04af63b2SSunho Kim           RequestedGVs.insert(&GV);
199*04af63b2SSunho Kim       });
200*04af63b2SSunho Kim     else {
201*04af63b2SSunho Kim       assert(Defs.count(Name) && "No definition for symbol");
202*04af63b2SSunho Kim       RequestedGVs.insert(Defs[Name]);
203*04af63b2SSunho Kim     }
204*04af63b2SSunho Kim   }
205*04af63b2SSunho Kim 
206*04af63b2SSunho Kim   /// Perform partitioning with the context lock held, since the partition
207*04af63b2SSunho Kim   /// function is allowed to access the globals to compute the partition.
208*04af63b2SSunho Kim   auto GVsToExtract =
209*04af63b2SSunho Kim       TSM.withModuleDo([&](Module &M) { return Partition(RequestedGVs); });
210*04af63b2SSunho Kim 
211*04af63b2SSunho Kim   // Take a 'None' partition to mean the whole module (as opposed to an empty
212*04af63b2SSunho Kim   // partition, which means "materialize nothing"). Emit the whole module
213*04af63b2SSunho Kim   // unmodified to the base layer.
214*04af63b2SSunho Kim   if (GVsToExtract == std::nullopt) {
215*04af63b2SSunho Kim     Defs.clear();
216*04af63b2SSunho Kim     BaseLayer.emit(std::move(R), std::move(TSM));
217*04af63b2SSunho Kim     return;
218*04af63b2SSunho Kim   }
219*04af63b2SSunho Kim 
220*04af63b2SSunho Kim   // If the partition is empty, return the whole module to the symbol table.
221*04af63b2SSunho Kim   if (GVsToExtract->empty()) {
222*04af63b2SSunho Kim     if (auto Err =
223*04af63b2SSunho Kim             R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
224*04af63b2SSunho Kim                 std::move(TSM),
225*04af63b2SSunho Kim                 MaterializationUnit::Interface(R->getSymbols(),
226*04af63b2SSunho Kim                                                R->getInitializerSymbol()),
227*04af63b2SSunho Kim                 std::move(Defs), *this))) {
228*04af63b2SSunho Kim       getExecutionSession().reportError(std::move(Err));
229*04af63b2SSunho Kim       R->failMaterialization();
230*04af63b2SSunho Kim       return;
231*04af63b2SSunho Kim     }
232*04af63b2SSunho Kim     return;
233*04af63b2SSunho Kim   }
234*04af63b2SSunho Kim 
235*04af63b2SSunho Kim   // Ok -- we actually need to partition the symbols. Promote the symbol
236*04af63b2SSunho Kim   // linkages/names, expand the partition to include any required symbols
237*04af63b2SSunho Kim   // (i.e. symbols that can't be separated from our partition), and
238*04af63b2SSunho Kim   // then extract the partition.
239*04af63b2SSunho Kim   //
240*04af63b2SSunho Kim   // FIXME: We apply this promotion once per partitioning. It's safe, but
241*04af63b2SSunho Kim   // overkill.
242*04af63b2SSunho Kim   auto ExtractedTSM = TSM.withModuleDo([&](Module &M)
243*04af63b2SSunho Kim                                            -> Expected<ThreadSafeModule> {
244*04af63b2SSunho Kim     auto PromotedGlobals = PromoteSymbols(M);
245*04af63b2SSunho Kim     if (!PromotedGlobals.empty()) {
246*04af63b2SSunho Kim 
247*04af63b2SSunho Kim       MangleAndInterner Mangle(ES, M.getDataLayout());
248*04af63b2SSunho Kim       SymbolFlagsMap SymbolFlags;
249*04af63b2SSunho Kim       IRSymbolMapper::add(ES, *getManglingOptions(), PromotedGlobals,
250*04af63b2SSunho Kim                           SymbolFlags);
251*04af63b2SSunho Kim 
252*04af63b2SSunho Kim       if (auto Err = R->defineMaterializing(SymbolFlags))
253*04af63b2SSunho Kim         return std::move(Err);
254*04af63b2SSunho Kim     }
255*04af63b2SSunho Kim 
256*04af63b2SSunho Kim     expandPartition(*GVsToExtract);
257*04af63b2SSunho Kim 
258*04af63b2SSunho Kim     // Submodule name is given by hashing the names of the globals.
259*04af63b2SSunho Kim     std::string SubModuleName;
260*04af63b2SSunho Kim     {
261*04af63b2SSunho Kim       std::vector<const GlobalValue *> HashGVs;
262*04af63b2SSunho Kim       HashGVs.reserve(GVsToExtract->size());
263*04af63b2SSunho Kim       for (const auto *GV : *GVsToExtract)
264*04af63b2SSunho Kim         HashGVs.push_back(GV);
265*04af63b2SSunho Kim       llvm::sort(HashGVs, [](const GlobalValue *LHS, const GlobalValue *RHS) {
266*04af63b2SSunho Kim         return LHS->getName() < RHS->getName();
267*04af63b2SSunho Kim       });
268*04af63b2SSunho Kim       hash_code HC(0);
269*04af63b2SSunho Kim       for (const auto *GV : HashGVs) {
270*04af63b2SSunho Kim         assert(GV->hasName() && "All GVs to extract should be named by now");
271*04af63b2SSunho Kim         auto GVName = GV->getName();
272*04af63b2SSunho Kim         HC = hash_combine(HC, hash_combine_range(GVName.begin(), GVName.end()));
273*04af63b2SSunho Kim       }
274*04af63b2SSunho Kim       raw_string_ostream(SubModuleName)
275*04af63b2SSunho Kim           << ".submodule."
276*04af63b2SSunho Kim           << formatv(sizeof(size_t) == 8 ? "{0:x16}" : "{0:x8}",
277*04af63b2SSunho Kim                      static_cast<size_t>(HC))
278*04af63b2SSunho Kim           << ".ll";
279*04af63b2SSunho Kim     }
280*04af63b2SSunho Kim 
281*04af63b2SSunho Kim     // Extract the requested partiton (plus any necessary aliases) and
282*04af63b2SSunho Kim     // put the rest back into the impl dylib.
283*04af63b2SSunho Kim     auto ShouldExtract = [&](const GlobalValue &GV) -> bool {
284*04af63b2SSunho Kim       return GVsToExtract->count(&GV);
285*04af63b2SSunho Kim     };
286*04af63b2SSunho Kim 
287*04af63b2SSunho Kim     return extractSubModule(TSM, SubModuleName, ShouldExtract);
288*04af63b2SSunho Kim   });
289*04af63b2SSunho Kim 
290*04af63b2SSunho Kim   if (!ExtractedTSM) {
291*04af63b2SSunho Kim     ES.reportError(ExtractedTSM.takeError());
292*04af63b2SSunho Kim     R->failMaterialization();
293*04af63b2SSunho Kim     return;
294*04af63b2SSunho Kim   }
295*04af63b2SSunho Kim 
296*04af63b2SSunho Kim   if (auto Err = R->replace(std::make_unique<PartitioningIRMaterializationUnit>(
297*04af63b2SSunho Kim           ES, *getManglingOptions(), std::move(TSM), *this))) {
298*04af63b2SSunho Kim     ES.reportError(std::move(Err));
299*04af63b2SSunho Kim     R->failMaterialization();
300*04af63b2SSunho Kim     return;
301*04af63b2SSunho Kim   }
302*04af63b2SSunho Kim   BaseLayer.emit(std::move(R), std::move(*ExtractedTSM));
303*04af63b2SSunho Kim }
304