1 //===----- IRPartitionLayer.cpp - Partition IR module into submodules -----===//
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/IRPartitionLayer.h"
10 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
11 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.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
= std::string(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
,
71 const IRSymbolMapper::ManglingOptions
&MO
,
73 IRPartitionLayer
&Parent
)
74 : IRMaterializationUnit(ES
, MO
, std::move(TSM
)), Parent(Parent
) {}
76 PartitioningIRMaterializationUnit(
77 ThreadSafeModule TSM
, Interface I
,
78 SymbolNameToDefinitionMap SymbolToDefinition
, IRPartitionLayer
&Parent
)
79 : IRMaterializationUnit(std::move(TSM
), std::move(I
),
80 std::move(SymbolToDefinition
)),
84 void materialize(std::unique_ptr
<MaterializationResponsibility
> R
) override
{
85 Parent
.emitPartition(std::move(R
), std::move(TSM
),
86 std::move(SymbolToDefinition
));
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");
96 IRPartitionLayer
&Parent
;
102 IRPartitionLayer::IRPartitionLayer(ExecutionSession
&ES
, IRLayer
&BaseLayer
)
103 : IRLayer(ES
, BaseLayer
.getManglingOptions()), BaseLayer(BaseLayer
) {}
105 void IRPartitionLayer::setPartitionFunction(PartitionFunction Partition
) {
106 this->Partition
= Partition
;
109 std::optional
<IRPartitionLayer::GlobalValueSet
>
110 IRPartitionLayer::compileRequested(GlobalValueSet Requested
) {
111 return std::move(Requested
);
114 std::optional
<IRPartitionLayer::GlobalValueSet
>
115 IRPartitionLayer::compileWholeModule(GlobalValueSet Requested
) {
119 void IRPartitionLayer::emit(std::unique_ptr
<MaterializationResponsibility
> R
,
120 ThreadSafeModule TSM
) {
121 assert(TSM
&& "Null module");
123 auto &ES
= getExecutionSession();
124 TSM
.withModuleDo([&](Module
&M
) {
125 // First, do some cleanup on the module:
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();
138 void IRPartitionLayer::cleanUpModule(Module
&M
) {
139 for (auto &F
: M
.functions()) {
140 if (F
.isDeclaration())
143 if (F
.hasAvailableExternallyLinkage()) {
145 F
.setPersonalityFn(nullptr);
151 void 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
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");
160 const Module
&M
= *(*Partition
.begin())->getParent();
161 bool ContainsGlobalVariables
= false;
162 std::vector
<const GlobalValue
*> GVsToAdd
;
164 for (const auto *GV
: Partition
)
165 if (isa
<GlobalAlias
>(GV
))
167 cast
<GlobalValue
>(cast
<GlobalAlias
>(GV
)->getAliasee()));
168 else if (isa
<GlobalVariable
>(GV
))
169 ContainsGlobalVariables
= true;
171 for (auto &A
: M
.aliases())
172 if (Partition
.count(cast
<GlobalValue
>(A
.getAliasee())))
173 GVsToAdd
.push_back(&A
);
175 if (ContainsGlobalVariables
)
176 for (auto &G
: M
.globals())
177 GVsToAdd
.push_back(&G
);
179 for (const auto *GV
: GVsToAdd
)
180 Partition
.insert(GV
);
183 void IRPartitionLayer::emitPartition(
184 std::unique_ptr
<MaterializationResponsibility
> R
, ThreadSafeModule TSM
,
185 IRMaterializationUnit::SymbolNameToDefinitionMap Defs
) {
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.
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
);
201 assert(Defs
.count(Name
) && "No definition for symbol");
202 RequestedGVs
.insert(Defs
[Name
]);
206 /// Perform partitioning with the context lock held, since the partition
207 /// function is allowed to access the globals to compute the partition.
209 TSM
.withModuleDo([&](Module
&M
) { return Partition(RequestedGVs
); });
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
) {
216 BaseLayer
.emit(std::move(R
), std::move(TSM
));
220 // If the partition is empty, return the whole module to the symbol table.
221 if (GVsToExtract
->empty()) {
223 R
->replace(std::make_unique
<PartitioningIRMaterializationUnit
>(
225 MaterializationUnit::Interface(R
->getSymbols(),
226 R
->getInitializerSymbol()),
227 std::move(Defs
), *this))) {
228 getExecutionSession().reportError(std::move(Err
));
229 R
->failMaterialization();
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.
240 // FIXME: We apply this promotion once per partitioning. It's safe, but
242 auto ExtractedTSM
= TSM
.withModuleDo([&](Module
&M
)
243 -> Expected
<ThreadSafeModule
> {
244 auto PromotedGlobals
= PromoteSymbols(M
);
245 if (!PromotedGlobals
.empty()) {
247 MangleAndInterner
Mangle(ES
, M
.getDataLayout());
248 SymbolFlagsMap SymbolFlags
;
249 IRSymbolMapper::add(ES
, *getManglingOptions(), PromotedGlobals
,
252 if (auto Err
= R
->defineMaterializing(SymbolFlags
))
253 return std::move(Err
);
256 expandPartition(*GVsToExtract
);
258 // Submodule name is given by hashing the names of the globals.
259 std::string SubModuleName
;
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();
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()));
274 raw_string_ostream(SubModuleName
)
276 << formatv(sizeof(size_t) == 8 ? "{0:x16}" : "{0:x8}",
277 static_cast<size_t>(HC
))
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
);
287 return extractSubModule(TSM
, SubModuleName
, ShouldExtract
);
291 ES
.reportError(ExtractedTSM
.takeError());
292 R
->failMaterialization();
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();
302 BaseLayer
.emit(std::move(R
), std::move(*ExtractedTSM
));