Skip to content

Commit 0e940d5

Browse files
committed
[ORC] Add TargetProcessControl and TPCIndirectionUtils APIs.
TargetProcessControl is a new API for communicating with JIT target processes. It supports memory allocation and access, and inspection of some process properties, e.g. the target proces triple and page size. Centralizing these APIs allows utilities written against TargetProcessControl to remain independent of the communication procotol with the target process (which may be direct memory access/allocation for in-process JITing, or may involve some form of IPC or RPC). An initial set of TargetProcessControl-based utilities for lazy compilation is provided by the TPCIndirectionUtils class. An initial implementation of TargetProcessControl for in-process JITing is provided by the SelfTargetProcessControl class. An example program showing how the APIs can be used is provided in llvm/examples/OrcV2Examples/LLJITWithTargetProcessControl.
1 parent 72958c9 commit 0e940d5

File tree

9 files changed

+1067
-4
lines changed

9 files changed

+1067
-4
lines changed

llvm/examples/OrcV2Examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_subdirectory(LLJITWithInitializers)
55
add_subdirectory(LLJITWithLazyReexports)
66
add_subdirectory(LLJITWithObjectCache)
77
add_subdirectory(LLJITWithObjectLinkingLayerPlugin)
8+
add_subdirectory(LLJITWithTargetProcessControl)
89
add_subdirectory(OrcV2CBindingsAddObjectFile)
910
add_subdirectory(OrcV2CBindingsBasicUsage)
1011
add_subdirectory(OrcV2CBindingsReflectProcessSymbols)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Core
3+
ExecutionEngine
4+
IRReader
5+
OrcJIT
6+
Support
7+
nativecodegen
8+
)
9+
10+
add_llvm_example(LLJITWithTargetProcessControl
11+
LLJITWithTargetProcessControl.cpp
12+
)
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
//===--- LLJITWithLazyReexports.cpp - LLJIT example with custom laziness --===//
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+
// In this example we will use the lazy re-exports utility to lazily compile
10+
// IR modules. We will do this in seven steps:
11+
//
12+
// 1. Create an LLJIT instance.
13+
// 2. Install a transform so that we can see what is being compiled.
14+
// 3. Create an indirect stubs manager and lazy call-through manager.
15+
// 4. Add two modules that will be conditionally compiled, plus a main module.
16+
// 5. Add lazy-rexports of the symbols in the conditionally compiled modules.
17+
// 6. Dump the ExecutionSession state to see the symbol table prior to
18+
// executing any code.
19+
// 7. Verify that only modules containing executed code are compiled.
20+
//
21+
//===----------------------------------------------------------------------===//
22+
23+
#include "llvm/ADT/StringMap.h"
24+
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
25+
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
26+
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
27+
#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
28+
#include "llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h"
29+
#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h"
30+
#include "llvm/Support/InitLLVM.h"
31+
#include "llvm/Support/TargetSelect.h"
32+
#include "llvm/Support/raw_ostream.h"
33+
34+
#include "../ExampleModules.h"
35+
36+
#include <future>
37+
38+
using namespace llvm;
39+
using namespace llvm::orc;
40+
41+
ExitOnError ExitOnErr;
42+
43+
// Example IR modules.
44+
//
45+
// Note that in the conditionally compiled modules, FooMod and BarMod, functions
46+
// have been given an _body suffix. This is to ensure that their names do not
47+
// clash with their lazy-reexports.
48+
// For clients who do not wish to rename function bodies (e.g. because they want
49+
// to re-use cached objects between static and JIT compiles) techniques exist to
50+
// avoid renaming. See the lazy-reexports section of the ORCv2 design doc.
51+
52+
const llvm::StringRef FooMod =
53+
R"(
54+
define i32 @foo_body() {
55+
entry:
56+
ret i32 1
57+
}
58+
)";
59+
60+
const llvm::StringRef BarMod =
61+
R"(
62+
define i32 @bar_body() {
63+
entry:
64+
ret i32 2
65+
}
66+
)";
67+
68+
const llvm::StringRef MainMod =
69+
R"(
70+
71+
define i32 @entry(i32 %argc) {
72+
entry:
73+
%and = and i32 %argc, 1
74+
%tobool = icmp eq i32 %and, 0
75+
br i1 %tobool, label %if.end, label %if.then
76+
77+
if.then: ; preds = %entry
78+
%call = tail call i32 @foo() #2
79+
br label %return
80+
81+
if.end: ; preds = %entry
82+
%call1 = tail call i32 @bar() #2
83+
br label %return
84+
85+
return: ; preds = %if.end, %if.then
86+
%retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ]
87+
ret i32 %retval.0
88+
}
89+
90+
declare i32 @foo()
91+
declare i32 @bar()
92+
)";
93+
94+
static void *reenter(void *Ctx, void *TrampolineAddr) {
95+
std::promise<void *> LandingAddressP;
96+
auto LandingAddressF = LandingAddressP.get_future();
97+
98+
auto *TPCIU = static_cast<TPCIndirectionUtils *>(Ctx);
99+
TPCIU->getLazyCallThroughManager().resolveTrampolineLandingAddress(
100+
pointerToJITTargetAddress(TrampolineAddr),
101+
[&](JITTargetAddress LandingAddress) {
102+
LandingAddressP.set_value(
103+
jitTargetAddressToPointer<void *>(LandingAddress));
104+
});
105+
return LandingAddressF.get();
106+
}
107+
108+
cl::list<std::string> InputArgv(cl::Positional,
109+
cl::desc("<program arguments>..."));
110+
111+
int main(int argc, char *argv[]) {
112+
// Initialize LLVM.
113+
InitLLVM X(argc, argv);
114+
115+
InitializeNativeTarget();
116+
InitializeNativeTargetAsmPrinter();
117+
118+
cl::ParseCommandLineOptions(argc, argv, "LLJITWithLazyReexports");
119+
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
120+
121+
// (1) Create LLJIT instance.
122+
auto J = ExitOnErr(LLJITBuilder().create());
123+
124+
// (2) Install transform to print modules as they are compiled:
125+
J->getIRTransformLayer().setTransform(
126+
[](ThreadSafeModule TSM,
127+
const MaterializationResponsibility &R) -> Expected<ThreadSafeModule> {
128+
TSM.withModuleDo([](Module &M) { dbgs() << "---Compiling---\n" << M; });
129+
return std::move(TSM); // Not a redundant move: fix build on gcc-7.5
130+
});
131+
132+
// (3) Create stubs and call-through managers:
133+
134+
auto TPC = ExitOnErr(SelfTargetProcessControl::Create());
135+
auto TPCIU = ExitOnErr(TPCIndirectionUtils::Create(*TPC));
136+
ExitOnErr(TPCIU->writeResolverBlock(pointerToJITTargetAddress(&reenter),
137+
pointerToJITTargetAddress(TPCIU.get())));
138+
TPCIU->createLazyCallThroughManager(J->getExecutionSession(), 0);
139+
auto ISM = TPCIU->createIndirectStubsManager();
140+
141+
// (4) Add modules.
142+
ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(FooMod, "foo-mod"))));
143+
ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(BarMod, "bar-mod"))));
144+
ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(MainMod, "main-mod"))));
145+
146+
// (5) Add lazy reexports.
147+
MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout());
148+
SymbolAliasMap ReExports(
149+
{{Mangle("foo"),
150+
{Mangle("foo_body"),
151+
JITSymbolFlags::Exported | JITSymbolFlags::Callable}},
152+
{Mangle("bar"),
153+
{Mangle("bar_body"),
154+
JITSymbolFlags::Exported | JITSymbolFlags::Callable}}});
155+
ExitOnErr(J->getMainJITDylib().define(
156+
lazyReexports(TPCIU->getLazyCallThroughManager(), *ISM,
157+
J->getMainJITDylib(), std::move(ReExports))));
158+
159+
// (6) Dump the ExecutionSession state.
160+
dbgs() << "---Session state---\n";
161+
J->getExecutionSession().dump(dbgs());
162+
dbgs() << "\n";
163+
164+
// (7) Execute the JIT'd main function and pass the example's command line
165+
// arguments unmodified. This should cause either ExampleMod1 or ExampleMod2
166+
// to be compiled, and either "1" or "2" returned depending on the number of
167+
// arguments passed.
168+
169+
// Look up the JIT'd function, cast it to a function pointer, then call it.
170+
auto EntrySym = ExitOnErr(J->lookup("entry"));
171+
auto *Entry = (int (*)(int))EntrySym.getAddress();
172+
173+
int Result = Entry(argc);
174+
outs() << "---Result---\n"
175+
<< "entry(" << argc << ") = " << Result << "\n";
176+
177+
return 0;
178+
}

llvm/include/llvm/ExecutionEngine/Orc/LazyReexports.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class LazyCallThroughManager {
4040
using NotifyResolvedFunction =
4141
unique_function<Error(JITTargetAddress ResolvedAddr)>;
4242

43+
LazyCallThroughManager(ExecutionSession &ES,
44+
JITTargetAddress ErrorHandlerAddr, TrampolinePool *TP);
45+
4346
// Return a free call-through trampoline and bind it to look up and call
4447
// through to the given symbol.
4548
Expected<JITTargetAddress>
@@ -56,9 +59,6 @@ class LazyCallThroughManager {
5659
using NotifyLandingResolvedFunction =
5760
TrampolinePool::NotifyLandingResolvedFunction;
5861

59-
LazyCallThroughManager(ExecutionSession &ES,
60-
JITTargetAddress ErrorHandlerAddr, TrampolinePool *TP);
61-
6262
struct ReexportsEntry {
6363
JITDylib *SourceJD;
6464
SymbolStringPtr SymbolName;

0 commit comments

Comments
 (0)