Author: David Schneider <david.schnei...@picle.org>
Branch: 
Changeset: r64957:162603593f00
Date: 2013-06-23 04:24 -0500
http://bitbucket.org/pypy/pypy/changeset/162603593f00/

Log:    ctypes/_rawffi can generate unaligned access to floating point
        fields in structures. Directly reading unaligned floats from memory
        is not supported on ARM. This change adds a wrapper around the reads
        and writes that checks the pointer alignment and uses memcpy in case
        of unaligned access on ARM.

diff --git a/pypy/module/_rawffi/array.py b/pypy/module/_rawffi/array.py
--- a/pypy/module/_rawffi/array.py
+++ b/pypy/module/_rawffi/array.py
@@ -13,19 +13,9 @@
 from pypy.module._rawffi.interp_rawffi import TYPEMAP
 from pypy.module._rawffi.interp_rawffi import size_alignment
 from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
+from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
 from rpython.rlib.rarithmetic import r_uint
 
-def push_elem(ll_array, pos, value):
-    TP = lltype.typeOf(value)
-    ll_array = rffi.cast(rffi.CArrayPtr(TP), ll_array)
-    ll_array[pos] = value
-push_elem._annspecialcase_ = 'specialize:argtype(2)'
-
-def get_elem(ll_array, pos, ll_t):
-    ll_array = rffi.cast(rffi.CArrayPtr(ll_t), ll_array)
-    return ll_array[pos]
-get_elem._annspecialcase_ = 'specialize:arg(2)'
-
 
 class W_Array(W_DataShape):
     def __init__(self, basicffitype, size):
@@ -57,7 +47,7 @@
                                                 " array length"))
             for num in range(iterlength):
                 w_item = items_w[num]
-                unwrap_value(space, push_elem, result.ll_buffer, num,
+                unwrap_value(space, write_ptr, result.ll_buffer, num,
                              self.itemcode, w_item)
         return space.wrap(result)
 
@@ -118,7 +108,7 @@
             raise segfault_exception(space, "setting element of freed array")
         if num >= self.length or num < 0:
             raise OperationError(space.w_IndexError, space.w_None)
-        unwrap_value(space, push_elem, self.ll_buffer, num,
+        unwrap_value(space, write_ptr, self.ll_buffer, num,
                      self.shape.itemcode, w_value)
 
     def descr_setitem(self, space, w_index, w_value):
@@ -136,7 +126,7 @@
             raise segfault_exception(space, "accessing elements of freed 
array")
         if num >= self.length or num < 0:
             raise OperationError(space.w_IndexError, space.w_None)
-        return wrap_value(space, get_elem, self.ll_buffer, num,
+        return wrap_value(space, read_ptr, self.ll_buffer, num,
                           self.shape.itemcode)
 
     def descr_getitem(self, space, w_index):
diff --git a/pypy/module/_rawffi/callback.py b/pypy/module/_rawffi/callback.py
--- a/pypy/module/_rawffi/callback.py
+++ b/pypy/module/_rawffi/callback.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from rpython.rtyper.lltypesystem import lltype, rffi
-from pypy.module._rawffi.array import push_elem
+from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
 from pypy.module._rawffi.structure import W_Structure
 from pypy.module._rawffi.interp_rawffi import (W_DataInstance, letter2tp,
      unwrap_value, unpack_argshapes, got_libffi_error)
@@ -40,7 +40,7 @@
                 args_w[i] = space.wrap(rffi.cast(rffi.ULONG, ll_args[i]))
         w_res = space.call(w_callable, space.newtuple(args_w))
         if callback_ptr.result is not None: # don't return void
-            unwrap_value(space, push_elem, ll_res, 0,
+            unwrap_value(space, write_ptr, ll_res, 0,
                          callback_ptr.result, w_res)
     except OperationError, e:
         tbprint(space, space.wrap(e.get_traceback()),
diff --git a/pypy/module/_rawffi/interp_rawffi.py 
b/pypy/module/_rawffi/interp_rawffi.py
--- a/pypy/module/_rawffi/interp_rawffi.py
+++ b/pypy/module/_rawffi/interp_rawffi.py
@@ -5,6 +5,7 @@
 
 from rpython.rlib.clibffi import *
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.tool import rffi_platform
 from rpython.rlib.unroll import unrolling_iterable
 import rpython.rlib.rposix as rposix
 
@@ -43,6 +44,8 @@
 }
 TYPEMAP_PTR_LETTERS = "POszZ"
 TYPEMAP_NUMBER_LETTERS = "bBhHiIlLqQ?"
+TYPEMAP_FLOAT_LETTERS = "fd" # XXX long doubles are not propperly supported in
+                             # rpython, so we ignore then here
 
 if _MS_WINDOWS:
     TYPEMAP['X'] = ffi_type_pointer
@@ -239,6 +242,52 @@
 )
 
 unroll_letters_for_numbers = unrolling_iterable(TYPEMAP_NUMBER_LETTERS)
+unroll_letters_for_floats = unrolling_iterable(TYPEMAP_FLOAT_LETTERS)
+
+_ARM = rffi_platform.getdefined('__arm__', '')
+
+def read_ptr(ptr, ofs, TP):
+    T = lltype.Ptr(rffi.CArray(TP))
+    for c in unroll_letters_for_floats:
+        # Note: if we are on ARM and have a float-ish value that is not word
+        # aligned accessing it directly causes a SIGBUS. Instead we use memcpy
+        # to avoid the problem
+        if (_ARM and LL_TYPEMAP[c] is TP
+                    and rffi.cast(lltype.Signed, ptr) & 3 != 0):
+            if ofs != 0:
+                ptr = rffi.ptradd(ptr, ofs*rffi.sizeof(TP))
+            with lltype.scoped_alloc(T.TO, 1) as t_array:
+                rffi.c_memcpy(
+                    rffi.cast(rffi.VOIDP, t_array),
+                    rffi.cast(rffi.VOIDP, ptr),
+                    rffi.sizeof(TP))
+                ptr_val = t_array[0]
+                return ptr_val
+    else:
+        return rffi.cast(T, ptr)[ofs]
+read_ptr._annspecialcase_ = 'specialize:arg(2)'
+
+def write_ptr(ptr, ofs, value):
+    TP = lltype.typeOf(value)
+    T = lltype.Ptr(rffi.CArray(TP))
+    for c in unroll_letters_for_floats:
+        # Note: if we are on ARM and have a float-ish value that is not word
+        # aligned accessing it directly causes a SIGBUS. Instead we use memcpy
+        # to avoid the problem
+        if (_ARM and LL_TYPEMAP[c] is TP
+                    and rffi.cast(lltype.Signed, ptr) & 3 != 0):
+            if ofs != 0:
+                ptr = rffi.ptradd(ptr, ofs*rffi.sizeof(TP))
+            with lltype.scoped_alloc(T.TO, 1) as s_array:
+                s_array[0] = value
+                rffi.c_memcpy(
+                    rffi.cast(rffi.VOIDP, ptr),
+                    rffi.cast(rffi.VOIDP, s_array),
+                    rffi.sizeof(TP))
+                return
+    else:
+        rffi.cast(T, ptr)[ofs] = value
+write_ptr._annspecialcase_ = 'specialize:argtype(2)'
 
 def segfault_exception(space, reason):
     w_mod = space.getbuiltinmodule("_rawffi")
diff --git a/pypy/module/_rawffi/structure.py b/pypy/module/_rawffi/structure.py
--- a/pypy/module/_rawffi/structure.py
+++ b/pypy/module/_rawffi/structure.py
@@ -12,8 +12,10 @@
 from pypy.module._rawffi.interp_rawffi import W_DataShape, W_DataInstance
 from pypy.module._rawffi.interp_rawffi import wrap_value, unwrap_value
 from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length
-from pypy.module._rawffi.interp_rawffi import size_alignment, LL_TYPEMAP
+from pypy.module._rawffi.interp_rawffi import LL_TYPEMAP
 from pypy.module._rawffi.interp_rawffi import unroll_letters_for_numbers
+from pypy.module._rawffi.interp_rawffi import size_alignment
+from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr
 from rpython.rlib import clibffi
 from rpython.rlib.rarithmetic import intmask, signedtype, widen
 from rpython.rlib.rarithmetic import r_uint, r_ulonglong, r_longlong
@@ -289,19 +291,18 @@
                 value = widen(value)
                 bitmask = BIT_MASK(numbits, TP)
                 #
-                current = widen(rffi.cast(T, ptr)[0])
+                current = widen(read_ptr(ptr, 0, TP))
                 current &= ~ (bitmask << lowbit)
                 current |= (value & bitmask) << lowbit
                 value = rffi.cast(TP, current)
             break
-
-    rffi.cast(T, ptr)[0] = value
+    write_ptr(ptr, 0, value)
 push_field._annspecialcase_ = 'specialize:argtype(2)'
 
 def cast_pos(self, i, ll_t):
     pos = rffi.ptradd(self.ll_buffer, self.shape.ll_positions[i])
     TP = lltype.Ptr(rffi.CArray(ll_t))
-    value = rffi.cast(TP, pos)[0]
+    value = read_ptr(pos, 0, ll_t)
 
     # Handle bitfields
     for c in unroll_letters_for_numbers:
diff --git a/pypy/module/_rawffi/test/test__rawffi.py 
b/pypy/module/_rawffi/test/test__rawffi.py
--- a/pypy/module/_rawffi/test/test__rawffi.py
+++ b/pypy/module/_rawffi/test/test__rawffi.py
@@ -211,6 +211,7 @@
         cls.w_platform = space.wrap(platform.name)
         cls.w_sizes_and_alignments = space.wrap(dict(
             [(k, (v.c_size, v.c_alignment)) for k,v in TYPEMAP.iteritems()]))
+        cls.w_typemap = space.wrap(TYPEMAP.keys())
 
     def test_libload(self):
         import _rawffi
@@ -751,6 +752,18 @@
             assert _rawffi.sizeof(k) == s
             assert _rawffi.alignment(k) == a
 
+    def test_unaligned(self):
+        import _rawffi
+        for k in self.typemap:
+            if k not in 'fdg':
+                continue
+            S = _rawffi.Structure([('pad', 'c'), ('value', k)], pack=1)
+            s = S()
+            s.value = 4
+            assert s.value == 4
+            s.free()
+
+
     def test_array_addressof(self):
         import _rawffi
         lib = _rawffi.CDLL(self.lib_name)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to