Skip to content

Commit 371cb1a

Browse files
committed
[ORC] LLJIT updates: ExecutorNativePlatform, default link order, Process JD.
This commit includes several related ergonomic improvements to LLJIT. (1) Adds a default JITDylibSearchOrder to be appended to the initial link order of JITDylibs created via LLJIT::createJITDylib (dropping any duplicate entries). This was introduced to support automatic reflection of process symbols (see (2) below), but has been made visible to clients as it's generically useful, e.g. if clients have some extra set of libraries that they want to be visible to JIT'd code by default. The default JITDylibSearchOrder is only appended to the link order of JITDylibs created via LLJIT::createJITDylib, and will not be apply to JITDylibs created by directly calling the underlying ExecutionSession -- in that case clients can set up the link order manually. (2) Makes process symbols visible to JIT'd code by default via the new "Process" JITDylib, which is added to the default link order. LLJIT clients usually want symbols in the executor process to be accessible to JIT'd code. Until now clients have been left to set this up themselves by adding a DynamicLibrarySearchGenerator to the Main JITDylib. This patch adds a new process symbols JITDylib that will be created by default (with an EPCDynamicLibrarySearchGenerator attached) and added to the default link order, making process symbols available to JIT'd code. Clients who do not want process symbols to be visible to JIT'd code by default can call setLinkProcessSymbolsByDefault(false) on their LLJITBuilder to disable this: LLJITBuilder() ... .setLinkProcessSymbolsByDefault(false) ... .create(); Clients can also call setProcessSymbolsJITDylibSetup to take over responsibility for configuring the process symbols JITDylib (the callback that the client supplies will be called on the bare process symbols JITDylib immediately after it is created). If setLinkProcessSymbolsByDefault(false) is called and no JITDylib setup callback has been set then the process symbols JITDylib will not be created and LLJIT::getProcessSymbolsJITDylib will return null. (3) Adds an ExecutorNativePlatform utility that makes it easier to enable native platform features. Some object format features (e.g. native static initializers and thread locals) require runtime support in the executing process. Support for these features in ORC is implemented cooperatively between the ORC runtime and the LLVM Platform subclasses (COFFPlatform, ELFNixPlatform, and MachOPlatform). ExecutorNativePlatfrom simplifies the process of loading the ORC runtime and creating the appropriate platform class for the executor process. ExecutorNativePlatform takes a path to the ORC runtime (or a MemoryBuffer containing the runtime) and other required runtimes for the executor platform (e.g. MSVC on Windows) and then configures LLJIT with an appropriate platform class based on the executor's target triple: LLJITBuilder() .setPlatformSetUp(ExecutorNativePlatform("/path/to/orc-runtime.a")); (The ORC runtime is built as part of compiler-rt, and the exact name of the archive is platform dependent). The ORC runtime and platform symbols will be added to a new "Platform" JITDylib, which will be added to the *front* of the default link order (so JIT'd code will prefer symbol definitions in the platform/runtime to definitions in the executor process). ExecutorNativePlatform assumes that the Process JITDylib is available, as the ORC runtime may depend on symbols provided by the executor process. Differential Revision: https://reviews.llvm.org/D144276
1 parent 4665f3c commit 371cb1a

File tree

6 files changed

+388
-147
lines changed

6 files changed

+388
-147
lines changed

llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ class COFFPlatform : public Platform {
3939

4040
/// Try to create a COFFPlatform instance, adding the ORC runtime to the
4141
/// given JITDylib.
42+
static Expected<std::unique_ptr<COFFPlatform>>
43+
Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
44+
JITDylib &PlatformJD,
45+
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
46+
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime = false,
47+
const char *VCRuntimePath = nullptr,
48+
std::optional<SymbolAliasMap> RuntimeAliases = std::nullopt);
49+
4250
static Expected<std::unique_ptr<COFFPlatform>>
4351
Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
4452
JITDylib &PlatformJD, const char *OrcRuntimePath,
@@ -136,10 +144,14 @@ class COFFPlatform : public Platform {
136144

137145
static bool supportedTarget(const Triple &TT);
138146

139-
COFFPlatform(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
140-
JITDylib &PlatformJD, const char *OrcRuntimePath,
141-
LoadDynamicLibrary LoadDynamicLibrary, bool StaticVCRuntime,
142-
const char *VCRuntimePath, Error &Err);
147+
COFFPlatform(
148+
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
149+
JITDylib &PlatformJD,
150+
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
151+
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
152+
std::unique_ptr<object::Archive> OrcRuntimeArchive,
153+
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
154+
const char *VCRuntimePath, Error &Err);
143155

144156
// Associate COFFPlatform JIT-side runtime support functions with handlers.
145157
Error associateRuntimeSupportFunctions(JITDylib &PlatformJD);

llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ExecutorProcessControl;
3737
class LLJIT {
3838
template <typename, typename, typename> friend class LLJITBuilderSetters;
3939

40-
friend void setUpGenericLLVMIRPlatform(LLJIT &J);
40+
friend Expected<JITDylibSP> setUpGenericLLVMIRPlatform(LLJIT &J);
4141

4242
public:
4343
/// Initializer support for LLJIT.
@@ -70,6 +70,20 @@ class LLJIT {
7070
/// Returns a reference to the JITDylib representing the JIT'd main program.
7171
JITDylib &getMainJITDylib() { return *Main; }
7272

73+
/// Returns the ProcessSymbols JITDylib, which by default reflects non-JIT'd
74+
/// symbols in the host process.
75+
///
76+
/// Note: JIT'd code should not be added to the ProcessSymbols JITDylib. Use
77+
/// the main JITDylib or a custom JITDylib instead.
78+
JITDylibSP getProcessSymbolsJITDylib();
79+
80+
/// Returns the Platform JITDylib, which will contain the ORC runtime (if
81+
/// given) and any platform symbols.
82+
///
83+
/// Note: JIT'd code should not be added to the Platform JITDylib. Use the
84+
/// main JITDylib or a custom JITDylib instead.
85+
JITDylibSP getPlatformJITDylib();
86+
7387
/// Returns the JITDylib with the given name, or nullptr if no JITDylib with
7488
/// that name exists.
7589
JITDylib *getJITDylibByName(StringRef Name) {
@@ -108,9 +122,12 @@ class LLJIT {
108122
/// input or elsewhere in the environment then the client should check
109123
/// (e.g. by calling getJITDylibByName) that the given name is not already in
110124
/// use.
111-
Expected<JITDylib &> createJITDylib(std::string Name) {
112-
return ES->createJITDylib(std::move(Name));
113-
}
125+
Expected<JITDylib &> createJITDylib(std::string Name);
126+
127+
/// Returns the default link order for this LLJIT instance. This link order
128+
/// will be appended to the link order of JITDylibs created by LLJIT's
129+
/// createJITDylib method.
130+
JITDylibSearchOrder defaultLinkOrder() { return DefaultLinks; }
114131

115132
/// Adds an IR module with the given ResourceTracker.
116133
Error addIRModule(ResourceTrackerSP RT, ThreadSafeModule TSM);
@@ -229,6 +246,10 @@ class LLJIT {
229246
std::unique_ptr<PlatformSupport> PS;
230247

231248
JITDylib *Main = nullptr;
249+
JITDylib *ProcessSymbols = nullptr;
250+
JITDylib *Platform = nullptr;
251+
252+
JITDylibSearchOrder DefaultLinks;
232253

233254
DataLayout DL;
234255
Triple TT;
@@ -284,12 +305,17 @@ class LLJITBuilderState {
284305
std::function<Expected<std::unique_ptr<IRCompileLayer::IRCompiler>>(
285306
JITTargetMachineBuilder JTMB)>;
286307

287-
using PlatformSetupFunction = std::function<Error(LLJIT &J)>;
308+
using ProcessSymbolsJITDylibSetupFunction =
309+
std::function<Error(JITDylib &JD)>;
310+
311+
using PlatformSetupFunction = unique_function<Expected<JITDylibSP>(LLJIT &J)>;
288312

289313
std::unique_ptr<ExecutorProcessControl> EPC;
290314
std::unique_ptr<ExecutionSession> ES;
291315
std::optional<JITTargetMachineBuilder> JTMB;
292316
std::optional<DataLayout> DL;
317+
bool LinkProcessSymbolsByDefault = true;
318+
ProcessSymbolsJITDylibSetupFunction SetupProcessSymbolsJITDylib;
293319
ObjectLinkingLayerCreator CreateObjectLinkingLayer;
294320
CompileFunctionCreator CreateCompileFunction;
295321
PlatformSetupFunction SetUpPlatform;
@@ -342,6 +368,28 @@ class LLJITBuilderSetters {
342368
return impl();
343369
}
344370

371+
/// The LinkProcessSymbolsDyDefault flag determines whether the "Process"
372+
/// JITDylib will be added to the default link order at LLJIT construction
373+
/// time. If true, the Process JITDylib will be added as the last item in the
374+
/// default link order. If false (or if the Process JITDylib is disabled via
375+
/// setProcessSymbolsJITDylibSetup) then the Process JITDylib will not appear
376+
/// in the default link order.
377+
SetterImpl &setLinkProcessSymbolsByDefault(bool LinkProcessSymbolsByDefault) {
378+
impl().LinkProcessSymbolsByDefault = LinkProcessSymbolsByDefault;
379+
return impl();
380+
}
381+
382+
/// Set a setup function for the process symbols dylib. If not provided,
383+
/// but LinkProcessSymbolsJITDylibByDefault is true, then the process-symbols
384+
/// JITDylib will be configured with a DynamicLibrarySearchGenerator with a
385+
/// default symbol filter.
386+
SetterImpl &setProcessSymbolsJITDylibSetup(
387+
LLJITBuilderState::ProcessSymbolsJITDylibSetupFunction
388+
SetupProcessSymbolsJITDylib) {
389+
impl().SetupProcessSymbolsJITDylib = std::move(SetupProcessSymbolsJITDylib);
390+
return impl();
391+
}
392+
345393
/// Set an ObjectLinkingLayer creation function.
346394
///
347395
/// If this method is not called, a default creation function will be used
@@ -473,20 +521,49 @@ class LLLazyJITBuilder
473521
public LLLazyJITBuilderSetters<LLLazyJIT, LLLazyJITBuilder,
474522
LLLazyJITBuilderState> {};
475523

476-
/// Configure the LLJIT instance to use orc runtime support.
477-
Error setUpOrcPlatform(LLJIT& J);
524+
/// Configure the LLJIT instance to use orc runtime support. This overload
525+
/// assumes that the client has manually configured a Platform object.
526+
Error setUpOrcPlatformManually(LLJIT &J);
527+
528+
/// Configure the LLJIT instance to use the ORC runtime and the detected
529+
/// native target for the executor.
530+
class ExecutorNativePlatform {
531+
public:
532+
/// Set up using path to Orc runtime.
533+
ExecutorNativePlatform(std::string OrcRuntimePath)
534+
: OrcRuntime(std::move(OrcRuntimePath)) {}
535+
536+
/// Set up using the given memory buffer.
537+
ExecutorNativePlatform(std::unique_ptr<MemoryBuffer> OrcRuntimeMB)
538+
: OrcRuntime(std::move(OrcRuntimeMB)) {}
539+
540+
// TODO: add compiler-rt.
541+
542+
/// Add a path to the VC runtime.
543+
ExecutorNativePlatform &addVCRuntime(std::string VCRuntimePath,
544+
bool StaticVCRuntime) {
545+
VCRuntime = {std::move(VCRuntimePath), StaticVCRuntime};
546+
return *this;
547+
}
548+
549+
Expected<JITDylibSP> operator()(LLJIT &J);
550+
551+
private:
552+
std::variant<std::string, std::unique_ptr<MemoryBuffer>> OrcRuntime;
553+
std::optional<std::pair<std::string, bool>> VCRuntime;
554+
};
478555

479556
/// Configure the LLJIT instance to scrape modules for llvm.global_ctors and
480557
/// llvm.global_dtors variables and (if present) build initialization and
481558
/// deinitialization functions. Platform specific initialization configurations
482559
/// should be preferred where available.
483-
void setUpGenericLLVMIRPlatform(LLJIT &J);
560+
Expected<JITDylibSP> setUpGenericLLVMIRPlatform(LLJIT &J);
484561

485562
/// Configure the LLJIT instance to disable platform support explicitly. This is
486563
/// useful in two cases: for platforms that don't have such requirements and for
487564
/// platforms, that we have no explicit support yet and that don't work well
488565
/// with the generic IR platform.
489-
Error setUpInactivePlatform(LLJIT &J);
566+
Expected<JITDylibSP> setUpInactivePlatform(LLJIT &J);
490567

491568
} // End namespace orc
492569
} // End namespace llvm

llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,11 @@ class COFFHeaderMaterializationUnit : public MaterializationUnit {
159159
namespace llvm {
160160
namespace orc {
161161

162-
Expected<std::unique_ptr<COFFPlatform>>
163-
COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
164-
JITDylib &PlatformJD, const char *OrcRuntimePath,
165-
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
166-
const char *VCRuntimePath,
167-
std::optional<SymbolAliasMap> RuntimeAliases) {
162+
Expected<std::unique_ptr<COFFPlatform>> COFFPlatform::Create(
163+
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
164+
JITDylib &PlatformJD, std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
165+
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
166+
const char *VCRuntimePath, std::optional<SymbolAliasMap> RuntimeAliases) {
168167

169168
// If the target is not supported then bail out immediately.
170169
if (!supportedTarget(ES.getTargetTriple()))
@@ -174,6 +173,22 @@ COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
174173

175174
auto &EPC = ES.getExecutorProcessControl();
176175

176+
auto GeneratorArchive =
177+
object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef());
178+
if (!GeneratorArchive)
179+
return GeneratorArchive.takeError();
180+
181+
auto OrcRuntimeArchiveGenerator = StaticLibraryDefinitionGenerator::Create(
182+
ObjLinkingLayer, nullptr, std::move(*GeneratorArchive));
183+
if (!OrcRuntimeArchiveGenerator)
184+
return OrcRuntimeArchiveGenerator.takeError();
185+
186+
// We need a second instance of the archive (for now) for the Platform. We
187+
// can `cantFail` this call, since if it were going to fail it would have
188+
// failed above.
189+
auto RuntimeArchive = cantFail(
190+
object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef()));
191+
177192
// Create default aliases if the caller didn't supply any.
178193
if (!RuntimeAliases)
179194
RuntimeAliases = standardPlatformAliases(ES);
@@ -199,13 +214,30 @@ COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
199214
// Create the instance.
200215
Error Err = Error::success();
201216
auto P = std::unique_ptr<COFFPlatform>(new COFFPlatform(
202-
ES, ObjLinkingLayer, PlatformJD, OrcRuntimePath,
217+
ES, ObjLinkingLayer, PlatformJD, std::move(*OrcRuntimeArchiveGenerator),
218+
std::move(OrcRuntimeArchiveBuffer), std::move(RuntimeArchive),
203219
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err));
204220
if (Err)
205221
return std::move(Err);
206222
return std::move(P);
207223
}
208224

225+
Expected<std::unique_ptr<COFFPlatform>>
226+
COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
227+
JITDylib &PlatformJD, const char *OrcRuntimePath,
228+
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
229+
const char *VCRuntimePath,
230+
std::optional<SymbolAliasMap> RuntimeAliases) {
231+
232+
auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath);
233+
if (!ArchiveBuffer)
234+
return createFileError(OrcRuntimePath, ArchiveBuffer.getError());
235+
236+
return Create(ES, ObjLinkingLayer, PlatformJD, std::move(*ArchiveBuffer),
237+
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath,
238+
std::move(RuntimeAliases));
239+
}
240+
209241
Expected<MemoryBufferRef> COFFPlatform::getPerJDObjectFile() {
210242
auto PerJDObj = OrcRuntimeArchive->findSym("__orc_rt_coff_per_jd_marker");
211243
if (!PerJDObj)
@@ -349,37 +381,22 @@ bool COFFPlatform::supportedTarget(const Triple &TT) {
349381
}
350382
}
351383

352-
COFFPlatform::COFFPlatform(ExecutionSession &ES,
353-
ObjectLinkingLayer &ObjLinkingLayer,
354-
JITDylib &PlatformJD, const char *OrcRuntimePath,
355-
LoadDynamicLibrary LoadDynamicLibrary,
356-
bool StaticVCRuntime, const char *VCRuntimePath,
357-
Error &Err)
384+
COFFPlatform::COFFPlatform(
385+
ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
386+
JITDylib &PlatformJD,
387+
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
388+
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
389+
std::unique_ptr<object::Archive> OrcRuntimeArchive,
390+
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
391+
const char *VCRuntimePath, Error &Err)
358392
: ES(ES), ObjLinkingLayer(ObjLinkingLayer),
359-
LoadDynLibrary(std::move(LoadDynamicLibrary)),
393+
LoadDynLibrary(std::move(LoadDynLibrary)),
394+
OrcRuntimeArchiveBuffer(std::move(OrcRuntimeArchiveBuffer)),
395+
OrcRuntimeArchive(std::move(OrcRuntimeArchive)),
360396
StaticVCRuntime(StaticVCRuntime),
361397
COFFHeaderStartSymbol(ES.intern("__ImageBase")) {
362398
ErrorAsOutParameter _(&Err);
363399

364-
// Create a generator for the ORC runtime archive.
365-
auto OrcRuntimeArchiveGenerator =
366-
StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath);
367-
if (!OrcRuntimeArchiveGenerator) {
368-
Err = OrcRuntimeArchiveGenerator.takeError();
369-
return;
370-
}
371-
372-
auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath);
373-
if (!ArchiveBuffer) {
374-
Err = createFileError(OrcRuntimePath, ArchiveBuffer.getError());
375-
return;
376-
}
377-
OrcRuntimeArchiveBuffer = std::move(*ArchiveBuffer);
378-
OrcRuntimeArchive =
379-
std::make_unique<object::Archive>(*OrcRuntimeArchiveBuffer, Err);
380-
if (Err)
381-
return;
382-
383400
Bootstrapping.store(true);
384401
ObjLinkingLayer.addPlugin(std::make_unique<COFFPlatformPlugin>(*this));
385402

@@ -392,7 +409,7 @@ COFFPlatform::COFFPlatform(ExecutionSession &ES,
392409
}
393410
VCRuntimeBootstrap = std::move(*VCRT);
394411

395-
for (auto &Lib : (*OrcRuntimeArchiveGenerator)->getImportedDynamicLibraries())
412+
for (auto &Lib : OrcRuntimeGenerator->getImportedDynamicLibraries())
396413
DylibsToPreload.insert(Lib);
397414

398415
auto ImportedLibs =
@@ -406,7 +423,7 @@ COFFPlatform::COFFPlatform(ExecutionSession &ES,
406423
for (auto &Lib : *ImportedLibs)
407424
DylibsToPreload.insert(Lib);
408425

409-
PlatformJD.addGenerator(std::move(*OrcRuntimeArchiveGenerator));
426+
PlatformJD.addGenerator(std::move(OrcRuntimeGenerator));
410427

411428
// PlatformJD hasn't been set up by the platform yet (since we're creating
412429
// the platform now), so set it up.
@@ -416,10 +433,10 @@ COFFPlatform::COFFPlatform(ExecutionSession &ES,
416433
}
417434

418435
for (auto& Lib : DylibsToPreload)
419-
if (auto E2 = LoadDynLibrary(PlatformJD, Lib)) {
420-
Err = std::move(E2);
421-
return;
422-
}
436+
if (auto E2 = this->LoadDynLibrary(PlatformJD, Lib)) {
437+
Err = std::move(E2);
438+
return;
439+
}
423440

424441
if (StaticVCRuntime)
425442
if (auto E2 = VCRuntimeBootstrap->initializeStaticVCRuntime(PlatformJD)) {

0 commit comments

Comments
 (0)