diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ee6a8c983..911dc2336 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -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::>>(); + 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 diff --git a/src/error/libfuncs.rs b/src/error/libfuncs.rs index 174269d3d..0b0ce819f 100644 --- a/src/error/libfuncs.rs +++ b/src/error/libfuncs.rs @@ -58,6 +58,8 @@ impl From 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, } } } diff --git a/src/error/types.rs b/src/error/types.rs index 19e401652..9548bb088 100644 --- a/src/error/types.rs +++ b/src/error/types.rs @@ -47,4 +47,8 @@ pub enum ErrorImpl { ProgramRegistryError(#[from] Box), #[error(transparent)] TryFromIntError(#[from] TryFromIntError), + #[error(transparent)] + MlirError(#[from] melior::Error), + #[error(transparent)] + LibFuncError(#[from] crate::error::libfuncs::Error), } diff --git a/src/libfuncs/drop.rs b/src/libfuncs/drop.rs index e7ae4dd18..6c273b8f5 100644 --- a/src/libfuncs/drop.rs +++ b/src/libfuncs/drop.rs @@ -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, + context: &'ctx Context, + registry: &ProgramRegistry, 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, @@ -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(()) diff --git a/src/metadata/realloc_bindings.rs b/src/metadata/realloc_bindings.rs index 6f92af932..9321e1f2c 100644 --- a/src/metadata/realloc_bindings.rs +++ b/src/metadata/realloc_bindings.rs @@ -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, @@ -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, + ) + } } diff --git a/src/metadata/runtime_bindings.rs b/src/metadata/runtime_bindings.rs index e78fee70a..e7a5dd045 100644 --- a/src/metadata/runtime_bindings.rs +++ b/src/metadata/runtime_bindings.rs @@ -28,6 +28,7 @@ enum RuntimeBinding { DictNew, DictGet, DictInsert, + DictFree, } /// Runtime library bindings metadata. @@ -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> + 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. diff --git a/src/types.rs b/src/types.rs index 14f307eb8..3925772af 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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::{ @@ -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, @@ -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, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + self_ty: &ConcreteTypeId, + ) -> Result<(), Self::Error>; } impl TypeBuilder for CoreTypeConcrete @@ -427,6 +447,59 @@ where _ => None, } } + + fn build_drop<'ctx, 'this>( + &self, + context: &'ctx Context, + registry: &ProgramRegistry, + 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::().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)] diff --git a/src/utils.rs b/src/utils.rs index a50c78411..305236cdb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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 diff --git a/src/values.rs b/src/values.rs index 4c6c6fb37..e4fa8395c 100644 --- a/src/values.rs +++ b/src/values.rs @@ -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), @@ -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,