Skip to content

Commit 2130c1e

Browse files
committed
[ORC] Add LazyObjectLinkingLayer, add lazy-linking support to llvm-jitlink.
LazyObjectLinkingLayer can be used to add object files that will not be linked in unless they're actually called at runtime. LazyObjectLinkingLayer uses lazyReexports to insert stubs for each function in an object file, and an ObjectLinkingLayer::Plugin to rename the function bodies at link-time. The llvm-jitlink utility is extended with a -lazy option that can be passed before input files or archives to add them using the lazy linking layer rather than the base ObjectLinkingLayer.
1 parent 7111d03 commit 2130c1e

File tree

9 files changed

+324
-15
lines changed

9 files changed

+324
-15
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===- RedirectionManager.h - Redirection manager interface -----*- C++ -*-===//
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+
//===----------------------------------------------------------------------===//
8+
//
9+
// Redirection manager interface that redirects a call to symbol to another.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#ifndef LLVM_EXECUTIONENGINE_ORC_LAZYOBJECTLINKINGLAYER_H
13+
#define LLVM_EXECUTIONENGINE_ORC_LAZYOBJECTLINKINGLAYER_H
14+
15+
#include "llvm/ExecutionEngine/Orc/Core.h"
16+
#include "llvm/ExecutionEngine/Orc/Layer.h"
17+
18+
namespace llvm::orc {
19+
20+
class ObjectLinkingLayer;
21+
class LazyCallThroughManager;
22+
class RedirectableSymbolManager;
23+
24+
class LazyObjectLinkingLayer : public ObjectLayer {
25+
public:
26+
LazyObjectLinkingLayer(ObjectLinkingLayer &BaseLayer,
27+
LazyCallThroughManager &LCTMgr,
28+
RedirectableSymbolManager &RSMgr);
29+
30+
llvm::Error add(llvm::orc::ResourceTrackerSP RT,
31+
std::unique_ptr<llvm::MemoryBuffer> O,
32+
llvm::orc::MaterializationUnit::Interface I) override;
33+
34+
void emit(std::unique_ptr<MaterializationResponsibility> R,
35+
std::unique_ptr<MemoryBuffer> O) override;
36+
37+
private:
38+
class RenamerPlugin;
39+
40+
ObjectLinkingLayer &BaseLayer;
41+
LazyCallThroughManager &LCTMgr;
42+
RedirectableSymbolManager &RSMgr;
43+
};
44+
45+
} // namespace llvm::orc
46+
47+
#endif // LLVM_EXECUTIONENGINE_ORC_LAZYOBJECTLINKINGLAYER_H

llvm/lib/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_llvm_component_library(LLVMOrcJIT
2828
IRTransformLayer.cpp
2929
IRPartitionLayer.cpp
3030
JITTargetMachineBuilder.cpp
31+
LazyObjectLinkingLayer.cpp
3132
LazyReexports.cpp
3233
Layer.cpp
3334
LoadLinkableFile.cpp

llvm/lib/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void JITLinkRedirectableSymbolManager::emitRedirectableSymbols(
2929
Triple TT = ES.getTargetTriple();
3030

3131
auto G = std::make_unique<jitlink::LinkGraph>(
32-
("<INDIRECT STUBS #" + Twine(++StubGraphIdx) + ">").str(), TT,
32+
("<indirect stubs graph #" + Twine(++StubGraphIdx) + ">").str(), TT,
3333
TT.isArch64Bit() ? 8 : 4,
3434
TT.isLittleEndian() ? endianness::little : endianness::big,
3535
jitlink::getGenericEdgeKindName);
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===//
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+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/ExecutionEngine/Orc/LazyObjectLinkingLayer.h"
10+
11+
#include "llvm/ExecutionEngine/Orc/LazyReexports.h"
12+
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
13+
#include "llvm/ExecutionEngine/Orc/RedirectionManager.h"
14+
15+
using namespace llvm;
16+
using namespace llvm::jitlink;
17+
18+
namespace {
19+
20+
constexpr StringRef FnBodySuffix = "$orc_fnbody";
21+
22+
} // anonymous namespace
23+
24+
namespace llvm::orc {
25+
26+
class LazyObjectLinkingLayer::RenamerPlugin
27+
: public ObjectLinkingLayer::Plugin {
28+
public:
29+
void modifyPassConfig(MaterializationResponsibility &MR,
30+
jitlink::LinkGraph &LG,
31+
jitlink::PassConfiguration &Config) override {
32+
// We need to insert this before the mark-live pass to ensure that we don't
33+
// delete the bodies (their names won't match the responsibility set until
34+
// after this pass completes.
35+
Config.PrePrunePasses.insert(
36+
Config.PrePrunePasses.begin(),
37+
[&MR](LinkGraph &G) { return renameFunctionBodies(G, MR); });
38+
}
39+
40+
Error notifyFailed(MaterializationResponsibility &MR) override {
41+
return Error::success();
42+
}
43+
44+
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
45+
return Error::success();
46+
}
47+
48+
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
49+
ResourceKey SrcKey) override {}
50+
51+
private:
52+
static Error renameFunctionBodies(LinkGraph &G,
53+
MaterializationResponsibility &MR) {
54+
DenseMap<StringRef, NonOwningSymbolStringPtr> SymsToRename;
55+
for (auto &[Name, Flags] : MR.getSymbols())
56+
if ((*Name).ends_with(FnBodySuffix))
57+
SymsToRename[(*Name).drop_back(FnBodySuffix.size())] =
58+
NonOwningSymbolStringPtr(Name);
59+
60+
for (auto *Sym : G.defined_symbols()) {
61+
if (!Sym->hasName())
62+
continue;
63+
auto I = SymsToRename.find(Sym->getName());
64+
if (I == SymsToRename.end())
65+
continue;
66+
Sym->setName(G.allocateName(*I->second));
67+
}
68+
69+
return Error::success();
70+
}
71+
};
72+
73+
LazyObjectLinkingLayer::LazyObjectLinkingLayer(ObjectLinkingLayer &BaseLayer,
74+
LazyCallThroughManager &LCTMgr,
75+
RedirectableSymbolManager &RSMgr)
76+
: ObjectLayer(BaseLayer.getExecutionSession()), BaseLayer(BaseLayer),
77+
LCTMgr(LCTMgr), RSMgr(RSMgr) {
78+
BaseLayer.addPlugin(std::make_unique<RenamerPlugin>());
79+
}
80+
81+
Error LazyObjectLinkingLayer::add(ResourceTrackerSP RT,
82+
std::unique_ptr<MemoryBuffer> O,
83+
MaterializationUnit::Interface I) {
84+
85+
// Object files with initializer symbols can't be lazy.
86+
if (I.InitSymbol)
87+
return BaseLayer.add(std::move(RT), std::move(O), std::move(I));
88+
89+
auto &ES = getExecutionSession();
90+
SymbolAliasMap LazySymbols;
91+
for (auto &[Name, Flags] : I.SymbolFlags)
92+
if (Flags.isCallable())
93+
LazySymbols[Name] = {ES.intern((*Name + FnBodySuffix).str()), Flags};
94+
95+
for (auto &[Name, AI] : LazySymbols) {
96+
I.SymbolFlags.erase(Name);
97+
I.SymbolFlags[AI.Aliasee] = AI.AliasFlags;
98+
}
99+
100+
if (auto Err = BaseLayer.add(RT, std::move(O), std::move(I)))
101+
return Err;
102+
103+
auto &JD = RT->getJITDylib();
104+
return JD.define(lazyReexports(LCTMgr, RSMgr, JD, std::move(LazySymbols)),
105+
std::move(RT));
106+
}
107+
108+
void LazyObjectLinkingLayer::emit(
109+
std::unique_ptr<MaterializationResponsibility> MR,
110+
std::unique_ptr<MemoryBuffer> Obj) {
111+
return BaseLayer.emit(std::move(MR), std::move(Obj));
112+
}
113+
114+
} // namespace llvm::orc
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
define i32 @foo() {
2+
entry:
3+
ret i32 42
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@x = global i32 42
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
; Check that files passed with the -lazy option aren't linked unless they're
2+
; needed. The foo-ret-42.ll file, which contains only code, should not be
3+
; needed in this -noexec case, whereas x.o, which contains a global variable
4+
; referenced by main, should be linked (despite being passed with -lazy).
5+
;
6+
; RUN: rm -rf %t && mkdir -p %t
7+
; RUN: llc -filetype=obj -o %t/foo.o %S/Inputs/foo-ret-42.ll
8+
; RUN: llc -filetype=obj -o %t/x.o %S/Inputs/var-x-42.ll
9+
; RUN: llc -filetype=obj -o %t/main.o %s
10+
; RUN: llvm-jitlink -noexec -show-linked-files %t/main.o -lazy %t/foo.o \
11+
; RUN: -lazy %t/x.o | FileCheck %s
12+
13+
; CHECK: Linking {{.*}}main.o
14+
; CHECK-DAG: Linking <indirect stubs graph #1>
15+
; CHECK-DAG: Linking {{.*}}x.o
16+
; CHECK-NOT: Linking {{.*}}foo.o
17+
18+
declare i32 @foo()
19+
@x = external global i32
20+
21+
define i32 @main(i32 %argc, ptr %argv) {
22+
entry:
23+
%foo_result = call i32 @foo()
24+
%x_val = load i32, ptr @x
25+
%result = add nsw i32 %foo_result, %x_val
26+
ret i32 %result
27+
}

0 commit comments

Comments
 (0)