Author: Antonio Cuni <[email protected]>
Branch: 
Changeset: r63222:cfe04a93cc3e
Date: 2013-04-11 11:27 +0200
http://bitbucket.org/pypy/pypy/changeset/cfe04a93cc3e/

Log:    merge (again) the refactor-call_release_gil branch. This fixes a
        nasty bug which occoured when JITting the call to a cffi function
        which calls a callback which causes the failure of guard_not_forced:
        in that case, during blackholing we got the wrong result from
        call_release_gil, because it was not passed to fail_args.

        The two tests which demonstrates the bug are
        - rpython/jit/metainterp/test/test_fficall.py::test_guard_not_forced_f
        ails
        - pypy/module/pypyjit/test_pypy_c/test__ffi.py::test_cffi_call_guard_n
        ot_force d_fails

diff --git a/pypy/module/pypyjit/test_pypy_c/test__ffi.py 
b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test__ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test__ffi.py
@@ -110,7 +110,6 @@
         loops = log.loops_by_id('sleep')
         assert len(loops) == 1 # make sure that we actually JITted the loop
 
-
     def test_ctypes_call(self):
         from rpython.rlib.test.test_clibffi import get_libm_name
         def main(libm_name):
@@ -209,3 +208,65 @@
         # so far just check that call_release_gil() is produced.
         # later, also check that the arguments to call_release_gil()
         # are constants, and that the numerous raw_mallocs are removed
+
+    def test_cffi_call_guard_not_forced_fails(self):
+        # this is the test_pypy_c equivalent of
+        # rpython/jit/metainterp/test/test_fficall::test_guard_not_forced_fails
+        #
+        # it requires cffi to be installed for pypy in order to run
+        def main():
+            import sys
+            try:
+                import cffi
+            except ImportError:
+                sys.stderr.write('SKIP: cannot import cffi\n')
+                return 0
+                
+            ffi = cffi.FFI()
+
+            ffi.cdef("""
+            typedef void (*functype)(int);
+            int foo(int n, functype func);
+            """)
+
+            lib = ffi.verify("""
+            #include <signal.h>
+            typedef void (*functype)(int);
+
+            int foo(int n, functype func) {
+                if (n >= 2000) {
+                    func(n);
+                }
+                return n*2;
+            }
+            """)
+
+            @ffi.callback("functype")
+            def mycallback(n):
+                if n < 5000:
+                    return
+                # make sure that guard_not_forced fails
+                d = {}
+                f = sys._getframe()
+                while f:
+                    d.update(f.f_locals)
+                    f = f.f_back
+
+            n = 0
+            while n < 10000:
+                res = lib.foo(n, mycallback)  # ID: cfficall
+                # this is the real point of the test: before the
+                # refactor-call_release_gil branch, the assert failed when
+                # res == 5000
+                assert res == n*2
+                n += 1
+            return n
+
+        log = self.run(main, [], import_site=True)
+        assert log.result == 10000
+        loop, = log.loops_by_id('cfficall')
+        assert loop.match_by_id('cfficall', """
+            ...
+            f1 = call_release_gil(..., descr=<Calli 4 ii EF=6 OS=62>)
+            ...
+        """)
diff --git a/rpython/jit/backend/llgraph/runner.py 
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -840,10 +840,22 @@
             # manipulation here (as a hack, instead of really doing
             # the aroundstate manipulation ourselves)
             return self.execute_call_may_force(descr, func, *args)
+        guard_op = self.lltrace.operations[self.current_index + 1]
+        assert guard_op.getopnum() == rop.GUARD_NOT_FORCED
+        self.force_guard_op = guard_op
         call_args = support.cast_call_args_in_order(descr.ARGS, args)
-        FUNC = lltype.FuncType(descr.ARGS, descr.RESULT)
-        func_to_call = rffi.cast(lltype.Ptr(FUNC), func)
-        result = func_to_call(*call_args)
+        #
+        func_adr = llmemory.cast_int_to_adr(func)
+        if hasattr(func_adr.ptr._obj, '_callable'):
+            # this is needed e.g. by test_fficall.test_guard_not_forced_fails,
+            # because to actually force the virtualref we need to llinterp the
+            # graph, not to directly execute the python function
+            result = self.cpu.maybe_on_top_of_llinterp(func, call_args, 
descr.RESULT)
+        else:
+            FUNC = lltype.FuncType(descr.ARGS, descr.RESULT)
+            func_to_call = rffi.cast(lltype.Ptr(FUNC), func)
+            result = func_to_call(*call_args)
+        del self.force_guard_op
         return support.cast_result(descr.RESULT, result)
 
     def execute_call_assembler(self, descr, *args):
diff --git a/rpython/jit/backend/llsupport/llmodel.py 
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -275,6 +275,10 @@
     def cast_adr_to_int(x):
         return rffi.cast(lltype.Signed, x)
 
+    @specialize.arg(2)
+    def cast_int_to_ptr(self, x, TYPE):
+        return rffi.cast(TYPE, x)
+
     def sizeof(self, S):
         return get_size_descr(self.gc_ll_descr, S)
 
diff --git a/rpython/jit/backend/model.py b/rpython/jit/backend/model.py
--- a/rpython/jit/backend/model.py
+++ b/rpython/jit/backend/model.py
@@ -1,6 +1,6 @@
 import weakref
 from rpython.rlib.debug import debug_start, debug_print, debug_stop
-from rpython.rtyper.lltypesystem import lltype
+from rpython.rtyper.lltypesystem import lltype, llmemory
 
 class CPUTotalTracker(object):
     total_compiled_loops = 0
@@ -194,6 +194,11 @@
     def typedescrof(self, TYPE):
         raise NotImplementedError
 
+    @staticmethod
+    def cast_int_to_ptr(x, TYPE):
+        x = llmemory.cast_int_to_adr(x)
+        return llmemory.cast_adr_to_ptr(x, TYPE)
+
     # ---------- the backend-dependent operations ----------
 
     # lltype specific operations
diff --git a/rpython/jit/backend/x86/test/test_fficall.py 
b/rpython/jit/backend/x86/test/test_fficall.py
--- a/rpython/jit/backend/x86/test/test_fficall.py
+++ b/rpython/jit/backend/x86/test/test_fficall.py
@@ -5,4 +5,19 @@
 class TestFfiCall(Jit386Mixin, test_fficall.FfiCallTests):
     # for the individual tests see
     # ====> ../../../metainterp/test/test_fficall.py
-    pass
+
+    def _add_libffi_types_to_ll2types_maybe(self):
+        # this is needed by test_guard_not_forced_fails, because it produces a
+        # loop which reads the value of types.* in a variable, then a guard
+        # fail and we switch to blackhole: the problem is that at this point
+        # the blackhole interp has a real integer, but it needs to convert it
+        # back to a lltype pointer (which is handled by ll2ctypes, deeply in
+        # the logic). The workaround is to teach ll2ctypes in advance which
+        # are the addresses of the various types.* structures.
+        # Try to comment this code out and run the test to see how it fails :)
+        from rpython.rtyper.lltypesystem import rffi, lltype, ll2ctypes
+        from rpython.rlib.jit_libffi import types
+        for key, value in types.__dict__.iteritems():
+            if isinstance(value, lltype._ptr):
+                addr = rffi.cast(lltype.Signed, value)
+                ll2ctypes._int2obj[addr] = value
diff --git a/rpython/jit/codewriter/jtransform.py 
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1747,6 +1747,11 @@
             assert False, 'unsupported oopspec: %s' % oopspec_name
         return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)
 
+    def rewrite_op_jit_ffi_save_result(self, op):
+        kind = op.args[0].value
+        assert kind in ('int', 'float')
+        return SpaceOperation('libffi_save_result_%s' % kind, op.args[1:], 
None)
+
     def rewrite_op_jit_force_virtual(self, op):
         return self._do_builtin_call(op)
 
diff --git a/rpython/jit/metainterp/blackhole.py 
b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -8,8 +8,9 @@
 from rpython.rlib.rarithmetic import intmask, LONG_BIT, r_uint, ovfcheck
 from rpython.rlib.rtimer import read_timestamp
 from rpython.rlib.unroll import unrolling_iterable
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rlib.jit_libffi import CIF_DESCRIPTION_P
 
 
 def arguments(*argtypes, **kwds):
@@ -1350,6 +1351,26 @@
     def bhimpl_ll_read_timestamp():
         return read_timestamp()
 
+    @arguments("cpu", "i", "i", "i")
+    def bhimpl_libffi_save_result_int(self, cif_description, exchange_buffer, 
result):
+        ARRAY = lltype.Ptr(rffi.CArray(lltype.Signed))
+        cif_description = self.cast_int_to_ptr(cif_description, 
CIF_DESCRIPTION_P)
+        exchange_buffer = self.cast_int_to_ptr(exchange_buffer, rffi.CCHARP)
+        #
+        data_out = rffi.ptradd(exchange_buffer, 
cif_description.exchange_result)
+        rffi.cast(ARRAY, data_out)[0] = result
+
+    @arguments("cpu", "i", "i", "f")
+    def bhimpl_libffi_save_result_float(self, cif_description, 
exchange_buffer, result):
+        result = longlong.getrealfloat(result)
+        ARRAY = lltype.Ptr(rffi.CArray(lltype.Float))
+        cif_description = self.cast_int_to_ptr(cif_description, 
CIF_DESCRIPTION_P)
+        exchange_buffer = self.cast_int_to_ptr(exchange_buffer, rffi.CCHARP)
+        #
+        data_out = rffi.ptradd(exchange_buffer, 
cif_description.exchange_result)
+        rffi.cast(ARRAY, data_out)[0] = result
+
+
     # ----------
     # helpers to resume running in blackhole mode when a guard failed
 
diff --git a/rpython/jit/metainterp/history.py 
b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -775,8 +775,8 @@
 
     def show(self, errmsg=None):
         "NOT_RPYTHON"
-        from rpython.jit.metainterp.graphpage import display_loops
-        display_loops([self], errmsg)
+        from rpython.jit.metainterp.graphpage import display_procedures
+        display_procedures([self], errmsg)
 
     def check_consistency(self):     # for testing
         "NOT_RPYTHON"
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -1185,6 +1185,30 @@
     def opimpl_ll_read_timestamp(self):
         return self.metainterp.execute_and_record(rop.READ_TIMESTAMP, None)
 
+    @arguments("box", "box", "box")
+    def opimpl_libffi_save_result_int(self, box_cif_description, 
box_exchange_buffer,
+                                      box_result):
+        from rpython.rtyper.lltypesystem import llmemory
+        from rpython.rlib.jit_libffi import CIF_DESCRIPTION_P
+        from rpython.jit.backend.llsupport.ffisupport import get_arg_descr
+
+        cif_description = box_cif_description.getint()
+        cif_description = llmemory.cast_int_to_adr(cif_description)
+        cif_description = llmemory.cast_adr_to_ptr(cif_description,
+                                                   CIF_DESCRIPTION_P)
+
+        kind, descr, itemsize = get_arg_descr(self.metainterp.cpu, 
cif_description.rtype)
+        
+        if kind != 'v':
+            ofs = cif_description.exchange_result
+            assert ofs % itemsize == 0     # alignment check (result)
+            self.metainterp.history.record(rop.SETARRAYITEM_RAW,
+                                           [box_exchange_buffer,
+                                            ConstInt(ofs // itemsize), 
box_result],
+                                           None, descr)
+
+    opimpl_libffi_save_result_float = opimpl_libffi_save_result_int
+
     # ------------------------------
 
     def setup_call(self, argboxes):
@@ -2589,27 +2613,15 @@
                                 box_arg, descr)
             arg_boxes.append(box_arg)
         #
-        kind, descr, itemsize = get_arg_descr(self.cpu, cif_description.rtype)
-        if kind == 'i':
-            box_result = history.BoxInt()
-        elif kind == 'f':
-            box_result = history.BoxFloat()
-        else:
-            assert kind == 'v'
-            box_result = None
+        box_result = op.result
         self.history.record(rop.CALL_RELEASE_GIL,
                             [op.getarg(2)] + arg_boxes,
                             box_result, calldescr)
         #
         self.history.operations.extend(extra_guards)
         #
-        if box_result is not None:
-            ofs = cif_description.exchange_result
-            assert ofs % itemsize == 0     # alignment check (result)
-            self.history.record(rop.SETARRAYITEM_RAW,
-                                [box_exchange_buffer,
-                                 ConstInt(ofs // itemsize), box_result],
-                                None, descr)
+        # note that the result is written back to the exchange_buffer by the
+        # special op libffi_save_result_{int,float}
 
     def direct_call_release_gil(self):
         op = self.history.operations.pop()
diff --git a/rpython/jit/metainterp/test/test_fficall.py 
b/rpython/jit/metainterp/test/test_fficall.py
--- a/rpython/jit/metainterp/test/test_fficall.py
+++ b/rpython/jit/metainterp/test/test_fficall.py
@@ -1,13 +1,18 @@
-
 import py
+from _pytest.monkeypatch import monkeypatch
+import sys
 import ctypes, math
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.annlowlevel import llhelper
 from rpython.jit.metainterp.test.support import LLJitMixin
+from rpython.jit.codewriter.longlong import is_longlong
 from rpython.rlib import jit
-from rpython.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from rpython.rlib import jit_libffi
+from rpython.rlib.jit_libffi import (types, CIF_DESCRIPTION, FFI_TYPE_PP,
+                                     jit_ffi_call, jit_ffi_save_result)
 from rpython.rlib.unroll import unrolling_iterable
-from rpython.rlib.rarithmetic import intmask
-
+from rpython.rlib.rarithmetic import intmask, r_longlong
+from rpython.rlib.longlong2float import float2longlong
 
 def get_description(atypes, rtype):
     p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
@@ -21,10 +26,26 @@
         p.atypes[i] = atypes[i]
     return p
 
+class FakeFFI(object):
+    """
+    Context manager to monkey patch jit_libffi with our custom "libffi-like"
+    function
+    """
+    
+    def __init__(self, fake_call_impl_any):
+        self.fake_call_impl_any = fake_call_impl_any
+        self.monkey = monkeypatch()
+        
+    def __enter__(self, *args):
+        self.monkey.setattr(jit_libffi, 'jit_ffi_call_impl_any', 
self.fake_call_impl_any)
+
+    def __exit__(self, *args):
+        self.monkey.undo()
+
 
 class FfiCallTests(object):
 
-    def _run(self, atypes, rtype, avalues, rvalue):
+    def _run(self, atypes, rtype, avalues, rvalue, 
expected_call_release_gil=1):
         cif_description = get_description(atypes, rtype)
 
         def verify(*args):
@@ -41,8 +62,7 @@
 
         unroll_avalues = unrolling_iterable(avalues)
 
-        @jit.oopspec("libffi_call(cif_description,func_addr,exchange_buffer)")
-        def fake_call(cif_description, func_addr, exchange_buffer):
+        def fake_call_impl_any(cif_description, func_addr, exchange_buffer):
             ofs = 16
             for avalue in unroll_avalues:
                 TYPE = rffi.CArray(lltype.typeOf(avalue))
@@ -67,7 +87,7 @@
                 rffi.cast(lltype.Ptr(TYPE), data)[0] = avalue
                 ofs += 16
 
-            fake_call(cif_description, func_addr, exbuf)
+            jit_ffi_call(cif_description, func_addr, exbuf)
 
             if rvalue is None:
                 res = 654321
@@ -78,14 +98,19 @@
             lltype.free(exbuf, flavor='raw')
             return res
 
-        res = f()
-        assert res == rvalue or (res, rvalue) == (654321, None)
-        res = self.interp_operations(f, [])
-        assert res == rvalue or (res, rvalue) == (654321, None)
-        self.check_operations_history(call_may_force=0,
-                                      call_release_gil=1)
+        with FakeFFI(fake_call_impl_any):
+            res = f()
+            assert res == rvalue or (res, rvalue) == (654321, None)
+            res = self.interp_operations(f, [])
+            if is_longlong(FUNC.RESULT):
+                # longlongs are passed around as floats inside the JIT, we
+                # need to convert it back before checking the value
+                res = float2longlong(res)
+            assert res == rvalue or (res, rvalue) == (654321, None)
+            self.check_operations_history(call_may_force=0,
+                                          
call_release_gil=expected_call_release_gil)
 
-    def test_simple_call(self):
+    def test_simple_call_int(self):
         self._run([types.signed] * 2, types.signed, [456, 789], -42)
 
     def test_many_arguments(self):
@@ -97,6 +122,17 @@
     def test_simple_call_float(self):
         self._run([types.double] * 2, types.double, [45.6, 78.9], -4.2)
 
+    def test_simple_call_longlong(self):
+        maxint32 = 2147483647
+        a = r_longlong(maxint32) + 1
+        b = r_longlong(maxint32) + 2
+        self._run([types.slonglong] * 2, types.slonglong, [a, b], a)
+
+    def test_simple_call_longdouble(self):
+        # longdouble is not supported, so we expect NOT to generate a 
call_release_gil
+        self._run([types.longdouble] * 2, types.longdouble, [12.3, 45.6], 78.9,
+                  expected_call_release_gil=0)
+
     def test_returns_none(self):
         self._run([types.signed] * 2, types.void, [456, 789], None)
 
@@ -104,6 +140,91 @@
         self._run([types.signed], types.sint8, [456],
                   rffi.cast(rffi.SIGNEDCHAR, -42))
 
+    def _add_libffi_types_to_ll2types_maybe(self):
+        # not necessary on the llgraph backend, but needed for x86.
+        # see rpython/jit/backend/x86/test/test_fficall.py
+        pass
+
+    def test_guard_not_forced_fails(self):
+        self._add_libffi_types_to_ll2types_maybe()
+        FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
+
+        cif_description = get_description([types.slong], types.slong)
+        cif_description.exchange_args[0] = 16
+        cif_description.exchange_result = 32
+
+        ARRAY = lltype.Ptr(rffi.CArray(lltype.Signed))
+
+        @jit.dont_look_inside
+        def fn(n):
+            if n >= 50:
+                exctx.m = exctx.topframeref().n # forces the frame
+            return n*2
+
+        # this function simulates what a real libffi_call does: reading from
+        # the buffer, calling a function (which can potentially call callbacks
+        # and force frames) and write back to the buffer
+        def fake_call_impl_any(cif_description, func_addr, exchange_buffer):
+            # read the args from the buffer
+            data_in = rffi.ptradd(exchange_buffer, 16)
+            n = rffi.cast(ARRAY, data_in)[0]
+            #
+            # logic of the function
+            func_ptr = rffi.cast(lltype.Ptr(FUNC), func_addr)
+            n = func_ptr(n)
+            #
+            # write the result to the buffer
+            data_out = rffi.ptradd(exchange_buffer, 32)
+            rffi.cast(ARRAY, data_out)[0] = n
+
+        def do_call(n):
+            func_ptr = llhelper(lltype.Ptr(FUNC), fn)
+            exbuf = lltype.malloc(rffi.CCHARP.TO, 48, flavor='raw', zero=True)
+            data_in = rffi.ptradd(exbuf, 16)
+            rffi.cast(ARRAY, data_in)[0] = n
+            jit_ffi_call(cif_description, func_ptr, exbuf)
+            data_out = rffi.ptradd(exbuf, 32)
+            res = rffi.cast(ARRAY, data_out)[0]
+            lltype.free(exbuf, flavor='raw')
+            return res
+
+        #
+        #
+        class XY:
+            pass
+        class ExCtx:
+            pass
+        exctx = ExCtx()
+        myjitdriver = jit.JitDriver(greens = [], reds = ['n'])
+        def f():
+            n = 0
+            while n < 100:
+                myjitdriver.jit_merge_point(n=n)
+                xy = XY()
+                xy.n = n
+                exctx.topframeref = vref = jit.virtual_ref(xy)
+                res = do_call(n) # this is equivalent of a cffi call which
+                                 # sometimes forces a frame
+
+                # when n==50, fn() will force the frame, so guard_not_forced
+                # fails and we enter blackholing: this test makes sure that
+                # the result of call_release_gil is kept alive before the
+                # libffi_save_result, and that the corresponding box is passed
+                # in the fail_args. Before the fix, the result of
+                # call_release_gil was simply lost and when guard_not_forced
+                # failed, and the value of "res" was unpredictable.
+                # See commit b84ff38f34bd and subsequents.
+                assert res == n*2
+                jit.virtual_ref_finish(vref, xy)
+                exctx.topframeref = jit.vref_None
+                n += 1
+            return n
+
+        with FakeFFI(fake_call_impl_any):
+            assert f() == 100
+            res = self.meta_interp(f, [])
+            assert res == 100
+        
 
 class TestFfiCall(FfiCallTests, LLJitMixin):
     def test_jit_ffi_vref(self):
diff --git a/rpython/rlib/jit_libffi.py b/rpython/rlib/jit_libffi.py
--- a/rpython/rlib/jit_libffi.py
+++ b/rpython/rlib/jit_libffi.py
@@ -1,6 +1,8 @@
 
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.extregistry import ExtRegistryEntry
 from rpython.rlib import clibffi, jit
+from rpython.rlib.nonconst import NonConstant
 
 
 FFI_CIF = clibffi.FFI_CIFP.TO
@@ -65,11 +67,88 @@
     return rffi.cast(lltype.Signed, res)
 
 
[email protected]("libffi_call(cif_description, func_addr, exchange_buffer)")
+# =============================
+# jit_ffi_call and its helpers
+# =============================
+
+## Problem: jit_ffi_call is turned into call_release_gil by pyjitpl. Before
+## the refactor-call_release_gil branch, the resulting code looked like this:
+##
+##     buffer = ...
+##     i0 = call_release_gil(...)
+##     guard_not_forced()
+##     setarray_item_raw(buffer, ..., i0)
+##
+## The problem is that the result box i0 was generated freshly inside pyjitpl,
+## and the codewriter did not know about its liveness: the result was that i0
+## was not in the fail_args of guard_not_forced. See
+## test_fficall::test_guard_not_forced_fails for a more detalied explanation
+## of the problem.
+##
+## The solution is to create a new separate operation libffi_save_result whose
+## job is to write the result in the exchange_buffer: during normal execution
+## this is a no-op because the buffer is already filled by libffi, but during
+## jitting the behavior is to actually write into the buffer.
+##
+## The result is that now the jitcode looks like this:
+##
+##     %i0 = libffi_call_int(...)
+##     -live-
+##     libffi_save_result_int(..., %i0)
+##
+## the "-live-" is the key, because it make sure that the value is not lost if
+## guard_not_forced fails.
+
+
 def jit_ffi_call(cif_description, func_addr, exchange_buffer):
     """Wrapper around ffi_call().  Must receive a CIF_DESCRIPTION_P that
     describes the layout of the 'exchange_buffer'.
     """
+    reskind = types.getkind(cif_description.rtype)
+    if reskind == 'v':
+        jit_ffi_call_impl_void(cif_description, func_addr, exchange_buffer)
+    elif reskind == 'f' or reskind == 'L': # L is for longlongs, on 32bit
+        result = jit_ffi_call_impl_float(cif_description, func_addr, 
exchange_buffer)
+        jit_ffi_save_result('float', cif_description, exchange_buffer, result)
+    elif reskind == 'i' or reskind == 'u':
+        result = jit_ffi_call_impl_int(cif_description, func_addr, 
exchange_buffer)
+        jit_ffi_save_result('int', cif_description, exchange_buffer, result)
+    else:
+        # the result kind is not supported: we disable the jit_ffi_call
+        # optimization by calling directly jit_ffi_call_impl_any, so the JIT
+        # does not see any libffi_call oopspec.
+        #
+        # Since call_release_gil is not generated, there is no need to
+        # jit_ffi_save_result
+        jit_ffi_call_impl_any(cif_description, func_addr, exchange_buffer)
+
+
+# we must return a NonConstant else we get the constant -1 as the result of
+# the flowgraph, and the codewriter does not produce a box for the
+# result. Note that when not-jitted, the result is unused, but when jitted the
+# box of the result contains the actual value returned by the C function.
+
[email protected]("libffi_call(cif_description,func_addr,exchange_buffer)")
+def jit_ffi_call_impl_int(cif_description, func_addr, exchange_buffer):
+    jit_ffi_call_impl_any(cif_description, func_addr, exchange_buffer)
+    return NonConstant(-1)
+
[email protected]("libffi_call(cif_description,func_addr,exchange_buffer)")
+def jit_ffi_call_impl_float(cif_description, func_addr, exchange_buffer):
+    jit_ffi_call_impl_any(cif_description, func_addr, exchange_buffer)
+    return NonConstant(-1.0)
+
[email protected]("libffi_call(cif_description,func_addr,exchange_buffer)")
+def jit_ffi_call_impl_void(cif_description, func_addr, exchange_buffer):
+    jit_ffi_call_impl_any(cif_description, func_addr, exchange_buffer)
+    return None
+
+def jit_ffi_call_impl_any(cif_description, func_addr, exchange_buffer):
+    """
+    This is the function which actually calls libffi. All the rest if just
+    infrastructure to convince the JIT to pass a typed result box to
+    jit_ffi_save_result
+    """
     buffer_array = rffi.cast(rffi.VOIDPP, exchange_buffer)
     for i in range(cif_description.nargs):
         data = rffi.ptradd(exchange_buffer, cif_description.exchange_args[i])
@@ -79,6 +158,31 @@
     clibffi.c_ffi_call(cif_description.cif, func_addr,
                        rffi.cast(rffi.VOIDP, resultdata),
                        buffer_array)
+    return -1
+
+
+
+def jit_ffi_save_result(kind, cif_description, exchange_buffer, result):
+    """
+    This is a no-op during normal execution, but actually fills the buffer
+    when jitted
+    """
+    pass
+
+class Entry(ExtRegistryEntry):
+    _about_ = jit_ffi_save_result
+
+    def compute_result_annotation(self, kind_s, *args_s):
+        from rpython.annotator import model as annmodel
+        assert isinstance(kind_s, annmodel.SomeString)
+        assert kind_s.const in ('int', 'float')
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        vlist = hop.inputargs(lltype.Void, *hop.args_r[1:])
+        return hop.genop('jit_ffi_save_result', vlist,
+                         resulttype=lltype.Void)
+    
 
 # ____________________________________________________________
 
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -456,6 +456,7 @@
     'jit_is_virtual':       LLOp(canrun=True),
     'jit_force_quasi_immutable': LLOp(canrun=True),
     'jit_record_known_class'  : LLOp(canrun=True),
+    'jit_ffi_save_result':  LLOp(canrun=True),
     'get_exception_addr':   LLOp(),
     'get_exc_value_addr':   LLOp(),
     'do_malloc_fixedsize_clear':LLOp(canmallocgc=True),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -597,6 +597,9 @@
 def op_jit_record_known_class(x, y):
     pass
 
+def op_jit_ffi_save_result(*args):
+    pass
+
 def op_get_group_member(TYPE, grpptr, memberoffset):
     from rpython.rtyper.lltypesystem import llgroup
     assert isinstance(memberoffset, llgroup.GroupMemberOffset)
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -843,6 +843,9 @@
     def OP_JIT_FORCE_QUASI_IMMUTABLE(self, op):
         return '/* JIT_FORCE_QUASI_IMMUTABLE %s */' % op
 
+    def OP_JIT_FFI_SAVE_RESULT(self, op):
+        return '/* JIT_FFI_SAVE_RESULT %s */' % op
+
     def OP_GET_GROUP_MEMBER(self, op):
         typename = self.db.gettype(op.result.concretetype)
         return '%s = (%s)_OP_GET_GROUP_MEMBER(%s, %s);' % (
diff --git a/rpython/translator/cli/opcodes.py 
b/rpython/translator/cli/opcodes.py
--- a/rpython/translator/cli/opcodes.py
+++ b/rpython/translator/cli/opcodes.py
@@ -99,6 +99,7 @@
     'jit_force_virtual':        DoNothing,
     'jit_force_quasi_immutable':Ignore,
     'jit_is_virtual':           [PushPrimitive(ootype.Bool, False)],
+    'jit_ffi_save_result':      Ignore,
     }
 
 # __________ numeric operations __________
diff --git a/rpython/translator/jvm/opcodes.py 
b/rpython/translator/jvm/opcodes.py
--- a/rpython/translator/jvm/opcodes.py
+++ b/rpython/translator/jvm/opcodes.py
@@ -102,6 +102,7 @@
     'jit_force_virtual':        DoNothing,
     'jit_force_quasi_immutable': Ignore,
     'jit_is_virtual':           PushPrimitive(ootype.Bool, False),
+    'jit_ffi_save_result':      Ignore,
 
     'debug_assert':              [], # TODO: implement?
     'debug_start_traceback':    Ignore,
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to