From 0a1b3effcd7e767e6cb36b32f1016094d4166700 Mon Sep 17 00:00:00 2001 From: pixelsystem Date: Wed, 21 Feb 2024 15:47:16 -0500 Subject: [PATCH 1/4] First implementation --- src/lib.rs | 2 + src/module.rs | 31 +++++++ src/orc2.rs | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 src/orc2.rs diff --git a/src/lib.rs b/src/lib.rs index 42e30526bdb..baac1b3769e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,9 @@ pub mod intrinsics; pub mod memory_buffer; #[deny(missing_docs)] pub mod module; +#[deny(missing_docs)] pub mod object_file; +pub mod orc2; pub mod passes; pub mod targets; pub mod types; diff --git a/src/module.rs b/src/module.rs index 26e2fbbb4f5..4338b906fb8 100644 --- a/src/module.rs +++ b/src/module.rs @@ -45,6 +45,7 @@ use crate::data_layout::DataLayout; use crate::debug_info::{DICompileUnit, DWARFEmissionKind, DWARFSourceLanguage, DebugInfoBuilder}; use crate::execution_engine::ExecutionEngine; use crate::memory_buffer::MemoryBuffer; +use crate::orc2::LLJITExecutionEngine; #[llvm_versions(13.0..=latest)] use crate::passes::PassBuilderOptions; use crate::support::{to_c_str, LLVMString}; @@ -169,6 +170,7 @@ pub struct Module<'ctx> { data_layout: RefCell>, pub(crate) module: Cell, pub(crate) owned_by_ee: RefCell>>, + pub(crate) owned_by_lljit : RefCell>>, _marker: PhantomData<&'ctx Context>, } @@ -184,6 +186,7 @@ impl<'ctx> Module<'ctx> { Module { module: Cell::new(module), owned_by_ee: RefCell::new(None), + owned_by_lljit : RefCell::new(None), data_layout: RefCell::new(Some(Module::get_borrowed_data_layout(module))), _marker: PhantomData, } @@ -440,6 +443,30 @@ impl<'ctx> Module<'ctx> { unsafe { TargetTriple::new(LLVMString::create_from_c_str(CStr::from_ptr(target_str))) } } + /// Crates a [crate::orc2::LLJitExecutionEngine] from this `Module` + /// # Example + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::module::Module; + /// use inkwell::targets::{InitializationConfig, Target}; + /// + /// Target::initialize_native(&InitializationConfig::default()).expect("Failed to initialize native target"); + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let lljit_engine = module.create_lljit_engine().unwrap(); + /// + /// ``` + pub fn create_lljit_engine(self) -> Result, LLVMString>{ + Target::initialize_native(&InitializationConfig::default()).map_err(|mut err_string| { + err_string.push('\0'); + + LLVMString::create_from_str(&err_string) + })?; + + crate::orc2::LLJITBuilder::new().create() + } + /// Creates an `ExecutionEngine` from this `Module`. /// /// # Example @@ -468,6 +495,10 @@ impl<'ctx> Module<'ctx> { let string = "This module is already owned by an ExecutionEngine.\0"; return Err(LLVMString::create_from_str(string)); } + if self.owned_by_lljit.borrow().is_some() { + let string = "This module is already owned by an LLJITExecutionEngine\0"; + return Err(LLVMString::create_from_str(string)); + } let mut execution_engine = MaybeUninit::uninit(); let mut err_string = MaybeUninit::uninit(); diff --git a/src/orc2.rs b/src/orc2.rs new file mode 100644 index 00000000000..bdf8763f8fa --- /dev/null +++ b/src/orc2.rs @@ -0,0 +1,229 @@ + +use core::fmt; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fmt::{Formatter,Debug}; +use std::marker::PhantomData; +use std::mem::MaybeUninit; + +use llvm_sys::error::LLVMGetErrorMessage; +use llvm_sys::orc2::lljit::{LLVMOrcCreateLLJIT, LLVMOrcCreateLLJITBuilder, LLVMOrcDisposeLLJIT as DisposeLLJIT, LLVMOrcDisposeLLJITBuilder, LLVMOrcLLJITAddLLVMIRModule, LLVMOrcLLJITBuilderRef, LLVMOrcLLJITGetMainJITDylib, LLVMOrcLLJITLookup, LLVMOrcLLJITMangleAndIntern, LLVMOrcLLJITRef}; +use llvm_sys::orc2::{LLVMJITEvaluatedSymbol, LLVMJITSymbolFlags, LLVMOrcAbsoluteSymbols, LLVMOrcCSymbolMapPair, LLVMOrcCreateNewThreadSafeContext, LLVMOrcCreateNewThreadSafeModule, LLVMOrcJITDylibDefine, LLVMOrcJITDylibRef}; +use crate::context::Context; +use crate::module::Module; +use crate::support::{to_c_str,LLVMString}; +use crate::values::{AnyValue, AnyValueEnum, GlobalValue}; + +/// A light wrapper around llvm::orc::LLJit. +/// Should be constructed from [crate::module::Module::crate_lljit_engine] +#[derive(Debug, PartialEq, Eq)] +pub struct LLJITExecutionEngine<'ctx>(LLVMOrcLLJITRef,PhantomData<&'ctx ()>); + +impl Drop for LLJITExecutionEngine<'_> { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { DisposeLLJIT(self.0) }; + } + } +} + +impl<'ctx> LLJITExecutionEngine<'ctx> { + pub unsafe fn get_function(&self,name:impl AsRef) -> Result,LLVMString> { + let name = to_c_str(name.as_ref()); + let mut address = MaybeUninit::uninit(); + let err = LLVMOrcLLJITLookup(self.0, address.as_mut_ptr(), name.as_ptr()); + if !err.is_null() { + let msg = LLVMGetErrorMessage(err); + Err(LLVMString::new(msg)) + } else { + let address = address.assume_init(); + if address == 0 { + Err(LLVMString::create_from_str("Unknown Error in getting a jit function.")) + } else { + Ok(LLJITFunction::<'ctx,F> { addr: address, _f: Default::default() }) + } + } + } + + /// Maps the specified name to an address. + /// + /// # Example + /// ```no_run + /// use inkwell::targets::{InitializationConfig, Target}; + /// use inkwell::context::Context; + /// use inkwell::OptimizationLevel; + /// + /// Target::initialize_native(&InitializationConfig::default()).unwrap(); + /// + /// extern fn sumf(a: f64, b: f64) -> f64 { + /// a + b + /// } + /// + /// let context = Context::create(); + /// let module = context.create_module("test"); + /// let builder = context.create_builder(); + /// + /// let ft = context.f64_type(); + /// let fnt = ft.fn_type(&[], false); + /// + /// let f = module.add_function("test_fn", fnt, None); + /// let b = context.append_basic_block(f, "entry"); + /// + /// builder.position_at_end(b); + /// + /// let extf = module.add_function("sumf", ft.fn_type(&[ft.into(), ft.into()], false), None); + /// + /// let argf = ft.const_float(64.); + /// let call_site_value = builder.build_call(extf, &[argf.into(), argf.into()], "retv").unwrap(); + /// let retv = call_site_value.try_as_basic_value().left().unwrap().into_float_value(); + /// + /// builder.build_return(Some(&retv)).unwrap(); + /// + /// let mut ee = module.create_lljit_engine().unwrap(); + /// ee.add_global_mapping("sumf", sumf as usize); + /// + /// let fun = ee.get_function::("test_fn"); + /// let result = fun(); + /// + /// assert_eq!(result, 128.); + /// ``` + pub fn add_global_mapping(&self, name : impl AsRef, addr : usize) -> Result<(),LLVMString> { + let name = to_c_str(name.as_ref()); + self.add_global_mapping_impl(name, addr) + } + + /// A grouped version [Self::add_global_mapping] + pub fn add_global_mappings(&self, mappings:&[(&str,usize)]) -> Result<(),LLVMString> { + self.add_global_mappings_impl(mappings.iter().map(|(name,addr)| (to_c_str(name),*addr))) + } + + fn add_global_mapping_impl(&self,name:Cow<'_,CStr>, addr:usize) -> Result<(),LLVMString> { + self.add_global_mappings_impl(std::iter::once((name,addr))) + } + + fn add_global_mappings_impl<'a>(&self,mappings:impl Iterator,usize)>)->Result<(),LLVMString> { + unsafe { + let jd = self.get_main_dylib(); + let mut mappings = mappings.map(|(name,addr)| { + LLVMOrcCSymbolMapPair { + Name:LLVMOrcLLJITMangleAndIntern(self.0, name.as_ptr()), + Sym:LLVMJITEvaluatedSymbol { + Address:addr as u64, + Flags: LLVMJITSymbolFlags { + //todo? find the correct flags? + GenericFlags:0, + TargetFlags:0 + } + } + } + }).collect::>(); + let mu = LLVMOrcAbsoluteSymbols(mappings.as_mut_ptr(), mappings.len()); + let err = LLVMOrcJITDylibDefine(jd, mu); + if !err.is_null() { + let msg = LLVMGetErrorMessage(err); + Err(LLVMString::new(msg)) + } else { + Ok(()) + } + } + } + + pub fn add_global_mapping_by_value(&self,v:&GlobalValue<'ctx>, addr:usize) -> Result<(),LLVMString> { + let name= v.get_name(); + self.add_global_mapping_impl(Cow::from(name), addr) + } + + fn get_main_dylib(&self) -> LLVMOrcJITDylibRef { + unsafe { LLVMOrcLLJITGetMainJITDylib(self.0) } + } + + + pub fn add_module(&self, m : Module<'ctx>) -> Result<(),LLVMString> { + unsafe { + let tsc = LLVMOrcCreateNewThreadSafeContext(); + let tsm = LLVMOrcCreateNewThreadSafeModule(m.as_mut_ptr(), tsc); + let dylib = self.get_main_dylib(); + let err = LLVMOrcLLJITAddLLVMIRModule(self.0, dylib, tsm); + if !err.is_null() { + let msg = LLVMGetErrorMessage(err); + Err(LLVMString::new(msg)) + } else { + Ok(()) + } + } + } +} + +pub struct LLJITFunction<'ctx,F> { + addr : u64, + _f : PhantomData<&'ctx F>, +} + +impl Debug for LLJITFunction<'_,F> { + fn fmt(&self, f:&mut Formatter) -> fmt::Result { + f.debug_tuple("LLJITFunction").field(&"").finish() + } +} + +macro_rules! impl_unsafe_fn { + (@recurse $first:ident $( , $rest:ident )*) => { + impl_unsafe_fn!($( $rest ),*); + }; + + (@recurse) => {}; + + ($( $param:ident ),*) => { + impl LLJITFunction<'_, unsafe extern "C" fn($( $param ),*) -> Output> { + /// This method allows you to call the underlying function while making + /// sure that the backing storage is not dropped too early and + /// preserves the `unsafe` marker for any calls. + #[allow(non_snake_case)] + #[inline(always)] + pub unsafe fn call(&self, $( $param: $param ),*) -> Output { + let addr = ::std::mem::transmute:: Output>(self.addr); + (addr)($( $param ),*) + } + } + + impl_unsafe_fn!(@recurse $( $param ),*); + }; +} + +impl_unsafe_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M); + +#[derive(Debug)] +pub(crate) struct LLJITBuilder<'ctx>(LLVMOrcLLJITBuilderRef,PhantomData<&'ctx ()>); +impl Drop for LLJITBuilder<'_> { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { LLVMOrcDisposeLLJITBuilder(self.0) }; + } + } +} + +impl<'ctx> LLJITBuilder<'ctx> { + pub(crate) fn new() -> Self { + Self(unsafe { LLVMOrcCreateLLJITBuilder() },PhantomData::default()) + } + + /// TODO? extra options? + + pub(crate) fn create(self) -> Result,LLVMString> { + let data = self.1; + let builder = self.0; + std::mem::forget(self); + let mut out = MaybeUninit::uninit(); + let err = unsafe { LLVMOrcCreateLLJIT(out.as_mut_ptr(), builder) }; + if !err.is_null() { + let msg = unsafe { LLVMGetErrorMessage(err) }; + Err(unsafe { LLVMString::new(msg) }) + } else { + let out = unsafe { out.assume_init() }; + if out.is_null() { + Err(LLVMString::create_from_str("LLJIT failed to be constructed for unknown reasons")) + } else { + Ok(LLJITExecutionEngine(out, data)) + } + } + } +} \ No newline at end of file From a65362261b90972d243689d01c6f56745fd54a0a Mon Sep 17 00:00:00 2001 From: pixelsystem Date: Wed, 21 Feb 2024 16:27:12 -0500 Subject: [PATCH 2/4] Can't believe I forgot to add the module when creating the engine XD --- src/module.rs | 4 +++- src/orc2.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/module.rs b/src/module.rs index 4338b906fb8..f86551c79d7 100644 --- a/src/module.rs +++ b/src/module.rs @@ -464,7 +464,9 @@ impl<'ctx> Module<'ctx> { LLVMString::create_from_str(&err_string) })?; - crate::orc2::LLJITBuilder::new().create() + let engine = crate::orc2::LLJITBuilder::new().create()?; + engine.add_module(self)?; + Ok(engine) } /// Creates an `ExecutionEngine` from this `Module`. diff --git a/src/orc2.rs b/src/orc2.rs index bdf8763f8fa..b93d167f615 100644 --- a/src/orc2.rs +++ b/src/orc2.rs @@ -9,10 +9,9 @@ use std::mem::MaybeUninit; use llvm_sys::error::LLVMGetErrorMessage; use llvm_sys::orc2::lljit::{LLVMOrcCreateLLJIT, LLVMOrcCreateLLJITBuilder, LLVMOrcDisposeLLJIT as DisposeLLJIT, LLVMOrcDisposeLLJITBuilder, LLVMOrcLLJITAddLLVMIRModule, LLVMOrcLLJITBuilderRef, LLVMOrcLLJITGetMainJITDylib, LLVMOrcLLJITLookup, LLVMOrcLLJITMangleAndIntern, LLVMOrcLLJITRef}; use llvm_sys::orc2::{LLVMJITEvaluatedSymbol, LLVMJITSymbolFlags, LLVMOrcAbsoluteSymbols, LLVMOrcCSymbolMapPair, LLVMOrcCreateNewThreadSafeContext, LLVMOrcCreateNewThreadSafeModule, LLVMOrcJITDylibDefine, LLVMOrcJITDylibRef}; -use crate::context::Context; use crate::module::Module; use crate::support::{to_c_str,LLVMString}; -use crate::values::{AnyValue, AnyValueEnum, GlobalValue}; +use crate::values::GlobalValue; /// A light wrapper around llvm::orc::LLJit. /// Should be constructed from [crate::module::Module::crate_lljit_engine] @@ -28,6 +27,60 @@ impl Drop for LLJITExecutionEngine<'_> { } impl<'ctx> LLJITExecutionEngine<'ctx> { + /// Try to load a function from the execution engine. + /// + /// The [`UnsafeFunctionPointer`] trait is designed so only `unsafe extern + /// "C"` functions can be retrieved via the `get_function()` method. If you + /// get funny type errors then it's probably because you have specified the + /// wrong calling convention or forgotten to specify the retrieved function + /// as `unsafe`. + /// + /// # Examples + /// + /// + /// ```rust,no_run + /// # use inkwell::targets::{InitializationConfig, Target}; + /// # use inkwell::context::Context; + /// # use inkwell::OptimizationLevel; + /// # Target::initialize_native(&InitializationConfig::default()).unwrap(); + /// let context = Context::create(); + /// let module = context.create_module("test"); + /// let builder = context.create_builder(); + /// + /// // Set up the function signature + /// let double = context.f64_type(); + /// let sig = double.fn_type(&[], false); + /// + /// // Add the function to our module + /// let f = module.add_function("test_fn", sig, None); + /// let b = context.append_basic_block(f, "entry"); + /// builder.position_at_end(b); + /// + /// // Insert a return statement + /// let ret = double.const_float(64.0); + /// builder.build_return(Some(&ret)).unwrap(); + /// + /// // create the JIT engine + /// let mut ee = module.create_jit_execution_engine(OptimizationLevel::None).unwrap(); + /// + /// // fetch our JIT'd function and execute it + /// unsafe { + /// let test_fn = ee.get_function:: f64>("test_fn").unwrap(); + /// let return_value = test_fn.call(); + /// assert_eq!(return_value, 64.0); + /// } + /// ``` + /// + /// # Safety + /// + /// It is the caller's responsibility to ensure they call the function with + /// the correct signature and calling convention. + /// + /// The `JitFunction` wrapper ensures a function won't accidentally outlive the + /// execution engine it came from, but adding functions after calling this + /// method *may* invalidate the function pointer. + /// + /// [`UnsafeFunctionPointer`]: trait.UnsafeFunctionPointer.html pub unsafe fn get_function(&self,name:impl AsRef) -> Result,LLVMString> { let name = to_c_str(name.as_ref()); let mut address = MaybeUninit::uninit(); @@ -82,8 +135,10 @@ impl<'ctx> LLJITExecutionEngine<'ctx> { /// let mut ee = module.create_lljit_engine().unwrap(); /// ee.add_global_mapping("sumf", sumf as usize); /// - /// let fun = ee.get_function::("test_fn"); - /// let result = fun(); + /// let result = unsafe { + /// let fun = ee.get_function::("test_fn"); + /// fun.call() + /// }; /// /// assert_eq!(result, 128.); /// ``` @@ -92,7 +147,7 @@ impl<'ctx> LLJITExecutionEngine<'ctx> { self.add_global_mapping_impl(name, addr) } - /// A grouped version [Self::add_global_mapping] + /// A grouped version of [Self::add_global_mapping] pub fn add_global_mappings(&self, mappings:&[(&str,usize)]) -> Result<(),LLVMString> { self.add_global_mappings_impl(mappings.iter().map(|(name,addr)| (to_c_str(name),*addr))) } From a7cea9060136178cd71f19ead2f37ba44606c9cb Mon Sep 17 00:00:00 2001 From: pixelsystem Date: Thu, 22 Feb 2024 10:40:12 -0500 Subject: [PATCH 3/4] More comments! --- src/orc2.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/orc2.rs b/src/orc2.rs index b93d167f615..3baa66eba0a 100644 --- a/src/orc2.rs +++ b/src/orc2.rs @@ -183,6 +183,51 @@ impl<'ctx> LLJITExecutionEngine<'ctx> { } } + + /// Maps a global value to an address + /// Restricted to [crate::values::GlobalValue] as that's all that is supported for remapping by the LLJIT engine. + /// # Example + /// ```no_run + /// # use inkwell::targets::{InitializationConfig, Target}; + /// # use inkwell::context::Context; + /// # use inkwell::OptimizationLevel; + /// + /// Target::initialize_native(&InitializationConfig::default()).unwrap(); + /// + /// extern fn sumf(a: f64, b: f64) -> f64 { + /// a + b + /// } + /// + /// let context = Context::create(); + /// let module = context.create_module("test"); + /// let builder = context.create_builder(); + /// + /// let ft = context.f64_type(); + /// let fnt = ft.fn_type(&[], false); + /// + /// let f = module.add_function("test_fn", fnt, None); + /// let b = context.append_basic_block(f, "entry"); + /// + /// builder.position_at_end(b); + /// + /// let extf = module.add_function("sumf", ft.fn_type(&[ft.into(), ft.into()], false), None); + /// + /// let argf = ft.const_float(64.); + /// let call_site_value = builder.build_call(extf, &[argf.into(), argf.into()], "retv").unwrap(); + /// let retv = call_site_value.try_as_basic_value().left().unwrap().into_float_value(); + /// + /// builder.build_return(Some(&retv)).unwrap(); + /// + /// let mut ee = module.create_lljit_engine().unwrap(); + /// ee.add_global_mapping_by_value(&extf.as_global_value(), sumf as usize); + /// + /// let result = unsafe { + /// let fun = ee.get_function::("test_fn"); + /// fun.call() + /// }; + /// + /// assert_eq!(result, 128.); + /// ``` pub fn add_global_mapping_by_value(&self,v:&GlobalValue<'ctx>, addr:usize) -> Result<(),LLVMString> { let name= v.get_name(); self.add_global_mapping_impl(Cow::from(name), addr) @@ -192,7 +237,31 @@ impl<'ctx> LLJITExecutionEngine<'ctx> { unsafe { LLVMOrcLLJITGetMainJITDylib(self.0) } } - + /// Adds a module to the engine. + /// It takes ownership as it is illegal to modify the module once it has been added. + /// # Example + /// ``` + /// # use inkwell::targets::{InitializationConfig, Target}; + /// # use inkwell::context::Context; + /// let ctx = Context::create(); + /// let base_module = ctx.create_module("base"); + /// let ee = base_module.create_lljit_engine().unwrap(); + /// + /// let new_module = ctx.create_module("new"); + /// let fun_ty = ctx.i32_type().fn_type(&[],false); + /// let fun = new_module.add_function("fun",fun_ty,None); + /// let builder = ctx.create_builder(); + /// let bb = ctx.append_basic_block(fun,"entry"); + /// builder.position_at_end(bb); + /// let retv = ctx.i32_type().const_int(3,false); + /// builder.build_return(Some(&retv)); + /// ee.add_module(new_module); + /// unsafe { + /// let fun = ee.get_function:: i32>("fun").unwrap(); + /// let result = fun.call(); + /// assert_eq!(result,3); + /// } + /// ``` pub fn add_module(&self, m : Module<'ctx>) -> Result<(),LLVMString> { unsafe { let tsc = LLVMOrcCreateNewThreadSafeContext(); From cebeb1a1985bf15c27d4b0f737a15ff902f67753 Mon Sep 17 00:00:00 2001 From: pixelsystem Date: Thu, 22 Feb 2024 12:07:43 -0500 Subject: [PATCH 4/4] feature gating. appears to be LLVM11+ feature --- src/lib.rs | 1 + src/module.rs | 1 + src/orc2.rs | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index baac1b3769e..fb145c29dc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub mod memory_buffer; pub mod module; #[deny(missing_docs)] pub mod object_file; +#[cfg(not(any(feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0",feature = "llvm7-0", feature = "llvm8-0", feature = "llvm9-0",feature = "llvm10-0")))] pub mod orc2; pub mod passes; pub mod targets; diff --git a/src/module.rs b/src/module.rs index f86551c79d7..bf1f75c47e3 100644 --- a/src/module.rs +++ b/src/module.rs @@ -457,6 +457,7 @@ impl<'ctx> Module<'ctx> { /// let lljit_engine = module.create_lljit_engine().unwrap(); /// /// ``` + #[llvm_versions(11.0..=latest)] pub fn create_lljit_engine(self) -> Result, LLVMString>{ Target::initialize_native(&InitializationConfig::default()).map_err(|mut err_string| { err_string.push('\0'); diff --git a/src/orc2.rs b/src/orc2.rs index 3baa66eba0a..be8c2f29d76 100644 --- a/src/orc2.rs +++ b/src/orc2.rs @@ -1,4 +1,4 @@ - +///! everything related to llvm::orc::LLJIT use core::fmt; use std::borrow::Cow; use std::ffi::CStr; @@ -278,6 +278,10 @@ impl<'ctx> LLJITExecutionEngine<'ctx> { } } + +/// A wrapper around a function pointer which ensures the function being pointed +/// to doesn't accidentally outlive its execution engine. +#[derive(Clone)] pub struct LLJITFunction<'ctx,F> { addr : u64, _f : PhantomData<&'ctx F>,