Skip to content
This repository has been archived by the owner on Mar 20, 2024. It is now read-only.

Manage lifetimes of LLVM modules #379

Open
brson opened this issue Sep 20, 2023 · 0 comments
Open

Manage lifetimes of LLVM modules #379

brson opened this issue Sep 20, 2023 · 0 comments
Labels
bug Something isn't working

Comments

@brson
Copy link
Collaborator

brson commented Sep 20, 2023

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant