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