Manage lifetimes of LLVM modules #379
Description
From our GlobalContext we create a raw LLVM module (global_cx.llvm_cx.create_module), then pass a reference to that module into various other contexts. From comments in the code:
// FIXME: this should be handled with lifetimes.
// Context (global_cx) must outlive llvm module (entry_llmod).
drop(entry_llmod);
drop(global_cx);
This is true of all calls to create_module
- the lifetime of the llvm module is manually ensured to be less than the GlobalContext.
The obvious solution is to have one of the existing other contexts own the llvm module, but this doesn't work because it would require self-referential lifetimes, e.g.:
pub struct EntrypointGenerator<'mm, 'up> {
pub env: &'mm mm::GlobalEnv,
pub llvm_cx: &'up llvm::Context,
pub llvm_module: &'up llvm::Module,
pub llvm_builder: llvm::Builder,
pub options: &'up Options,
pub rtty_cx: RttyContext<'mm, 'up>,
fn_decls: RefCell<BTreeMap<String, llvm::Function>>,
ll_fn_solana_entrypoint: llvm::Function,
entry_slice_ptr: llvm::AnyValue,
entry_slice_len: llvm::AnyValue,
insn_data_ptr: llvm::AnyValue,
offset: llvm::Alloca,
retval: llvm::AnyValue,
exit_bb: llvm::BasicBlock,
entries: Cell<u64>,
target_machine: &'up llvm::TargetMachine,
}
In the above it would nice to convert llvm_module
from &'up Module
to just Module
. This can't be done though because the RttyContext
contains a reference to llvm_module
that must be named in EntrypointGenerator
.
Without doing significant restructuring, the best way I can think of to ensure the LLVM module goes out of scope before GlobalContext is to introduce a new type responsible just for containing both the GlobalContext lifetime and the llvm Module:
struct LlvmContext<'g> {
global_context: &'g GlobalContext, // or PhantomData<&'g GlobalContext>
llmod: llvm::Module,
}
impl GlobalContext {
fn create_llvm_module(&self) -> LlvmContext {
...
}
}
LlvmContext exists just to tie the lifetime of the LLVM module and GlobalContext through a stack variable. It doesn't need to be passed anywhere else, just live on the stack. Then a reference to the LLVM module can continue to be passed everywhere it already is.
This only works if GlobalContext never needs accessed through a mutable reference, which appears to be the case, since this will hold an immutable reference to it.
cc https://github.com/solana-labs/move/pull/374/files#r1330870435
Activity