Skip to content

Add managed allocator. #1054

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
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
22 changes: 13 additions & 9 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
pub use self::{aot::AotNativeExecutor, contract::AotContractExecutor, jit::JitNativeExecutor};
use crate::{
arch::{AbiArgument, ValueWithInfoWrapper},
error::{panic::ToNativeAssertError, Error},
error::{panic::ToNativeAssertError, Error, Result},
execution_result::{BuiltinStats, ExecutionResult},
native_panic,
runtime::BUILTIN_COSTS,
Expand Down Expand Up @@ -79,7 +79,7 @@ fn invoke_dynamic(
Option<extern "C" fn(*mut c_void, *mut c_void)>,
Option<extern "C" fn(*mut c_void)>,
),
) -> Result<ExecutionResult, Error> {
) -> Result<ExecutionResult> {
tracing::info!("Invoking function with signature: {function_signature:?}.");
let arena = Bump::new();
let mut invoke_data = Vec::<u8>::new();
Expand All @@ -106,7 +106,7 @@ fn invoke_dynamic(

Ok((!(type_info.is_builtin() && is_zst)).then_some(id)).transpose()
})
.collect::<Result<Vec<_>, _>>()?
.collect::<Result<Vec<_>>>()?
.into_iter()
.peekable();

Expand All @@ -120,7 +120,7 @@ fn invoke_dynamic(
{
let layout = ret_types_iter.try_fold(Layout::new::<()>(), |layout, id| {
let type_info = registry.get_type(id)?;
Result::<_, Error>::Ok(layout.extend(type_info.layout(registry)?)?.0)
Result::Ok(layout.extend(type_info.layout(registry)?)?.0)
})?;

let return_ptr = arena.alloc_layout(layout).cast::<()>();
Expand Down Expand Up @@ -223,10 +223,14 @@ fn invoke_dynamic(
ret_registers.as_mut_ptr(),
);
};
#[cfg(feature = "with-segfault-catcher")]
crate::utils::safe_runner::run_safely(run_trampoline).map_err(Error::SafeRunner)?;
#[cfg(not(feature = "with-segfault-catcher"))]
run_trampoline();
crate::utils::allocator::run_with_allocator(|| {
#[cfg(feature = "with-segfault-catcher")]
crate::utils::safe_runner::run_safely(run_trampoline).map_err(Error::SafeRunner)?;
#[cfg(not(feature = "with-segfault-catcher"))]
run_trampoline();

Result::Ok(())
})?;

// Restore the previous syscall handler and builtin costs.
#[cfg(feature = "with-cheatcode")]
Expand Down Expand Up @@ -387,7 +391,7 @@ fn parse_result(
mut return_ptr: Option<NonNull<()>>,
#[cfg(target_arch = "x86_64")] mut ret_registers: [u64; 2],
#[cfg(target_arch = "aarch64")] mut ret_registers: [u64; 4],
) -> Result<Value, Error> {
) -> Result<Value> {
let type_info = registry.get_type(type_id)?;

// Align the pointer to the actual return value.
Expand Down
12 changes: 8 additions & 4 deletions src/executor/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,10 +432,14 @@ impl AotContractExecutor {
ret_registers.as_mut_ptr(),
);
};
#[cfg(feature = "with-segfault-catcher")]
crate::utils::safe_runner::run_safely(run_trampoline).map_err(Error::SafeRunner)?;
#[cfg(not(feature = "with-segfault-catcher"))]
run_trampoline();
crate::utils::allocator::run_with_allocator(|| {
#[cfg(feature = "with-segfault-catcher")]
crate::utils::safe_runner::run_safely(run_trampoline).map_err(Error::SafeRunner)?;
#[cfg(not(feature = "with-segfault-catcher"))]
run_trampoline();

Result::Ok(())
})?;

// Parse final gas.
unsafe fn read_value<T>(ptr: &mut NonNull<()>) -> &T {
Expand Down
1 change: 1 addition & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use std::{
};
use thiserror::Error;

pub mod allocator;
mod block_ext;
pub mod mem_tracing;
mod program_registry_ext;
Expand Down
109 changes: 109 additions & 0 deletions src/utils/allocator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::{
alloc::Layout,
cell::UnsafeCell,
collections::{hash_map::Entry, HashMap},
ptr,
};

thread_local! {
static ALLOCATOR: UnsafeCell<ManagedAllocator> = UnsafeCell::new(ManagedAllocator::default());
}

// TODO: Replace `crate::utils::libc_free`, `crate::utils::libc_malloc`,
// `crate::utils::libc_realloc` with our implementation.
// TODO: Merge runtime crate into library (after #1051).
// TODO: Register runtime symbols (after #1051).

pub fn register_runtime_symbols(find_symbol: impl Fn(&str) -> Option<*mut ()>) {
if let Some(symbol) = find_symbol("cairo_native__alloc") {
unsafe {
*symbol.cast::<*const ()>() =
impl_alloc as *const extern "C" fn(u64, u64) -> *mut () as *const ()
}
}

if let Some(symbol) = find_symbol("cairo_native__realloc") {
unsafe {
*symbol.cast::<*const ()>() =
impl_realloc as *const extern "C" fn(*mut (), u64) -> *mut () as *const ()
}
}

if let Some(symbol) = find_symbol("cairo_native__free") {
unsafe {
*symbol.cast::<*const ()>() = impl_free as *const extern "C" fn(*mut ()) as *const ()
}
}
}

pub fn run_with_allocator<T>(f: impl FnOnce() -> T) -> T {
let prev_allocator =
ALLOCATOR.with(|x| unsafe { ptr::replace(x.get(), ManagedAllocator::default()) });

let result = f();

ALLOCATOR.with(|x| unsafe { ptr::write(x.get(), prev_allocator) });
result
}

#[derive(Debug, Default)]
struct ManagedAllocator {
allocs: HashMap<*mut u8, Layout>,
}

impl ManagedAllocator {
pub fn alloc(&mut self, layout: Layout) -> *mut u8 {
let ptr = unsafe { std::alloc::alloc(layout) };
self.allocs.insert(ptr, layout);

ptr
}

pub fn realloc(&mut self, ptr: *mut u8, new_size: usize) -> *mut u8 {
assert!(!ptr.is_null());
match self.allocs.entry(ptr) {
Entry::Occupied(mut entry) => {
let new_ptr = unsafe { std::alloc::realloc(ptr, *entry.get(), new_size) };
let new_layout = {
let layout = *entry.get();
Layout::from_size_align(layout.size(), layout.align()).unwrap()
};

if ptr == new_ptr {
entry.insert(new_layout);
} else {
entry.remove();
self.allocs.insert(new_ptr, new_layout);
}

new_ptr
}
Entry::Vacant(_) => panic!(),
}
}

pub fn dealloc(&mut self, ptr: *mut u8) {
let layout = self.allocs.remove(&ptr).unwrap();
unsafe { std::alloc::dealloc(ptr, layout) }
}
}

impl Drop for ManagedAllocator {
fn drop(&mut self) {
for (ptr, layout) in self.allocs.drain() {
unsafe { std::alloc::dealloc(ptr, layout) }
}
}
}

extern "C" fn impl_alloc(size: u64, align: u64) -> *mut () {
// let layout = Layout::from_size_align(size, align).unwrap();

todo!()
}

extern "C" fn impl_realloc(ptr: *mut (), new_size: u64) -> *mut () {
todo!()
}

extern "C" fn impl_free(ptr: *mut ()) {}
Loading