Skip to content

Commit 570ecdc

Browse files
authored
[ORC] Introduce LazyReexportsManager, JITLinkTrampolines, ORC-RT base… (llvm#118923)
…d reentry. These utilities provide new, more generic and easier to use support for lazy compilation in ORC. LazyReexportsManager is an alternative to LazyCallThroughManager. It takes requests for lazy re-entry points in the form of an alias map: lazy-reexports = { ( <entry point symbol rust-lang#1>, <implementation symbol rust-lang#1> ), ( <entry point symbol rust-lang#2>, <implementation symbol rust-lang#2> ), ... ( <entry point symbol #n>, <implementation symbol #n> ) } LazyReexportsManager then: 1. binds the entry points to the implementation names in an internal table. 2. creates a JIT re-entry trampoline for each entry point. 3. creates a redirectable symbol for each of the entry point name and binds redirectable symbol to the corresponding reentry trampoline. When an entry point symbol is first called at runtime (which may be on any thread of the JIT'd program) it will re-enter the JIT via the trampoline and trigger a lookup for the implementation symbol stored in LazyReexportsManager's internal table. When the lookup completes the entry point symbol will be updated (via the RedirectableSymbolManager) to point at the implementation symbol, and execution will proceed to the implementation symbol. Actual construction of the re-entry trampolines and redirectable symbols is delegated to an EmitTrampolines functor and the RedirectableSymbolsManager respectively. JITLinkReentryTrampolines.h provides a JITLink-based implementation of the EmitTrampolines functor. (AArch64 only in this patch, but other architectures will be added in the near future). Register state save and reentry functionality is added to the ORC runtime in the __orc_rt_sysv_resolve and __orc_rt_resolve_implementation functions (the latter is generic, the former will need custom implementations for each ABI and architecture to be supported, however this should be much less effort than the existing OrcABISupport approach, since the ORC runtime allows this code to be written as native assembly). The resulting system: 1. Works equally well for in-process and out-of-process JIT'd code. 2. Requires less boilerplate to set up. Given an ObjectLinkingLayer and PlatformJD (JITDylib containing the ORC runtime), setup is just: ```c++ auto RSMgr = JITLinkRedirectableSymbolManager::Create(OLL); if (!RSMgr) return RSMgr.takeError(); auto LRMgr = createJITLinkLazyReexportsManager(OLL, **RSMgr, PlatformJD); if (!LRMgr) return LRMgr.takeError(); ``` after which lazy reexports can be introduced with: ```c++ JD.define(lazyReexports(LRMgr, <alias map>)); ``` LazyObectLinkingLayer is updated to use this new method, but the LLVM-IR level CompileOnDemandLayer will continue to use LazyCallThroughManager and OrcABISupport until the new system supports a wider range of architectures and ABIs. The llvm-jitlink utility's -lazy option now uses the new scheme. Since it depends on the ORC runtime, the lazy-link.ll testcase and associated helpers are moved to the ORC runtime.
1 parent 156da98 commit 570ecdc

26 files changed

+814
-75
lines changed

compiler-rt/lib/orc/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ if (APPLE)
5151
set(ORC_ASM_SOURCES
5252
macho_tlv.x86-64.S
5353
macho_tlv.arm64.S
54+
sysv_reentry.arm64.S
5455
)
5556

5657
set(ORC_IMPL_HEADERS
@@ -61,6 +62,7 @@ if (APPLE)
6162
set(ORC_SOURCES
6263
${ORC_COMMON_SOURCES}
6364
macho_platform.cpp
65+
sysv_resolve.cpp
6466
)
6567

6668
add_compiler_rt_object_libraries(RTOrc
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===-- sysv_reentry.arm64.s ------------------------------------*- ASM -*-===//
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+
// This file is a part of the ORC runtime support library.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// The content of this file is arm64-only
14+
#if defined(__arm64__) || defined(__aarch64__)
15+
16+
.text
17+
18+
// Saves GPRs, calls __orc_rt_sysv_resolve
19+
.globl __orc_rt_sysv_reentry
20+
__orc_rt_sysv_reentry:
21+
// Save register state, set up new stack frome.
22+
stp x27, x28, [sp, #-16]!
23+
stp x25, x26, [sp, #-16]!
24+
stp x23, x24, [sp, #-16]!
25+
stp x21, x22, [sp, #-16]!
26+
stp x19, x20, [sp, #-16]!
27+
stp x14, x15, [sp, #-16]!
28+
stp x12, x13, [sp, #-16]!
29+
stp x10, x11, [sp, #-16]!
30+
stp x8, x9, [sp, #-16]!
31+
stp x6, x7, [sp, #-16]!
32+
stp x4, x5, [sp, #-16]!
33+
stp x2, x3, [sp, #-16]!
34+
stp x0, x1, [sp, #-16]!
35+
stp q30, q31, [sp, #-32]!
36+
stp q28, q29, [sp, #-32]!
37+
stp q26, q27, [sp, #-32]!
38+
stp q24, q25, [sp, #-32]!
39+
stp q22, q23, [sp, #-32]!
40+
stp q20, q21, [sp, #-32]!
41+
stp q18, q19, [sp, #-32]!
42+
stp q16, q17, [sp, #-32]!
43+
stp q14, q15, [sp, #-32]!
44+
stp q12, q13, [sp, #-32]!
45+
stp q10, q11, [sp, #-32]!
46+
stp q8, q9, [sp, #-32]!
47+
stp q6, q7, [sp, #-32]!
48+
stp q4, q5, [sp, #-32]!
49+
stp q2, q3, [sp, #-32]!
50+
stp q0, q1, [sp, #-32]!
51+
52+
// Look up the return address and subtract 8 from it (on the
53+
// assumption that it's a standard arm64 reentry trampoline) to get
54+
// back the trampoline's address.
55+
sub x0, x30, #8
56+
57+
// Call __orc_rt_sysv_resolve to look up the implementation
58+
// corresponding to the calling stub, then store this in x17 (which
59+
// we'll return to below.
60+
#if !defined(__APPLE__)
61+
bl __orc_rt_sysv_resolve
62+
#else
63+
bl ___orc_rt_sysv_resolve
64+
#endif
65+
mov x17, x0
66+
67+
// Restore the register state.
68+
ldp q0, q1, [sp], #32
69+
ldp q2, q3, [sp], #32
70+
ldp q4, q5, [sp], #32
71+
ldp q6, q7, [sp], #32
72+
ldp q8, q9, [sp], #32
73+
ldp q10, q11, [sp], #32
74+
ldp q12, q13, [sp], #32
75+
ldp q14, q15, [sp], #32
76+
ldp q16, q17, [sp], #32
77+
ldp q18, q19, [sp], #32
78+
ldp q20, q21, [sp], #32
79+
ldp q22, q23, [sp], #32
80+
ldp q24, q25, [sp], #32
81+
ldp q26, q27, [sp], #32
82+
ldp q28, q29, [sp], #32
83+
ldp q30, q31, [sp], #32
84+
ldp x0, x1, [sp], #16
85+
ldp x2, x3, [sp], #16
86+
ldp x4, x5, [sp], #16
87+
ldp x6, x7, [sp], #16
88+
ldp x8, x9, [sp], #16
89+
ldp x10, x11, [sp], #16
90+
ldp x12, x13, [sp], #16
91+
ldp x14, x15, [sp], #16
92+
ldp x19, x20, [sp], #16
93+
ldp x21, x22, [sp], #16
94+
ldp x23, x24, [sp], #16
95+
ldp x25, x26, [sp], #16
96+
ldp x27, x28, [sp], #16
97+
ldp x29, x30, [sp], #16
98+
99+
// Return to the function implementation (rather than the stub).
100+
ret x17
101+
102+
#endif // defined(__arm64__) || defined(__aarch64__)

compiler-rt/lib/orc/sysv_resolve.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===- sysv_resolve.cpp ---------------------------------------------------===//
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+
// This file contains a generic "resolver" function compatible with the SysV
10+
// ABI.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "executor_symbol_def.h"
15+
#include "jit_dispatch.h"
16+
#include "wrapper_function_utils.h"
17+
18+
#include <stdio.h>
19+
20+
#define DEBUG_TYPE "sysv_resolve"
21+
22+
using namespace orc_rt;
23+
24+
// Declare function tags for functions in the JIT process.
25+
ORC_RT_JIT_DISPATCH_TAG(__orc_rt_resolve_tag)
26+
27+
// FIXME: Make this configurable via an alias.
28+
static void __orc_rt_sysv_fail(void *Caller, const char *ErrMsg) {
29+
fprintf(stderr, "error resolving implementation for stub %p: %s\n", Caller,
30+
ErrMsg);
31+
abort();
32+
}
33+
34+
extern "C" ORC_RT_HIDDEN void *__orc_rt_sysv_resolve(void *Caller) {
35+
Expected<ExecutorSymbolDef> Result((ExecutorSymbolDef()));
36+
if (auto Err = WrapperFunction<SPSExpected<SPSExecutorSymbolDef>(
37+
SPSExecutorAddr)>::call(JITDispatch(&__orc_rt_resolve_tag), Result,
38+
ExecutorAddr::fromPtr(Caller))) {
39+
__orc_rt_sysv_fail(Caller, toString(std::move(Err)).c_str());
40+
return nullptr; // Unreachable.
41+
}
42+
43+
if (!Result) {
44+
__orc_rt_sysv_fail(Caller, toString(Result.takeError()).c_str());
45+
return nullptr; // Unreachable.
46+
}
47+
48+
return Result->getAddress().toPtr<void *>();
49+
}

llvm/test/ExecutionEngine/JITLink/Generic/lazy-link.ll compiler-rt/test/orc/TestCases/Generic/lazy-link.ll

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
; referenced by main, should be linked (despite being passed with -lazy).
55
;
66
; 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 \
7+
; RUN: %clang -c -o %t/foo.o %S/Inputs/foo-ret-42.ll
8+
; RUN: %clang -c -o %t/x.o %S/Inputs/var-x-42.ll
9+
; RUN: %clang -c -o %t/main.o %s
10+
; RUN: %llvm_jitlink -noexec -show-linked-files %t/main.o -lazy %t/foo.o \
1111
; RUN: -lazy %t/x.o | FileCheck %s
1212
;
13-
; UNSUPPORTED: system-windows, target={{arm[^6][^4].*}}, target=powerpc64{{.*}}
13+
; UNSUPPORTED: system-windows
14+
; REQUIRES: target={{(arm|aarch)64.*}}
1415
;
1516
; CHECK: Linking {{.*}}main.o
1617
; CHECK-DAG: Linking <indirect stubs graph #1>

compiler-rt/test/orc/TestCases/Generic/orc-rt-executor-usage.test

-6
This file was deleted.

compiler-rt/test/orc/lit.cfg.py

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
host_arch_compatible = True
1717
if host_arch_compatible:
1818
config.available_features.add("host-arch-compatible")
19+
20+
# If the target OS hasn't been set then assume host.
21+
if not config.target_os:
22+
config.target_os = config.host_os
23+
1924
config.test_target_is_host_executable = (
2025
config.target_os == config.host_os and host_arch_compatible
2126
)

llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h

+26
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,32 @@ inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
755755
sizeof(PointerJumpStubContent), true, false);
756756
}
757757

758+
/// AArch64 reentry trampoline.
759+
///
760+
/// Contains the instruction sequence for a trampoline that stores its return
761+
/// address on the stack and passes its own address in x0:
762+
/// STP x29, x30, [sp, #-16]!
763+
/// BL <reentry-symbol>
764+
extern const char ReentryTrampolineContent[8];
765+
766+
/// Create a block of N reentry trampolines.
767+
inline Block &createReentryTrampolineBlock(LinkGraph &G,
768+
Section &TrampolineSection,
769+
Symbol &ReentrySymbol) {
770+
auto &B = G.createContentBlock(TrampolineSection, ReentryTrampolineContent,
771+
orc::ExecutorAddr(~uint64_t(7)), 4, 0);
772+
B.addEdge(Branch26PCRel, 4, ReentrySymbol, 0);
773+
return B;
774+
}
775+
776+
inline Symbol &createAnonymousReentryTrampoline(LinkGraph &G,
777+
Section &TrampolineSection,
778+
Symbol &ReentrySymbol) {
779+
return G.addAnonymousSymbol(
780+
createReentryTrampolineBlock(G, TrampolineSection, ReentrySymbol), 0,
781+
sizeof(ReentryTrampolineContent), true, false);
782+
}
783+
758784
/// Global Offset Table Builder.
759785
class GOTTableManager : public TableManager<GOTTableManager> {
760786
public:

llvm/include/llvm/ExecutionEngine/Orc/Core.h

+27
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ enum class SymbolState : uint8_t;
5151
using ResourceTrackerSP = IntrusiveRefCntPtr<ResourceTracker>;
5252
using JITDylibSP = IntrusiveRefCntPtr<JITDylib>;
5353

54+
/// A definition of a Symbol within a JITDylib.
55+
class SymbolInstance {
56+
public:
57+
using LookupAsyncOnCompleteFn =
58+
unique_function<void(Expected<ExecutorSymbolDef>)>;
59+
60+
SymbolInstance(JITDylibSP JD, SymbolStringPtr Name)
61+
: JD(std::move(JD)), Name(std::move(Name)) {}
62+
63+
const JITDylib &getJITDylib() const { return *JD; }
64+
const SymbolStringPtr &getName() const { return Name; }
65+
66+
Expected<ExecutorSymbolDef> lookup() const;
67+
void lookupAsync(LookupAsyncOnCompleteFn OnComplete) const;
68+
69+
private:
70+
JITDylibSP JD;
71+
SymbolStringPtr Name;
72+
};
73+
5474
using ResourceKey = uintptr_t;
5575

5676
/// API to remove / transfer ownership of JIT resources.
@@ -550,6 +570,9 @@ class MaterializationResponsibility {
550570
/// emitted or notified of an error.
551571
~MaterializationResponsibility();
552572

573+
/// Return the ResourceTracker associated with this instance.
574+
const ResourceTrackerSP &getResourceTracker() const { return RT; }
575+
553576
/// Runs the given callback under the session lock, passing in the associated
554577
/// ResourceKey. This is the safe way to associate resources with trackers.
555578
template <typename Func> Error withResourceKeyDo(Func &&F) const {
@@ -1748,6 +1771,10 @@ class ExecutionSession {
17481771
JITDispatchHandlers;
17491772
};
17501773

1774+
inline Expected<ExecutorSymbolDef> SymbolInstance::lookup() const {
1775+
return JD->getExecutionSession().lookup({JD.get()}, Name);
1776+
}
1777+
17511778
template <typename Func> Error ResourceTracker::withResourceKeyDo(Func &&F) {
17521779
return getJITDylib().getExecutionSession().runSessionLocked([&]() -> Error {
17531780
if (isDefunct())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===- JITLinkLazyCallThroughManager.h - JITLink based laziness -*- 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+
// Redirectable Symbol Manager implementation using JITLink
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_ORC_JITLINKLAZYCALLTHROUGHMANAGER_H
14+
#define LLVM_EXECUTIONENGINE_ORC_JITLINKLAZYCALLTHROUGHMANAGER_H
15+
16+
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
17+
#include "llvm/ExecutionEngine/Orc/RedirectionManager.h"
18+
#include "llvm/Support/StringSaver.h"
19+
20+
#include <atomic>
21+
22+
namespace llvm {
23+
namespace orc {} // namespace orc
24+
} // namespace llvm
25+
26+
#endif // LLVM_EXECUTIONENGINE_ORC_JITLINKLAZYCALLTHROUGHMANAGER_H

llvm/include/llvm/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.h

+8-6
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,6 @@ class JITLinkRedirectableSymbolManager : public RedirectableSymbolManager {
3939
ObjLinkingLayer, AnonymousPtrCreator, PtrJumpStubCreator));
4040
}
4141

42-
void emitRedirectableSymbols(std::unique_ptr<MaterializationResponsibility> R,
43-
SymbolMap InitialDests) override;
44-
45-
Error redirect(JITDylib &JD, const SymbolMap &NewDests) override;
46-
47-
private:
4842
JITLinkRedirectableSymbolManager(
4943
ObjectLinkingLayer &ObjLinkingLayer,
5044
jitlink::AnonymousPointerCreator &AnonymousPtrCreator,
@@ -53,6 +47,14 @@ class JITLinkRedirectableSymbolManager : public RedirectableSymbolManager {
5347
AnonymousPtrCreator(std::move(AnonymousPtrCreator)),
5448
PtrJumpStubCreator(std::move(PtrJumpStubCreator)) {}
5549

50+
ObjectLinkingLayer &getObjectLinkingLayer() const { return ObjLinkingLayer; }
51+
52+
void emitRedirectableSymbols(std::unique_ptr<MaterializationResponsibility> R,
53+
SymbolMap InitialDests) override;
54+
55+
Error redirect(JITDylib &JD, const SymbolMap &NewDests) override;
56+
57+
private:
5658
ObjectLinkingLayer &ObjLinkingLayer;
5759
jitlink::AnonymousPointerCreator AnonymousPtrCreator;
5860
jitlink::PointerJumpStubCreator PtrJumpStubCreator;

0 commit comments

Comments
 (0)