]> Repositorios git - scryer-prolog.git/commitdiff
make ffi support full {i,u}64 range
authorBennet Bleßmann <[email protected]>
Wed, 22 Jan 2025 23:40:31 +0000 (00:40 +0100)
committerBennet Bleßmann <[email protected]>
Fri, 1 Aug 2025 17:07:07 +0000 (19:07 +0200)
src/ffi.rs
src/machine/system_calls.rs
src/parser/ast.rs
tests/scryer/ffi.rs

index 340131894e3016e20d32f8e6f9d6a1a26161c4fb..2f949bf82fe53cea464685c7479e789ca8e6fc4f 100644 (file)
@@ -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<dyn Any>;
                         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<Value>) -> Result<Value, FFIError> {
+    pub fn exec(
+        &mut self,
+        name: &str,
+        mut args: Vec<Value>,
+        arena: &mut Arena,
+    ) -> Result<Value, FFIError> {
         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<u64> = 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<f32> = 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<f64> = 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<Value, FFIError> {
         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::<u64>()));
-                        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::<u64>());
-                    }
+                    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::<f32>()));
                         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::<f32>());
                     }
                     libffi::raw::FFI_TYPE_DOUBLE => {
                         field_ptr =
                             field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f64>()));
                         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::<f64>());
                     }
                     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<Value>),
 }
 
 impl Value {
-    fn as_int(&self) -> Result<i64, FFIError> {
+    fn as_int<I>(&self) -> Result<I, FFIError>
+    where
+        Integer: TryInto<I>,
+        i64: TryInto<I>,
+    {
         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<f64, FFIError> {
         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),
         }
     }
index b6191f292fd7622c926941beb043eff39a0c486f..b7d5b5955e4da6a8202305145fbe1604714f976c 100644 (file)
@@ -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<Value>) -> Result<HeapCellValue, usize> {
         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::<Result<_, usize>>()?;
 
-        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")]
index 18389225f22085a64219d53ba9741f0d6027ad06..cb48fca4e3c1b7cf6cdb0a8e841176d2c0f106ee 100644 (file)
@@ -583,10 +583,13 @@ mod private {
         }
     }
 
+    impl<T: FitsInFixnumSeal> 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)]
index b8cad1f99164563aaac131d4a2caee50db612caa..13a9c3cae6014dd81f5cd163dc329397a57e42bf 100644 (file)
@@ -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
     );