[pypy-commit] pypy ffi-backend: hg merge
arigo
noreply at buildbot.pypy.org
Fri Aug 3 13:07:03 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: ffi-backend
Changeset: r56557:50c0afab1e32
Date: 2012-08-03 11:05 +0000
http://bitbucket.org/pypy/pypy/changeset/50c0afab1e32/
Log: hg merge
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -89,7 +89,6 @@
"_rawffi": [("objspace.usemodules.struct", True)],
"cpyext": [("translation.secondaryentrypoints", "cpyext"),
("translation.shared", sys.platform == "win32")],
- "_ffi": [("translation.jit_ffi", True)],
}
module_import_dependencies = {
diff --git a/pypy/config/test/test_pypyoption.py b/pypy/config/test/test_pypyoption.py
--- a/pypy/config/test/test_pypyoption.py
+++ b/pypy/config/test/test_pypyoption.py
@@ -72,8 +72,3 @@
for path in c.getpaths(include_groups=True):
fn = prefix + "." + path + ".txt"
yield fn, check_file_exists, fn
-
-def test__ffi_opt():
- config = get_pypy_config(translating=True)
- config.objspace.usemodules._ffi = True
- assert config.translation.jit_ffi
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -122,8 +122,6 @@
ChoiceOption("jit_profiler", "integrate profiler support into the JIT",
["off", "oprofile"],
default="off"),
- # jit_ffi is automatically turned on by withmod-_ffi (which is enabled by default)
- BoolOption("jit_ffi", "optimize libffi calls", default=False, cmdline=None),
BoolOption("check_str_without_nul",
"Forbid NUL chars in strings in some external function calls",
default=False, cmdline=None),
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -63,7 +63,8 @@
FLOAT_ARRAY_TP = lltype.Ptr(lltype.Array(lltype.Float, hints={"nolength": True}))
def maybe_uncast(TP, array):
- if array._TYPE.TO._hints.get("uncast_on_llgraph"):
+ if array._TYPE.TO.OF != lltype.Float:
+ # array._TYPE.TO._hints.get("uncast_on_llgraph"):
array = rffi.cast(TP, array)
return array
@@ -802,7 +803,7 @@
if arraydescr.typeinfo == REF:
raise NotImplementedError("getarrayitem_raw -> gcref")
elif arraydescr.typeinfo == INT:
- return do_getarrayitem_raw_int(array, index)
+ return do_getarrayitem_raw_int(array, index, arraydescr.ofs)
elif arraydescr.typeinfo == FLOAT:
return do_getarrayitem_raw_float(array, index)
else:
@@ -879,7 +880,7 @@
if arraydescr.typeinfo == REF:
raise NotImplementedError("setarrayitem_raw <- gcref")
elif arraydescr.typeinfo == INT:
- do_setarrayitem_raw_int(array, index, newvalue)
+ do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
elif arraydescr.typeinfo == FLOAT:
do_setarrayitem_raw_float(array, index, newvalue)
else:
@@ -1448,9 +1449,13 @@
array = array._obj.container
return cast_to_int(array.getitem(index))
-def do_getarrayitem_raw_int(array, index):
- array = array.adr.ptr._obj
- return cast_to_int(array.getitem(index))
+def do_getarrayitem_raw_int(array, index, itemsize):
+ array = array.adr.ptr
+ ITEMTYPE = lltype.typeOf(array).TO.OF
+ TYPE = symbolic.Size2Type[itemsize]
+ if TYPE.OF != ITEMTYPE:
+ array = rffi.cast(lltype.Ptr(TYPE), array)
+ return cast_to_int(array._obj.getitem(index))
def do_getarrayitem_gc_float(array, index):
array = array._obj.container
@@ -1546,10 +1551,13 @@
newvalue = cast_from_int(ITEMTYPE, newvalue)
array.setitem(index, newvalue)
-def do_setarrayitem_raw_int(array, index, newvalue):
+def do_setarrayitem_raw_int(array, index, newvalue, itemsize):
array = array.adr.ptr
ITEMTYPE = lltype.typeOf(array).TO.OF
- newvalue = cast_from_int(ITEMTYPE, newvalue)
+ TYPE = symbolic.Size2Type[itemsize]
+ if TYPE.OF != ITEMTYPE:
+ array = rffi.cast(lltype.Ptr(TYPE), array)
+ newvalue = cast_from_int(TYPE.OF, newvalue)
array._obj.setitem(index, newvalue)
def do_setarrayitem_gc_float(array, index, newvalue):
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -360,21 +360,21 @@
return self.getdescr(0, token[0], extrainfo=extrainfo,
arg_types=''.join(arg_types))
- def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+ def calldescrof_dynamic(self, cif_description, extrainfo):
from pypy.jit.backend.llsupport.ffisupport import get_ffi_type_kind
from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
arg_types = []
try:
- for arg in ffi_args:
+ for arg in cif_description.atypes:
kind = get_ffi_type_kind(self, arg)
if kind != history.VOID:
arg_types.append(kind)
- reskind = get_ffi_type_kind(self, ffi_result)
+ reskind = get_ffi_type_kind(self, cif_description.rtype)
except UnsupportedKind:
return None
return self.getdescr(0, reskind, extrainfo=extrainfo,
arg_types=''.join(arg_types),
- ffi_flags=ffi_flags)
+ ffi_flags=cif_description.abi)
def grab_exc_value(self):
@@ -411,7 +411,7 @@
return llimpl.do_getarrayitem_gc_int(array, index)
def bh_getarrayitem_raw_i(self, arraydescr, array, index):
assert isinstance(arraydescr, Descr)
- return llimpl.do_getarrayitem_raw_int(array, index)
+ return llimpl.do_getarrayitem_raw_int(array, index, arraydescr.ofs)
def bh_getarrayitem_gc_r(self, arraydescr, array, index):
assert isinstance(arraydescr, Descr)
return llimpl.do_getarrayitem_gc_ptr(array, index)
@@ -507,7 +507,7 @@
def bh_setarrayitem_raw_i(self, arraydescr, array, index, newvalue):
assert isinstance(arraydescr, Descr)
- llimpl.do_setarrayitem_raw_int(array, index, newvalue)
+ llimpl.do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
def bh_setarrayitem_gc_r(self, arraydescr, array, index, newvalue):
assert isinstance(arraydescr, Descr)
diff --git a/pypy/jit/backend/llsupport/ffisupport.py b/pypy/jit/backend/llsupport/ffisupport.py
--- a/pypy/jit/backend/llsupport/ffisupport.py
+++ b/pypy/jit/backend/llsupport/ffisupport.py
@@ -1,43 +1,81 @@
from pypy.rlib.rarithmetic import intmask
-from pypy.jit.metainterp import history
-from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.objectmodel import specialize
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.backend.llsupport.descr import CallDescr
class UnsupportedKind(Exception):
pass
-def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo, ffi_flags):
- """Get a call descr: the types of result and args are represented by
- rlib.libffi.types.*"""
+def get_call_descr_dynamic(cpu, cif_description, extrainfo):
+ """Get a call descr from the given CIF_DESCRIPTION"""
+ ffi_result = cif_description.rtype
try:
reskind = get_ffi_type_kind(cpu, ffi_result)
- argkinds = [get_ffi_type_kind(cpu, arg) for arg in ffi_args]
+ argkinds = [get_ffi_type_kind(cpu, atype)
+ for atype in cif_description.atypes]
except UnsupportedKind:
return None
- if reskind == history.VOID:
+ if reskind == 'v':
result_size = 0
else:
result_size = intmask(ffi_result.c_size)
argkinds = ''.join(argkinds)
return CallDescr(argkinds, reskind, is_ffi_type_signed(ffi_result),
- result_size, extrainfo, ffi_flags=ffi_flags)
+ result_size, extrainfo, ffi_flags=cif_description.abi)
def get_ffi_type_kind(cpu, ffi_type):
- from pypy.rlib.libffi import types
+ from pypy.rlib.jit_libffi import types
+ kind = types.getkind(ffi_type)
+ if ((not cpu.supports_floats and kind == 'f') or
+ (not cpu.supports_longlong and kind == 'L') or
+ (not cpu.supports_singlefloats and kind == 'S') or
+ kind == '*'):
+ raise UnsupportedKind("Unsupported kind '%s'" % kind)
+ if kind == 'u':
+ kind = 'i'
+ return kind
+
+def is_ffi_type_signed(ffi_type):
+ from pypy.rlib.jit_libffi import types
+ kind = types.getkind(ffi_type)
+ return kind != 'u'
+
+ at specialize.memo()
+def _get_ffi2descr_dict(cpu):
+ d = {('v', 0): ('v', None)}
+ if cpu.supports_floats:
+ d[('f', 0)] = ('f', cpu.arraydescrof(rffi.CArray(lltype.Float)))
+ if cpu.supports_singlefloats:
+ d[('S', 0)] = cpu.arraydescrof(rffi.CArray(lltype.SingleFloat))
+ for SIGNED_TYPE in [rffi.SIGNEDCHAR,
+ rffi.SHORT,
+ rffi.INT,
+ rffi.LONG,
+ rffi.LONGLONG]:
+ key = ('i', rffi.sizeof(SIGNED_TYPE))
+ kind = 'i'
+ if key[1] > rffi.sizeof(lltype.Signed):
+ if not cpu.supports_longlong:
+ continue
+ key = ('L', 0)
+ kind = 'f'
+ d[key] = (kind, cpu.arraydescrof(rffi.CArray(SIGNED_TYPE)))
+ for UNSIGNED_TYPE in [rffi.UCHAR,
+ rffi.USHORT,
+ rffi.UINT,
+ rffi.ULONG,
+ rffi.ULONGLONG]:
+ key = ('u', rffi.sizeof(UNSIGNED_TYPE))
+ if key[1] > rffi.sizeof(lltype.Signed):
+ continue
+ d[key] = ('i', cpu.arraydescrof(rffi.CArray(UNSIGNED_TYPE)))
+ return d
+
+def get_arg_descr(cpu, ffi_type):
+ from pypy.rlib.jit_libffi import types
kind = types.getkind(ffi_type)
if kind == 'i' or kind == 'u':
- return history.INT
- elif cpu.supports_floats and kind == 'f':
- return history.FLOAT
- elif kind == 'v':
- return history.VOID
- elif cpu.supports_longlong and (kind == 'I' or kind == 'U'): # longlong
- return 'L'
- elif cpu.supports_singlefloats and kind == 's': # singlefloat
- return 'S'
- raise UnsupportedKind("Unsupported kind '%s'" % kind)
-
-def is_ffi_type_signed(ffi_type):
- from pypy.rlib.libffi import types
- kind = types.getkind(ffi_type)
- return kind != 'u'
+ size = rffi.getintfield(ffi_type, 'c_size')
+ else:
+ size = 0
+ return _get_ffi2descr_dict(cpu)[kind, size]
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -280,10 +280,10 @@
def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
return get_call_descr(self.gc_ll_descr, ARGS, RESULT, extrainfo)
- def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+ def calldescrof_dynamic(self, cif_description, extrainfo):
from pypy.jit.backend.llsupport import ffisupport
- return ffisupport.get_call_descr_dynamic(self, ffi_args, ffi_result,
- extrainfo, ffi_flags)
+ return ffisupport.get_call_descr_dynamic(self, cif_description,
+ extrainfo)
def get_overflow_error(self):
ovf_vtable = self.cast_adr_to_int(self._ovf_error_vtable)
diff --git a/pypy/jit/backend/llsupport/test/test_ffisupport.py b/pypy/jit/backend/llsupport/test/test_ffisupport.py
--- a/pypy/jit/backend/llsupport/test/test_ffisupport.py
+++ b/pypy/jit/backend/llsupport/test/test_ffisupport.py
@@ -1,4 +1,5 @@
-from pypy.rlib.libffi import types
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.codewriter.longlong import is_64_bit
from pypy.jit.backend.llsupport.descr import *
from pypy.jit.backend.llsupport.ffisupport import *
@@ -12,11 +13,21 @@
self.supports_longlong = supports_longlong
self.supports_singlefloats = supports_singlefloats
+def grab(cpu, atypes, rtype):
+ p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+ flavor='raw', immortal=True)
+ rffi.setintfield(p, 'abi', 42)
+ p.nargs = len(atypes)
+ p.rtype = rtype
+ p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+ flavor='raw', immortal=True)
+ for i in range(len(atypes)):
+ p.atypes[i] = atypes[i]
+ return get_call_descr_dynamic(cpu, p, None)
def test_call_descr_dynamic():
args = [types.sint, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.sint, None,
- ffi_flags=42)
+ descr = grab(FakeCPU(), args, types.sint)
assert isinstance(descr, CallDescr)
assert descr.result_type == 'i'
assert descr.result_flag == FLAG_SIGNED
@@ -24,43 +35,39 @@
assert descr.get_ffi_flags() == 42
args = [types.sint, types.double, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.void, None, 42)
+ descr = grab(FakeCPU(), args, types.void)
assert descr is None # missing floats
- descr = get_call_descr_dynamic(FakeCPU(supports_floats=True),
- args, types.void, None, ffi_flags=43)
+ descr = grab(FakeCPU(supports_floats=True), args, types.void)
assert descr.result_type == 'v'
assert descr.result_flag == FLAG_VOID
assert descr.arg_classes == 'ifi'
- assert descr.get_ffi_flags() == 43
+ assert descr.get_ffi_flags() == 42
- descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8, None, 42)
+ descr = grab(FakeCPU(), [], types.sint8)
assert descr.get_result_size() == 1
assert descr.result_flag == FLAG_SIGNED
assert descr.is_result_signed() == True
- descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8, None, 42)
+ descr = grab(FakeCPU(), [], types.uint8)
assert isinstance(descr, CallDescr)
assert descr.get_result_size() == 1
assert descr.result_flag == FLAG_UNSIGNED
assert descr.is_result_signed() == False
if not is_64_bit or is_emulated_long:
- descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong,
- None, 42)
+ descr = grab(FakeCPU(), [], types.slonglong)
assert descr is None # missing longlongs
- descr = get_call_descr_dynamic(FakeCPU(supports_longlong=True),
- [], types.slonglong, None, ffi_flags=43)
+ descr = grab(FakeCPU(supports_longlong=True), [], types.slonglong)
assert isinstance(descr, CallDescr)
assert descr.result_flag == FLAG_FLOAT
assert descr.result_type == 'L'
- assert descr.get_ffi_flags() == 43
+ assert descr.get_ffi_flags() == 42
else:
assert types.slonglong is types.slong
- descr = get_call_descr_dynamic(FakeCPU(), [], types.float, None, 42)
+ descr = grab(FakeCPU(), [], types.float)
assert descr is None # missing singlefloats
- descr = get_call_descr_dynamic(FakeCPU(supports_singlefloats=True),
- [], types.float, None, ffi_flags=44)
+ descr = grab(FakeCPU(supports_singlefloats=True), [], types.float)
assert descr.result_flag == FLAG_UNSIGNED
assert descr.result_type == 'S'
- assert descr.get_ffi_flags() == 44
+ assert descr.get_ffi_flags() == 42
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -998,6 +998,24 @@
getattr(self.mc, asmop)(arglocs[0], arglocs[1])
return genop_binary
+ def _binaryop_or_lea(asmop, is_add):
+ def genop_binary_or_lea(self, op, arglocs, result_loc):
+ # use a regular ADD or SUB if result_loc is arglocs[0],
+ # and a LEA only if different.
+ if result_loc is arglocs[0]:
+ getattr(self.mc, asmop)(arglocs[0], arglocs[1])
+ else:
+ loc = arglocs[0]
+ argloc = arglocs[1]
+ assert isinstance(loc, RegLoc)
+ assert isinstance(argloc, ImmedLoc)
+ assert isinstance(result_loc, RegLoc)
+ delta = argloc.value
+ if not is_add: # subtraction
+ delta = -delta
+ self.mc.LEA_rm(result_loc.value, (loc.value, delta))
+ return genop_binary_or_lea
+
def _cmpop(cond, rev_cond):
def genop_cmp(self, op, arglocs, result_loc):
rl = result_loc.lowest8bits()
@@ -1224,8 +1242,8 @@
genop_int_neg = _unaryop("NEG")
genop_int_invert = _unaryop("NOT")
- genop_int_add = _binaryop("ADD", True)
- genop_int_sub = _binaryop("SUB")
+ genop_int_add = _binaryop_or_lea("ADD", True)
+ genop_int_sub = _binaryop_or_lea("SUB", False)
genop_int_mul = _binaryop("IMUL", True)
genop_int_and = _binaryop("AND", True)
genop_int_or = _binaryop("OR", True)
@@ -1721,15 +1739,15 @@
guard_op.getopname())
def genop_guard_int_add_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_add(op, arglocs, result_loc)
+ self.mc.ADD(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_int_sub_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_sub(op, arglocs, result_loc)
+ self.mc.SUB(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_int_mul_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
- self.genop_int_mul(op, arglocs, result_loc)
+ self.mc.IMUL(arglocs[0], arglocs[1])
return self._gen_guard_overflow(guard_op, guard_token)
def genop_guard_guard_false(self, ign_1, guard_op, guard_token, locs, ign_2):
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -23,6 +23,7 @@
TempBox
from pypy.jit.backend.x86.arch import WORD, FRAME_FIXED_SIZE
from pypy.jit.backend.x86.arch import IS_X86_32, IS_X86_64, MY_COPY_OF_REGS
+from pypy.jit.backend.x86 import rx86
from pypy.rlib.rarithmetic import r_longlong
class X86RegisterManager(RegisterManager):
@@ -610,9 +611,31 @@
loc, argloc = self._consider_binop_part(op)
self.Perform(op, [loc, argloc], loc)
- consider_int_add = _consider_binop
+ def _consider_lea(self, op, loc):
+ argloc = self.loc(op.getarg(1))
+ self.rm.possibly_free_var(op.getarg(0))
+ resloc = self.force_allocate_reg(op.result)
+ self.Perform(op, [loc, argloc], resloc)
+
+ def consider_int_add(self, op):
+ loc = self.loc(op.getarg(0))
+ y = op.getarg(1)
+ if (isinstance(loc, RegLoc) and
+ isinstance(y, ConstInt) and rx86.fits_in_32bits(y.value)):
+ self._consider_lea(op, loc)
+ else:
+ self._consider_binop(op)
+
+ def consider_int_sub(self, op):
+ loc = self.loc(op.getarg(0))
+ y = op.getarg(1)
+ if (isinstance(loc, RegLoc) and
+ isinstance(y, ConstInt) and rx86.fits_in_32bits(-y.value)):
+ self._consider_lea(op, loc)
+ else:
+ self._consider_binop(op)
+
consider_int_mul = _consider_binop
- consider_int_sub = _consider_binop
consider_int_and = _consider_binop
consider_int_or = _consider_binop
consider_int_xor = _consider_binop
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -45,13 +45,7 @@
OS_UNIEQ_LENGTHOK = 51 #
_OS_offset_uni = OS_UNI_CONCAT - OS_STR_CONCAT
#
- OS_LIBFFI_PREPARE = 60
- OS_LIBFFI_PUSH_ARG = 61
OS_LIBFFI_CALL = 62
- OS_LIBFFI_STRUCT_GETFIELD = 63
- OS_LIBFFI_STRUCT_SETFIELD = 64
- OS_LIBFFI_GETARRAYITEM = 65
- OS_LIBFFI_SETARRAYITEM = 66
#
OS_LLONG_INVERT = 69
OS_LLONG_ADD = 70
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -1728,27 +1728,9 @@
# rlib.libffi
def _handle_libffi_call(self, op, oopspec_name, args):
- if oopspec_name == 'libffi_prepare_call':
- oopspecindex = EffectInfo.OS_LIBFFI_PREPARE
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name.startswith('libffi_push_'):
- oopspecindex = EffectInfo.OS_LIBFFI_PUSH_ARG
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name.startswith('libffi_call_'):
+ if oopspec_name == 'libffi_call':
oopspecindex = EffectInfo.OS_LIBFFI_CALL
extraeffect = EffectInfo.EF_RANDOM_EFFECTS
- elif oopspec_name == 'libffi_struct_getfield':
- oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_GETFIELD
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_struct_setfield':
- oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_SETFIELD
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_array_getitem':
- oopspecindex = EffectInfo.OS_LIBFFI_GETARRAYITEM
- extraeffect = EffectInfo.EF_CANNOT_RAISE
- elif oopspec_name == 'libffi_array_setitem':
- oopspecindex = EffectInfo.OS_LIBFFI_SETARRAYITEM
- extraeffect = EffectInfo.EF_CANNOT_RAISE
else:
assert False, 'unsupported oopspec: %s' % oopspec_name
return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -431,31 +431,6 @@
return llop.uint_mod(lltype.Unsigned, xll, yll)
-# libffi support
-# --------------
-
-def func(llfunc):
- from pypy.rlib.libffi import Func
- return cast_base_ptr_to_instance(Func, llfunc)
-
-def _ll_1_libffi_prepare_call(llfunc):
- return func(llfunc)._prepare()
-
-def _ll_4_libffi_push_int(llfunc, value, ll_args, i):
- return func(llfunc)._push_int(value, ll_args, i)
-
-def _ll_4_libffi_push_float(llfunc, value, ll_args, i):
- return func(llfunc)._push_float(value, ll_args, i)
-
-def _ll_3_libffi_call_int(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, rffi.LONG)
-
-def _ll_3_libffi_call_float(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, rffi.DOUBLE)
-
-def _ll_3_libffi_call_void(llfunc, funcsym, ll_args):
- return func(llfunc)._do_call(funcsym, ll_args, lltype.Void)
-
# in the following calls to builtins, the JIT is allowed to look inside:
inline_calls_to = [
('int_floordiv_ovf_zer', [lltype.Signed, lltype.Signed], lltype.Signed),
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -5,7 +5,6 @@
from pypy.jit.metainterp.optimizeopt.heap import OptHeap
from pypy.jit.metainterp.optimizeopt.vstring import OptString
from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll
-from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
from pypy.jit.metainterp.optimizeopt.pure import OptPure
from pypy.jit.metainterp.optimizeopt.earlyforce import OptEarlyForce
@@ -21,7 +20,6 @@
('earlyforce', OptEarlyForce),
('pure', OptPure),
('heap', OptHeap),
- ('ffi', None),
('unroll', None)]
# no direct instantiation of unroll
unroll_all_opts = unrolling_iterable(ALL_OPTS)
@@ -42,11 +40,6 @@
if opt is not None:
o = opt()
optimizations.append(o)
- elif name == 'ffi' and config.translation.jit_ffi:
- # we cannot put the class directly in the unrolling_iterable,
- # because we do not want it to be seen at all (to avoid to
- # introduce a dependency on libffi in case we do not need it)
- optimizations.append(OptFfiCall())
if ('rewrite' not in enable_opts or 'virtualize' not in enable_opts
or 'heap' not in enable_opts or 'unroll' not in enable_opts
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ /dev/null
@@ -1,210 +0,0 @@
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
-from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.rlib import clibffi, libffi
-from pypy.rlib.debug import debug_print
-from pypy.rlib.libffi import Func
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.rarithmetic import intmask
-
-
-class FuncInfo(object):
-
- argtypes = None
- restype = None
- descr = None
- prepare_op = None
-
- def __init__(self, funcval, cpu, prepare_op):
- self.funcval = funcval
- self.opargs = []
- argtypes, restype, flags = self._get_signature(funcval)
- self.descr = cpu.calldescrof_dynamic(argtypes, restype,
- EffectInfo.MOST_GENERAL,
- ffi_flags=flags)
- # ^^^ may be None if unsupported
- self.prepare_op = prepare_op
- self.delayed_ops = []
-
- def _get_signature(self, funcval):
- """
- given the funcval, return a tuple (argtypes, restype, flags), where
- the actuall types are libffi.types.*
-
- The implementation is tricky because we have three possible cases:
-
- - translated: the easiest case, we can just cast back the pointer to
- the original Func instance and read .argtypes, .restype and .flags
-
- - completely untranslated: this is what we get from test_optimizeopt
- tests. funcval contains a FakeLLObject whose _fake_class is Func,
- and we can just get .argtypes, .restype and .flags
-
- - partially translated: this happens when running metainterp tests:
- funcval contains the low-level equivalent of a Func, and thus we
- have to fish inst_argtypes and inst_restype by hand. Note that
- inst_argtypes is actually a low-level array, but we can use it
- directly since the only thing we do with it is to read its items
- """
-
- llfunc = funcval.box.getref_base()
- if we_are_translated():
- func = cast_base_ptr_to_instance(Func, llfunc)
- return func.argtypes, func.restype, func.flags
- elif getattr(llfunc, '_fake_class', None) is Func:
- # untranslated
- return llfunc.argtypes, llfunc.restype, llfunc.flags
- else:
- # partially translated
- # llfunc contains an opaque pointer to something like the following:
- # <GcStruct pypy.rlib.libffi.Func { super, inst_argtypes, inst_funcptr,
- # inst_funcsym, inst_restype }>
- #
- # Unfortunately, we cannot use the proper lltype.cast_opaque_ptr,
- # because we don't have the exact TYPE to cast to. Instead, we
- # just fish it manually :-(
- f = llfunc._obj.container
- return f.inst_argtypes, f.inst_restype, f.inst_flags
-
-
-class OptFfiCall(Optimization):
-
- def setup(self):
- self.funcinfo = None
- if self.optimizer.loop is not None:
- self.logops = self.optimizer.loop.logops
- else:
- self.logops = None
-
- def new(self):
- return OptFfiCall()
-
- def begin_optimization(self, funcval, op):
- self.rollback_maybe('begin_optimization', op)
- self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)
-
- def commit_optimization(self):
- self.funcinfo = None
-
- def rollback_maybe(self, msg, op):
- if self.funcinfo is None:
- return # nothing to rollback
- #
- # we immediately set funcinfo to None to prevent recursion when
- # calling emit_op
- if self.logops is not None:
- debug_print('rollback: ' + msg + ': ', self.logops.repr_of_resop(op))
- funcinfo = self.funcinfo
- self.funcinfo = None
- self.emit_operation(funcinfo.prepare_op)
- for op in funcinfo.opargs:
- self.emit_operation(op)
- for delayed_op in funcinfo.delayed_ops:
- self.emit_operation(delayed_op)
-
- def emit_operation(self, op):
- # we cannot emit any operation during the optimization
- self.rollback_maybe('invalid op', op)
- Optimization.emit_operation(self, op)
-
- def optimize_CALL(self, op):
- oopspec = self._get_oopspec(op)
- ops = [op]
- if oopspec == EffectInfo.OS_LIBFFI_PREPARE:
- ops = self.do_prepare_call(op)
- elif oopspec == EffectInfo.OS_LIBFFI_PUSH_ARG:
- ops = self.do_push_arg(op)
- elif oopspec == EffectInfo.OS_LIBFFI_CALL:
- ops = self.do_call(op)
- #
- for op in ops:
- self.emit_operation(op)
-
- optimize_CALL_MAY_FORCE = optimize_CALL
-
- def optimize_FORCE_TOKEN(self, op):
- # The handling of force_token needs a bit of explanation.
- # The original trace which is getting optimized looks like this:
- # i1 = force_token()
- # setfield_gc(p0, i1, ...)
- # call_may_force(...)
- #
- # In theory, fficall should take care of both force_token and
- # setfield_gc. However, the lazy setfield optimization in heap.py
- # delays the setfield_gc, with the effect that fficall.py sees them in
- # this order:
- # i1 = force_token()
- # call_may_force(...)
- # setfield_gc(p0, i1, ...)
- #
- # This means that see the setfield_gc only the call_may_force, when
- # the optimization has already been done, and thus we need to take
- # special care just of force_token.
- #
- # Finally, the method force_lazy_setfield in heap.py reorders the
- # call_may_force and the setfield_gc, so the final result we get is
- # again force_token/setfield_gc/call_may_force.
- #
- # However, note that nowadays we also allow to have any setfield_gc
- # between libffi_prepare and libffi_call, so while the comment above
- # it's a bit superfluous, it has been left there for future reference.
- if self.funcinfo is None:
- self.emit_operation(op)
- else:
- self.funcinfo.delayed_ops.append(op)
-
- optimize_SETFIELD_GC = optimize_FORCE_TOKEN
-
- def do_prepare_call(self, op):
- self.rollback_maybe('prepare call', op)
- funcval = self._get_funcval(op)
- if not funcval.is_constant():
- return [op] # cannot optimize
- self.begin_optimization(funcval, op)
- return []
-
- def do_push_arg(self, op):
- funcval = self._get_funcval(op)
- if not self.funcinfo or self.funcinfo.funcval is not funcval:
- return [op] # cannot optimize
- self.funcinfo.opargs.append(op)
- return []
-
- def do_call(self, op):
- funcval = self._get_funcval(op)
- funcinfo = self.funcinfo
- if (not funcinfo or funcinfo.funcval is not funcval or
- funcinfo.descr is None):
- return [op] # cannot optimize
- funcsymval = self.getvalue(op.getarg(2))
- arglist = [funcsymval.get_key_box()]
- for push_op in funcinfo.opargs:
- argval = self.getvalue(push_op.getarg(2))
- arglist.append(argval.get_key_box())
- newop = ResOperation(rop.CALL_RELEASE_GIL, arglist, op.result,
- descr=funcinfo.descr)
- self.commit_optimization()
- ops = []
- for delayed_op in funcinfo.delayed_ops:
- ops.append(delayed_op)
- ops.append(newop)
- return ops
-
- def propagate_forward(self, op):
- if self.logops is not None:
- debug_print(self.logops.repr_of_resop(op))
- dispatch_opt(self, op)
-
- def _get_oopspec(self, op):
- effectinfo = op.getdescr().get_extra_info()
- return effectinfo.oopspecindex
-
- def _get_funcval(self, op):
- return self.getvalue(op.getarg(1))
-
-dispatch_opt = make_dispatcher_method(OptFfiCall, 'optimize_',
- default=OptFfiCall.emit_operation)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ /dev/null
@@ -1,315 +0,0 @@
-from pypy.rpython.lltypesystem import llmemory
-from pypy.rlib.libffi import Func, types
-from pypy.jit.metainterp.history import AbstractDescr
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import BaseTestBasic
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import LLtypeMixin
-
-class MyCallDescr(AbstractDescr):
- """
- Fake calldescr to be used inside the tests.
-
- The particularity is that it provides an __eq__ method, so that it
- comparses by value by comparing the arg_types and typeinfo fields, so you
- can check that the signature of a call is really what you want.
- """
-
- def __init__(self, arg_types, typeinfo, flags):
- self.arg_types = arg_types
- self.typeinfo = typeinfo # return type
- self.flags = flags
-
- def __eq__(self, other):
- return (self.arg_types == other.arg_types and
- self.typeinfo == other.typeinfo and
- self.flags == other.get_ffi_flags())
-
-class FakeLLObject(object):
-
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
- self._TYPE = llmemory.GCREF
-
- def _identityhash(self):
- return id(self)
-
-
-class TestFfiCall(BaseTestBasic, LLtypeMixin):
-
- enable_opts = "intbounds:rewrite:virtualize:string:pure:earlyforce:heap:ffi"
-
- class namespace:
- cpu = LLtypeMixin.cpu
- FUNC = LLtypeMixin.FUNC
- vable_token_descr = LLtypeMixin.valuedescr
- valuedescr = LLtypeMixin.valuedescr
-
- int_float__int_42 = MyCallDescr('if', 'i', 42)
- int_float__int_43 = MyCallDescr('if', 'i', 43)
- funcptr = FakeLLObject()
- func = FakeLLObject(_fake_class=Func,
- argtypes=[types.sint, types.double],
- restype=types.sint,
- flags=42)
- func2 = FakeLLObject(_fake_class=Func,
- argtypes=[types.sint, types.double],
- restype=types.sint,
- flags=43)
- #
- ffi_slong = types.slong
- dyn_123_field = cpu.fielddescrof_dynamic(offset=123,
- fieldsize=types.slong.c_size,
- is_pointer=False,
- is_float=False,
- is_signed=True)
- #
- def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
- if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
- f = None # means "can force all" really
- else:
- f = []
- einfo = EffectInfo(f, f, f, f, oopspecindex=oopspecindex,
- extraeffect=extraeffect)
- return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
- #
- libffi_prepare = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PREPARE)
- libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG)
- libffi_call = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL,
- EffectInfo.EF_RANDOM_EFFECTS)
- libffi_struct_getfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_GETFIELD)
- libffi_struct_setfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_SETFIELD)
-
- namespace = namespace.__dict__
-
- # ----------------------------------------------------------------------
- # this group of tests is the most important, as they represent the "real"
- # cases you actually get when using rlib.libffi
-
- def test_ffi_call_opt(self):
- ops = """
- [i0, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- expected = """
- [i0, f1]
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_call_nonconst(self):
- ops = """
- [i0, f1, p2]
- call(0, p2, descr=libffi_prepare)
- call(0, p2, i0, descr=libffi_push_arg)
- call(0, p2, f1, descr=libffi_push_arg)
- i3 = call_may_force(0, p2, 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_handle_virtualizables(self):
- # this test needs an explanation to understand what goes on: see the
- # comment in optimize_FORCE_TOKEN
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i4 = force_token()
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- expected = """
- [i0, f1, p2]
- i4 = force_token()
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- loop = self.optimize_loop(ops, expected)
-
- # ----------------------------------------------------------------------
- # in pratice, the situations described in these tests should never happen,
- # but we still want to ensure correctness
-
- def test_rollback_if_op_in_between(self):
- ops = """
- [i0, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- i1 = int_add(i0, 1)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_multiple_calls(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- #
- # this is the culprit!
- call(0, ConstPtr(func2), descr=libffi_prepare)
- #
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_multiple_prepare(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this is the culprit!
- call(0, ConstPtr(func2), descr=libffi_prepare)
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_optimize_nested_call(self):
- ops = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this "nested" call is nicely optimized
- call(0, ConstPtr(func2), descr=libffi_prepare)
- call(0, ConstPtr(func2), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func2), f1, descr=libffi_push_arg)
- i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- expected = """
- [i0, i2, f1]
- call(0, ConstPtr(func), descr=libffi_prepare)
- #
- # this "nested" call is nicely optimized
- i4 = call_release_gil(67890, i0, f1, descr=int_float__int_43)
- guard_not_forced() []
- guard_no_exception() []
- #
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, i4, f1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_rollback_force_token(self):
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- i4 = force_token()
- i5 = int_add(i0, 1) # culprit!
- setfield_gc(p2, i4, descr=vable_token_descr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() [p2]
- guard_no_exception() [p2]
- jump(i3, f1, p2)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
-
- def test_allow_setfields_in_between(self):
- ops = """
- [i0, f1, p2]
- call(0, ConstPtr(func), descr=libffi_prepare)
- call(0, ConstPtr(func), i0, descr=libffi_push_arg)
- call(0, ConstPtr(func), f1, descr=libffi_push_arg)
- setfield_gc(p2, i0, descr=valuedescr)
- i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- expected = """
- [i0, f1, p2]
- setfield_gc(p2, i0, descr=valuedescr)
- i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
- guard_not_forced() []
- guard_no_exception() []
- jump(i3, f1, p2)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_struct_fields(self):
- ops = """
- [i0]
- i1 = call(0, ConstClass(ffi_slong), i0, 123, descr=libffi_struct_getfield)
- i2 = int_add(i1, 1)
- call(0, ConstClass(ffi_slong), i0, 123, i2, descr=libffi_struct_setfield)
- jump(i1)
- """
- expected = """
- [i0]
- i1 = getfield_raw(i0, descr=dyn_123_field)
- i2 = int_add(i1, 1)
- setfield_raw(i0, i2, descr=dyn_123_field)
- jump(i1)
- """
- loop = self.optimize_loop(ops, expected)
-
- def test_ffi_struct_fields_nonconst(self):
- ops = """
- [i0, i1]
- i2 = call(0, ConstClass(ffi_slong), i0, i1, descr=libffi_struct_getfield)
- i3 = call(0, i1 , i0, 123, descr=libffi_struct_getfield)
- jump(i1)
- """
- expected = ops
- loop = self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -41,14 +41,6 @@
#
chain, _ = build_opt_chain(metainterp_sd, "aaa:bbb")
check(chain, ["OptSimplify"])
- #
- chain, _ = build_opt_chain(metainterp_sd, "ffi")
- check(chain, ["OptFfiCall", "OptSimplify"])
- #
- metainterp_sd.config = get_pypy_config(translating=True)
- assert not metainterp_sd.config.translation.jit_ffi
- chain, _ = build_opt_chain(metainterp_sd, "ffi")
- check(chain, ["OptSimplify"])
# ____________________________________________________________
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -346,7 +346,6 @@
self.options = Fake()
self.globaldata = Fake()
self.config = get_pypy_config(translating=True)
- self.config.translation.jit_ffi = True
class logger_noopt:
@classmethod
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -1390,6 +1390,8 @@
if vablebox is not None:
self.metainterp.history.record(rop.KEEPALIVE, [vablebox], None)
self.metainterp.handle_possible_exception()
+ if effectinfo.oopspecindex == effectinfo.OS_LIBFFI_CALL:
+ self.metainterp.direct_libffi_call()
return resbox
else:
effect = effectinfo.extraeffect
@@ -2533,6 +2535,85 @@
else:
return None
+ def direct_libffi_call(self):
+ """Generate a direct call to C code, patching the CALL_MAY_FORCE
+ to jit_ffi_call() that occurred just now.
+ """
+ from pypy.rpython.lltypesystem import llmemory
+ from pypy.rlib.jit_libffi import CIF_DESCRIPTION_P
+ from pypy.jit.backend.llsupport.ffisupport import get_arg_descr
+ #
+ num_extra_guards = 0
+ while True:
+ op = self.history.operations[-1-num_extra_guards]
+ if op.getopnum() == rop.CALL_MAY_FORCE:
+ break
+ assert op.is_guard()
+ num_extra_guards += 1
+ #
+ box_cif_description = op.getarg(1)
+ if not isinstance(box_cif_description, ConstInt):
+ return
+ 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)
+ calldescr = self.cpu.calldescrof_dynamic(cif_description,
+ op.getdescr().extrainfo)
+ if calldescr is None:
+ return
+ #
+ extra_guards = []
+ for i in range(num_extra_guards):
+ extra_guards.append(self.history.operations.pop())
+ extra_guards.reverse()
+ #
+ box_exchange_buffer = op.getarg(3)
+ self.history.operations.pop()
+ arg_boxes = []
+ for i in range(cif_description.nargs):
+ kind, descr = get_arg_descr(self.cpu, cif_description.atypes[i])
+ if kind == 'i':
+ box_arg = history.BoxInt()
+ elif kind == 'f':
+ box_arg = history.BoxFloat()
+ else:
+ assert kind == 'v'
+ continue
+ ofs = cif_description.exchange_args[i]
+ box_argpos = history.BoxInt()
+ self.history.record(rop.INT_ADD,
+ [box_exchange_buffer, ConstInt(ofs)],
+ box_argpos)
+ self.history.record(rop.GETARRAYITEM_RAW,
+ [box_argpos, ConstInt(0)],
+ box_arg, descr)
+ arg_boxes.append(box_arg)
+ #
+ kind, descr = 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
+ 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
+ box_resultpos = history.BoxInt()
+ self.history.record(rop.INT_ADD,
+ [box_exchange_buffer, ConstInt(ofs)],
+ box_resultpos)
+ self.history.record(rop.SETARRAYITEM_RAW,
+ [box_resultpos, ConstInt(0), box_result],
+ None, descr)
+
# ____________________________________________________________
class ChangeFrame(JitException):
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -3797,6 +3797,7 @@
assert res == 3
def test_float_bytes(self):
+ from pypy.rlib.rfloat import isnan
def f(n):
ll = float2longlong(n)
return longlong2float(ll)
@@ -3804,7 +3805,7 @@
for x in [2.5, float("nan"), -2.5, float("inf")]:
# There are tests elsewhere to verify the correctness of this.
res = self.interp_operations(f, [x])
- assert res == x or math.isnan(x) and math.isnan(res)
+ assert res == x or isnan(x) and isnan(res)
class TestLLtype(BaseLLtypeTests, LLJitMixin):
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,210 +1,106 @@
-from __future__ import with_statement
import py
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rlib.unroll import unrolling_iterable
-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, struct_setfield_int, struct_getfield_int)
-from pypy.rlib.objectmodel import specialize
-from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.tool.sourcetools import func_with_new_name
+def get_description(atypes, rtype):
+ p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+ flavor='raw', immortal=True)
+ rffi.setintfield(p, 'abi', 42)
+ p.nargs = len(atypes)
+ p.rtype = rtype
+ p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+ flavor='raw', immortal=True)
+ for i in range(len(atypes)):
+ p.atypes[i] = atypes[i]
+ return p
-class FfiCallTests(_TestLibffiCall):
- # ===> ../../../rlib/test/test_libffi.py
- def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
- """
- Call the function specified by funcspec in a loop, and let the jit to
- see and optimize it.
- """
- #
- lib, name, argtypes, restype = funcspec
- method_and_args = []
- for argval in args:
- if isinstance(argval, tuple):
- method_name, argval = argval
+class FfiCallTests(object):
+
+ def _run(self, atypes, rtype, avalues, rvalue):
+ cif_description = get_description(atypes, rtype)
+
+ def verify(*args):
+ assert args == tuple(avalues)
+ return rvalue
+ FUNC = lltype.FuncType([lltype.typeOf(avalue) for avalue in avalues],
+ lltype.typeOf(rvalue))
+ func = lltype.functionptr(FUNC, 'verify', _callable=verify)
+ func_addr = rffi.cast(rffi.VOIDP, func)
+
+ for i in range(len(avalues)):
+ cif_description.exchange_args[i] = (i+1) * 16
+ cif_description.exchange_result = (len(avalues)+1) * 16
+
+ unroll_avalues = unrolling_iterable(avalues)
+
+ @jit.oopspec("libffi_call(cif_description,func_addr,exchange_buffer)")
+ def fake_call(cif_description, func_addr, exchange_buffer):
+ ofs = 16
+ for avalue in unroll_avalues:
+ TYPE = rffi.CArray(lltype.typeOf(avalue))
+ data = rffi.ptradd(exchange_buffer, ofs)
+ assert rffi.cast(lltype.Ptr(TYPE), data)[0] == avalue
+ ofs += 16
+ if rvalue is not None:
+ write_rvalue = rvalue
else:
- method_name = 'arg'
- method_and_args.append((method_name, argval))
- method_and_args = unrolling_iterable(method_and_args)
- #
- reds = ['n', 'res', 'func']
- if (RESULT is rffi.DOUBLE or
- IS_32_BIT and RESULT in [rffi.LONGLONG, rffi.ULONGLONG]):
- reds = ['n', 'func', 'res'] # 'double' floats must be *after* refs
- driver = JitDriver(reds=reds, greens=[])
- init_result = rffi.cast(RESULT, 0)
- #
- def g(func):
- # a different function, which is marked as "dont_look_inside"
- # in case it uses an unsupported argument
- argchain = ArgChain()
- # this loop is unrolled
- for method_name, argval in method_and_args:
- getattr(argchain, method_name)(argval)
- return func.call(argchain, RESULT, is_struct=is_struct)
- #
- def f(n):
- func = lib.getpointer(name, argtypes, restype)
- res = init_result
- while n < 10:
- driver.jit_merge_point(n=n, res=res, func=func)
- promote(func)
- res = g(func)
- n += 1
+ write_rvalue = 12923 # ignored
+ TYPE = rffi.CArray(lltype.typeOf(write_rvalue))
+ data = rffi.ptradd(exchange_buffer, ofs)
+ rffi.cast(lltype.Ptr(TYPE), data)[0] = write_rvalue
+
+ def f():
+ exbuf = lltype.malloc(rffi.CCHARP.TO, (len(avalues)+2) * 16,
+ flavor='raw', zero=True)
+ ofs = 16
+ for avalue in unroll_avalues:
+ TYPE = rffi.CArray(lltype.typeOf(avalue))
+ data = rffi.ptradd(exbuf, ofs)
+ rffi.cast(lltype.Ptr(TYPE), data)[0] = avalue
+ ofs += 16
+
+ fake_call(cif_description, func_addr, exbuf)
+
+ if rvalue is None:
+ res = 654321
+ else:
+ TYPE = rffi.CArray(lltype.typeOf(rvalue))
+ data = rffi.ptradd(exbuf, ofs)
+ res = rffi.cast(lltype.Ptr(TYPE), data)[0]
+ lltype.free(exbuf, flavor='raw')
return res
- #
- res = self.meta_interp(f, [0], backendopt=True,
- supports_floats = self.supports_all,
- supports_longlong = self.supports_all,
- supports_singlefloats = self.supports_all)
- d = {'floats': self.supports_all,
- 'longlong': self.supports_all or not IS_32_BIT,
- 'singlefloats': self.supports_all,
- 'byval': False}
- supported = all(d[check] for check in jitif)
- if supported:
- self.check_resops(
- call_release_gil=2, # a CALL_RELEASE_GIL, and no other CALLs
- call=0,
- call_may_force=0,
- guard_no_exception=2,
- guard_not_forced=2,
- int_add=2,
- int_lt=2,
- guard_true=2,
- jump=1)
- else:
- self.check_resops(
- call_release_gil=0, # no CALL_RELEASE_GIL
- int_add=2,
- int_lt=2,
- guard_true=2,
- jump=1)
- return res
- def test_byval_result(self):
- _TestLibffiCall.test_byval_result(self)
- test_byval_result.__doc__ = _TestLibffiCall.test_byval_result.__doc__
- test_byval_result.dont_track_allocations = True
+ 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)
-class FfiLookupTests(object):
- def test_array_fields(self):
- myjitdriver = JitDriver(
- greens = [],
- reds = ["n", "i", "points", "result_point"],
- )
+ def test_simple_call(self):
+ self._run([types.signed] * 2, types.signed, [456, 789], -42)
- POINT = lltype.Struct("POINT",
- ("x", lltype.Signed),
- ("y", lltype.Signed),
- )
- def f(points, result_point, n):
- i = 0
- while i < n:
- myjitdriver.jit_merge_point(i=i, points=points, n=n,
- result_point=result_point)
- x = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, 0
- )
- y = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, rffi.sizeof(lltype.Signed)
- )
+ def test_many_arguments(self):
+ for i in [0, 6, 20]:
+ self._run([types.signed] * i, types.signed,
+ [-123456*j for j in range(i)],
+ -42434445)
- cur_x = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0
- )
- cur_y = array_getitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed)
- )
+ def test_simple_call_float(self):
+ self._run([types.double] * 2, types.double, [45.6, 78.9], -4.2)
- array_setitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0, cur_x + x
- )
- array_setitem(
- types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed), cur_y + y
- )
- i += 1
+ def test_returns_none(self):
+ self._run([types.signed] * 2, types.void, [456, 789], None)
- def main(n):
- with lltype.scoped_alloc(rffi.CArray(POINT), n) as points:
- with lltype.scoped_alloc(rffi.CArray(POINT), 1) as result_point:
- 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[0].x = 0
- result_point[0].y = 0
- result_point = rffi.cast(rffi.CArrayPtr(lltype.Char), result_point)
- f(points, result_point, n)
- result_point = rffi.cast(rffi.CArrayPtr(POINT), result_point)
- return result_point[0].x * result_point[0].y
-
- assert self.meta_interp(main, [10]) == main(10) == 9000
- self.check_resops({'jump': 1, 'int_lt': 2, 'setinteriorfield_raw': 4,
- 'getinteriorfield_raw': 8, 'int_add': 6, 'guard_true': 2})
-
- def _test_getitem_type(self, TYPE, ffitype, COMPUTE_TYPE):
- reds = ["n", "i", "s", "data"]
- if COMPUTE_TYPE is lltype.Float:
- # Move the float var to the back.
- reds.remove("s")
- reds.append("s")
- myjitdriver = JitDriver(
- greens = [],
- reds = reds,
- )
- def f(data, n):
- i = 0
- s = rffi.cast(COMPUTE_TYPE, 0)
- while i < n:
- myjitdriver.jit_merge_point(n=n, i=i, s=s, data=data)
- s += rffi.cast(COMPUTE_TYPE, array_getitem(ffitype, rffi.sizeof(TYPE), data, 0, 0))
- i += 1
- return s
- def main(n):
- with lltype.scoped_alloc(rffi.CArray(TYPE), 1) as data:
- data[0] = rffi.cast(TYPE, 200)
- return f(data, n)
- assert self.meta_interp(main, [10]) == 2000
-
- def test_array_getitem_uint8(self):
- self._test_getitem_type(rffi.UCHAR, types.uchar, lltype.Signed)
- self.check_resops({'jump': 1, 'int_lt': 2, 'getinteriorfield_raw': 2,
- 'guard_true': 2, 'int_add': 4})
-
- def test_array_getitem_float(self):
- self._test_getitem_type(rffi.FLOAT, types.float, lltype.Float)
+ def test_returns_signedchar(self):
+ self._run([types.signed], types.sint8, [456],
+ rffi.cast(rffi.SIGNEDCHAR, -42))
class TestFfiCall(FfiCallTests, LLJitMixin):
- supports_all = False
-
-class TestFfiCallSupportAll(FfiCallTests, LLJitMixin):
- supports_all = True # supports_{floats,longlong,singlefloats}
-
- def test_struct_getfield(self):
- myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'addr'])
-
- def f(n):
- i = 0
- addr = lltype.malloc(rffi.VOIDP.TO, 10, flavor='raw')
- while i < n:
- myjitdriver.jit_merge_point(n=n, i=i, addr=addr)
- struct_setfield_int(types.slong, addr, 0, 1)
- i += struct_getfield_int(types.slong, addr, 0)
- lltype.free(addr, flavor='raw')
- return i
- assert self.meta_interp(f, [20]) == f(20)
- self.check_resops(
- setfield_raw=2,
- getfield_raw=2,
- call=0)
-
-
-class TestFfiLookup(FfiLookupTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -79,10 +79,6 @@
translator.config.translation.list_comprehension_operations = True
except ConfigError:
pass
- try:
- translator.config.translation.jit_ffi = True
- except ConfigError:
- pass
warmrunnerdesc = WarmRunnerDesc(translator, backendopt=backendopt, **kwds)
for jd in warmrunnerdesc.jitdrivers_sd:
jd.warmstate.set_param_threshold(3) # for tests
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -5,7 +5,10 @@
import sys
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.rlib import jit, clibffi
+from pypy.rlib import jit, clibffi, jit_libffi
+from pypy.rlib.jit_libffi import CIF_DESCRIPTION, CIF_DESCRIPTION_P
+from pypy.rlib.jit_libffi import FFI_TYPE, FFI_TYPE_P, FFI_TYPE_PP
+from pypy.rlib.jit_libffi import SIZE_OF_FFI_ARG
from pypy.rlib.objectmodel import we_are_translated, instantiate
from pypy.rlib.objectmodel import keepalive_until_here
@@ -120,42 +123,24 @@
mustfree_max_plus_1 = 0
buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
try:
- buffer_array = rffi.cast(rffi.VOIDPP, buffer)
for i in range(len(args_w)):
data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
- buffer_array[i] = data
w_obj = args_w[i]
argtype = self.fargs[i]
if argtype.convert_argument_from_object(data, w_obj):
# argtype is a pointer type, and w_obj a list/tuple/str
mustfree_max_plus_1 = i + 1
- resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
ec = cerrno.get_errno_container(space)
cerrno.restore_errno_from(ec)
- clibffi.c_ffi_call(cif_descr.cif,
- rffi.cast(rffi.VOIDP, funcaddr),
- rffi.cast(rffi.VOIDP, resultdata),
- buffer_array)
+ jit_libffi.jit_ffi_call(cif_descr,
+ rffi.cast(rffi.VOIDP, funcaddr),
+ buffer)
e = cerrno.get_real_errno()
cerrno.save_errno_into(ec, e)
- if self.ctitem.is_primitive_integer:
- if BIG_ENDIAN:
- # For results of precisely these types, libffi has a
- # strange rule that they will be returned as a whole
- # 'ffi_arg' if they are smaller. The difference
- # only matters on big-endian.
- if self.ctitem.size < SIZE_OF_FFI_ARG:
- diff = SIZE_OF_FFI_ARG - self.ctitem.size
- resultdata = rffi.ptradd(resultdata, diff)
- w_res = self.ctitem.convert_to_object(resultdata)
- elif isinstance(self.ctitem, W_CTypeVoid):
- w_res = space.w_None
- elif isinstance(self.ctitem, W_CTypeStructOrUnion):
- w_res = self.ctitem.copy_and_convert_to_object(resultdata)
- else:
- w_res = self.ctitem.convert_to_object(resultdata)
+ resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
+ w_res = self.ctitem.copy_and_convert_to_object(resultdata)
finally:
for i in range(mustfree_max_plus_1):
argtype = self.fargs[i]
@@ -180,46 +165,11 @@
# ____________________________________________________________
-# The "cif" is a block of raw memory describing how to do a call via libffi.
-# It starts with a block of memory of type FFI_CIF, which is used by libffi
-# itself. Following it, we find _cffi_backend-specific information:
-#
-# - 'exchange_size': an integer that tells how big a buffer we must
-# allocate for the call; this buffer should start with an array of
-# pointers to the actual argument values.
-#
-# - 'exchange_result': the offset in that buffer for the result of the call.
-#
-# - 'exchange_args[nargs]': the offset in that buffer for each argument.
-#
-# Following this, we have other data structures for libffi (with direct
-# pointers from the FFI_CIF to these data structures):
-#
-# - the argument types, as an array of 'ffi_type *'.
-#
-# - optionally, the result's and the arguments' ffi type data
-# (this is used only for 'struct' ffi types; in other cases the
-# 'ffi_type *' just points to static data like 'ffi_type_sint32').
-FFI_CIF = clibffi.FFI_CIFP.TO
-FFI_TYPE = clibffi.FFI_TYPE_P.TO
-FFI_TYPE_P = clibffi.FFI_TYPE_P
-FFI_TYPE_PP = clibffi.FFI_TYPE_PP
-SIZE_OF_FFI_ARG = rffi.sizeof(clibffi.ffi_arg)
+W_CTypeFunc.cif_descr = lltype.nullptr(CIF_DESCRIPTION) # default value
+
BIG_ENDIAN = sys.byteorder == 'big'
-CIF_DESCRIPTION = lltype.Struct(
- 'CIF_DESCRIPTION',
- ('cif', FFI_CIF),
- ('exchange_size', lltype.Signed),
- ('exchange_result', lltype.Signed),
- ('exchange_args', lltype.Array(lltype.Signed,
- hints={'nolength': True, 'immutable': True})),
- hints={'immutable': True})
-
-CIF_DESCRIPTION_P = lltype.Ptr(CIF_DESCRIPTION)
-W_CTypeFunc.cif_descr = lltype.nullptr(CIF_DESCRIPTION) # default value
-
# ----------
# We attach to the classes small methods that return a 'ffi_type'
@@ -351,6 +301,16 @@
def fb_build(self):
+ # Build a CIF_DESCRIPTION. Actually this computes the size and
+ # allocates a larger amount of data. It starts with a
+ # CIF_DESCRIPTION and continues with data needed for the CIF:
+ #
+ # - the argument types, as an array of 'ffi_type *'.
+ #
+ # - optionally, the result's and the arguments' ffi type data
+ # (this is used only for 'struct' ffi types; in other cases the
+ # 'ffi_type *' just points to static data like 'ffi_type_sint32').
+ #
nargs = len(self.fargs)
# start with a cif_description (cif and exchange_* fields)
@@ -380,13 +340,23 @@
exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs
exchange_offset = self.align_arg(exchange_offset)
cif_descr.exchange_result = exchange_offset
+ cif_descr.exchange_result_libffi = exchange_offset
- # then enough room for the result --- which means at least
- # sizeof(ffi_arg), according to the ffi docs
+ if BIG_ENDIAN and self.fresult.is_primitive_integer:
+ # For results of precisely these types, libffi has a
+ # strange rule that they will be returned as a whole
+ # 'ffi_arg' if they are smaller. The difference
+ # only matters on big-endian.
+ if self.fresult.size < SIZE_OF_FFI_ARG:
+ diff = SIZE_OF_FFI_ARG - self.fresult.size
+ cif_descr.exchange_result += diff
+
+ # then enough room for the result, rounded up to sizeof(ffi_arg)
exchange_offset += max(rffi.getintfield(self.rtype, 'c_size'),
SIZE_OF_FFI_ARG)
# loop over args
+ cif_descr.exchange_nb_args = len(self.fargs)
for i, farg in enumerate(self.fargs):
if isinstance(farg, W_CTypePointer):
exchange_offset += 1 # for the "must free" flag
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -162,6 +162,9 @@
"cdata '%s' has no attribute '%s'",
self.name, attr)
+ def copy_and_convert_to_object(self, cdata):
+ return self.convert_to_object(cdata)
+
W_CType.typedef = TypeDef(
'CTypeDescr',
diff --git a/pypy/module/_cffi_backend/ctypevoid.py b/pypy/module/_cffi_backend/ctypevoid.py
--- a/pypy/module/_cffi_backend/ctypevoid.py
+++ b/pypy/module/_cffi_backend/ctypevoid.py
@@ -11,3 +11,6 @@
def __init__(self, space):
W_CType.__init__(self, space, -1, "void", len("void"))
+
+ def copy_and_convert_to_object(self, cdata):
+ return self.space.w_None
diff --git a/pypy/rlib/clibffi.py b/pypy/rlib/clibffi.py
--- a/pypy/rlib/clibffi.py
+++ b/pypy/rlib/clibffi.py
@@ -202,7 +202,7 @@
FFI_TYPE_P.TO.become(cConfig.ffi_type)
size_t = cConfig.size_t
-ffi_abi = cConfig.ffi_abi
+FFI_ABI = cConfig.ffi_abi
ffi_arg = cConfig.ffi_arg
for name in type_names:
@@ -333,7 +333,7 @@
VOIDPP = rffi.CArrayPtr(rffi.VOIDP)
-c_ffi_prep_cif = external('ffi_prep_cif', [FFI_CIFP, ffi_abi, rffi.UINT,
+c_ffi_prep_cif = external('ffi_prep_cif', [FFI_CIFP, FFI_ABI, rffi.UINT,
FFI_TYPE_P, FFI_TYPE_PP], rffi.INT)
if _MSVC:
c_ffi_call_return_type = rffi.INT
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -402,7 +402,7 @@
"""Inconsistency in the JIT hints."""
ENABLE_ALL_OPTS = (
- 'intbounds:rewrite:virtualize:string:earlyforce:pure:heap:ffi:unroll')
+ 'intbounds:rewrite:virtualize:string:earlyforce:pure:heap:unroll')
PARAMETER_DOCS = {
'threshold': 'number of times a loop has to run for it to become hot',
diff --git a/pypy/rlib/jit_libffi.py b/pypy/rlib/jit_libffi.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/jit_libffi.py
@@ -0,0 +1,125 @@
+import sys
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib import clibffi, jit
+
+
+FFI_CIF = clibffi.FFI_CIFP.TO
+FFI_TYPE = clibffi.FFI_TYPE_P.TO
+FFI_TYPE_P = clibffi.FFI_TYPE_P
+FFI_TYPE_PP = clibffi.FFI_TYPE_PP
+FFI_ABI = clibffi.FFI_ABI
+SIZE_OF_FFI_ARG = rffi.sizeof(clibffi.ffi_arg)
+
+# "cif_description" is a block of raw memory describing how to do the call.
+# It starts with a block of memory of type FFI_CIF, which is used by libffi
+# itself. Following it, we find jit_libffi-specific information:
+#
+# - 'exchange_size': an integer that tells how big a buffer we must
+# allocate for the call; this buffer should have enough room at the
+# beginning for an array of pointers to the actual argument values,
+# which is initialized internally by jit_ffi_call().
+#
+# - 'exchange_result': the offset in that buffer for the result of the call.
+#
+# - 'exchange_result_libffi': the actual offset passed to ffi_call().
+# Differs on big-endian machines if the result is an integer type smaller
+# than SIZE_OF_FFI_ARG (blame libffi).
+#
+# - 'exchange_args[nargs]': the offset in that buffer for each argument.
+
+CIF_DESCRIPTION = lltype.Struct(
+ 'CIF_DESCRIPTION',
+ ('cif', FFI_CIF),
+ ('abi', FFI_ABI),
+ ('nargs', lltype.Signed),
+ ('rtype', FFI_TYPE_P),
+ ('atypes', FFI_TYPE_PP),
+ ('exchange_size', lltype.Signed),
+ ('exchange_result', lltype.Signed),
+ ('exchange_result_libffi', lltype.Signed),
+ ('exchange_args', lltype.Array(lltype.Signed,
+ hints={'nolength': True, 'immutable': True})),
+ hints={'immutable': True})
+
+CIF_DESCRIPTION_P = lltype.Ptr(CIF_DESCRIPTION)
+
+
+ at jit.oopspec("libffi_call(cif_description, func_addr, exchange_buffer)")
+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'.
+ """
+ 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])
+ buffer_array[i] = data
+ resultdata = rffi.ptradd(exchange_buffer,
+ cif_description.exchange_result_libffi)
+ clibffi.c_ffi_call(cif_description.cif, func_addr,
+ rffi.cast(rffi.VOIDP, resultdata),
+ buffer_array)
+
+# ____________________________________________________________
+
+class types(object):
+ """
+ This namespace contains the mapping the JIT needs from ffi types to
+ a less strict "kind" character.
+ """
+
+ @classmethod
+ def _import(cls):
+ prefix = 'ffi_type_'
+ for key, value in clibffi.__dict__.iteritems():
+ if key.startswith(prefix):
+ name = key[len(prefix):]
+ setattr(cls, name, value)
+ cls.slong = clibffi.cast_type_to_ffitype(rffi.LONG)
+ cls.ulong = clibffi.cast_type_to_ffitype(rffi.ULONG)
+ cls.slonglong = clibffi.cast_type_to_ffitype(rffi.LONGLONG)
+ cls.ulonglong = clibffi.cast_type_to_ffitype(rffi.ULONGLONG)
+ cls.signed = clibffi.cast_type_to_ffitype(rffi.SIGNED)
+ cls.wchar_t = clibffi.cast_type_to_ffitype(lltype.UniChar)
+ del cls._import
+
+ @staticmethod
+ @jit.elidable
+ def getkind(ffi_type):
+ """Returns 'v' for void, 'f' for float, 'i' for signed integer,
+ 'u' for unsigned integer, 'S' for singlefloat, 'L' for long long
+ integer (signed or unsigned), or '*' for struct.
+ """
+ if ffi_type is types.void: return 'v'
+ elif ffi_type is types.double: return 'f'
+ elif ffi_type is types.float: return 'S'
+ elif ffi_type is types.pointer: return 'i'
+ #
+ elif ffi_type is types.schar: return 'i'
+ elif ffi_type is types.uchar: return 'u'
+ elif ffi_type is types.sshort: return 'i'
+ elif ffi_type is types.ushort: return 'u'
+ elif ffi_type is types.sint: return 'i'
+ elif ffi_type is types.uint: return 'u'
+ elif ffi_type is types.slong: return 'i'
+ elif ffi_type is types.ulong: return 'u'
+ #
+ elif ffi_type is types.sint8: return 'i'
+ elif ffi_type is types.uint8: return 'u'
+ elif ffi_type is types.sint16: return 'i'
+ elif ffi_type is types.uint16: return 'u'
+ elif ffi_type is types.sint32: return 'i'
+ elif ffi_type is types.uint32: return 'u'
+ ## (note that on 64-bit platforms, types.sint64 is types.slong and the
+ ## case is caught above)
+ elif ffi_type is types.sint64: return 'L'
+ elif ffi_type is types.uint64: return 'L'
+ #
+ elif types.is_struct(ffi_type): return '*'
+ raise KeyError
+
+ @staticmethod
+ @jit.elidable
+ def is_struct(ffi_type):
+ return intmask(ffi_type.c_type) == FFI_TYPE_STRUCT
+
+types._import()
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
deleted file mode 100644
--- a/pypy/rlib/libffi.py
+++ /dev/null
@@ -1,434 +0,0 @@
-from __future__ import with_statement
-
-from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.rlib.objectmodel import specialize, enforceargs
-from pypy.rlib.rarithmetic import intmask, r_uint, r_singlefloat, r_longlong
-from pypy.rlib import jit
-from pypy.rlib import clibffi
-from pypy.rlib.clibffi import FUNCFLAG_CDECL, FUNCFLAG_STDCALL, \
- AbstractFuncPtr, push_arg_as_ffiptr, c_ffi_call, FFI_TYPE_STRUCT
-from pypy.rlib.rdynload import dlopen, dlclose, dlsym, dlsym_byordinal
-from pypy.rlib.rdynload import DLLHANDLE
-
-import os
-
-class types(object):
- """
- This namespace contains the primitive types you can use to declare the
- signatures of the ffi functions.
-
- In general, the name of the types are closely related to the ones of the
- C-level ffi_type_*: e.g, instead of ffi_type_sint you should use
- libffi.types.sint.
-
- However, you should not rely on a perfect correspondence: in particular,
- the exact meaning of ffi_type_{slong,ulong} changes a lot between libffi
- versions, so types.slong could be different than ffi_type_slong.
- """
-
- @classmethod
- def _import(cls):
- prefix = 'ffi_type_'
- for key, value in clibffi.__dict__.iteritems():
- if key.startswith(prefix):
- name = key[len(prefix):]
- setattr(cls, name, value)
- cls.slong = clibffi.cast_type_to_ffitype(rffi.LONG)
- cls.ulong = clibffi.cast_type_to_ffitype(rffi.ULONG)
- cls.slonglong = clibffi.cast_type_to_ffitype(rffi.LONGLONG)
- cls.ulonglong = clibffi.cast_type_to_ffitype(rffi.ULONGLONG)
- cls.signed = clibffi.cast_type_to_ffitype(rffi.SIGNED)
- cls.wchar_t = clibffi.cast_type_to_ffitype(lltype.UniChar)
- del cls._import
-
- @staticmethod
- @jit.elidable
- def getkind(ffi_type):
- """Returns 'v' for void, 'f' for float, 'i' for signed integer,
- and 'u' for unsigned integer.
- """
- if ffi_type is types.void: return 'v'
- elif ffi_type is types.double: return 'f'
- elif ffi_type is types.float: return 's'
- elif ffi_type is types.pointer: return 'u'
- #
- elif ffi_type is types.schar: return 'i'
- elif ffi_type is types.uchar: return 'u'
- elif ffi_type is types.sshort: return 'i'
- elif ffi_type is types.ushort: return 'u'
- elif ffi_type is types.sint: return 'i'
- elif ffi_type is types.uint: return 'u'
- elif ffi_type is types.slong: return 'i'
- elif ffi_type is types.ulong: return 'u'
- #
- elif ffi_type is types.sint8: return 'i'
- elif ffi_type is types.uint8: return 'u'
- elif ffi_type is types.sint16: return 'i'
- elif ffi_type is types.uint16: return 'u'
- elif ffi_type is types.sint32: return 'i'
- elif ffi_type is types.uint32: return 'u'
- ## (note that on 64-bit platforms, types.sint64 is types.slong and the
- ## case is caught above)
- elif ffi_type is types.sint64: return 'I'
- elif ffi_type is types.uint64: return 'U'
- #
- elif types.is_struct(ffi_type): return 'S'
- raise KeyError
-
- @staticmethod
- @jit.elidable
- def is_struct(ffi_type):
- return intmask(ffi_type.c_type) == FFI_TYPE_STRUCT
-
-types._import()
-
-# this was '_fits_into_long', which is not adequate, because long is
-# not necessary the type where we compute with. Actually meant is
-# the type 'Signed'.
-
- at specialize.arg(0)
-def _fits_into_signed(TYPE):
- if isinstance(TYPE, lltype.Ptr):
- return True # pointers always fits into Signeds
- if not isinstance(TYPE, lltype.Primitive):
- return False
- if TYPE is lltype.Void or TYPE is rffi.FLOAT or TYPE is rffi.DOUBLE:
- return False
- sz = rffi.sizeof(TYPE)
- return sz <= rffi.sizeof(rffi.SIGNED)
-
-
-# ======================================================================
-
-IS_32_BIT = (r_uint.BITS == 32)
-
- at specialize.memo()
-def _check_type(TYPE):
- if isinstance(TYPE, lltype.Ptr):
- if TYPE.TO._gckind != 'raw':
- raise TypeError, "Can only push raw values to C, not 'gc'"
- # XXX probably we should recursively check for struct fields here,
- # lets just ignore that for now
- if isinstance(TYPE.TO, lltype.Array) and 'nolength' not in TYPE.TO._hints:
- raise TypeError, "Can only push to C arrays without length info"
-
-
-class ArgChain(object):
- first = None
- last = None
- numargs = 0
-
- @specialize.argtype(1)
- def arg(self, val):
- TYPE = lltype.typeOf(val)
- _check_type(TYPE)
- if _fits_into_signed(TYPE):
- cls = IntArg
- val = rffi.cast(rffi.SIGNED, val)
- elif TYPE is rffi.DOUBLE:
- cls = FloatArg
- elif TYPE is rffi.LONGLONG or TYPE is rffi.ULONGLONG:
- cls = LongLongArg
- val = rffi.cast(rffi.LONGLONG, val)
- elif TYPE is rffi.FLOAT:
- cls = SingleFloatArg
- else:
- raise TypeError, 'Unsupported argument type: %s' % TYPE
- self._append(cls(val))
- return self
-
- def arg_raw(self, val):
- self._append(RawArg(val))
-
- def _append(self, arg):
- if self.first is None:
- self.first = self.last = arg
- else:
- self.last.next = arg
- self.last = arg
- self.numargs += 1
-
-
-class AbstractArg(object):
- next = None
-
-class IntArg(AbstractArg):
- """ An argument holding an integer
- """
-
- def __init__(self, intval):
- self.intval = intval
-
- def push(self, func, ll_args, i):
- func._push_int(self.intval, ll_args, i)
-
-
-class FloatArg(AbstractArg):
- """ An argument holding a python float (i.e. a C double)
- """
-
- def __init__(self, floatval):
- self.floatval = floatval
-
- def push(self, func, ll_args, i):
- func._push_float(self.floatval, ll_args, i)
-
-class RawArg(AbstractArg):
- """ An argument holding a raw pointer to put inside ll_args
- """
-
- def __init__(self, ptrval):
- self.ptrval = ptrval
-
- def push(self, func, ll_args, i):
- func._push_raw(self.ptrval, ll_args, i)
-
-class SingleFloatArg(AbstractArg):
- """ An argument representing a C float
- """
-
- def __init__(self, singlefloatval):
- self.singlefloatval = singlefloatval
-
- def push(self, func, ll_args, i):
- func._push_singlefloat(self.singlefloatval, ll_args, i)
-
-
-class LongLongArg(AbstractArg):
- """ An argument representing a C long long
- """
-
- def __init__(self, longlongval):
- self.longlongval = longlongval
-
- def push(self, func, ll_args, i):
- func._push_longlong(self.longlongval, ll_args, i)
-
-
-# ======================================================================
-
-
-class Func(AbstractFuncPtr):
-
- _immutable_fields_ = ['funcsym']
- argtypes = []
- restype = clibffi.FFI_TYPE_NULL
- flags = 0
- funcsym = lltype.nullptr(rffi.VOIDP.TO)
-
- def __init__(self, name, argtypes, restype, funcsym, flags=FUNCFLAG_CDECL,
- keepalive=None):
- AbstractFuncPtr.__init__(self, name, argtypes, restype, flags)
- self.keepalive = keepalive
- self.funcsym = funcsym
-
- # ========================================================================
- # PUBLIC INTERFACE
- # ========================================================================
-
- @jit.unroll_safe
- @specialize.arg(2, 3)
- def call(self, argchain, RESULT, is_struct=False):
- # WARNING! This code is written carefully in a way that the JIT
- # optimizer will see a sequence of calls like the following:
- #
- # libffi_prepare_call
- # libffi_push_arg
- # libffi_push_arg
- # ...
- # libffi_call
- #
- # 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 argchain is completely virtual.
- self = jit.promote(self)
- if argchain.numargs != len(self.argtypes):
- raise TypeError, 'Wrong number of arguments: %d expected, got %d' %\
- (len(self.argtypes), argchain.numargs)
- ll_args = self._prepare()
- i = 0
- arg = argchain.first
- while arg:
- arg.push(self, ll_args, i)
- i += 1
- arg = arg.next
- #
- if is_struct:
- assert types.is_struct(self.restype)
- res = self._do_call_raw(self.funcsym, ll_args)
- elif _fits_into_signed(RESULT):
- assert not types.is_struct(self.restype)
- res = self._do_call_int(self.funcsym, ll_args)
- elif RESULT is rffi.DOUBLE:
- return self._do_call_float(self.funcsym, ll_args)
- elif RESULT is rffi.FLOAT:
- return self._do_call_singlefloat(self.funcsym, ll_args)
- elif RESULT is rffi.LONGLONG or RESULT is rffi.ULONGLONG:
- assert IS_32_BIT
- res = self._do_call_longlong(self.funcsym, ll_args)
- elif RESULT is lltype.Void:
- return self._do_call_void(self.funcsym, ll_args)
- else:
- raise TypeError, 'Unsupported result type: %s' % RESULT
- #
- return rffi.cast(RESULT, res)
-
- # END OF THE PUBLIC INTERFACE
- # ------------------------------------------------------------------------
-
- # JIT friendly interface
- # the following methods are supposed to be seen opaquely by the optimizer
-
- @jit.oopspec('libffi_prepare_call(self)')
- def _prepare(self):
- ll_args = lltype.malloc(rffi.VOIDPP.TO, len(self.argtypes), flavor='raw')
- return ll_args
-
-
- # _push_* and _do_call_* in theory could be automatically specialize()d by
- # the annotator. However, specialization doesn't work well with oopspec,
- # so we specialize them by hand
-
- @jit.oopspec('libffi_push_int(self, value, ll_args, i)')
- @enforceargs( None, int, None, int) # fix the annotation for tests
- def _push_int(self, value, ll_args, i):
- self._push_arg(value, ll_args, i)
-
- @jit.dont_look_inside
- def _push_raw(self, value, ll_args, i):
- ll_args[i] = value
-
- @jit.oopspec('libffi_push_float(self, value, ll_args, i)')
- @enforceargs( None, float, None, int) # fix the annotation for tests
- def _push_float(self, value, ll_args, i):
- self._push_arg(value, ll_args, i)
-
- @jit.oopspec('libffi_push_singlefloat(self, value, ll_args, i)')
- @enforceargs(None, r_singlefloat, None, int) # fix the annotation for tests
- def _push_singlefloat(self, value, ll_args, i):
- self._push_arg(value, ll_args, i)
-
- @jit.oopspec('libffi_push_longlong(self, value, ll_args, i)')
- @enforceargs(None, r_longlong, None, int) # fix the annotation for tests
- def _push_longlong(self, value, ll_args, i):
- self._push_arg(value, ll_args, i)
-
- @jit.oopspec('libffi_call_int(self, funcsym, ll_args)')
- def _do_call_int(self, funcsym, ll_args):
- return self._do_call(funcsym, ll_args, rffi.SIGNED)
-
- @jit.oopspec('libffi_call_float(self, funcsym, ll_args)')
- def _do_call_float(self, funcsym, ll_args):
- return self._do_call(funcsym, ll_args, rffi.DOUBLE)
-
- @jit.oopspec('libffi_call_singlefloat(self, funcsym, ll_args)')
- def _do_call_singlefloat(self, funcsym, ll_args):
- return self._do_call(funcsym, ll_args, rffi.FLOAT)
-
- @jit.dont_look_inside
- def _do_call_raw(self, funcsym, ll_args):
- # same as _do_call_int, but marked as jit.dont_look_inside
- return self._do_call(funcsym, ll_args, rffi.SIGNED)
-
- @jit.oopspec('libffi_call_longlong(self, funcsym, ll_args)')
- def _do_call_longlong(self, funcsym, ll_args):
- return self._do_call(funcsym, ll_args, rffi.LONGLONG)
-
- @jit.oopspec('libffi_call_void(self, funcsym, ll_args)')
- def _do_call_void(self, funcsym, ll_args):
- return self._do_call(funcsym, ll_args, lltype.Void)
-
- # ------------------------------------------------------------------------
- # private methods
-
- @specialize.argtype(1)
- def _push_arg(self, value, ll_args, i):
- # XXX: check the type is not translated?
- argtype = self.argtypes[i]
- c_size = intmask(argtype.c_size)
- ll_buf = lltype.malloc(rffi.CCHARP.TO, c_size, flavor='raw')
- push_arg_as_ffiptr(argtype, value, ll_buf)
- ll_args[i] = ll_buf
-
- @specialize.arg(3)
- def _do_call(self, funcsym, ll_args, RESULT):
- # XXX: check len(args)?
- ll_result = lltype.nullptr(rffi.CCHARP.TO)
- if self.restype != types.void:
- ll_result = lltype.malloc(rffi.CCHARP.TO,
- intmask(self.restype.c_size),
- flavor='raw')
- ffires = c_ffi_call(self.ll_cif,
- self.funcsym,
- rffi.cast(rffi.VOIDP, ll_result),
- rffi.cast(rffi.VOIDPP, ll_args))
- if RESULT is not lltype.Void:
- TP = lltype.Ptr(rffi.CArray(RESULT))
- buf = rffi.cast(TP, ll_result)
- if types.is_struct(self.restype):
- assert RESULT == rffi.SIGNED
- # for structs, we directly return the buffer and transfer the
- # ownership
- res = rffi.cast(RESULT, buf)
- else:
- res = buf[0]
- else:
- res = None
- self._free_buffers(ll_result, ll_args)
- clibffi.check_fficall_result(ffires, self.flags)
- return res
-
- def _free_buffers(self, ll_result, ll_args):
- if ll_result:
- self._free_buffer_maybe(rffi.cast(rffi.VOIDP, ll_result), self.restype)
- for i in range(len(self.argtypes)):
- argtype = self.argtypes[i]
- self._free_buffer_maybe(ll_args[i], argtype)
- lltype.free(ll_args, flavor='raw')
-
- def _free_buffer_maybe(self, buf, ffitype):
- # if it's a struct, the buffer is not freed and the ownership is
- # already of the caller (in case of ll_args buffers) or transferred to
- # it (in case of ll_result buffer)
- if not types.is_struct(ffitype):
- lltype.free(buf, flavor='raw')
-
-
-# ======================================================================
-
-
-# XXX: it partially duplicate the code in clibffi.py
-class CDLL(object):
- def __init__(self, libname, mode=-1):
- """Load the library, or raises DLOpenError."""
- self.lib = rffi.cast(DLLHANDLE, 0)
- with rffi.scoped_str2charp(libname) as ll_libname:
- self.lib = dlopen(ll_libname, mode)
-
- def __del__(self):
- if self.lib:
- dlclose(self.lib)
- self.lib = rffi.cast(DLLHANDLE, 0)
-
- def getpointer(self, name, argtypes, restype, flags=FUNCFLAG_CDECL):
- return Func(name, argtypes, restype, dlsym(self.lib, name),
- flags=flags, keepalive=self)
-
- def getpointer_by_ordinal(self, name, argtypes, restype,
- flags=FUNCFLAG_CDECL):
- return Func('by_ordinal', argtypes, restype,
- dlsym_byordinal(self.lib, name),
- flags=flags, keepalive=self)
- def getaddressindll(self, name):
- return dlsym(self.lib, name)
-
-if os.name == 'nt':
- class WinDLL(CDLL):
- def getpointer(self, name, argtypes, restype, flags=FUNCFLAG_STDCALL):
- return Func(name, argtypes, restype, dlsym(self.lib, name),
- flags=flags, keepalive=self)
- def getpointer_by_ordinal(self, name, argtypes, restype,
- flags=FUNCFLAG_STDCALL):
- return Func(name, argtypes, restype, dlsym_byordinal(self.lib, name),
- flags=flags, keepalive=self)
diff --git a/pypy/rlib/test/test_libffi.py b/pypy/rlib/test/test_libffi.py
deleted file mode 100644
--- a/pypy/rlib/test/test_libffi.py
+++ /dev/null
@@ -1,610 +0,0 @@
-import os
-
-import py
-
-from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.test.test_clibffi import BaseFfiTest, make_struct_ffitype_e
-from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.rpython.lltypesystem.ll2ctypes import ALLOCATED
-from pypy.rpython.llinterp import LLException
-from pypy.rlib.libffi import (CDLL, ArgChain, types,
- IS_32_BIT, array_getitem, array_setitem)
-from pypy.rlib.libffi import (struct_getfield_int, struct_setfield_int,
- struct_getfield_longlong, struct_setfield_longlong,
- struct_getfield_float, struct_setfield_float,
- struct_getfield_singlefloat, struct_setfield_singlefloat)
-
-class TestLibffiMisc(BaseFfiTest):
-
- CDLL = CDLL
-
- def test_argchain(self):
- chain = ArgChain()
- assert chain.numargs == 0
- chain2 = chain.arg(42)
- assert chain2 is chain
- assert chain.numargs == 1
- intarg = chain.first
- assert chain.last is intarg
- assert intarg.intval == 42
- chain.arg(123.45)
- assert chain.numargs == 2
- assert chain.first is intarg
- assert intarg.next is chain.last
- floatarg = intarg.next
- assert floatarg.floatval == 123.45
-
- def test_wrong_args(self):
- # so far the test passes but for the wrong reason :-), i.e. because
- # .arg() only supports integers and floats
- chain = ArgChain()
- x = lltype.malloc(lltype.GcStruct('xxx'))
- y = lltype.malloc(lltype.GcArray(rffi.SIGNED), 3)
- z = lltype.malloc(lltype.Array(rffi.SIGNED), 4, flavor='raw')
- py.test.raises(TypeError, "chain.arg(x)")
- py.test.raises(TypeError, "chain.arg(y)")
- py.test.raises(TypeError, "chain.arg(z)")
- lltype.free(z, flavor='raw')
-
- def test_library_open(self):
- lib = self.get_libc()
- del lib
- assert not ALLOCATED
-
- def test_library_get_func(self):
- lib = self.get_libc()
- ptr = lib.getpointer('fopen', [], types.void)
- py.test.raises(KeyError, lib.getpointer, 'xxxxxxxxxxxxxxx', [], types.void)
- del ptr
- del lib
- assert not ALLOCATED
-
- def test_struct_fields(self):
- longsize = 4 if IS_32_BIT else 8
- POINT = lltype.Struct('POINT',
- ('x', rffi.LONG),
- ('y', rffi.SHORT),
- ('z', rffi.VOIDP),
- )
- y_ofs = longsize
- z_ofs = longsize*2
- p = lltype.malloc(POINT, flavor='raw')
- p.x = 42
- p.y = rffi.cast(rffi.SHORT, -1)
- p.z = rffi.cast(rffi.VOIDP, 0x1234)
- addr = rffi.cast(rffi.VOIDP, p)
- assert struct_getfield_int(types.slong, addr, 0) == 42
- assert struct_getfield_int(types.sshort, addr, y_ofs) == -1
- assert struct_getfield_int(types.pointer, addr, z_ofs) == 0x1234
- #
- struct_setfield_int(types.slong, addr, 0, 43)
- struct_setfield_int(types.sshort, addr, y_ofs, 0x1234FFFE) # 0x1234 is masked out
- struct_setfield_int(types.pointer, addr, z_ofs, 0x4321)
- assert p.x == 43
- assert p.y == -2
- assert rffi.cast(rffi.LONG, p.z) == 0x4321
- #
- lltype.free(p, flavor='raw')
-
- 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")
-
-
- def test_struct_fields_longlong(self):
- POINT = lltype.Struct('POINT',
- ('x', rffi.LONGLONG),
- ('y', rffi.ULONGLONG)
- )
- y_ofs = 8
- p = lltype.malloc(POINT, flavor='raw')
- p.x = r_longlong(123)
- p.y = r_ulonglong(456)
- addr = rffi.cast(rffi.VOIDP, p)
- assert struct_getfield_longlong(types.slonglong, addr, 0) == 123
- assert struct_getfield_longlong(types.ulonglong, addr, y_ofs) == 456
- #
- v = rffi.cast(lltype.SignedLongLong, r_ulonglong(9223372036854775808))
- struct_setfield_longlong(types.slonglong, addr, 0, v)
- struct_setfield_longlong(types.ulonglong, addr, y_ofs, r_longlong(-1))
- assert p.x == -9223372036854775808
- assert rffi.cast(lltype.UnsignedLongLong, p.y) == 18446744073709551615
- #
- lltype.free(p, flavor='raw')
-
- def test_struct_fields_float(self):
- POINT = lltype.Struct('POINT',
- ('x', rffi.DOUBLE),
- ('y', rffi.DOUBLE)
- )
- y_ofs = 8
- p = lltype.malloc(POINT, flavor='raw')
- p.x = 123.4
- p.y = 567.8
- addr = rffi.cast(rffi.VOIDP, p)
- assert struct_getfield_float(types.double, addr, 0) == 123.4
- assert struct_getfield_float(types.double, addr, y_ofs) == 567.8
- #
- struct_setfield_float(types.double, addr, 0, 321.0)
- struct_setfield_float(types.double, addr, y_ofs, 876.5)
- assert p.x == 321.0
- assert p.y == 876.5
- #
- lltype.free(p, flavor='raw')
-
- def test_struct_fields_singlefloat(self):
- POINT = lltype.Struct('POINT',
- ('x', rffi.FLOAT),
- ('y', rffi.FLOAT)
- )
- y_ofs = 4
- p = lltype.malloc(POINT, flavor='raw')
- p.x = r_singlefloat(123.4)
- p.y = r_singlefloat(567.8)
- addr = rffi.cast(rffi.VOIDP, p)
- assert struct_getfield_singlefloat(types.double, addr, 0) == r_singlefloat(123.4)
- assert struct_getfield_singlefloat(types.double, addr, y_ofs) == r_singlefloat(567.8)
- #
- struct_setfield_singlefloat(types.double, addr, 0, r_singlefloat(321.0))
- struct_setfield_singlefloat(types.double, addr, y_ofs, r_singlefloat(876.5))
- assert p.x == r_singlefloat(321.0)
- assert p.y == r_singlefloat(876.5)
- #
- lltype.free(p, flavor='raw')
-
- def test_windll(self):
- if os.name != 'nt':
- skip('Run only on windows')
- from pypy.rlib.libffi import WinDLL
- dll = WinDLL('Kernel32.dll')
- sleep = dll.getpointer('Sleep',[types.uint], types.void)
- chain = ArgChain()
- chain.arg(10)
- sleep.call(chain, lltype.Void, is_struct=False)
-
-class TestLibffiCall(BaseFfiTest):
- """
- Test various kind of calls through libffi.
-
- The peculiarity of these tests is that they are run both directly (going
- really through libffi) and by jit/metainterp/test/test_fficall.py, which
- tests the call when JITted.
-
- If you need to test a behaviour than it's not affected by JITing (e.g.,
- typechecking), you should put your test in TestLibffiMisc.
- """
-
- CDLL = CDLL
-
- @classmethod
- def setup_class(cls):
- from pypy.tool.udir import udir
- from pypy.translator.tool.cbuild import ExternalCompilationInfo
- from pypy.translator.tool.cbuild import STANDARD_DEFINES
- from pypy.translator.platform import platform
-
- BaseFfiTest.setup_class()
- # prepare C code as an example, so we can load it and call
- # it via rlib.libffi
- c_file = udir.ensure("test_libffi", dir=1).join("foolib.c")
- # automatically collect the C source from the docstrings of the tests
- snippets = []
- exports = []
- for name in dir(cls):
- if name.startswith('test_'):
- meth = getattr(cls, name)
- # the heuristic to determine it it's really C code could be
- # improved: so far we just check that there is a '{' :-)
- if meth.__doc__ is not None and '{' in meth.__doc__:
- snippets.append(meth.__doc__)
- import re
- for match in re.finditer(" ([A-Za-z_]+)\(", meth.__doc__):
- exports.append(match.group(1))
- #
- c_file.write(STANDARD_DEFINES + str(py.code.Source('\n'.join(snippets))))
- eci = ExternalCompilationInfo(export_symbols=exports)
- cls.libfoo_name = str(platform.compile([c_file], eci, 'x',
- standalone=False))
- cls.dll = cls.CDLL(cls.libfoo_name)
-
- def teardown_class(cls):
- if cls.dll:
- cls.dll.__del__()
- # Why doesn't this call cls.dll.__del__() ?
- #del cls.dll
-
- def get_libfoo(self):
- return self.dll
-
- def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
- """
- Call the specified function after constructing and ArgChain with the
- arguments in ``args``.
-
- The function is specified with ``funcspec``, which is a tuple of the
- form (lib, name, argtypes, restype).
-
- 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)
- chain = ArgChain()
- for arg in args:
- if isinstance(arg, tuple):
- methname, arg = arg
- meth = getattr(chain, methname)
- meth(arg)
- else:
- chain.arg(arg)
- return func.call(chain, RESULT, is_struct=is_struct)
-
- # ------------------------------------------------------------------------
-
- def test_very_simple(self):
- """
- int diff_xy(int x, Signed y)
- {
- return x - y;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'diff_xy', [types.sint, types.signed], types.sint)
- res = self.call(func, [50, 8], lltype.Signed)
- assert res == 42
-
- def test_simple(self):
- """
- int sum_xy(int x, double y)
- {
- return (x + (int)y);
- }
- """
- 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
-
- def test_float_result(self):
- libm = self.get_libm()
- func = (libm, 'pow', [types.double, types.double], types.double)
- res = self.call(func, [2.0, 3.0], rffi.DOUBLE, jitif=["floats"])
- assert res == 8.0
-
- def test_cast_result(self):
- """
- unsigned char cast_to_uchar_and_ovf(int x)
- {
- return 200+(unsigned char)x;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'cast_to_uchar_and_ovf', [types.sint], types.uchar)
- res = self.call(func, [0], rffi.UCHAR)
- assert res == 200
-
- def test_cast_argument(self):
- """
- int many_args(char a, int b)
- {
- return a+b;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'many_args', [types.uchar, types.sint], types.sint)
- res = self.call(func, [chr(20), 22], rffi.SIGNED)
- 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)
- {
- return x+y;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'sum_xy_us', [types.ushort, types.ushort], types.ushort)
- res = self.call(func, [32000, 8000], rffi.USHORT)
- assert res == 40000
-
-
- def test_pointer_as_argument(self):
- """#include <stdlib.h>
- Signed inc(Signed* x)
- {
- Signed oldval;
- if (x == NULL)
- return -1;
- oldval = *x;
- *x = oldval+1;
- return oldval;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'inc', [types.pointer], types.signed)
- null = lltype.nullptr(rffi.SIGNEDP.TO)
- res = self.call(func, [null], rffi.SIGNED)
- assert res == -1
- #
- ptr_result = lltype.malloc(rffi.SIGNEDP.TO, 1, flavor='raw')
- ptr_result[0] = 41
- res = self.call(func, [ptr_result], rffi.SIGNED)
- if self.__class__ is TestLibffiCall:
- # the function was called only once
- assert res == 41
- assert ptr_result[0] == 42
- lltype.free(ptr_result, flavor='raw')
- # the test does not make sense when run with the JIT through
- # meta_interp, because the __del__ are not properly called (hence
- # we "leak" memory)
- del libfoo
- assert not ALLOCATED
- else:
- # the function as been called 9 times
- assert res == 50
- assert ptr_result[0] == 51
- lltype.free(ptr_result, flavor='raw')
-
- def test_return_pointer(self):
- """
- struct pair {
- Signed a;
- Signed b;
- };
-
- struct pair my_static_pair = {10, 20};
-
- Signed* get_pointer_to_b()
- {
- return &my_static_pair.b;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'get_pointer_to_b', [], types.pointer)
- res = self.call(func, [], rffi.SIGNEDP)
- assert res[0] == 20
-
- def test_void_result(self):
- """
- int dummy;
- void set_dummy(int val) { dummy = val; }
- int get_dummy() { return dummy; }
- """
- libfoo = self.get_libfoo()
- set_dummy = (libfoo, 'set_dummy', [types.sint], types.void)
- get_dummy = (libfoo, 'get_dummy', [], types.sint)
- #
- initval = self.call(get_dummy, [], rffi.SIGNED)
- #
- res = self.call(set_dummy, [initval+1], lltype.Void)
- assert res is None
- #
- res = self.call(get_dummy, [], rffi.SIGNED)
- assert res == initval+1
-
- def test_single_float_args(self):
- """
- float sum_xy_float(float x, float y)
- {
- return x+y;
- }
- """
- from ctypes import c_float # this is used only to compute the expected result
- libfoo = self.get_libfoo()
- func = (libfoo, 'sum_xy_float', [types.float, types.float], types.float)
- x = r_singlefloat(12.34)
- y = r_singlefloat(56.78)
- res = self.call(func, [x, y], rffi.FLOAT, jitif=["singlefloats"])
- expected = c_float(c_float(12.34).value + c_float(56.78).value).value
- assert float(res) == expected
-
- def test_slonglong_args(self):
- """
- long long sum_xy_longlong(long long x, long long y)
- {
- return x+y;
- }
- """
- maxint32 = 2147483647 # we cannot really go above maxint on 64 bits
- # (and we would not test anything, as there long
- # is the same as long long)
- libfoo = self.get_libfoo()
- func = (libfoo, 'sum_xy_longlong', [types.slonglong, types.slonglong],
- types.slonglong)
- if IS_32_BIT:
- x = r_longlong(maxint32+1)
- y = r_longlong(maxint32+2)
- else:
- x = maxint32+1
- y = maxint32+2
- res = self.call(func, [x, y], rffi.LONGLONG, jitif=["longlong"])
- expected = maxint32*2 + 3
- assert res == expected
-
- def test_ulonglong_args(self):
- """
- unsigned long long sum_xy_ulonglong(unsigned long long x,
- unsigned long long y)
- {
- return x+y;
- }
- """
- maxint64 = 9223372036854775807 # maxint64+1 does not fit into a
- # longlong, but it does into a
- # ulonglong
- libfoo = self.get_libfoo()
- func = (libfoo, 'sum_xy_ulonglong', [types.ulonglong, types.ulonglong],
- types.ulonglong)
- x = r_ulonglong(maxint64+1)
- y = r_ulonglong(2)
- res = self.call(func, [x, y], rffi.ULONGLONG, jitif=["longlong"])
- expected = maxint64 + 3
- assert res == expected
-
- def test_wrong_number_of_arguments(self):
- from pypy.rpython.llinterp import LLException
- libfoo = self.get_libfoo()
- func = (libfoo, 'sum_xy', [types.sint, types.double], types.sint)
-
- glob = globals()
- loc = locals()
- def my_raises(s):
- try:
- exec s in glob, loc
- except TypeError:
- pass
- except LLException, e:
- if str(e) != "<LLException 'TypeError'>":
- raise
- else:
- assert False, 'Did not raise'
-
- my_raises("self.call(func, [38], rffi.SIGNED)") # one less
- my_raises("self.call(func, [38, 12.3, 42], rffi.SIGNED)") # one more
-
-
- def test_byval_argument(self):
- """
- struct Point {
- Signed x;
- Signed y;
- };
-
- Signed sum_point(struct Point p) {
- return p.x + p.y;
- }
- """
- libfoo = CDLL(self.libfoo_name)
- ffi_point_struct = make_struct_ffitype_e(0, 0, [types.signed, types.signed])
- ffi_point = ffi_point_struct.ffistruct
- sum_point = (libfoo, 'sum_point', [ffi_point], types.signed)
- #
- ARRAY = rffi.CArray(rffi.SIGNED)
- buf = lltype.malloc(ARRAY, 2, flavor='raw')
- buf[0] = 30
- buf[1] = 12
- adr = rffi.cast(rffi.VOIDP, buf)
- res = self.call(sum_point, [('arg_raw', adr)], rffi.SIGNED,
- jitif=["byval"])
- assert res == 42
- # check that we still have the ownership on the buffer
- assert buf[0] == 30
- assert buf[1] == 12
- lltype.free(buf, flavor='raw')
- lltype.free(ffi_point_struct, flavor='raw')
-
- def test_byval_result(self):
- """
- struct Point make_point(Signed x, Signed y) {
- struct Point p;
- p.x = x;
- p.y = y;
- return p;
- }
- """
- libfoo = CDLL(self.libfoo_name)
- ffi_point_struct = make_struct_ffitype_e(0, 0, [types.signed, types.signed])
- ffi_point = ffi_point_struct.ffistruct
-
- libfoo = CDLL(self.libfoo_name)
- make_point = (libfoo, 'make_point', [types.signed, types.signed], ffi_point)
- #
- PTR = lltype.Ptr(rffi.CArray(rffi.SIGNED))
- p = self.call(make_point, [12, 34], PTR, is_struct=True,
- jitif=["byval"])
- assert p[0] == 12
- assert p[1] == 34
- lltype.free(p, flavor='raw')
- lltype.free(ffi_point_struct, flavor='raw')
-
- if os.name == 'nt':
- def test_stdcall_simple(self):
- """
- int __stdcall std_diff_xy(int x, Signed y)
- {
- return x - y;
- }
- """
- libfoo = self.get_libfoo()
- func = (libfoo, 'std_diff_xy', [types.sint, types.signed], types.sint)
- try:
- self.call(func, [50, 8], lltype.Signed)
- except ValueError, e:
- assert e.message == 'Procedure called with not enough ' + \
- 'arguments (8 bytes missing) or wrong calling convention'
- except LLException, e:
- #jitted code raises this
- assert str(e) == "<LLException 'StackCheckError'>"
- else:
- assert 0, 'wrong calling convention should have raised'
-
- def test_by_ordinal(self):
- """
- int AAA_first_ordinal_function()
- {
- return 42;
- }
- """
- libfoo = self.get_libfoo()
- f_by_name = libfoo.getpointer('AAA_first_ordinal_function' ,[],
- types.uint)
- f_by_ordinal = libfoo.getpointer_by_ordinal(1 ,[], types.uint)
- print dir(f_by_name)
- assert f_by_name.funcsym == f_by_ordinal.funcsym
-
- def test_by_ordinal2(self):
- """
- int __stdcall BBB_second_ordinal_function()
- {
- return 24;
- }
- """
- from pypy.rlib.libffi import WinDLL
- dll = WinDLL(self.libfoo_name)
- f_by_name = dll.getpointer('BBB_second_ordinal_function' ,[],
- types.uint)
- f_by_ordinal = dll.getpointer_by_ordinal(2 ,[], types.uint)
- print dir(f_by_name)
- assert f_by_name.funcsym == f_by_ordinal.funcsym
- chain = ArgChain()
- assert 24 == f_by_ordinal.call(chain, lltype.Signed, is_struct=False)
-
-
-
diff --git a/pypy/rpython/lltypesystem/llmemory.py b/pypy/rpython/lltypesystem/llmemory.py
--- a/pypy/rpython/lltypesystem/llmemory.py
+++ b/pypy/rpython/lltypesystem/llmemory.py
@@ -541,8 +541,12 @@
def __nonzero__(self):
return bool(self.adr)
def __add__(self, ofs):
+ if (isinstance(ofs, int) and
+ getattr(self.adr.ptr._TYPE.TO, 'OF', None) == lltype.Char):
+ return AddressAsInt(self.adr + ItemOffset(lltype.Char, ofs))
if isinstance(ofs, FieldOffset) and ofs.TYPE is self.adr.ptr._TYPE.TO:
- return AddressAsInt(cast_ptr_to_adr(self.adr.ptr.b))
+ fieldadr = getattr(self.adr.ptr, ofs.fieldname)
+ return AddressAsInt(cast_ptr_to_adr(fieldadr))
return NotImplemented
def __repr__(self):
try:
More information about the pypy-commit
mailing list