Background:
Initializers and deinitializers may be used to implement static constructor
and destructor support, runtime registration (e.g. ObjC) and other tasks
that would typically be performed when a dylib/shared-object is loaded or
unloaded.
Until now, MCJIT and ORC have provided limited support for static constructors
and destructors by scanning the llvm.global_ctors and llvm.global_dtors
variables. This approach suffers from two main drawbacks: It requires LLVM IR to
enable initializer discovery (objects loaded directly or from a compile cache
will not have their initializers run), and not all initializers are described by
llvm.global_ctors (e.g. ObjC registration is described by special sections
instead).
This patch introduces several interdependent changes to ORCv2 to enable full
support for initializers and deinitializers:
(1) The MaterializationUnit and MaterializationResponsibility classes are
extended to describe an optional "initializer" symbol for the module (see the
getInitializerSymbol method on each class). The presence or absence of this
symbol indicates whether the module contains any initializers or
deinitializers. The initializer symbol otherwise behaves like any other:
searching for it triggers materialization.
(2) A new Platform interface is introduced in llvm/ExecutionEngine/Orc/Core.h
which provides the following callback interface:
- Error setupJITDylib(JITDylib &JD): Can be used to install standard symbols in JITDylibs upon creation. E.g. __dso_handle.
- Error notifyAdding(JITDylib &JD, const MaterializationUnit &MU): Generally used to record initializer symbols.
- Error notifyRemoving(JITDylib &JD, VModuleKey K): Used to notify a platform that a module is being removed.
- void notifyReady(JITDylib &JD, SymbolNameVector Symbols): Called to notify a platform that some symbols are ready but have not yet been initialized.
Platform implementations can use these callbacks to track outstanding
initializers and implement a platform-specific approach for executing them. For
example, the MachOPlatform installs plugins in the JIT linker and uses them to
scan both the __mod_inits section (for C++ static constructors) and ObjC
metadata sections, then register and run initializers according to JITDylib
link order (i.e. in the same order that they would have been run if the modules
had been linked into corresponding dylibs on the command line).
(3) An initialized state for symbols. The initialized state comes after the
existing states (NeverSearched, Materializing, Resolved, Emitted, Ready) and
can be used to prevent symbol queries from returning until the searched-for
symbol has been initialized.
This patch updates LLJIT to use this scheme for execution of static initializers
and deinitializers. Two LLJIT::PlatformSupport classes are implemented: A
GenericIR platform and a MachO platform. The GenericIR platform implements a
modified version of the previous llvm.global-ctor scraping scheme to provide
support for Windows and Linux. LLJIT's MachO platform support uses the
MachOPlatform class to provide MachO specific initialization (currently just
static initializers scraped from the __mod_inits section, but will include
ObjC support in the near future).
Is GV promotion here related to the initializers change? Is it always necessary or can we skip it in the whole module partitioning case?