Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] thread safe-ish execution engine #487

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct CodeGen<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,
builder: Builder<'ctx>,
execution_engine: ExecutionEngine<'ctx>,
execution_engine: ExecutionEngine,
}

impl<'ctx> CodeGen<'ctx> {
Expand Down
2 changes: 1 addition & 1 deletion examples/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct CodeGen<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,
builder: Builder<'ctx>,
execution_engine: ExecutionEngine<'ctx>,
execution_engine: ExecutionEngine,
}

impl<'ctx> CodeGen<'ctx> {
Expand Down
1 change: 1 addition & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use crate::{AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate};

use std::cell::Cell;
use std::marker::PhantomData;
use std::sync::Arc;

#[derive(Debug, PartialEq, Clone, Copy)]
enum PositionState {
Expand Down
58 changes: 31 additions & 27 deletions src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::marker::PhantomData;
use std::mem::{forget, size_of, transmute_copy, MaybeUninit};
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;

static EE_INNER_PANIC: &str = "ExecutionEngineInner should exist until Drop";

Expand Down Expand Up @@ -88,21 +89,21 @@ impl Display for RemoveModuleError {
/// copies. The underlying LLVM object will be automatically deallocated when
/// there are no more references to it.
#[derive(PartialEq, Eq, Debug)]
pub struct ExecutionEngine<'ctx> {
execution_engine: Option<ExecEngineInner<'ctx>>,
pub struct ExecutionEngine {
execution_engine: Option<ExecEngineInner>,
target_data: Option<TargetData>,
jit_mode: bool,
}

impl<'ctx> ExecutionEngine<'ctx> {
pub unsafe fn new(execution_engine: Rc<LLVMExecutionEngineRef>, jit_mode: bool) -> Self {
impl ExecutionEngine {
pub unsafe fn new(execution_engine: Arc<LLVMExecutionEngineRef>, jit_mode: bool) -> Self {
assert!(!execution_engine.is_null());

// REVIEW: Will we have to do this for LLVMGetExecutionEngineTargetMachine too?
let target_data = LLVMGetExecutionEngineTargetData(*execution_engine);

ExecutionEngine {
execution_engine: Some(ExecEngineInner(execution_engine, PhantomData)),
execution_engine: Some(ExecEngineInner(execution_engine)),
target_data: Some(TargetData::new(target_data)),
jit_mode,
}
Expand All @@ -113,7 +114,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
self.execution_engine_inner()
}

pub(crate) fn execution_engine_rc(&self) -> &Rc<LLVMExecutionEngineRef> {
pub(crate) fn execution_engine_rc(&self) -> &Arc<LLVMExecutionEngineRef> {
&self.execution_engine.as_ref().expect(EE_INNER_PANIC).0
}

Expand Down Expand Up @@ -177,7 +178,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
///
/// assert_eq!(result, 128.);
/// ```
pub fn add_global_mapping(&self, value: &dyn AnyValue<'ctx>, addr: usize) {
pub fn add_global_mapping<'ctx>(&self, value: &dyn AnyValue<'ctx>, addr: usize) {
unsafe { LLVMAddGlobalMapping(self.execution_engine_inner(), value.as_value_ref(), addr as *mut _) }
}

Expand All @@ -198,7 +199,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
///
/// assert!(ee.add_module(&module).is_err());
/// ```
pub fn add_module(&self, module: &Module<'ctx>) -> Result<(), ()> {
pub fn add_module(&self, module: &Module<'_>) -> Result<(), ()> {
unsafe { LLVMAddModule(self.execution_engine_inner(), module.module.get()) }

if module.owned_by_ee.borrow().is_some() {
Expand All @@ -210,7 +211,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
Ok(())
}

pub fn remove_module(&self, module: &Module<'ctx>) -> Result<(), RemoveModuleError> {
pub fn remove_module<'ctx>(&self, module: &Module<'ctx>) -> Result<(), RemoveModuleError> {
match *module.owned_by_ee.borrow() {
Some(ref ee) if ee.execution_engine_inner() != self.execution_engine_inner() => {
return Err(RemoveModuleError::IncorrectModuleOwner)
Expand Down Expand Up @@ -302,7 +303,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
/// method *may* invalidate the function pointer.
///
/// [`UnsafeFunctionPointer`]: trait.UnsafeFunctionPointer.html
pub unsafe fn get_function<F>(&self, fn_name: &str) -> Result<JitFunction<'ctx, F>, FunctionLookupError>
pub unsafe fn get_function<F>(&self, fn_name: &str) -> Result<JitFunction<F>, FunctionLookupError>
where
F: UnsafeFunctionPointer,
{
Expand All @@ -318,10 +319,10 @@ impl<'ctx> ExecutionEngine<'ctx> {
"The type `F` must have the same size as a function pointer"
);

let execution_engine = self.execution_engine.as_ref().expect(EE_INNER_PANIC);
let execution_engine = self.execution_engine.clone().expect(EE_INNER_PANIC);

Ok(JitFunction {
_execution_engine: execution_engine.clone(),
_execution_engine: execution_engine,
inner: transmute_copy(&address),
})
}
Expand Down Expand Up @@ -363,7 +364,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
// do have a global flag for anything initialized. Catch is that it must be initialized
// before EE is created
// REVIEW: Should FunctionValue lifetime be tied to self not 'ctx?
pub fn get_function_value(&self, fn_name: &str) -> Result<FunctionValue<'ctx>, FunctionLookupError> {
pub fn get_function_value<'ctx>(&self, fn_name: &str) -> Result<FunctionValue<'ctx>, FunctionLookupError> {
if !self.jit_mode {
return Err(FunctionLookupError::JITNotEnabled);
}
Expand All @@ -382,7 +383,7 @@ impl<'ctx> ExecutionEngine<'ctx> {

// TODOC: Marked as unsafe because input function could very well do something unsafe. It's up to the caller
// to ensure that doesn't happen by defining their function correctly.
pub unsafe fn run_function(
pub unsafe fn run_function<'ctx>(
&self,
function: FunctionValue<'ctx>,
args: &[&GenericValue<'ctx>],
Expand All @@ -402,7 +403,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
// TODOC: Marked as unsafe because input function could very well do something unsafe. It's up to the caller
// to ensure that doesn't happen by defining their function correctly.
// SubType: Only for JIT EEs?
pub unsafe fn run_function_as_main(&self, function: FunctionValue<'ctx>, args: &[&str]) -> c_int {
pub unsafe fn run_function_as_main<'ctx>(&self, function: FunctionValue<'ctx>, args: &[&str]) -> c_int {
let cstring_args: Vec<_> = args.iter().map(|&arg| to_c_str(arg)).collect();
let raw_args: Vec<*const _> = cstring_args.iter().map(|arg| arg.as_ptr()).collect();

Expand All @@ -417,7 +418,7 @@ impl<'ctx> ExecutionEngine<'ctx> {
) // REVIEW: usize to u32 cast ok??
}

pub fn free_fn_machine_code(&self, function: FunctionValue<'ctx>) {
pub fn free_fn_machine_code<'ctx>(&self, function: FunctionValue<'ctx>) {
unsafe { LLVMFreeMachineCodeForFunction(self.execution_engine_inner(), function.as_value_ref()) }
}

Expand All @@ -434,7 +435,7 @@ impl<'ctx> ExecutionEngine<'ctx> {

// Modules owned by the EE will be discarded by the EE so we don't
// want owned modules to drop.
impl Drop for ExecutionEngine<'_> {
impl Drop for ExecutionEngine {
fn drop(&mut self) {
forget(
self.target_data
Expand All @@ -449,7 +450,7 @@ impl Drop for ExecutionEngine<'_> {
}
}

impl Clone for ExecutionEngine<'_> {
impl Clone for ExecutionEngine {
fn clone(&self) -> Self {
let execution_engine_rc = self.execution_engine_rc().clone();

Expand All @@ -459,19 +460,19 @@ impl Clone for ExecutionEngine<'_> {

/// A smart pointer which wraps the `Drop` logic for `LLVMExecutionEngineRef`.
#[derive(Debug, Clone, PartialEq, Eq)]
struct ExecEngineInner<'ctx>(Rc<LLVMExecutionEngineRef>, PhantomData<&'ctx Context>);
struct ExecEngineInner(Arc<LLVMExecutionEngineRef>);

impl Drop for ExecEngineInner<'_> {
impl Drop for ExecEngineInner {
fn drop(&mut self) {
if Rc::strong_count(&self.0) == 1 {
if Arc::strong_count(&self.0) == 1 {
unsafe {
LLVMDisposeExecutionEngine(*self.0);
}
}
}
}

impl Deref for ExecEngineInner<'_> {
impl Deref for ExecEngineInner {
type Target = LLVMExecutionEngineRef;

fn deref(&self) -> &Self::Target {
Expand All @@ -482,12 +483,15 @@ impl Deref for ExecEngineInner<'_> {
/// A wrapper around a function pointer which ensures the function being pointed
/// to doesn't accidentally outlive its execution engine.
#[derive(Clone)]
pub struct JitFunction<'ctx, F> {
_execution_engine: ExecEngineInner<'ctx>,
pub struct JitFunction<F> {
inner: F,
_execution_engine: ExecEngineInner,
// This needs some thread safe way to ensure the Context doesn't get dropped.
// The following doesn't work, and is just for demonstration purposes:
_context: Arc<Context>,
}

impl<'ctx, F: Copy> JitFunction<'ctx, F> {
impl<F: Copy> JitFunction<F> {
/// Returns the raw function pointer, consuming self in the process.
/// This function is unsafe because the function pointer may dangle
/// if the ExecutionEngine it came from is dropped. The caller is
Expand All @@ -505,7 +509,7 @@ impl<'ctx, F: Copy> JitFunction<'ctx, F> {
}
}

impl<F> Debug for JitFunction<'_, F> {
impl<F> Debug for JitFunction<F> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("JitFunction").field(&"<unnamed>").finish()
}
Expand Down Expand Up @@ -534,7 +538,7 @@ macro_rules! impl_unsafe_fn {
($( $param:ident ),*) => {
impl<Output, $( $param ),*> private::SealedUnsafeFunctionPointer for unsafe extern "C" fn($( $param ),*) -> Output {}

impl<Output, $( $param ),*> JitFunction<'_, unsafe extern "C" fn($( $param ),*) -> Output> {
impl<Output, $( $param ),*> JitFunction<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.
Expand Down
18 changes: 8 additions & 10 deletions src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use std::mem::{forget, MaybeUninit};
use std::path::Path;
use std::ptr;
use std::rc::Rc;
use std::sync::Arc;

#[llvm_versions(7.0..=latest)]
use crate::comdat::Comdat;
Expand Down Expand Up @@ -168,7 +169,7 @@ pub enum Linkage {
pub struct Module<'ctx> {
data_layout: RefCell<Option<DataLayout>>,
pub(crate) module: Cell<LLVMModuleRef>,
pub(crate) owned_by_ee: RefCell<Option<ExecutionEngine<'ctx>>>,
pub(crate) owned_by_ee: RefCell<Option<ExecutionEngine>>,
_marker: PhantomData<&'ctx Context>,
}

Expand Down Expand Up @@ -457,7 +458,7 @@ impl<'ctx> Module<'ctx> {
/// assert_eq!(module.get_context(), context);
/// ```
// SubType: ExecutionEngine<Basic?>
pub fn create_execution_engine(&self) -> Result<ExecutionEngine<'ctx>, LLVMString> {
pub fn create_execution_engine(&self) -> Result<ExecutionEngine, LLVMString> {
Target::initialize_native(&InitializationConfig::default()).map_err(|mut err_string| {
err_string.push('\0');

Expand Down Expand Up @@ -487,7 +488,7 @@ impl<'ctx> Module<'ctx> {
}

let execution_engine = unsafe { execution_engine.assume_init() };
let execution_engine = unsafe { ExecutionEngine::new(Rc::new(execution_engine), false) };
let execution_engine = unsafe { ExecutionEngine::new(Arc::new(execution_engine), false) };

*self.owned_by_ee.borrow_mut() = Some(execution_engine.clone());

Expand All @@ -511,7 +512,7 @@ impl<'ctx> Module<'ctx> {
/// assert_eq!(module.get_context(), context);
/// ```
// SubType: ExecutionEngine<Interpreter>
pub fn create_interpreter_execution_engine(&self) -> Result<ExecutionEngine<'ctx>, LLVMString> {
pub fn create_interpreter_execution_engine(&self) -> Result<ExecutionEngine, LLVMString> {
Target::initialize_native(&InitializationConfig::default()).map_err(|mut err_string| {
err_string.push('\0');

Expand Down Expand Up @@ -542,7 +543,7 @@ impl<'ctx> Module<'ctx> {
}

let execution_engine = unsafe { execution_engine.assume_init() };
let execution_engine = unsafe { ExecutionEngine::new(Rc::new(execution_engine), false) };
let execution_engine = unsafe { ExecutionEngine::new(Arc::new(execution_engine), false) };

*self.owned_by_ee.borrow_mut() = Some(execution_engine.clone());

Expand All @@ -567,10 +568,7 @@ impl<'ctx> Module<'ctx> {
/// assert_eq!(module.get_context(), context);
/// ```
// SubType: ExecutionEngine<Jit>
pub fn create_jit_execution_engine(
&self,
opt_level: OptimizationLevel,
) -> Result<ExecutionEngine<'ctx>, LLVMString> {
pub fn create_jit_execution_engine(&self, opt_level: OptimizationLevel) -> Result<ExecutionEngine, LLVMString> {
Target::initialize_native(&InitializationConfig::default()).map_err(|mut err_string| {
err_string.push('\0');

Expand Down Expand Up @@ -602,7 +600,7 @@ impl<'ctx> Module<'ctx> {
}

let execution_engine = unsafe { execution_engine.assume_init() };
let execution_engine = unsafe { ExecutionEngine::new(Rc::new(execution_engine), true) };
let execution_engine = unsafe { ExecutionEngine::new(Arc::new(execution_engine), true) };

*self.owned_by_ee.borrow_mut() = Some(execution_engine.clone());

Expand Down
Loading