Skip to content

Commit 224290d

Browse files
authored
[ORC] Add LazyObjectLinkingLayer, lazy-linking support to llvm-jitlink (#116002)
LazyObjectLinkingLayer can be used to add object files that will not be linked into the executor unless some function that they define is called at runtime. (References to data members defined by these objects will still trigger immediate linking) To implement lazy linking, LazyObjectLinkingLayer uses the lazyReexports utility to construct stubs for each function in a given object file, and an ObjectLinkingLayer::Plugin to rename the function bodies at link-time. (Data symbols are not renamed) 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 521c996 commit 224290d

File tree

9 files changed

+326
-15
lines changed

9 files changed

+326
-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
@@ -29,6 +29,7 @@ add_llvm_component_library(LLVMOrcJIT
2929
IRTransformLayer.cpp
3030
IRPartitionLayer.cpp
3131
JITTargetMachineBuilder.cpp
32+
LazyObjectLinkingLayer.cpp
3233
LazyReexports.cpp
3334
Layer.cpp
3435
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
; UNSUPPORTED: system-windows
14+
;
15+
; CHECK: Linking {{.*}}main.o
16+
; CHECK-DAG: Linking <indirect stubs graph #1>
17+
; CHECK-DAG: Linking {{.*}}x.o
18+
; CHECK-NOT: Linking {{.*}}foo.o
19+
20+
declare i32 @foo()
21+
@x = external global i32
22+
23+
define i32 @main(i32 %argc, ptr %argv) {
24+
entry:
25+
%foo_result = call i32 @foo()
26+
%x_val = load i32, ptr @x
27+
%result = add nsw i32 %foo_result, %x_val
28+
ret i32 %result
29+
}

0 commit comments

Comments
 (0)