diff --git a/lib-python/2.7/ctypes/test/test_arrays.py b/lib-python/2.7/ctypes/test/test_arrays.py index c6cf3d939cba9d1c7b49a654f7860f61512dce89..54581651536ae9cc9d74404f81c52bf2ccd6b82f 100644 --- a/lib-python/2.7/ctypes/test/test_arrays.py +++ b/lib-python/2.7/ctypes/test/test_arrays.py @@ -6,20 +6,11 @@ from ctypes.test import need_symbol formats = "bBhHiIlLqQfd" -# c_longdouble commented out for PyPy, look at the commend in test_longdouble formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \ - c_long, c_ulonglong, c_float, c_double #, c_longdouble + c_long, c_ulonglong, c_float, c_double, c_longdouble class ArrayTestCase(unittest.TestCase): - @impl_detail('long double not supported by PyPy', pypy=False) - def test_longdouble(self): - """ - This test is empty. It's just here to remind that we commented out - c_longdouble in "formats". If pypy will ever supports c_longdouble, we - should kill this test and uncomment c_longdouble inside formats. - """ - def test_simple(self): # create classes holding simple numeric types, and check # various properties. diff --git a/lib-python/2.7/ctypes/test/test_cfuncs.py b/lib-python/2.7/ctypes/test/test_cfuncs.py index 14940fe4cc0afde5934c4552019b2686de701558..9c24cc0bb0908799c4eca9548bfe756991a0d963 100644 --- a/lib-python/2.7/ctypes/test/test_cfuncs.py +++ b/lib-python/2.7/ctypes/test/test_cfuncs.py @@ -160,14 +160,12 @@ class CFunctions(unittest.TestCase): self.assertEqual(self._dll.tf_bd(0, 42.), 14.) self.assertEqual(self.S(), 42) - @impl_detail('long double not supported by PyPy', pypy=False) def test_longdouble(self): self._dll.tf_D.restype = c_longdouble self._dll.tf_D.argtypes = (c_longdouble,) self.assertEqual(self._dll.tf_D(42.), 14.) self.assertEqual(self.S(), 42) - @impl_detail('long double not supported by PyPy', pypy=False) def test_longdouble_plus(self): self._dll.tf_bD.restype = c_longdouble self._dll.tf_bD.argtypes = (c_byte, c_longdouble) diff --git a/lib-python/2.7/ctypes/test/test_functions.py b/lib-python/2.7/ctypes/test/test_functions.py index b4ab1900d1f1a01ff48a05c4c583aa12a1214e25..7c0e9cd6826fb575c01811fa1750c2aa961bcf88 100644 --- a/lib-python/2.7/ctypes/test/test_functions.py +++ b/lib-python/2.7/ctypes/test/test_functions.py @@ -140,7 +140,6 @@ class FunctionTestCase(unittest.TestCase): self.assertEqual(result, -21) self.assertEqual(type(result), float) - @impl_detail('long double not supported by PyPy', pypy=False) def test_longdoubleresult(self): f = dll._testfunc_D_bhilfD f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_longdouble] diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py index d8f91caa2999cf00eddcb0b4ba865567ce09eb99..1a3aba7eff50173b9102301aee9e8369ba1f0e4d 100644 --- a/lib_pypy/_ctypes/basics.py +++ b/lib_pypy/_ctypes/basics.py @@ -297,6 +297,7 @@ _shape_to_ffi_type.typemap = { 'Q' : _ffi.types.ulonglong, 'f' : _ffi.types.float, 'd' : _ffi.types.double, + 'g' : _ffi.types.longdouble, 's' : _ffi.types.void_p, 'P' : _ffi.types.void_p, 'z' : _ffi.types.void_p, diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py index 84e7f4364ccd570e47d070fbb6ce30cf8ce76f91..be1b7c0a944b20d64a6a856ecdfc1b8b94ac5f8d 100644 --- a/lib_pypy/_ctypes/primitive.py +++ b/lib_pypy/_ctypes/primitive.py @@ -99,6 +99,8 @@ def swap_bytes(value, sizeof, typeof, get_or_set): return swap_double_float('f') elif typeof in ('c_double', 'c_double_le', 'c_double_be'): return swap_double_float('d') + elif typeof in ('c_longdouble', 'c_longdouble_le', 'c_longdouble_be'): + return swap_double_float('g') else: if sizeof == 2: return swap_2() diff --git a/pypy/module/_rawffi/alt/interp_ffitype.py b/pypy/module/_rawffi/alt/interp_ffitype.py index 85eac0b13b76514e9917daf9b253ee6dc5e4856d..f3be0b877409badef54e57301ab727b1251a51d2 100644 --- a/pypy/module/_rawffi/alt/interp_ffitype.py +++ b/pypy/module/_rawffi/alt/interp_ffitype.py @@ -90,6 +90,9 @@ class W_FFIType(W_Root): def is_singlefloat(self): return self is app_types.float + def is_longdouble(self): + return self is app_types.longdouble + def is_void(self): return self is app_types.void @@ -134,6 +137,7 @@ def build_ffi_types(): W_FFIType('char', libffi.types.uchar), W_FFIType('unichar', libffi.types.wchar_t), # + W_FFIType('longdouble',libffi.types.longdouble), W_FFIType('double', libffi.types.double), W_FFIType('float', libffi.types.float), W_FFIType('void', libffi.types.void), diff --git a/pypy/module/_rawffi/alt/interp_funcptr.py b/pypy/module/_rawffi/alt/interp_funcptr.py index 89fe4bdd940dd6d90c40738bdedc280c5776fea7..efcb0300db381a0bc2de9453268ac2df07818003 100644 --- a/pypy/module/_rawffi/alt/interp_funcptr.py +++ b/pypy/module/_rawffi/alt/interp_funcptr.py @@ -179,6 +179,9 @@ class PushArgumentConverter(FromAppLevelConverter): def handle_singlefloat(self, w_ffitype, w_obj, singlefloatval): self.argchain.arg(singlefloatval) + def handle_longdouble(self, w_ffitype, w_obj, longdoubleval): + self.argchain.arg(longdoubleval) + def handle_struct(self, w_ffitype, w_structinstance): # arg_raw directly takes value to put inside ll_args ptrval = w_structinstance.rawmem @@ -266,6 +269,9 @@ class CallFunctionConverter(ToAppLevelConverter): def get_singlefloat(self, w_ffitype): return self.func.call(self.argchain, rffi.FLOAT) + def get_longdouble(self, w_ffitype): + return self.func.call(self.argchain, rffi.LONGDOUBLE) + def get_struct(self, w_ffitype, w_structdescr): addr = self.func.call(self.argchain, rffi.LONG, is_struct=True) return w_structdescr.fromaddress(self.space, addr) diff --git a/pypy/module/_rawffi/alt/test/test_ffitype.py b/pypy/module/_rawffi/alt/test/test_ffitype.py index 93eb3ad4139c49b32f982196121bc987a940d558..8f5aaded95ad9ea8b68159ecaaa99c12f4aa2839 100644 --- a/pypy/module/_rawffi/alt/test/test_ffitype.py +++ b/pypy/module/_rawffi/alt/test/test_ffitype.py @@ -7,6 +7,7 @@ class AppTestFFIType(object): assert str(types.uint) == "" assert types.sint.name == 'sint' assert types.uint.name == 'uint' + assert types.longdouble.name == 'longdouble' def test_sizeof(self): from _rawffi.alt import types diff --git a/pypy/module/_rawffi/alt/test/test_funcptr.py b/pypy/module/_rawffi/alt/test/test_funcptr.py index 6451872e6c9cdbe27d0fbd41eafa00cd7f568d01..2cb7ea7eb13a92c5dc7828054da8214a18f7076b 100644 --- a/pypy/module/_rawffi/alt/test/test_funcptr.py +++ b/pypy/module/_rawffi/alt/test/test_funcptr.py @@ -415,6 +415,24 @@ class AppTestFFI(BaseAppTestFFI): assert res == self.f_12_34_plus_56_78 + def test_long_double_args(self): + """ + #include + DLLEXPORT long double sum_xy_longdouble(long double x, long double y) + { + /* fprintf(stdout, "x,y %g, %g\\n", x, y); */ + return x+y; + } + """ + from _rawffi.alt import CDLL, types + libfoo = CDLL(self.libfoo_name) + sum_xy = libfoo.getfunc('sum_xy_longdouble', + [types.longdouble, types.longdouble], + types.longdouble) + res = sum_xy(12.34, 56.78) + assert res - self.f_12_34_plus_56_78 < 0.01 + + def test_slonglong_args(self): """ DLLEXPORT long long sum_xy_longlong(long long x, long long y) diff --git a/pypy/module/_rawffi/alt/type_converter.py b/pypy/module/_rawffi/alt/type_converter.py index 0de4c6ee093c5d08a38f868317d6211a12ad4196..8490a1327268405358fc1064745ad141f4727ff9 100644 --- a/pypy/module/_rawffi/alt/type_converter.py +++ b/pypy/module/_rawffi/alt/type_converter.py @@ -48,6 +48,8 @@ class FromAppLevelConverter(object): self._float(w_ffitype, w_obj) elif w_ffitype.is_singlefloat(): self._singlefloat(w_ffitype, w_obj) + elif w_ffitype.is_longdouble(): + self._longdouble(w_ffitype, w_obj) elif w_ffitype.is_struct(): if isinstance(w_obj, W_StructureInstance): self.handle_struct_rawffi(w_ffitype, w_obj) @@ -77,6 +79,12 @@ class FromAppLevelConverter(object): singlefloatval = r_singlefloat(floatval) self.handle_singlefloat(w_ffitype, w_obj, singlefloatval) + def _longdouble(self, w_ffitype, w_obj): + # a separate function, which can be seen by the jit or not, + # depending on whether longdoubles are supported + val = self.space.float_w(w_obj, allow_conversion=False) + self.handle_longdouble(w_ffitype, w_obj, val) + def maybe_handle_char_or_unichar_p(self, w_ffitype, w_obj): w_type = jit.promote(self.space.type(w_obj)) if w_ffitype.is_char_p() and w_type is self.space.w_bytes: @@ -233,6 +241,8 @@ class ToAppLevelConverter(object): return self._float(w_ffitype) elif w_ffitype.is_singlefloat(): return self._singlefloat(w_ffitype) + elif w_ffitype.is_longdouble(): + return self._longdouble(w_ffitype) elif w_ffitype.is_struct(): w_structdescr = w_ffitype.w_structdescr if isinstance(w_structdescr, W__StructDescr): @@ -272,6 +282,12 @@ class ToAppLevelConverter(object): singlefloatval = self.get_singlefloat(w_ffitype) return self.space.newfloat(float(singlefloatval)) + def _longdouble(self, w_ffitype): + # a separate function, which can be seen by the jit or not, + # depending on whether longdoubles are supported + longdoubleval = self.get_longdouble(w_ffitype) + return self.space.newfloat(float(longdoubleval)) + def error(self, w_ffitype): raise oefmt(self.space.w_TypeError, "Unsupported ffi type to convert: %s", w_ffitype.name) diff --git a/rpython/rlib/clibffi.py b/rpython/rlib/clibffi.py index 241dbc0f86fd668265ca8eeb637674873b877905..b44425934cbc886111b71fceae2d1eb479ccb751 100644 --- a/rpython/rlib/clibffi.py +++ b/rpython/rlib/clibffi.py @@ -8,6 +8,8 @@ from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rtyper.tool import rffi_platform from rpython.rlib.unroll import unrolling_iterable from rpython.rlib.rarithmetic import intmask, is_emulated_long +from rpython.rlib.rstruct.ieee import pack_float80, unpack_float80 +from rpython.rlib.rstring import StringBuilder from rpython.rlib.objectmodel import we_are_translated from rpython.rlib.rmmap import alloc from rpython.rlib.rdynload import dlopen, dlclose, dlsym, dlsym_byordinal @@ -386,6 +388,14 @@ def push_arg_as_ffiptr(ffitp, arg, ll_buf): if c_size == TP_size: buf = rffi.cast(TP_P, ll_buf) buf[0] = arg + elif (TP is rffi.FLOAT or TP is rffi.DOUBLE) and c_size > TP_size: + # LongDouble. Convert the python float. The assert should not be needed + assert isinstance(arg, float) + result = StringBuilder(c_size) + pack_float80(result, arg, c_size, False) + asbytes = result.build() + for i in range(c_size): + ll_buf[i] = asbytes[i] else: # needs byte-by-byte copying. Make sure 'arg' is an integer type. # Note that this won't work for rffi.FLOAT/rffi.DOUBLE. diff --git a/rpython/rlib/libffi.py b/rpython/rlib/libffi.py index caea99753bac4196719c4e8c5702efb41bbf3c94..5c62411076754872255f5b466ba15f9d5ef88a9f 100644 --- a/rpython/rlib/libffi.py +++ b/rpython/rlib/libffi.py @@ -47,8 +47,6 @@ class types(object): cls.ulonglong = clibffi.cast_type_to_ffitype(rffi.ULONGLONG) cls.signed = clibffi.cast_type_to_ffitype(rffi.SIGNED) cls.wchar_t = clibffi.cast_type_to_ffitype(lltype.UniChar) - # XXX long double support: clibffi.ffi_type_longdouble, but then - # XXX fix the whole rest of this file to add a case for long double del cls._import @staticmethod @@ -57,10 +55,11 @@ class types(object): """Returns 'v' for void, 'f' for float, 'i' for signed integer, and 'u' for unsigned integer. """ - if ffi_type is types.void: return 'v' - elif ffi_type is types.double: return 'f' - elif ffi_type is types.float: return 's' - elif ffi_type is types.pointer: return 'u' + if ffi_type is types.void: return 'v' + elif ffi_type is types.double: return 'f' + elif ffi_type is types.longdouble: return 'g' + elif ffi_type is types.float: return 's' + elif ffi_type is types.pointer: return 'u' # elif ffi_type is types.schar: return 'i' elif ffi_type is types.uchar: return 'u' @@ -142,6 +141,8 @@ class ArgChain(object): val = rffi.cast(rffi.LONGLONG, val) elif TYPE is rffi.FLOAT: cls = SingleFloatArg + elif TYPE is rffi.LONGDOUBLE: + cls = LongDoubleArg else: raise TypeError('Unsupported argument type: %s' % TYPE) self._append(cls(val)) @@ -203,6 +204,18 @@ class SingleFloatArg(AbstractArg): def push(self, func, ll_args, i): func._push_singlefloat(self.singlefloatval, ll_args, i) +class LongDoubleArg(AbstractArg): + """ An argument representing a C long double + """ + + def __init__(self, longdoubleval): + # XXX what should this be? + raise NotImplementedError("fix me") + self.longdoubleval = longdoubleval + + def push(self, func, ll_args, i): + func._push_longdouble(self.longdoubleval, ll_args, i) + class LongLongArg(AbstractArg): """ An argument representing a C long long @@ -281,6 +294,8 @@ class Func(AbstractFuncPtr): return self._do_call_float(self.funcsym, ll_args) elif RESULT is rffi.FLOAT: return self._do_call_singlefloat(self.funcsym, ll_args) + elif RESULT is rffi.LONGDOUBLE: + return self._do_call_longdouble(self.funcsym, ll_args) elif RESULT is rffi.LONGLONG or RESULT is rffi.ULONGLONG: assert IS_32_BIT res = self._do_call_longlong(self.funcsym, ll_args) @@ -352,6 +367,11 @@ class Func(AbstractFuncPtr): def _do_call_singlefloat(self, funcsym, ll_args): return self._do_call(funcsym, ll_args, rffi.FLOAT) + #@jit.oopspec('libffi_call_longdouble(self, funcsym, ll_args)') + @jit.dont_look_inside + def _do_call_longdouble(self, funcsym, ll_args): + return self._do_call(funcsym, ll_args, rffi.LONGDOUBLE) + @jit.dont_look_inside def _do_call_raw(self, funcsym, ll_args): # same as _do_call_int, but marked as jit.dont_look_inside