[pypy-commit] pypy ppc-jit-backend: fix an issue in clibffi that is triggered on big endian platforms due to the byte order when casting a larger data type to smaller one to be passed to a function called through ffi

bivab noreply at buildbot.pypy.org
Wed Nov 9 16:26:22 CET 2011


Author: David Schneider <david.schneider at picle.org>
Branch: ppc-jit-backend
Changeset: r49020:1c63c71d3b29
Date: 2011-11-09 16:22 +0100
http://bitbucket.org/pypy/pypy/changeset/1c63c71d3b29/

Log:	fix an issue in clibffi that is triggered on big endian platforms
	due to the byte order when casting a larger data type to smaller one
	to be passed to a function called through ffi

diff --git a/pypy/rlib/clibffi.py b/pypy/rlib/clibffi.py
--- a/pypy/rlib/clibffi.py
+++ b/pypy/rlib/clibffi.py
@@ -30,6 +30,9 @@
 _MAC_OS = platform.name == "darwin"
 _FREEBSD_7 = platform.name == "freebsd7"
 
+_LITTLE_ENDIAN = sys.byteorder == 'little'
+_BIG_ENDIAN = sys.byteorder == 'big'
+
 if _WIN32:
     from pypy.rlib import rwin32
 
@@ -337,15 +340,46 @@
     return TYPE_MAP[tp]
 cast_type_to_ffitype._annspecialcase_ = 'specialize:memo'
 
-def push_arg_as_ffiptr(ffitp, arg, ll_buf):
+def push_arg_as_ffiptr_base(ffitp, arg, ll_buf):
+    # this is for primitive types. For structures and arrays
+    # would be something different (more dynamic)
+    # XXX is this valid in C?, for args that are larger than the size of
+    # ll_buf we write over the boundaries of the allocated char array and
+    # just keep as much bytes as we need for the target type. Maybe using
+    # memcpy would be better here. Also this
+    # only works on little endian architectures
+    TP = lltype.typeOf(arg)
+    TP_P = lltype.Ptr(rffi.CArray(TP))
+    buf = rffi.cast(TP_P, ll_buf)
+    buf[0] = arg
+push_arg_as_ffiptr_base._annspecialcase_ = 'specialize:argtype(1)'
+
+def push_arg_as_ffiptr_memcpy(ffitp, arg, ll_buf):
     # this is for primitive types. For structures and arrays
     # would be something different (more dynamic)
     TP = lltype.typeOf(arg)
     TP_P = lltype.Ptr(rffi.CArray(TP))
-    buf = rffi.cast(TP_P, ll_buf)
-    buf[0] = arg
-push_arg_as_ffiptr._annspecialcase_ = 'specialize:argtype(1)'
+    TP_size = rffi.sizeof(TP)
+    c_size = intmask(ffitp.c_size)
 
+    # if both types have the same size, we do not can directly write the
+    # value to the buffer
+    if c_size == TP_size:
+        return push_arg_as_ffiptr_base(ffitp, arg, ll_buf)
+
+    # store arg in a small box in memory
+    # and copy the relevant bytes over to the target buffer (ll_buf)
+    with lltype.scoped_alloc(TP_P.TO, TP_size) as argbuf:
+        argbuf[0] = arg
+        cargbuf = rffi.cast(rffi.CCHARP, argbuf)
+        ptr = rffi.ptradd(cargbuf, TP_size - c_size)
+        rffi.c_memcpy(ll_buf, ptr, c_size)
+push_arg_as_ffiptr_memcpy._annspecialcase_ = 'specialize:argtype(1)'
+
+if _LITTLE_ENDIAN:
+    push_arg_as_ffiptr = push_arg_as_ffiptr_base
+else:
+    push_arg_as_ffiptr = push_arg_as_ffiptr_memcpy
 
 # type defs for callback and closure userdata
 USERDATA_P = lltype.Ptr(lltype.ForwardReference())
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
--- a/pypy/rlib/libffi.py
+++ b/pypy/rlib/libffi.py
@@ -234,7 +234,7 @@
         # It is important that there is no other operation in the middle, else
         # the optimizer will fail to recognize the pattern and won't turn it
         # into a fast CALL.  Note that "arg = arg.next" is optimized away,
-        # assuming that archain is completely virtual.
+        # assuming that argchain is completely virtual.
         self = jit.promote(self)
         if argchain.numargs != len(self.argtypes):
             raise TypeError, 'Wrong number of arguments: %d expected, got %d' %\
diff --git a/pypy/rlib/test/test_libffi.py b/pypy/rlib/test/test_libffi.py
--- a/pypy/rlib/test/test_libffi.py
+++ b/pypy/rlib/test/test_libffi.py
@@ -179,6 +179,17 @@
         res = self.call(func, [chr(20), 22], rffi.LONG)
         assert res == 42
 
+    def test_char_args(self):
+        """
+        char sum_args(char a, char b) {
+            return a + b;
+        }
+        """
+        libfoo = self.get_libfoo()
+        func = (libfoo, 'sum_args', [types.schar, types.schar], types.schar)
+        res = self.call(func, [123, 43], rffi.CHAR)
+        assert res == chr(166)
+
     def test_unsigned_short_args(self):
         """
             unsigned short sum_xy_us(unsigned short x, unsigned short y)


More information about the pypy-commit mailing list