[pypy-commit] pypy jit-dynamic-getarrayitem: started working on support for creating array get/set item at compile time

alex_gaynor noreply at buildbot.pypy.org
Sun Nov 13 05:20:28 CET 2011


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: jit-dynamic-getarrayitem
Changeset: r49361:a731ffd298b4
Date: 2011-11-12 23:20 -0500
http://bitbucket.org/pypy/pypy/changeset/a731ffd298b4/

Log:	started working on support for creating array get/set item at
	compile time

diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -1,15 +1,16 @@
+import py
 
-import py
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
+from pypy.rlib.libffi import (ArgChain, IS_32_BIT, array_getitem, array_setitem,
+    types)
+from pypy.rlib.objectmodel import specialize
 from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.jit import JitDriver, promote, dont_look_inside
+from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
 from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.libffi import ArgChain
-from pypy.rlib.libffi import IS_32_BIT
-from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
 from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.rlib.objectmodel import specialize
 from pypy.tool.sourcetools import func_with_new_name
-from pypy.jit.metainterp.test.support import LLJitMixin
+
 
 class TestFfiCall(LLJitMixin, _TestLibffiCall):
     supports_all = False     # supports_{floats,longlong,singlefloats}
@@ -95,3 +96,61 @@
 
 class TestFfiCallSupportAll(TestFfiCall):
     supports_all = True     # supports_{floats,longlong,singlefloats}
+
+    def test_array_fields(self):
+        myjitdriver = JitDriver(
+            greens = [],
+            reds = ["n", "i", "signed_size", "points", "result_point"],
+        )
+
+        POINT = lltype.Struct("POINT",
+            ("x", lltype.Signed),
+            ("y", lltype.Signed),
+        )
+        def f(n):
+            points = lltype.malloc(rffi.CArray(POINT), n, flavor="raw")
+            for i in xrange(n):
+                points[i].x = i * 2
+                points[i].y = i * 2 + 1
+            points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
+            result_point = lltype.malloc(rffi.CArray(POINT), 1, flavor="raw")
+            result_point[0].x = 0
+            result_point[0].y = 0
+            result_point = rffi.cast(rffi.CArrayPtr(lltype.Char), result_point)
+            i = 0
+            signed_size = rffi.sizeof(lltype.Signed)
+            while i < n:
+                myjitdriver.jit_merge_point(i=i, points=points, n=n,
+                                            signed_size=signed_size,
+                                            result_point=result_point)
+                x = array_getitem(
+                    types.slong, signed_size * 2, points, i, 0
+                )
+                y = array_getitem(
+                    types.slong, signed_size * 2, points, i, signed_size
+                )
+
+                cur_x = array_getitem(
+                    types.slong, signed_size * 2, result_point, 0, 0
+                )
+                cur_y = array_getitem(
+                    types.slong, signed_size * 2, result_point, 0, signed_size
+                )
+
+                array_setitem(
+                    types.slong, signed_size * 2, result_point, 0, 0, cur_x + x
+                )
+                array_setitem(
+                    types.slong, signed_size * 2, result_point, 0, signed_size, cur_y + y
+                )
+                i += 1
+            result_point = rffi.cast(rffi.CArrayPtr(POINT), result_point)
+            result = result_point[0].x * result_point[0].y
+            lltype.free(result_point, flavor="raw")
+            lltype.free(points, flavor="raw")
+            return result
+
+        assert self.meta_interp(f, [10]) == f(10) == 9000
+        self.check_loops({"int_add": 3, "jump": 1, "int_lt": 1,
+                          "getinteriorfield_raw": 4, "setinteriorfield_raw": 2
+        })
diff --git a/pypy/rlib/clibffi.py b/pypy/rlib/clibffi.py
--- a/pypy/rlib/clibffi.py
+++ b/pypy/rlib/clibffi.py
@@ -30,9 +30,6 @@
 _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
 
@@ -213,26 +210,48 @@
     elif sz == 8: return ffi_type_uint64
     else: raise ValueError("unsupported type size for %r" % (TYPE,))
 
-TYPE_MAP = {
-    rffi.DOUBLE : ffi_type_double,
-    rffi.FLOAT  : ffi_type_float,
-    rffi.LONGDOUBLE : ffi_type_longdouble,
-    rffi.UCHAR  : ffi_type_uchar,
-    rffi.CHAR   : ffi_type_schar,
-    rffi.SHORT  : ffi_type_sshort,
-    rffi.USHORT : ffi_type_ushort,
-    rffi.UINT   : ffi_type_uint,
-    rffi.INT    : ffi_type_sint,
+__int_type_map = [
+    (rffi.UCHAR, ffi_type_uchar),
+    (rffi.SIGNEDCHAR, ffi_type_schar),
+    (rffi.SHORT, ffi_type_sshort),
+    (rffi.USHORT, ffi_type_ushort),
+    (rffi.UINT, ffi_type_uint),
+    (rffi.INT, ffi_type_sint),
     # xxx don't use ffi_type_slong and ffi_type_ulong - their meaning
     # changes from a libffi version to another :-((
-    rffi.ULONG     : _unsigned_type_for(rffi.ULONG),
-    rffi.LONG      : _signed_type_for(rffi.LONG),
-    rffi.ULONGLONG : _unsigned_type_for(rffi.ULONGLONG),
-    rffi.LONGLONG  : _signed_type_for(rffi.LONGLONG),
-    lltype.Void    : ffi_type_void,
-    lltype.UniChar : _unsigned_type_for(lltype.UniChar),
-    lltype.Bool    : _unsigned_type_for(lltype.Bool),
-    }
+    (rffi.ULONG, _unsigned_type_for(rffi.ULONG)),
+    (rffi.LONG, _signed_type_for(rffi.LONG)),
+    (rffi.ULONGLONG, _unsigned_type_for(rffi.ULONGLONG)),
+    (rffi.LONGLONG, _signed_type_for(rffi.LONGLONG)),
+    (lltype.UniChar, _unsigned_type_for(lltype.UniChar)),
+    (lltype.Bool, _unsigned_type_for(lltype.Bool)),
+    ]
+
+__float_type_map = [
+    (rffi.DOUBLE, ffi_type_double),
+    (rffi.FLOAT, ffi_type_float),
+    (rffi.LONGDOUBLE, ffi_type_longdouble),
+    ]
+
+__ptr_type_map = [
+    (rffi.VOIDP, ffi_type_pointer),
+    ]
+
+__type_map = __int_type_map + __float_type_map + [
+    (lltype.Void, ffi_type_void)
+    ]
+
+TYPE_MAP_INT = dict(__int_type_map)
+TYPE_MAP_FLOAT = dict(__float_type_map)
+TYPE_MAP = dict(__type_map)
+
+ffitype_map_int = unrolling_iterable(__int_type_map)
+ffitype_map_int_or_ptr = unrolling_iterable(__int_type_map + __ptr_type_map)
+ffitype_map_float = unrolling_iterable(__float_type_map)
+ffitype_map = unrolling_iterable(__type_map)
+
+del __int_type_map, __float_type_map, __ptr_type_map, __type_map
+
 
 def external(name, args, result, **kwds):
     return rffi.llexternal(name, args, result, compilation_info=eci, **kwds)
@@ -341,38 +360,15 @@
 cast_type_to_ffitype._annspecialcase_ = 'specialize:memo'
 
 def push_arg_as_ffiptr(ffitp, arg, ll_buf):
-    # This is for primitive types.  Note that the exact type of 'arg' may be
-    # different from the expected 'c_size'.  To cope with that, we fall back
-    # to a byte-by-byte copy.
+    # 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))
-    TP_size = rffi.sizeof(TP)
-    c_size = intmask(ffitp.c_size)
-    # if both types have the same size, we can directly write the
-    # value to the buffer
-    if c_size == TP_size:
-        buf = rffi.cast(TP_P, ll_buf)
-        buf[0] = arg
-    else:
-        # needs byte-by-byte copying.  Make sure 'arg' is an integer type.
-        # Note that this won't work for rffi.FLOAT/rffi.DOUBLE.
-        assert TP is not rffi.FLOAT and TP is not rffi.DOUBLE
-        if TP_size <= rffi.sizeof(lltype.Signed):
-            arg = rffi.cast(lltype.Unsigned, arg)
-        else:
-            arg = rffi.cast(lltype.UnsignedLongLong, arg)
-        if _LITTLE_ENDIAN:
-            for i in range(c_size):
-                ll_buf[i] = chr(arg & 0xFF)
-                arg >>= 8
-        elif _BIG_ENDIAN:
-            for i in range(c_size-1, -1, -1):
-                ll_buf[i] = chr(arg & 0xFF)
-                arg >>= 8
-        else:
-            raise AssertionError
+    buf = rffi.cast(TP_P, ll_buf)
+    buf[0] = arg
 push_arg_as_ffiptr._annspecialcase_ = 'specialize:argtype(1)'
 
+
 # type defs for callback and closure userdata
 USERDATA_P = lltype.Ptr(lltype.ForwardReference())
 CALLBACK_TP = lltype.Ptr(lltype.FuncType([rffi.VOIDPP, rffi.VOIDP, USERDATA_P],
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
--- a/pypy/rlib/libffi.py
+++ b/pypy/rlib/libffi.py
@@ -140,7 +140,7 @@
             self.last.next = arg
             self.last = arg
         self.numargs += 1
-    
+
 
 class AbstractArg(object):
     next = None
@@ -410,3 +410,22 @@
 
     def getaddressindll(self, name):
         return dlsym(self.lib, name)
+
+ at jit.oopspec("libffi_array_getitem")
+def array_getitem(ffitype, width, addr, index, offset):
+    for TYPE, ffitype2 in clibffi.ffitype_map:
+        if ffitype is ffitype2:
+            addr = rffi.ptradd(addr, index * width)
+            addr = rffi.ptradd(addr, offset)
+            return rffi.cast(rffi.CArrayPtr(TYPE), addr)[0]
+    assert False
+
+ at jit.oopspec("libffi_array_setitem")
+def array_setitem(ffitype, width, addr, index, offset, value):
+    for TYPE, ffitype2 in clibffi.ffitype_map:
+        if ffitype is ffitype2:
+            addr = rffi.ptradd(addr, index * width)
+            addr = rffi.ptradd(addr, offset)
+            rffi.cast(rffi.CArrayPtr(TYPE), addr)[0] = value
+            return
+    assert False
\ No newline at end of file
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
@@ -1,11 +1,13 @@
+import sys
+
 import py
-import sys
+
+from pypy.rlib.libffi import (CDLL, Func, get_libc_name, ArgChain, types,
+    IS_32_BIT, array_getitem, array_setitem)
+from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
+from pypy.rlib.test.test_clibffi import BaseFfiTest, get_libm_name, make_struct_ffitype_e
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.rpython.lltypesystem.ll2ctypes import ALLOCATED
-from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.test.test_clibffi import BaseFfiTest, get_libm_name, make_struct_ffitype_e
-from pypy.rlib.libffi import CDLL, Func, get_libc_name, ArgChain, types
-from pypy.rlib.libffi import IS_32_BIT
 
 class TestLibffiMisc(BaseFfiTest):
 
@@ -52,6 +54,34 @@
         del lib
         assert not ALLOCATED
 
+    def test_array_fields(self):
+        POINT = lltype.Struct("POINT",
+            ("x", lltype.Float),
+            ("y", lltype.Float),
+        )
+        points = lltype.malloc(rffi.CArray(POINT), 2, flavor="raw")
+        points[0].x = 1.0
+        points[0].y = 2.0
+        points[1].x = 3.0
+        points[1].y = 4.0
+        points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
+        assert array_getitem(types.double, 16, points, 0, 0) == 1.0
+        assert array_getitem(types.double, 16, points, 0, 8) == 2.0
+        assert array_getitem(types.double, 16, points, 1, 0) == 3.0
+        assert array_getitem(types.double, 16, points, 1, 8) == 4.0
+
+        array_setitem(types.double, 16, points, 0, 0, 10.0)
+        array_setitem(types.double, 16, points, 0, 8, 20.0)
+        array_setitem(types.double, 16, points, 1, 0, 30.0)
+        array_setitem(types.double, 16, points, 1, 8, 40.0)
+
+        assert array_getitem(types.double, 16, points, 0, 0) == 10.0
+        assert array_getitem(types.double, 16, points, 0, 8) == 20.0
+        assert array_getitem(types.double, 16, points, 1, 0) == 30.0
+        assert array_getitem(types.double, 16, points, 1, 8) == 40.0
+
+        lltype.free(points, flavor="raw")
+
 class TestLibffiCall(BaseFfiTest):
     """
     Test various kind of calls through libffi.
@@ -109,7 +139,7 @@
         This method is overridden by metainterp/test/test_fficall.py in
         order to do the call in a loop and JIT it. The optional arguments are
         used only by that overridden method.
-        
+
         """
         lib, name, argtypes, restype = funcspec
         func = lib.getpointer(name, argtypes, restype)
@@ -132,7 +162,7 @@
                 return x - y;
             }
         """
-        libfoo = self.get_libfoo() 
+        libfoo = self.get_libfoo()
         func = (libfoo, 'diff_xy', [types.sint, types.slong], types.sint)
         res = self.call(func, [50, 8], lltype.Signed)
         assert res == 42
@@ -144,7 +174,7 @@
                 return (x + (int)y);
             }
         """
-        libfoo = self.get_libfoo() 
+        libfoo = self.get_libfoo()
         func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint)
         res = self.call(func, [38, 4.2], lltype.Signed, jitif=["floats"])
         assert res == 42
@@ -249,7 +279,7 @@
             };
 
             struct pair my_static_pair = {10, 20};
-            
+
             long* get_pointer_to_b()
             {
                 return &my_static_pair.b;
@@ -340,7 +370,7 @@
 
     def test_wrong_number_of_arguments(self):
         from pypy.rpython.llinterp import LLException
-        libfoo = self.get_libfoo() 
+        libfoo = self.get_libfoo()
         func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint)
 
         glob = globals()


More information about the pypy-commit mailing list