Skip to content

Commit

Permalink
Free Allocations (#374)
Browse files Browse the repository at this point in the history
* free array and dict

* free boxes and nullables

* another free

* error handling
  • Loading branch information
edg-l authored Dec 14, 2023
1 parent 6c5e4e2 commit 80c0f6b
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 7 deletions.
17 changes: 17 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,23 @@ pub unsafe extern "C" fn cairo_native__dict_insert(
}
}

/// Frees the dictionary.
///
/// # Safety
///
/// This function is intended to be called from MLIR, deals with pointers, and is therefore
/// definitely unsafe to use manually.
#[no_mangle]
pub unsafe extern "C" fn cairo_native__dict_free(map: *mut std::ffi::c_void) {
let dict = map.cast::<HashMap<[u8; 32], NonNull<std::ffi::c_void>>>();
let dict = Box::from_raw(dict);

for (_, entry) in dict.into_iter() {
libc::free(entry.as_ptr().cast());
}
// freed by box drop
}

/// Compute `ec_point_from_x_nz(x)` and store it.
///
/// # Panics
Expand Down
2 changes: 2 additions & 0 deletions src/error/libfuncs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ impl From<super::CoreTypeBuilderError> for ErrorImpl {
super::types::ErrorImpl::ProgramRegistryError(e) => Self::ProgramRegistryError(e),
super::types::ErrorImpl::TryFromIntError(e) => Self::TryFromIntError(e),
super::types::ErrorImpl::LayoutErrorPolyfill(e) => Self::LayoutErrorPolyfill(e),
super::types::ErrorImpl::MlirError(e) => Self::MlirError(e),
super::types::ErrorImpl::LibFuncError(e) => e.source,
}
}
}
4 changes: 4 additions & 0 deletions src/error/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ pub enum ErrorImpl {
ProgramRegistryError(#[from] Box<ProgramRegistryError>),
#[error(transparent)]
TryFromIntError(#[from] TryFromIntError),
#[error(transparent)]
MlirError(#[from] melior::Error),
#[error(transparent)]
LibFuncError(#[from] crate::error::libfuncs::Error),
}
19 changes: 15 additions & 4 deletions src/libfuncs/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ use melior::{

/// Generate MLIR operations for the `drop` libfunc.
pub fn build<'ctx, 'this, TType, TLibfunc>(
_context: &'ctx Context,
_registry: &ProgramRegistry<TType, TLibfunc>,
context: &'ctx Context,
registry: &ProgramRegistry<TType, TLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
_metadata: &mut MetadataStorage,
_info: &SignatureOnlyConcreteLibfunc,
metadata: &mut MetadataStorage,
info: &SignatureOnlyConcreteLibfunc,
) -> Result<()>
where
TType: GenericType,
Expand All @@ -41,6 +41,17 @@ where
{
// TODO: Implement drop for arrays.

let ty = registry.get_type(&info.signature.param_signatures[0].ty)?;
ty.build_drop(
context,
registry,
entry,
location,
helper,
metadata,
&info.signature.param_signatures[0].ty,
)?;

entry.append_operation(helper.br(0, &[], location));

Ok(())
Expand Down
28 changes: 28 additions & 0 deletions src/metadata/realloc_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ impl ReallocBindingsMeta {
)],
Location::unknown(context),
));
module.body().append_operation(func::func(
context,
StringAttribute::new(context, "free"),
TypeAttribute::new(
FunctionType::new(context, &[llvm::r#type::opaque_pointer(context)], &[]).into(),
),
Region::new(),
&[(
Identifier::new(context, "sym_visibility"),
StringAttribute::new(context, "private").into(),
)],
Location::unknown(context),
));

Self {
phantom: PhantomData,
Expand All @@ -65,4 +78,19 @@ impl ReallocBindingsMeta {
location,
)
}

/// Calls the `free` function.
pub fn free<'c>(
context: &'c Context,
ptr: Value<'c, '_>,
location: Location<'c>,
) -> Operation<'c> {
func::call(
context,
FlatSymbolRefAttribute::new(context, "free"),
&[ptr],
&[],
location,
)
}
}
42 changes: 42 additions & 0 deletions src/metadata/runtime_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum RuntimeBinding {
DictNew,
DictGet,
DictInsert,
DictFree,
}

/// Runtime library bindings metadata.
Expand Down Expand Up @@ -512,6 +513,47 @@ impl RuntimeBindingsMeta {
)))
}

/// Register if necessary, then invoke the `dict_alloc_new()` function.
///
/// Returns a opaque pointer as the result.
#[allow(clippy::too_many_arguments)]
pub fn dict_alloc_free<'c, 'a>(
&mut self,
context: &'c Context,
module: &Module,
ptr: Value<'c, 'a>,
block: &'a Block<'c>,
location: Location<'c>,
) -> Result<OperationRef<'c, 'a>>
where
'c: 'a,
{
if self.active_map.insert(RuntimeBinding::DictFree) {
module.body().append_operation(func::func(
context,
StringAttribute::new(context, "cairo_native__dict_free"),
TypeAttribute::new(
FunctionType::new(context, &[llvm::r#type::opaque_pointer(context)], &[])
.into(),
),
Region::new(),
&[(
Identifier::new(context, "sym_visibility"),
StringAttribute::new(context, "private").into(),
)],
Location::unknown(context),
));
}

Ok(block.append_operation(func::call(
context,
FlatSymbolRefAttribute::new(context, "cairo_native__dict_free"),
&[ptr],
&[],
location,
)))
}

/// Register if necessary, then invoke the `dict_get()` function.
///
/// Gets the value for a given key, the returned pointer is null if not found.
Expand Down
77 changes: 75 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
use crate::{
error::CoreTypeBuilderError,
metadata::MetadataStorage,
utils::{get_integer_layout, layout_repeat},
libfuncs::LibfuncHelper,
metadata::{
realloc_bindings::ReallocBindingsMeta, runtime_bindings::RuntimeBindingsMeta,
MetadataStorage,
},
utils::{get_integer_layout, layout_repeat, ProgramRegistryExt},
};
use cairo_lang_sierra::{
extensions::{
Expand All @@ -16,6 +20,10 @@ use cairo_lang_sierra::{
ids::ConcreteTypeId,
program_registry::ProgramRegistry,
};
use melior::{
dialect::llvm::{self, r#type::opaque_pointer},
ir::{attribute::DenseI64ArrayAttribute, Block, Location, Value},
};
use melior::{
ir::{Module, Type},
Context,
Expand Down Expand Up @@ -88,6 +96,18 @@ where
///
/// TODO: How is it used?
fn variants(&self) -> Option<&[ConcreteTypeId]>;

#[allow(clippy::too_many_arguments)]
fn build_drop<'ctx, 'this>(
&self,
context: &'ctx Context,
registry: &ProgramRegistry<TType, TLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
self_ty: &ConcreteTypeId,
) -> Result<(), Self::Error>;
}

impl<TType, TLibfunc> TypeBuilder<TType, TLibfunc> for CoreTypeConcrete
Expand Down Expand Up @@ -427,6 +447,59 @@ where
_ => None,
}
}

fn build_drop<'ctx, 'this>(
&self,
context: &'ctx Context,
registry: &ProgramRegistry<TType, TLibfunc>,
entry: &'this Block<'ctx>,
location: Location<'ctx>,
helper: &LibfuncHelper<'ctx, 'this>,
metadata: &mut MetadataStorage,
self_ty: &ConcreteTypeId,
) -> Result<(), Self::Error> {
match self {
CoreTypeConcrete::Array(_info) => {
let array_ty = registry.build_type(context, helper, registry, metadata, self_ty)?;

let ptr_ty = crate::ffi::get_struct_field_type_at(&array_ty, 0);

let array_val = entry.argument(0)?.into();

let op = entry.append_operation(llvm::extract_value(
context,
array_val,
DenseI64ArrayAttribute::new(context, &[0]),
ptr_ty,
location,
));
let ptr: Value = op.result(0)?.into();

let ptr = entry
.append_operation(llvm::bitcast(ptr, opaque_pointer(context), location))
.result(0)?
.into();

entry.append_operation(ReallocBindingsMeta::free(context, ptr, location));
}
CoreTypeConcrete::Felt252Dict(_) | CoreTypeConcrete::SquashedFelt252Dict(_) => {
let runtime: &mut RuntimeBindingsMeta = metadata.get_mut().unwrap();
let ptr = entry.argument(0)?.into();

runtime.dict_alloc_free(context, helper, ptr, entry, location)?;
}
CoreTypeConcrete::Box(_) | CoreTypeConcrete::Nullable(_) => {
if metadata.get::<ReallocBindingsMeta>().is_none() {
metadata.insert(ReallocBindingsMeta::new(context, helper));
}

let ptr = entry.argument(0)?.into();
entry.append_operation(ReallocBindingsMeta::free(context, ptr, location));
}
_ => {}
};
Ok(())
}
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand Down
6 changes: 6 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,12 @@ pub fn register_runtime_symbols(engine: &ExecutionEngine) {
as *mut (),
);

engine.register_symbol(
"cairo_native__dict_free",
cairo_native_runtime::cairo_native__dict_free as *const fn(*mut std::ffi::c_void) -> ()
as *mut (),
);

engine.register_symbol(
"cairo_native__dict_get",
cairo_native_runtime::cairo_native__dict_get
Expand Down
8 changes: 7 additions & 1 deletion src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ impl JITValue {
array_value.push(Self::from_jit(cur_elem_ptr, &info.ty, registry));
}

libc::free(data_ptr.as_ptr().cast());

Self::Array(array_value)
}
CoreTypeConcrete::Box(info) => JITValue::from_jit(ptr, &info.ty, registry),
Expand Down Expand Up @@ -566,9 +568,13 @@ impl JITValue {
for (key, val_ptr) in map.iter() {
let key = Felt252::from_bytes_le(key.as_slice());
output_map.insert(key, Self::from_jit(val_ptr.cast(), &info.ty, registry));
// we need to free all the elements, which are allocated by libc realloc.
libc::free(val_ptr.as_ptr());
}

Box::leak(map); // we must leak to avoid a double free
// we must leak to avoid a double free
// as it was allocated in the arena and will be freed by it.
Box::leak(map);

JITValue::Felt252Dict {
value: output_map,
Expand Down

0 comments on commit 80c0f6b

Please sign in to comment.