unix/modffi: Use a union for passing/returning FFI values.
This fixes a bug where double arguments on a 32-bit architecture would not be passed correctly because they only had 4 bytes of storage (not 8). It also fixes a compiler warning/error in return_ffi_value on certian architectures: array subscript 'double[0]' is partly outside array bounds of 'ffi_arg[1]' {aka 'long unsigned int[1]'}. Fixes issue #7064. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
parent
8172c2e9c5
commit
9e29217c73
@ -56,6 +56,13 @@
|
||||
* but may be later.
|
||||
*/
|
||||
|
||||
// This union is large enough to hold any supported argument/return value.
|
||||
typedef union _ffi_union_t {
|
||||
ffi_arg ffi;
|
||||
float flt;
|
||||
double dbl;
|
||||
} ffi_union_t;
|
||||
|
||||
typedef struct _mp_obj_opaque_t {
|
||||
mp_obj_base_t base;
|
||||
void *val;
|
||||
@ -151,10 +158,10 @@ STATIC ffi_type *get_ffi_type(mp_obj_t o_in) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("unknown type"));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) {
|
||||
STATIC mp_obj_t return_ffi_value(ffi_union_t *val, char type) {
|
||||
switch (type) {
|
||||
case 's': {
|
||||
const char *s = (const char *)(intptr_t)val;
|
||||
const char *s = (const char *)(intptr_t)val->ffi;
|
||||
if (!s) {
|
||||
return mp_const_none;
|
||||
}
|
||||
@ -164,20 +171,16 @@ STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) {
|
||||
return mp_const_none;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'f': {
|
||||
union { ffi_arg ffi;
|
||||
float flt;
|
||||
} val_union = { .ffi = val };
|
||||
return mp_obj_new_float_from_f(val_union.flt);
|
||||
return mp_obj_new_float_from_f(val->flt);
|
||||
}
|
||||
case 'd': {
|
||||
double *p = (double *)&val;
|
||||
return mp_obj_new_float_from_d(*p);
|
||||
return mp_obj_new_float_from_d(val->dbl);
|
||||
}
|
||||
#endif
|
||||
case 'O':
|
||||
return (mp_obj_t)(intptr_t)val;
|
||||
return (mp_obj_t)(intptr_t)val->ffi;
|
||||
default:
|
||||
return mp_obj_new_int(val);
|
||||
return mp_obj_new_int(val->ffi);
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,28 +371,26 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
|
||||
assert(n_kw == 0);
|
||||
assert(n_args == self->cif.nargs);
|
||||
|
||||
ffi_arg values[n_args];
|
||||
ffi_union_t values[n_args];
|
||||
void *valueptrs[n_args];
|
||||
const char *argtype = self->argtypes;
|
||||
for (uint i = 0; i < n_args; i++, argtype++) {
|
||||
mp_obj_t a = args[i];
|
||||
if (*argtype == 'O') {
|
||||
values[i] = (ffi_arg)(intptr_t)a;
|
||||
values[i].ffi = (ffi_arg)(intptr_t)a;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
} else if (*argtype == 'f') {
|
||||
float *p = (float *)&values[i];
|
||||
*p = mp_obj_get_float_to_f(a);
|
||||
values[i].flt = mp_obj_get_float_to_f(a);
|
||||
} else if (*argtype == 'd') {
|
||||
double *p = (double *)&values[i];
|
||||
*p = mp_obj_get_float_to_d(a);
|
||||
values[i].dbl = mp_obj_get_float_to_d(a);
|
||||
#endif
|
||||
} else if (a == mp_const_none) {
|
||||
values[i] = 0;
|
||||
values[i].ffi = 0;
|
||||
} else if (mp_obj_is_int(a)) {
|
||||
values[i] = mp_obj_int_get_truncated(a);
|
||||
values[i].ffi = mp_obj_int_get_truncated(a);
|
||||
} else if (mp_obj_is_str(a)) {
|
||||
const char *s = mp_obj_str_get_str(a);
|
||||
values[i] = (ffi_arg)(intptr_t)s;
|
||||
values[i].ffi = (ffi_arg)(intptr_t)s;
|
||||
} else if (((mp_obj_base_t *)MP_OBJ_TO_PTR(a))->type->buffer_p.get_buffer != NULL) {
|
||||
mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(a);
|
||||
mp_buffer_info_t bufinfo;
|
||||
@ -397,32 +398,19 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
values[i] = (ffi_arg)(intptr_t)bufinfo.buf;
|
||||
values[i].ffi = (ffi_arg)(intptr_t)bufinfo.buf;
|
||||
} else if (mp_obj_is_type(a, &fficallback_type)) {
|
||||
mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a);
|
||||
values[i] = (ffi_arg)(intptr_t)p->func;
|
||||
values[i].ffi = (ffi_arg)(intptr_t)p->func;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
valueptrs[i] = &values[i];
|
||||
}
|
||||
|
||||
// If ffi_arg is not big enough to hold a double, then we must pass along a
|
||||
// pointer to a memory location of the correct size.
|
||||
// TODO check if this needs to be done for other types which don't fit into
|
||||
// ffi_arg.
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
if (sizeof(ffi_arg) == 4 && self->rettype == 'd') {
|
||||
double retval;
|
||||
ffi_call(&self->cif, self->func, &retval, valueptrs);
|
||||
return mp_obj_new_float_from_d(retval);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ffi_arg retval;
|
||||
ffi_call(&self->cif, self->func, &retval, valueptrs);
|
||||
return return_ffi_value(retval, self->rettype);
|
||||
}
|
||||
ffi_union_t retval;
|
||||
ffi_call(&self->cif, self->func, &retval, valueptrs);
|
||||
return return_ffi_value(&retval, self->rettype);
|
||||
|
||||
error:
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("don't know how to pass object to native function"));
|
||||
|
@ -35,6 +35,15 @@ print("%.6f" % strtod("1.23", None))
|
||||
# test passing double and float args
|
||||
libm = ffi_open(("libm.so", "libm.so.6", "libc.so.0", "libc.so.6", "libc.dylib"))
|
||||
tgamma = libm.func("d", "tgamma", "d")
|
||||
for fun in (tgamma,):
|
||||
for fun_name in ("tgamma",):
|
||||
fun = globals()[fun_name]
|
||||
for val in (0.5, 1, 1.0, 1.5, 4, 4.0):
|
||||
print("%.6f" % fun(val))
|
||||
print(fun_name, "%.5f" % fun(val))
|
||||
|
||||
# test passing 2x float/double args
|
||||
powf = libm.func("f", "powf", "ff")
|
||||
pow = libm.func("d", "pow", "dd")
|
||||
for fun_name in ("powf", "pow"):
|
||||
fun = globals()[fun_name]
|
||||
for args in ((0, 1), (1, 0), (2, 0.5), (3, 4)):
|
||||
print(fun_name, "%.5f" % fun(*args))
|
||||
|
@ -1,8 +1,16 @@
|
||||
1.230000
|
||||
1.230000
|
||||
1.772454
|
||||
1.000000
|
||||
1.000000
|
||||
0.886227
|
||||
6.000000
|
||||
6.000000
|
||||
tgamma 1.77245
|
||||
tgamma 1.00000
|
||||
tgamma 1.00000
|
||||
tgamma 0.88623
|
||||
tgamma 6.00000
|
||||
tgamma 6.00000
|
||||
powf 0.00000
|
||||
powf 1.00000
|
||||
powf 1.41421
|
||||
powf 81.00000
|
||||
pow 0.00000
|
||||
pow 1.00000
|
||||
pow 1.41421
|
||||
pow 81.00000
|
||||
|
Loading…
Reference in New Issue
Block a user