This patch enables integrating orc::LLJIT with the ORCv2 platforms (MachOPlatform and ELFNixPlatform) and the compiler-rt orc runtime. Changes include:
- Adding SPS wrapper functions for the orc runtime's dlfcn emulation functions, allowing initialization and deinitialization to be invoked by LLJIT
- Changing the LLJIT code generation default to add UseInitArray so that .init_array constructors are generated for ELF platforms
- Integrating the ORCv2 Platforms into lli, and adding a PlatformSupport implementation to the LLJIT instance used by lli which implements initialization and deinitialization by calling the new wrapper functions in the runtime.
Open issues:
- Should the ORCPlatformSupport class included in lli be moved into LLJIT?
- The integration test for Darwin explicitly sets -relocation-model=pic when invoking lli so that a __DATA,__mod_init_func section is generated; if this is not done the default code generation options use the static relocation mode, which causes a __TEXT, __constructor to be generated, and the runtime does not deal with these. Might there be a better way to handle this?
As a side note to this review: We'll want to re-think LLJIT::PlatformSupport when we finally get Windows support in JITLink and the ORC runtime: Generic initialize and deinitialize functions should be implemented in the ORC runtime, and LLJIT::initialize and LLJIT::deinitialize should just call those functions through EPC::callWrapper -- PlatformSupport should vanish entirely.
On MachO and ELF the runtime operations can be implemented in terms of dlopen / dlclose, and on Windows I guess they can be implemented in terms of LoadLibrary / FreeLibrary.
The only wrinkle is GenericIRPlatform, which is supposed to work without the ORC runtime. For this I think we can just inject some IR wrapper-plumbing like this to direct the calls to methods on the GenericIRPlatform instance:
; To be set up as an absolute symbol pointing at the current GenericIRPlatform instance. @GenericIRPlatformInstance = external global i8*, align 8 ; extern "C" CWrapperFunctionResult initializeImplWrapper(const char *Data, size_t Size, void *Instance); declare { i8*, i64 } @initializeImplWrapper(i8*, i64, i8*) ; extern "C" CWrapperFunctionResult deinitializeImplWrapper(const char *Data, size_t Size, void *Instance); declare { i8*, i64 } @deinitializeImplWrapper(i8*, i64, i8*) ; extern "C" CWrapperFunctionResult initialize(const char *Data, size_t Size) { ; return initializeImplWrapper(Data, Size, GenericIRPlatformInstance); ; } define { i8*, i64 } @initialize(i8* %0, i64 %1) local_unnamed_addr #0 { %3 = load i8*, i8** @GenericIRPlatformInstance, align 8, !tbaa !6 %4 = tail call { i8*, i64 } @initializeImplWrapper(i8* %0, i64 %1, i8* %3) ret { i8*, i64 } %4 } ; extern "C" CWrapperFunctionResult deinitialize(const char *Data, size_t Size) { ; return deinitializeImplWrapper(Data, Size, GenericIRPlatformInstance); ; } define { i8*, i64 } @deinitialize(i8* %0, i64 %1) { %3 = load i8*, i8** @GenericIRPlatformInstance, align 8, !tbaa !6 %4 = tail call { i8*, i64 } @deinitializeImplWrapper(i8* %0, i64 %1, i8* %3) ret { i8*, i64 } %4 }These wrappers can call through to static methods on the GenericIRPlatform class, which unwrap and call methods on the instance:
class GenericIRPlatform ... { // GenericIRPlatform installs init-function transform, as it does now. ... private: Error initialize(StringRef Path) { /* run recorded init functions */ } Error deinitialize(StringRef Path) { /* run recorded deinit functions */ } static CWrapperFunctionResult initializeImplWrapper(const char *Data, size_t Size, void *Instance) { return return WrapperFunction<SPSError(SPSString)>::handle( ArgData, ArgSize, [Instance](StringRef Path) -> Error { return static_cast<GenericIRPlatform*>(Instance)->initialize(Path); }) .release(); } static CWrapperFunctionResult initializeImplWrapper(const char *Data, size_t Size, void *Instance) { return return WrapperFunction<SPSError(SPSString)>::handle( ArgData, ArgSize, [Instance](StringRef Path) -> Error { return static_cast<GenericIRPlatform*>(Instance)->deinitialize(Path); }) .release(); } };That should be enough to support the initialize and deinitialize ops without the ORC runtime, which I think keeps us on a par with MCJIT's functionality.