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(&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::()); 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); }; } }