aboutsummaryrefslogblamecommitdiffhomepage
path: root/src/llvm/lljit.rs
blob: a68e9fa1a96f9c9361b521debc38aaf48ff1f5d4 (plain) (tree)




















                                                                                           
                                                                                         









































































































                                                                                                   



                                                                                            





















                                                                                   
use llvm_sys::orc2::{
    lljit::{
        LLVMOrcCreateLLJIT, LLVMOrcLLJITAddLLVMIRModuleWithRT, LLVMOrcLLJITGetGlobalPrefix,
        LLVMOrcLLJITGetMainJITDylib, LLVMOrcLLJITLookup, LLVMOrcLLJITRef,
    },
    LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess, LLVMOrcDefinitionGeneratorRef,
    LLVMOrcJITDylibAddGenerator, LLVMOrcJITDylibCreateResourceTracker, LLVMOrcJITDylibRef,
    LLVMOrcReleaseResourceTracker, LLVMOrcResourceTrackerRef, LLVMOrcResourceTrackerRemove,
};

use std::convert::TryFrom;
use std::marker::PhantomData;

use super::{Error, Module};
use crate::SmallCStr;

/// Marker trait to constrain function signatures that can be looked up in the JIT.
pub trait JitFn {}

impl JitFn for unsafe extern "C" fn() -> f64 {}

/// Wrapper for a LLVM [LLJIT](https://www.llvm.org/docs/ORCv2.html#lljit-and-lllazyjit).
pub struct LLJit {
    jit: LLVMOrcLLJITRef,
    dylib: LLVMOrcJITDylibRef,
}

impl LLJit {
    /// Create a new LLJit instance.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer or an error.
    pub fn new() -> LLJit {
        let (jit, dylib) = unsafe {
            let mut jit = std::ptr::null_mut();
            let err = LLVMOrcCreateLLJIT(
                &mut jit as _,
                std::ptr::null_mut(), /* builder: nullptr -> default */
            );

            if let Some(err) = Error::from(err) {
                panic!("Error: {}", err.as_str());
            }

            let dylib = LLVMOrcLLJITGetMainJITDylib(jit);
            assert!(!dylib.is_null());

            (jit, dylib)
        };

        LLJit { jit, dylib }
    }

    /// Add an LLVM IR module to the JIT. Return a [`ResourceTracker`], which when dropped, will
    /// remove the code of the LLVM IR module from the JIT.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns a `null` pointer or an error.
    pub fn add_module(&self, module: Module) -> ResourceTracker<'_> {
        let tsmod = module.into_raw_thread_safe_module();

        let rt = unsafe {
            let rt = LLVMOrcJITDylibCreateResourceTracker(self.dylib);
            let err = LLVMOrcLLJITAddLLVMIRModuleWithRT(self.jit, rt, tsmod);

            if let Some(err) = Error::from(err) {
                panic!("Error: {}", err.as_str());
            }

            rt
        };

        ResourceTracker::new(rt)
    }

    /// Find the symbol with the name `sym` in the JIT.
    ///
    /// # Panics
    ///
    /// Panics if the symbol is not found in the JIT.
    pub fn find_symbol<F: JitFn>(&self, sym: &str) -> F {
        let sym =
            SmallCStr::try_from(sym).expect("Failed to convert 'sym' argument to small C string!");

        unsafe {
            let mut addr = 0u64;
            let err = LLVMOrcLLJITLookup(self.jit, &mut addr as _, sym.as_ptr());

            if let Some(err) = Error::from(err) {
                panic!("Error: {}", err.as_str());
            }

            debug_assert_eq!(core::mem::size_of_val(&addr), core::mem::size_of::<F>());
            std::mem::transmute_copy(&addr)
        }
    }

    /// Enable lookup of dynamic symbols available in the current process from the JIT.
    ///
    /// # Panics
    ///
    /// Panics if LLVM API returns an error.
    pub fn enable_process_symbols(&self) {
        unsafe {
            let mut proc_syms_gen: LLVMOrcDefinitionGeneratorRef = std::ptr::null_mut();
            let err = LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess(
                &mut proc_syms_gen as _,
                self.global_prefix(),
                None,                 /* filter */
                std::ptr::null_mut(), /* filter ctx */
            );

            if let Some(err) = Error::from(err) {
                panic!("Error: {}", err.as_str());
            }

            LLVMOrcJITDylibAddGenerator(self.dylib, proc_syms_gen);
        }
    }

    /// Return the global prefix character according to the LLJITs data layout.
    fn global_prefix(&self) -> libc::c_char {
        unsafe { LLVMOrcLLJITGetGlobalPrefix(self.jit) }
    }
}

/// A resource handle for code added to an [`LLJit`] instance.
///
/// When a `ResourceTracker` handle is dropped, the code corresponding to the handle will be
/// removed from the JIT.
pub struct ResourceTracker<'jit>(LLVMOrcResourceTrackerRef, PhantomData<&'jit ()>);

impl<'jit> ResourceTracker<'jit> {
    fn new(rt: LLVMOrcResourceTrackerRef) -> ResourceTracker<'jit> {
        assert!(!rt.is_null());
        ResourceTracker(rt, PhantomData)
    }
}

impl Drop for ResourceTracker<'_> {
    fn drop(&mut self) {
        unsafe {
            let err = LLVMOrcResourceTrackerRemove(self.0);

            if let Some(err) = Error::from(err) {
                panic!("Error: {}", err.as_str());
            }

            LLVMOrcReleaseResourceTracker(self.0);
        };
    }
}