From bd1f8bb37efb70d3800f719cafcc6c5897c0c49b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 23 Jan 2025 00:40:31 +0100 Subject: [PATCH] make ffi support full {i,u}64 range --- src/ffi.rs | 105 ++++++++++++++++++------------------ src/machine/system_calls.rs | 88 ++++++++++++++---------------- src/parser/ast.rs | 3 ++ tests/scryer/ffi.rs | 5 +- 4 files changed, 96 insertions(+), 105 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 34013189..2f949bf8 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -19,12 +19,16 @@ and for each field: we add to the pointer until we're aligned to the next data t and finally we add the pointer the size of what we've written. */ +use crate::arena::Arena; use crate::atom_table::Atom; +use crate::forms::Number; +use crate::parser::ast::Fixnum; +use dashu::Integer; +use ordered_float::OrderedFloat; use std::alloc::{self, Layout}; use std::any::Any; use std::collections::HashMap; -use std::convert::TryFrom; use std::error::Error; use std::ffi::{c_void, CString}; use std::ptr::addr_of_mut; @@ -189,8 +193,7 @@ impl ForeignFunctionTable { unsafe { macro_rules! push_int { ($type:ty) => {{ - let n: $type = <$type>::try_from(args[i].as_int()?) - .map_err(|_| FFIError::ValueDontFit)?; + let n: $type = args[i].as_int()?; let mut box_value = Box::new(n) as Box; pointers.push(&mut *box_value as *mut _ as *mut c_void); _memory.push(box_value); @@ -263,8 +266,7 @@ impl ForeignFunctionTable { ($type:ty) => {{ field_ptr = field_ptr .add(field_ptr.align_offset(std::mem::align_of::<$type>())); - let n: $type = <$type>::try_from(struct_args[i].as_int()?) - .map_err(|_| FFIError::ValueDontFit)?; + let n: $type = struct_args[i].as_int()?; std::ptr::write(field_ptr as *mut $type, n); field_ptr = field_ptr.add(std::mem::size_of::<$type>()); }}; @@ -327,7 +329,12 @@ impl ForeignFunctionTable { } } - pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { + pub fn exec( + &mut self, + name: &str, + mut args: Vec, + arena: &mut Arena, + ) -> Result { let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?; @@ -335,14 +342,14 @@ impl ForeignFunctionTable { unsafe { macro_rules! call_and_return { ($type:ty) => {{ - let mut n: Box<$type> = Box::new(0); + let mut n: $type = 0; libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), - &mut *n as *mut _ as *mut c_void, + &mut n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr() as *mut *mut c_void, ); - Ok(Value::Int(i64::from(*n))) + Ok(Value::Number(fixnum!(Number, n, arena))) }}; } @@ -354,50 +361,37 @@ impl ForeignFunctionTable { libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16), libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32), libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32), - libffi::raw::FFI_TYPE_UINT64 => { - let mut n: Box = Box::new(0); - libffi::raw::ffi_call( - &mut function_impl.cif, - Some(*function_impl.code_ptr.as_safe_fun()), - &mut *n as *mut _ as *mut c_void, - pointer_args.pointers.as_mut_ptr(), - ); - Ok(Value::Int( - i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?, - )) - } + libffi::raw::FFI_TYPE_UINT64 => call_and_return!(u64), libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64), libffi::raw::FFI_TYPE_POINTER => { - let mut n: Box<*mut c_void> = Box::new(std::ptr::null_mut()); + let mut n: *mut c_void = std::ptr::null_mut(); libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), - &mut *n as *mut _ as *mut c_void, + &mut n as *mut *mut c_void as *mut c_void, pointer_args.pointers.as_mut_ptr(), ); - Ok(Value::Int( - i64::try_from(*n as isize).map_err(|_| FFIError::ValueDontFit)?, - )) + Ok(Value::Number(fixnum!(Number, n as isize, arena))) } libffi::raw::FFI_TYPE_FLOAT => { - let mut n: Box = Box::new(0.0); + let mut n: f32 = 0.0; libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), - &mut *n as *mut _ as *mut c_void, + &mut n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr(), ); - Ok(Value::Float((*n).into())) + Ok(Value::Number(Number::Float(OrderedFloat(n.into())))) } libffi::raw::FFI_TYPE_DOUBLE => { - let mut n: Box = Box::new(0.0); + let mut n: f64 = 0.0; libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), - &mut *n as *mut _ as *mut c_void, + &mut n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr(), ); - Ok(Value::Float(*n)) + Ok(Value::Number(Number::Float(OrderedFloat(n)))) } libffi::raw::FFI_TYPE_STRUCT => { let name = &function_impl @@ -422,7 +416,7 @@ impl ForeignFunctionTable { &mut *ptr as *mut _, pointer_args.pointers.as_mut_ptr(), ); - let struct_val = self.read_struct(ptr, name, struct_type); + let struct_val = self.read_struct(ptr, name, struct_type, arena); #[allow(clippy::from_raw_with_void_ptr)] drop(Box::from_raw(ptr)); struct_val @@ -437,6 +431,7 @@ impl ForeignFunctionTable { ptr: *mut c_void, name: &str, struct_type: &StructImpl, + arena: &mut Arena, ) -> Result { unsafe { let mut returns = Vec::new(); @@ -450,7 +445,7 @@ impl ForeignFunctionTable { field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>())); let n = std::ptr::read(field_ptr as *mut $type); - returns.push(Value::Int(i64::from(n))); + returns.push(Value::Number(fixnum!(Number, n, arena))); field_ptr = field_ptr.add(std::mem::size_of::<$type>()); }}; } @@ -462,29 +457,21 @@ impl ForeignFunctionTable { libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16), libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), - libffi::raw::FFI_TYPE_UINT64 => { - field_ptr = - field_ptr.add(field_ptr.align_offset(std::mem::align_of::())); - let n = std::ptr::read(field_ptr as *mut u64); - returns.push(Value::Int( - i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?, - )); - field_ptr = field_ptr.add(std::mem::size_of::()); - } + libffi::raw::FFI_TYPE_UINT64 => read_and_push_int!(u64), libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64), libffi::raw::FFI_TYPE_FLOAT => { field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::())); let n: f32 = std::ptr::read(field_ptr as *mut f32); - returns.push(Value::Float(n.into())); + returns.push(Value::Number(Number::Float(OrderedFloat(n.into())))); field_ptr = field_ptr.add(std::mem::size_of::()); } libffi::raw::FFI_TYPE_DOUBLE => { field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::())); let n: f64 = std::ptr::read(field_ptr as *mut f64); - returns.push(Value::Float(n)); + returns.push(Value::Number(Number::Float(OrderedFloat(n)))); field_ptr = field_ptr.add(std::mem::size_of::()); } libffi::raw::FFI_TYPE_STRUCT => { @@ -495,7 +482,8 @@ impl ForeignFunctionTable { .ok_or(FFIError::StructNotFound)?; field_ptr = field_ptr .add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize)); - let struct_val = self.read_struct(field_ptr, &substruct, struct_type); + let struct_val = + self.read_struct(field_ptr, &substruct, struct_type, arena); returns.push(struct_val?); field_ptr = field_ptr.add(struct_type.ffi_type.size); } @@ -511,24 +499,33 @@ impl ForeignFunctionTable { #[derive(Clone, Debug)] pub enum Value { - Int(i64), - Float(f64), + Number(Number), CString(CString), Struct(String, Vec), } impl Value { - fn as_int(&self) -> Result { + fn as_int(&self) -> Result + where + Integer: TryInto, + i64: TryInto, + { match self { - Value::Int(n) => Ok(*n), + Value::Number(Number::Integer(ibig_ptr)) => { + let ibig: &Integer = ibig_ptr; + ibig.clone().try_into().map_err(|_| FFIError::ValueDontFit) + } + Value::Number(Number::Fixnum(fixnum)) => fixnum + .get_num() + .try_into() + .map_err(|_| FFIError::ValueDontFit), _ => Err(FFIError::ValueCast), } } fn as_float(&self) -> Result { match self { - Value::Float(n) => Ok(*n), - Value::Int(n) => Ok(*n as f64), + &Value::Number(Number::Float(OrderedFloat(f))) => Ok(f), _ => Err(FFIError::ValueCast), } } @@ -536,7 +533,9 @@ impl Value { fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> { match self { Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void), - Value::Int(n) => Ok(std::ptr::with_exposed_provenance_mut(*n as usize)), + Value::Number(Number::Fixnum(fixnum)) => Ok(std::ptr::with_exposed_provenance_mut( + fixnum.get_num() as usize, + )), _ => Err(FFIError::ValueCast), } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index b6191f29..b7d5b595 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5002,8 +5002,6 @@ impl Machine { #[cfg(feature = "ffi")] #[inline(always)] pub(crate) fn foreign_call(&mut self) -> CallResult { - use dashu::integer::IBig; - let function_name = self.deref_register(1); let args_reg = self.deref_register(2); let return_value = self.deref_register(3); @@ -5011,8 +5009,7 @@ impl Machine { let stub_gen = || functor_stub(atom!("foreign_call"), 3); fn map_arg(machine_st: &mut MachineState, source: HeapCellValue) -> crate::ffi::Value { match Number::try_from((source, &machine_st.arena.f64_tbl)) { - Ok(Number::Fixnum(n)) => Value::Int(n.get_num()), - Ok(Number::Float(n)) => Value::Float(n.into_inner()), + Ok(number) => Value::Number(number), _ => { let stub_gen = || functor_stub(atom!("foreign_call"), 3); if let Some(string) = machine_st.value_to_str_like(source) { @@ -5047,28 +5044,29 @@ impl Machine { .into_iter() .map(|x| map_arg(&mut self.machine_st, x)) .collect(); - match self - .foreign_function_table - .exec(&function_name.as_str(), args) - { + match self.foreign_function_table.exec( + &function_name.as_str(), + args, + &mut self.machine_st.arena, + ) { Ok(result) => { match result { - Value::Int(n) => { - if let Ok(fixnum) = Fixnum::build_with_checked(n) { + Value::Number(n) => match n { + Number::Float(OrderedFloat(n)) => { + let n = float_alloc!(n, self.machine_st.arena); + self.machine_st.unify_f64(n, return_value) + } + Number::Integer(typed_arena_ptr) => { + self.machine_st.unify_big_int(typed_arena_ptr, return_value) + } + Number::Rational(typed_arena_ptr) => { + self.machine_st + .unify_rational(typed_arena_ptr, return_value); + } + Number::Fixnum(fixnum) => { self.machine_st.unify_fixnum(fixnum, return_value) - } else { - let bigint = IBig::from(n); - let bigint = arena_alloc!( - bigint.clone(), - &mut self.machine_st.arena - ); - self.machine_st.unify_big_int(bigint, return_value) } - } - Value::Float(n) => { - let n = float_alloc!(n, self.machine_st.arena); - self.machine_st.unify_f64(n, return_value) - } + }, Value::Struct(name, args) => { let struct_value = resource_error_call_result!( self.machine_st, @@ -5108,34 +5106,26 @@ impl Machine { fn build_struct(&mut self, name: &str, mut args: Vec) -> Result { args.insert(0, Value::CString(CString::new(name).unwrap())); - let mut expanded_args = Vec::with_capacity(args.len()); - - for val in args { - expanded_args.push(match val { - Value::Int(n) => { - if let Ok(fixnum) = Fixnum::build_with_checked(n) { - fixnum_as_cell!(fixnum) - } else { - integer_as_cell!(Number::Integer(arena_alloc!( - Integer::from(n), - &mut self.machine_st.arena - ))) - } - } - Value::Float(n) => HeapCellValue::from(float_alloc!(n, self.machine_st.arena)), - Value::CString(cstr) => atom_as_cell!(AtomTable::build_with( - &self.machine_st.atom_tbl, - &cstr.into_string().unwrap() - )), - Value::Struct(name, struct_args) => self.build_struct(&name, struct_args)?, - }); - } + let cells: Vec<_> = args + .into_iter() + .map(|val| { + Ok(match val { + Value::Number(n) => match n { + Number::Float(OrderedFloat(f)) => { + HeapCellValue::from(float_alloc!(f, self.machine_st.arena)) + } + _ => integer_as_cell!(n), + }, + Value::CString(cstr) => atom_as_cell!(AtomTable::build_with( + &self.machine_st.atom_tbl, + &cstr.into_string().unwrap() + )), + Value::Struct(name, struct_args) => self.build_struct(&name, struct_args)?, + }) + }) + .collect::>()?; - sized_iter_to_heap_list( - &mut self.machine_st.heap, - expanded_args.len(), - expanded_args.into_iter(), - ) + sized_iter_to_heap_list(&mut self.machine_st.heap, cells.len(), cells.into_iter()) } #[cfg(feature = "ffi")] diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 18389225..cb48fca4 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -583,10 +583,13 @@ mod private { } } + impl MightNotFitInFixnumSeal for T {} impl MightNotFitInFixnumSeal for i64 {} + impl MightNotFitInFixnumSeal for u64 {} impl MightNotFitInFixnumSeal for &Integer {} impl MightNotFitInFixnumSeal for Integer {} impl MightNotFitInFixnumSeal for usize {} + impl MightNotFitInFixnumSeal for isize {} } #[allow(private_bounds)] diff --git a/tests/scryer/ffi.rs b/tests/scryer/ffi.rs index b8cad1f9..13a9c3ca 100644 --- a/tests/scryer/ffi.rs +++ b/tests/scryer/ffi.rs @@ -143,8 +143,7 @@ fn ffi_return_values() { #[no_mangle] extern "C" fn ffi_return_values_u64() -> u64 { - // 0xFEDCBA9876543210 // too large for i64 - 0xBEEFBEE5C0DEB00 + 0xFEDCBA9876543210 } #[no_mangle] @@ -172,7 +171,7 @@ fn ffi_return_values() { -0xBEEFBEE, 0xC0DEB000u32, -0xBEEFBEE5C0DEB00i64, - 0xBEEFBEE5C0DEB00u64, + 0xFEDCBA9876543210u64, std::f32::consts::PI as f64, std::f64::consts::TAU ); -- 2.54.0