Author: Remi Meier <remi.me...@gmail.com> Branch: stmgc-c8 Changeset: r82417:3522d0e0b4ac Date: 2016-02-22 23:49 +0100 http://bitbucket.org/pypy/pypy/changeset/3522d0e0b4ac/
Log: Next merge with default diff too long, truncating to 2000 out of 5350 lines diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,4 @@ release/ !pypy/tool/release/ rpython/_cache/ -__pycache__/ +.cache/ diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -28,7 +28,7 @@ DEALINGS IN THE SOFTWARE. -PyPy Copyright holders 2003-2015 +PyPy Copyright holders 2003-2016 ----------------------------------- Except when otherwise stated (look for LICENSE files or information at diff --git a/lib-python/2.7/pickle.py b/lib-python/2.7/pickle.py --- a/lib-python/2.7/pickle.py +++ b/lib-python/2.7/pickle.py @@ -1376,6 +1376,7 @@ def decode_long(data): r"""Decode a long from a two's complement little-endian binary string. + This is overriden on PyPy by a RPython version that has linear complexity. >>> decode_long('') 0L @@ -1402,6 +1403,11 @@ n -= 1L << (nbytes * 8) return n +try: + from __pypy__ import decode_long +except ImportError: + pass + # Shorthands try: diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.4.0 +Version: 1.4.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.4.0" -__version_info__ = (1, 4, 0) +__version__ = "1.4.2" +__version_info__ = (1, 4, 2) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/pypy/doc/embedding.rst b/pypy/doc/embedding.rst --- a/pypy/doc/embedding.rst +++ b/pypy/doc/embedding.rst @@ -130,8 +130,13 @@ More complete example --------------------- -.. note:: This example depends on pypy_execute_source_ptr which is not available - in PyPy <= 2.2.1. +.. note:: Note that we do not make use of ``extern "Python"``, the new + way to do callbacks in CFFI 1.4: this is because these examples use + the ABI mode, not the API mode, and with the ABI mode you still have + to use ``ffi.callback()``. It is work in progress to integrate + ``extern "Python"`` with the idea of embedding (and it is expected + to ultimately lead to a better way to do embedding than the one + described here, and that would work equally well on CPython and PyPy). Typically we need something more to do than simply execute source. The following is a fully fledged example, please consult cffi documentation for details. diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -5,6 +5,8 @@ .. this is a revision shortly after release-4.0.1 .. startrev: 4b5c840d0da2 +Fixed ``_PyLong_FromByteArray()``, which was buggy. + .. branch: numpy-1.10 Fix tests to run cleanly with -A and start to fix micronumpy for upstream numpy @@ -44,6 +46,9 @@ .. branch: fix-setslice-can-resize +Make rlist's ll_listsetslice() able to resize the target list to help +simplify objspace/std/listobject.py. Was issue #2196. + .. branch: anntype2 A somewhat random bunch of changes and fixes following up on branch 'anntype'. Highlights: @@ -77,3 +82,24 @@ .. branch: always-enable-gil Simplify a bit the GIL handling in non-jitted code. Fixes issue #2205. + +.. branch: flowspace-cleanups + +Trivial cleanups in flowspace.operation : fix comment & duplicated method + +.. branch: test-AF_NETLINK + +Add a test for pre-existing AF_NETLINK support. Was part of issue #1942. + +.. branch: small-cleanups-misc + +Trivial misc cleanups: typo, whitespace, obsolete comments + +.. branch: cpyext-slotdefs +.. branch: fix-missing-canraise +.. branch: whatsnew + +.. branch: fix-2211 + +Fix the cryptic exception message when attempting to use extended slicing +in rpython. Was issue #2211. diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -89,6 +89,7 @@ 'set_code_callback' : 'interp_magic.set_code_callback', 'save_module_content_for_future_reload': 'interp_magic.save_module_content_for_future_reload', + 'decode_long' : 'interp_magic.decode_long', } if sys.platform == 'win32': interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp' diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -1,4 +1,4 @@ -from pypy.interpreter.error import OperationError, wrap_oserror +from pypy.interpreter.error import OperationError, oefmt, wrap_oserror from pypy.interpreter.gateway import unwrap_spec from pypy.interpreter.pycode import CodeHookCache from pypy.interpreter.pyframe import PyFrame @@ -158,4 +158,13 @@ if space.is_none(w_callable): cache._code_hook = None else: - cache._code_hook = w_callable \ No newline at end of file + cache._code_hook = w_callable + +@unwrap_spec(string=str, byteorder=str, signed=int) +def decode_long(space, string, byteorder='little', signed=1): + from rpython.rlib.rbigint import rbigint, InvalidEndiannessError + try: + result = rbigint.frombytes(string, byteorder, bool(signed)) + except InvalidEndiannessError: + raise oefmt(space.w_ValueError, "invalid byteorder argument") + return space.newlong_from_rbigint(result) diff --git a/pypy/module/__pypy__/test/test_magic.py b/pypy/module/__pypy__/test/test_magic.py --- a/pypy/module/__pypy__/test/test_magic.py +++ b/pypy/module/__pypy__/test/test_magic.py @@ -30,4 +30,20 @@ """ in d finally: __pypy__.set_code_callback(None) - assert d['f'].__code__ in l \ No newline at end of file + assert d['f'].__code__ in l + + def test_decode_long(self): + from __pypy__ import decode_long + assert decode_long('') == 0 + assert decode_long('\xff\x00') == 255 + assert decode_long('\xff\x7f') == 32767 + assert decode_long('\x00\xff') == -256 + assert decode_long('\x00\x80') == -32768 + assert decode_long('\x80') == -128 + assert decode_long('\x7f') == 127 + assert decode_long('\x55' * 97) == (1 << (97 * 8)) // 3 + assert decode_long('\x00\x80', 'big') == 128 + assert decode_long('\xff\x7f', 'little', False) == 32767 + assert decode_long('\x00\x80', 'little', False) == 32768 + assert decode_long('\x00\x80', 'little', True) == -32768 + raises(ValueError, decode_long, '', 'foo') diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -2,7 +2,7 @@ from pypy.interpreter.mixedmodule import MixedModule from rpython.rlib import rdynload, clibffi -VERSION = "1.4.0" +VERSION = "1.4.2" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/cglob.py b/pypy/module/_cffi_backend/cglob.py --- a/pypy/module/_cffi_backend/cglob.py +++ b/pypy/module/_cffi_backend/cglob.py @@ -3,6 +3,7 @@ from pypy.interpreter.typedef import TypeDef from pypy.module._cffi_backend.cdataobj import W_CData from pypy.module._cffi_backend import newtype +from rpython.rlib import rgil from rpython.rlib.objectmodel import we_are_translated from rpython.rtyper.lltypesystem import lltype, rffi from rpython.translator.tool.cbuild import ExternalCompilationInfo @@ -26,7 +27,9 @@ if not we_are_translated(): FNPTR = rffi.CCallback([], rffi.VOIDP) fetch_addr = rffi.cast(FNPTR, self.fetch_addr) + rgil.release() result = fetch_addr() + rgil.acquire() else: # careful in translated versions: we need to call fetch_addr, # but in a GIL-releasing way. The easiest is to invoke a 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 @@ -423,7 +423,9 @@ exchange_offset += rffi.getintfield(self.atypes[i], 'c_size') # store the exchange data size - cif_descr.exchange_size = exchange_offset + # we also align it to the next multiple of 8, in an attempt to + # work around bugs(?) of libffi (see cffi issue #241) + cif_descr.exchange_size = self.align_arg(exchange_offset) def fb_extra_fields(self, cif_descr): cif_descr.abi = self.fabi diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.4.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.4.2", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): diff --git a/pypy/module/_file/test/test_large_file.py b/pypy/module/_file/test/test_large_file.py --- a/pypy/module/_file/test/test_large_file.py +++ b/pypy/module/_file/test/test_large_file.py @@ -1,4 +1,4 @@ -import py +import py, sys from pypy.module._file.test.test_file import getfile @@ -13,6 +13,12 @@ def setup_method(self, meth): if getattr(meth, 'need_sparse_files', False): from rpython.translator.c.test.test_extfunc import need_sparse_files + if sys.maxsize < 2**32 and not self.runappdirect: + # this fails because it uses ll2ctypes to call the posix + # functions like 'open' and 'lseek', whereas a real compiled + # C program would macro-define them to their longlong versions + py.test.skip("emulation of files can't use " + "larger-than-long offsets") need_sparse_files() def test_large_seek_offsets(self): diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -251,7 +251,7 @@ from pypy.module._socket.interp_socket import addr_as_object if not hasattr(rsocket._c, 'sockaddr_ll'): py.test.skip("posix specific test") - # HACK: To get the correct interface numer of lo, which in most cases is 1, + # HACK: To get the correct interface number of lo, which in most cases is 1, # but can be anything (i.e. 39), we need to call the libc function # if_nametoindex to get the correct index import ctypes @@ -513,7 +513,7 @@ def test_getsetsockopt(self): import _socket as socket import struct - # A socket sould start with reuse == 0 + # A socket should start with reuse == 0 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) reuse = s.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) assert reuse == 0 @@ -627,6 +627,26 @@ self.foo = _socket.socket() +class AppTestNetlink: + def setup_class(cls): + if not hasattr(os, 'getpid'): + py.test.skip("AF_NETLINK needs os.getpid()") + w_ok = space.appexec([], "(): import _socket; " + + "return hasattr(_socket, 'AF_NETLINK')") + if not space.is_true(w_ok): + py.test.skip("no AF_NETLINK on this platform") + cls.space = space + + def test_connect_to_kernel_netlink_routing_socket(self): + import _socket, os + s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, _socket.NETLINK_ROUTE) + assert s.getsockname() == (0L, 0L) + s.bind((0, 0)) + a, b = s.getsockname() + assert a == os.getpid() + assert b == 0 + + class AppTestPacket: def setup_class(cls): if not hasattr(os, 'getuid') or os.getuid() != 0: diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -124,7 +124,7 @@ METH_COEXIST METH_STATIC METH_CLASS METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS -Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE +Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES """.split() for name in constant_names: setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name)) diff --git a/pypy/module/cpyext/longobject.py b/pypy/module/cpyext/longobject.py --- a/pypy/module/cpyext/longobject.py +++ b/pypy/module/cpyext/longobject.py @@ -228,26 +228,11 @@ def _PyLong_FromByteArray(space, bytes, n, little_endian, signed): little_endian = rffi.cast(lltype.Signed, little_endian) signed = rffi.cast(lltype.Signed, signed) - - result = rbigint() - negative = False - - for i in range(0, n): - if little_endian: - c = intmask(bytes[i]) - else: - c = intmask(bytes[n - i - 1]) - if i == 0 and signed and c & 0x80: - negative = True - if negative: - c = c ^ 0xFF - digit = rbigint.fromint(c) - - result = result.lshift(8) - result = result.add(digit) - - if negative: - result = result.neg() - + s = rffi.charpsize2str(rffi.cast(rffi.CCHARP, bytes), + rffi.cast(lltype.Signed, n)) + if little_endian: + byteorder = 'little' + else: + byteorder = 'big' + result = rbigint.frombytes(s, byteorder, signed != 0) return space.newlong_from_rbigint(result) - diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -4,14 +4,14 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( - cpython_api, generic_cpy_call, PyObject, Py_ssize_t) + cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES) from pypy.module.cpyext.typeobjectdefs import ( unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc, getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry, ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc, cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc, readbufferproc) -from pypy.module.cpyext.pyobject import from_ref +from pypy.module.cpyext.pyobject import from_ref, make_ref, Py_DecRef from pypy.module.cpyext.pyerrors import PyErr_Occurred from pypy.module.cpyext.state import State from pypy.interpreter.error import OperationError, oefmt @@ -65,22 +65,24 @@ func_binary = rffi.cast(binaryfunc, func) check_num_args(space, w_args, 1) args_w = space.fixedview(w_args) - - if not space.is_true(space.issubtype(space.type(args_w[0]), - space.type(w_self))): + ref = make_ref(space, w_self) + if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and + not space.is_true(space.issubtype(space.type(args_w[0]), + space.type(w_self)))): return space.w_NotImplemented - + Py_DecRef(space, ref) return generic_cpy_call(space, func_binary, w_self, args_w[0]) def wrap_binaryfunc_r(space, w_self, w_args, func): func_binary = rffi.cast(binaryfunc, func) check_num_args(space, w_args, 1) args_w = space.fixedview(w_args) - - if not space.is_true(space.issubtype(space.type(args_w[0]), - space.type(w_self))): + ref = make_ref(space, w_self) + if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and + not space.is_true(space.issubtype(space.type(args_w[0]), + space.type(w_self)))): return space.w_NotImplemented - + Py_DecRef(space, ref) return generic_cpy_call(space, func_binary, args_w[0], w_self) def wrap_inquirypred(space, w_self, w_args, func): @@ -378,6 +380,17 @@ space.call_function(delattr_fn, w_self, w_name) return 0 api_func = slot_tp_setattro.api_func + elif name == 'tp_getattro': + getattr_fn = w_type.getdictvalue(space, '__getattribute__') + if getattr_fn is None: + return + + @cpython_api([PyObject, PyObject], PyObject, + external=True) + @func_renamer("cpyext_tp_getattro_%s" % (typedef.name,)) + def slot_tp_getattro(space, w_self, w_name): + return space.call_function(getattr_fn, w_self, w_name) + api_func = slot_tp_getattro.api_func else: return diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py --- a/pypy/module/cpyext/test/test_longobject.py +++ b/pypy/module/cpyext/test/test_longobject.py @@ -175,10 +175,26 @@ little_endian, is_signed); """), ]) - assert module.from_bytearray(True, False) == 0x9ABC - assert module.from_bytearray(True, True) == -0x6543 - assert module.from_bytearray(False, False) == 0xBC9A - assert module.from_bytearray(False, True) == -0x4365 + assert module.from_bytearray(True, False) == 0xBC9A + assert module.from_bytearray(True, True) == -0x4366 + assert module.from_bytearray(False, False) == 0x9ABC + assert module.from_bytearray(False, True) == -0x6544 + + def test_frombytearray_2(self): + module = self.import_extension('foo', [ + ("from_bytearray", "METH_VARARGS", + """ + int little_endian, is_signed; + if (!PyArg_ParseTuple(args, "ii", &little_endian, &is_signed)) + return NULL; + return _PyLong_FromByteArray("\x9A\xBC\x41", 3, + little_endian, is_signed); + """), + ]) + assert module.from_bytearray(True, False) == 0x41BC9A + assert module.from_bytearray(True, True) == 0x41BC9A + assert module.from_bytearray(False, False) == 0x9ABC41 + assert module.from_bytearray(False, True) == -0x6543BF def test_fromunicode(self): module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -385,12 +385,64 @@ PyErr_SetString(PyExc_ValueError, "recursive tp_setattro"); return NULL; } + if (!args->ob_type->tp_getattro) + { + PyErr_SetString(PyExc_ValueError, "missing tp_getattro"); + return NULL; + } + if (args->ob_type->tp_getattro == + args->ob_type->tp_base->tp_getattro) + { + PyErr_SetString(PyExc_ValueError, "recursive tp_getattro"); + return NULL; + } Py_RETURN_TRUE; ''' ) ]) assert module.test_type(type(None)) + def test_tp_getattro(self): + module = self.import_extension('foo', [ + ("test_tp_getattro", "METH_VARARGS", + ''' + PyObject *obj = PyTuple_GET_ITEM(args, 0); + PyIntObject *value = PyTuple_GET_ITEM(args, 1); + if (!obj->ob_type->tp_getattro) + { + PyErr_SetString(PyExc_ValueError, "missing tp_getattro"); + return NULL; + } + PyObject *name = PyString_FromString("attr1"); + PyIntObject *attr = obj->ob_type->tp_getattro(obj, name); + if (attr->ob_ival != value->ob_ival) + { + PyErr_SetString(PyExc_ValueError, + "tp_getattro returned wrong value"); + return NULL; + } + Py_DECREF(name); + Py_DECREF(attr); + name = PyString_FromString("attr2"); + attr = obj->ob_type->tp_getattro(obj, name); + if (attr == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) + { + PyErr_Clear(); + } else { + PyErr_SetString(PyExc_ValueError, + "tp_getattro should have raised"); + return NULL; + } + Py_DECREF(name); + Py_RETURN_TRUE; + ''' + ) + ]) + class C: + def __init__(self): + self.attr1 = 123 + assert module.test_tp_getattro(C(), 123) + def test_nb_int(self): module = self.import_extension('foo', [ ("nb_int", "METH_O", @@ -591,45 +643,92 @@ def test_binaryfunc(self): module = self.import_extension('foo', [ - ("new_obj", "METH_NOARGS", + ("newInt", "METH_VARARGS", """ - FooObject *fooObj; + IntLikeObject *intObj; + long intval; - Foo_Type.tp_as_number = &foo_as_number; - foo_as_number.nb_add = foo_nb_add_call; - if (PyType_Ready(&Foo_Type) < 0) return NULL; - fooObj = PyObject_New(FooObject, &Foo_Type); - if (!fooObj) { + if (!PyArg_ParseTuple(args, "l", &intval)) + return NULL; + + IntLike_Type.tp_as_number = &intlike_as_number; + IntLike_Type.tp_flags |= Py_TPFLAGS_CHECKTYPES; + intlike_as_number.nb_add = intlike_nb_add; + if (PyType_Ready(&IntLike_Type) < 0) return NULL; + intObj = PyObject_New(IntLikeObject, &IntLike_Type); + if (!intObj) { return NULL; } - return (PyObject *)fooObj; + intObj->ival = intval; + return (PyObject *)intObj; + """), + ("newIntNoOp", "METH_VARARGS", + """ + IntLikeObjectNoOp *intObjNoOp; + long intval; + + if (!PyArg_ParseTuple(args, "l", &intval)) + return NULL; + + IntLike_Type_NoOp.tp_flags |= Py_TPFLAGS_CHECKTYPES; + if (PyType_Ready(&IntLike_Type_NoOp) < 0) return NULL; + intObjNoOp = PyObject_New(IntLikeObjectNoOp, &IntLike_Type_NoOp); + if (!intObjNoOp) { + return NULL; + } + + intObjNoOp->ival = intval; + return (PyObject *)intObjNoOp; """)], """ typedef struct { PyObject_HEAD - } FooObject; + long ival; + } IntLikeObject; static PyObject * - foo_nb_add_call(PyObject *self, PyObject *other) + intlike_nb_add(PyObject *self, PyObject *other) { - return PyInt_FromLong(42); + long val1 = ((IntLikeObject *)(self))->ival; + if (PyInt_Check(other)) { + long val2 = PyInt_AsLong(other); + return PyInt_FromLong(val1+val2); + } + + long val2 = ((IntLikeObject *)(other))->ival; + return PyInt_FromLong(val1+val2); } - PyTypeObject Foo_Type = { + PyTypeObject IntLike_Type = { PyObject_HEAD_INIT(0) /*ob_size*/ 0, - /*tp_name*/ "Foo", - /*tp_basicsize*/ sizeof(FooObject), + /*tp_name*/ "IntLike", + /*tp_basicsize*/ sizeof(IntLikeObject), }; - static PyNumberMethods foo_as_number; + static PyNumberMethods intlike_as_number; + + typedef struct + { + PyObject_HEAD + long ival; + } IntLikeObjectNoOp; + + PyTypeObject IntLike_Type_NoOp = { + PyObject_HEAD_INIT(0) + /*ob_size*/ 0, + /*tp_name*/ "IntLikeNoOp", + /*tp_basicsize*/ sizeof(IntLikeObjectNoOp), + }; """) - a = module.new_obj() - b = module.new_obj() + a = module.newInt(1) + b = module.newInt(2) c = 3 - assert (a + b) == 42 - raises(TypeError, "b + c") + d = module.newIntNoOp(4) + assert (a + b) == 3 + assert (b + c) == 5 + assert (d + a) == 5 def test_tp_new_in_subclass_of_type(self): skip("BROKEN") diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -582,6 +582,8 @@ pto.c_tp_free = base.c_tp_free if not pto.c_tp_setattro: pto.c_tp_setattro = base.c_tp_setattro + if not pto.c_tp_getattro: + pto.c_tp_getattro = base.c_tp_getattro finally: Py_DecRef(space, base_pyo) @@ -651,6 +653,12 @@ PyObject_GenericSetAttr.api_func.functype, PyObject_GenericSetAttr.api_func.get_wrapper(space)) + if not pto.c_tp_getattro: + from pypy.module.cpyext.object import PyObject_GenericGetAttr + pto.c_tp_getattro = llhelper( + PyObject_GenericGetAttr.api_func.functype, + PyObject_GenericGetAttr.api_func.get_wrapper(space)) + if w_obj.is_cpytype(): Py_DecRef(space, pto.c_tp_dict) w_dict = w_obj.getdict(space) diff --git a/pypy/module/mmap/test/test_mmap.py b/pypy/module/mmap/test/test_mmap.py --- a/pypy/module/mmap/test/test_mmap.py +++ b/pypy/module/mmap/test/test_mmap.py @@ -1,6 +1,6 @@ from __future__ import with_statement from rpython.tool.udir import udir -import os +import os, sys, py class AppTestMMap: spaceconfig = dict(usemodules=('mmap',)) @@ -8,6 +8,15 @@ def setup_class(cls): cls.w_tmpname = cls.space.wrap(str(udir.join('mmap-'))) + def setup_method(self, meth): + if getattr(meth, 'is_large', False): + if sys.maxsize < 2**32 and not self.runappdirect: + # this fails because it uses ll2ctypes to call the posix + # functions like 'open' and 'lseek', whereas a real compiled + # C program would macro-define them to their longlong versions + py.test.skip("emulation of files can't use " + "larger-than-long offsets") + def test_page_size(self): import mmap assert mmap.PAGESIZE > 0 @@ -648,6 +657,7 @@ assert m[0xFFFFFFF] == b'A' finally: m.close() + test_large_offset.is_large = True def test_large_filesize(self): import mmap @@ -665,6 +675,7 @@ assert m.size() == 0x180000000 finally: m.close() + test_large_filesize.is_large = True def test_all(self): # this is a global test, ported from test_mmap.py diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -299,7 +299,7 @@ return build_stat_result(space, st) def lstat(space, w_path): - "Like stat(path), but do no follow symbolic links." + "Like stat(path), but do not follow symbolic links." try: st = dispatch_filename(rposix_stat.lstat)(space, w_path) except OSError, e: diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -93,6 +93,12 @@ def setup_method(self, meth): if getattr(meth, 'need_sparse_files', False): + if sys.maxsize < 2**32 and not self.runappdirect: + # this fails because it uses ll2ctypes to call the posix + # functions like 'open' and 'lseek', whereas a real compiled + # C program would macro-define them to their longlong versions + py.test.skip("emulation of files can't use " + "larger-than-long offsets") need_sparse_files() def test_posix_is_pypy_s(self): diff --git a/pypy/module/pypyjit/test_pypy_c/test_struct.py b/pypy/module/pypyjit/test_pypy_c/test_struct.py --- a/pypy/module/pypyjit/test_pypy_c/test_struct.py +++ b/pypy/module/pypyjit/test_pypy_c/test_struct.py @@ -45,7 +45,7 @@ # the newstr and the strsetitems are because the string is forced, # which is in turn because the optimizer doesn't know how to handle a - # getarrayitem_gc_i on a virtual string. It could be improved, but it + # gc_load_indexed_i on a virtual string. It could be improved, but it # is also true that in real life cases struct.unpack is called on # strings which come from the outside, so it's a minor issue. assert loop.match_by_id("unpack", """ @@ -55,17 +55,17 @@ strsetitem(p88, 1, i14) strsetitem(p88, 2, i17) strsetitem(p88, 3, i20) - i91 = getarrayitem_gc_i(p88, 0, descr=<ArrayS 4>) + i91 = gc_load_indexed_i(p88, 0, 1, _, -4) """) def test_struct_object(self): def main(n): import struct - s = struct.Struct("i") + s = struct.Struct("ii") i = 1 while i < n: - buf = s.pack(i) # ID: pack - x = s.unpack(buf)[0] # ID: unpack + buf = s.pack(-1, i) # ID: pack + x = s.unpack(buf)[1] # ID: unpack i += x / i return i @@ -88,10 +88,15 @@ assert loop.match_by_id('unpack', """ # struct.unpack - p88 = newstr(4) - strsetitem(p88, 0, i11) - strsetitem(p88, 1, i14) - strsetitem(p88, 2, i17) - strsetitem(p88, 3, i20) - i91 = getarrayitem_gc_i(p88, 0, descr=<ArrayS 4>) + p88 = newstr(8) + strsetitem(p88, 0, 255) + strsetitem(p88, 1, 255) + strsetitem(p88, 2, 255) + strsetitem(p88, 3, 255) + strsetitem(p88, 4, i11) + strsetitem(p88, 5, i14) + strsetitem(p88, 6, i17) + strsetitem(p88, 7, i20) + i90 = gc_load_indexed_i(p88, 0, 1, _, -4) + i91 = gc_load_indexed_i(p88, 4, 1, _, -4) """) diff --git a/pypy/module/thread/__init__.py b/pypy/module/thread/__init__.py --- a/pypy/module/thread/__init__.py +++ b/pypy/module/thread/__init__.py @@ -30,7 +30,7 @@ space.threadlocals = stm.STMThreadLocals() else: from pypy.module.thread import gil - space.threadlocals = gil.GILThreadLocals() + space.threadlocals = gil.GILThreadLocals(space) space.threadlocals.initialize(space) if prev_ec is not None: space.threadlocals._set_ec(prev_ec) diff --git a/pypy/module/thread/test/test_gil.py b/pypy/module/thread/test/test_gil.py --- a/pypy/module/thread/test/test_gil.py +++ b/pypy/module/thread/test/test_gil.py @@ -65,7 +65,7 @@ except Exception, e: assert 0 thread.gc_thread_die() - my_gil_threadlocals = gil.GILThreadLocals() + my_gil_threadlocals = gil.GILThreadLocals(space) def f(): state.data = [] state.datalen1 = 0 diff --git a/pypy/module/thread/threadlocals.py b/pypy/module/thread/threadlocals.py --- a/pypy/module/thread/threadlocals.py +++ b/pypy/module/thread/threadlocals.py @@ -1,5 +1,7 @@ -from rpython.rlib import rthread +import weakref +from rpython.rlib import rthread, rshrinklist from rpython.rlib.objectmodel import we_are_translated +from rpython.rlib.rarithmetic import r_ulonglong from pypy.module.thread.error import wrap_thread_error from pypy.interpreter.executioncontext import ExecutionContext @@ -13,15 +15,51 @@ a thread finishes. This works as long as the thread was started by os_thread.bootstrap().""" - def __init__(self): + def __init__(self, space): "NOT_RPYTHON" - self._valuedict = {} # {thread_ident: ExecutionContext()} + # + # This object tracks code that enters and leaves threads. + # There are two APIs. For Python-level threads, we know when + # the thread starts and ends, and we call enter_thread() and + # leave_thread(). In a few other cases, like callbacks, we + # might be running in some never-seen-before thread: in this + # case, the callback logic needs to call try_enter_thread() at + # the start, and if this returns True it needs to call + # leave_thread() at the end. + # + # We implement an optimization for the second case (which only + # works if we translate with a framework GC and with + # rweakref). If try_enter_thread() is called in a + # never-seen-before thread, it still returns False and + # remembers the ExecutionContext with 'self._weaklist'. The + # next time we call try_enter_thread() again in the same + # thread, the ExecutionContext is reused. The optimization is + # not completely invisible to the user: 'thread._local()' + # values will remain. We can argue that it is the correct + # behavior to do that, and the behavior we get if the + # optimization is disabled is buggy (but hard to do better + # then). + # + # 'self._valuedict' is a dict mapping the thread idents to + # ExecutionContexts; it does not list the ExecutionContexts + # which are in 'self._weaklist'. (The latter is more precisely + # a list of AutoFreeECWrapper objects, defined below, which + # each references the ExecutionContext.) + # + self.space = space + self._valuedict = {} self._cleanup_() self.raw_thread_local = rthread.ThreadLocalReference(ExecutionContext, loop_invariant=True) + def can_optimize_with_weaklist(self): + config = self.space.config + return (config.translation.rweakref and + rthread.ThreadLocalReference.automatic_keepalive(config)) + def _cleanup_(self): self._valuedict.clear() + self._weaklist = None self._mainthreadident = 0 def initialize(self, space): @@ -35,19 +73,35 @@ self._set_ec(space.createexecutioncontext()) def try_enter_thread(self, space): - if rthread.get_ident() in self._valuedict: + # common case: the thread-local has already got a value + if self.raw_thread_local.get() is not None: return False - self.enter_thread(space) - return True - def _set_ec(self, ec): + # Else, make and attach a new ExecutionContext + ec = space.createexecutioncontext() + if not self.can_optimize_with_weaklist(): + self._set_ec(ec) + return True + + # If can_optimize_with_weaklist(), then 'rthread' keeps the + # thread-local values alive until the end of the thread. Use + # AutoFreeECWrapper as an object with a __del__; when this + # __del__ is called, it means the thread was really finished. + # In this case we don't want leave_thread() to be called + # explicitly, so we return False. + if self._weaklist is None: + self._weaklist = ListECWrappers() + self._weaklist.append(weakref.ref(AutoFreeECWrapper(ec))) + self._set_ec(ec, register_in_valuedict=False) + return False + + def _set_ec(self, ec, register_in_valuedict=True): ident = rthread.get_ident() if self._mainthreadident == 0 or self._mainthreadident == ident: ec._signals_enabled = 1 # the main thread is enabled self._mainthreadident = ident - self._valuedict[ident] = ec - # This logic relies on hacks and _make_sure_does_not_move(). - # It only works because we keep the 'ec' alive in '_valuedict' too. + if register_in_valuedict: + self._valuedict[ident] = ec self.raw_thread_local.set(ec) def leave_thread(self, space): @@ -90,7 +144,23 @@ ec._signals_enabled = new def getallvalues(self): - return self._valuedict + if self._weaklist is None: + return self._valuedict + # This logic walks the 'self._weaklist' list and adds the + # ExecutionContexts to 'result'. We are careful in case there + # are two AutoFreeECWrappers in the list which have the same + # 'ident'; in this case we must keep the most recent one (the + # older one should be deleted soon). Moreover, entries in + # self._valuedict have priority because they are never + # outdated. + result = {} + for h in self._weaklist.items(): + wrapper = h() + if wrapper is not None and not wrapper.deleted: + result[wrapper.ident] = wrapper.ec + # ^^ this possibly overwrites an older ec + result.update(self._valuedict) + return result def reinit_threads(self, space): "Called in the child process after a fork()" @@ -100,7 +170,31 @@ old_sig = ec._signals_enabled if ident != self._mainthreadident: old_sig += 1 - self._cleanup_() + self._cleanup_() # clears self._valuedict self._mainthreadident = ident self._set_ec(ec) ec._signals_enabled = old_sig + + +class AutoFreeECWrapper(object): + deleted = False + + def __init__(self, ec): + # this makes a loop between 'self' and 'ec'. It should not prevent + # the __del__ method here from being called. + self.ec = ec + ec._threadlocals_auto_free = self + self.ident = rthread.get_ident() + + def __del__(self): + from pypy.module.thread.os_local import thread_is_stopping + # this is always called in another thread: the thread + # referenced by 'self.ec' has finished at that point, and + # we're just after the GC which finds no more references to + # 'ec' (and thus to 'self'). + self.deleted = True + thread_is_stopping(self.ec) + +class ListECWrappers(rshrinklist.AbstractShrinkList): + def must_keep(self, wref): + return wref() is not None diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -521,7 +521,6 @@ def descr_getitem(self, space, w_index): if isinstance(w_index, W_SliceObject): - # XXX consider to extend rlist's functionality? length = self.length() start, stop, step, slicelength = w_index.indices4(space, length) assert slicelength >= 0 diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -358,3 +358,10 @@ assert 3L.__coerce__(4L) == (3L, 4L) assert 3L.__coerce__(4) == (3, 4) assert 3L.__coerce__(object()) == NotImplemented + + def test_linear_long_base_16(self): + # never finishes if long(_, 16) is not linear-time + size = 100000 + n = "a" * size + expected = (2 << (size * 4)) // 3 + assert long(n, 16) == expected diff --git a/rpython/annotator/signature.py b/rpython/annotator/signature.py --- a/rpython/annotator/signature.py +++ b/rpython/annotator/signature.py @@ -100,6 +100,7 @@ self.argtypes = argtypes def __call__(self, funcdesc, inputcells): + from rpython.rlib.objectmodel import NOT_CONSTANT from rpython.rtyper.lltypesystem import lltype args_s = [] from rpython.annotator import model as annmodel @@ -115,6 +116,9 @@ args_s.append(s_input) elif argtype is None: args_s.append(inputcells[i]) # no change + elif argtype is NOT_CONSTANT: + from rpython.annotator.model import not_const + args_s.append(not_const(inputcells[i])) else: args_s.append(annotation(argtype, bookkeeper=funcdesc.bookkeeper)) if len(inputcells) != len(args_s): diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -3516,6 +3516,32 @@ s = a.build_types(f, [unicode]) assert isinstance(s, annmodel.SomeUnicodeString) + def test_extended_slice(self): + a = self.RPythonAnnotator() + def f(start, end, step): + return [1, 2, 3][start:end:step] + with py.test.raises(AnnotatorError): + a.build_types(f, [int, int, int]) + a = self.RPythonAnnotator() + with py.test.raises(AnnotatorError): + a.build_types(f, [annmodel.SomeInteger(nonneg=True), + annmodel.SomeInteger(nonneg=True), + annmodel.SomeInteger(nonneg=True)]) + def f(x): + return x[::-1] + a = self.RPythonAnnotator() + with py.test.raises(AnnotatorError): + a.build_types(f, [str]) + def f(x): + return x[::2] + a = self.RPythonAnnotator() + with py.test.raises(AnnotatorError): + a.build_types(f, [str]) + def f(x): + return x[1:2:1] + a = self.RPythonAnnotator() + with py.test.raises(AnnotatorError): + a.build_types(f, [str]) def test_negative_slice(self): def f(s, e): diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py --- a/rpython/annotator/unaryop.py +++ b/rpython/annotator/unaryop.py @@ -441,7 +441,7 @@ def dict_contains(s_dct, s_element, position): s_dct.dictdef.generalize_key(s_element) if s_dct._is_empty(position): - s_bool =SomeBool() + s_bool = SomeBool() s_bool.const = False return s_bool return s_Bool diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py --- a/rpython/flowspace/operation.py +++ b/rpython/flowspace/operation.py @@ -1,5 +1,5 @@ """ -This module defines all the SpaceOeprations used in rpython.flowspace. +This module defines all the SpaceOperations used in rpython.flowspace. """ import __builtin__ @@ -196,21 +196,6 @@ return cls._dispatch(type(s_arg)) @classmethod - def get_specialization(cls, s_arg, *_ignored): - try: - impl = getattr(s_arg, cls.opname) - - def specialized(annotator, arg, *other_args): - return impl(*[annotator.annotation(x) for x in other_args]) - try: - specialized.can_only_throw = impl.can_only_throw - except AttributeError: - pass - return specialized - except AttributeError: - return cls._dispatch(type(s_arg)) - - @classmethod def register_transform(cls, Some_cls): def decorator(func): cls._transform[Some_cls] = func @@ -523,6 +508,14 @@ *[annotator.annotation(arg) for arg in self.args]) +class NewSlice(HLOperation): + opname = 'newslice' + canraise = [] + + def consider(self, annotator): + raise AnnotatorError("Cannot use extended slicing in rpython") + + class Pow(PureOperation): opname = 'pow' arity = 3 diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -19,7 +19,6 @@ from rpython.jit.backend.arm.locations import imm, RawSPStackLocation from rpython.jit.backend.llsupport import symbolic from rpython.jit.backend.llsupport.gcmap import allocate_gcmap -from rpython.jit.backend.llsupport.descr import InteriorFieldDescr from rpython.jit.backend.llsupport.assembler import GuardToken, BaseAssembler from rpython.jit.backend.llsupport.regalloc import get_scale from rpython.jit.metainterp.history import (AbstractFailDescr, ConstInt, @@ -655,31 +654,24 @@ pmc.B_offs(offset, c.EQ) return fcond - def emit_op_setfield_gc(self, op, arglocs, regalloc, fcond): - value_loc, base_loc, ofs, size = arglocs - scale = get_scale(size.value) - self._write_to_mem(value_loc, base_loc, - ofs, imm(scale), fcond) + def emit_op_gc_store(self, op, arglocs, regalloc, fcond): + value_loc, base_loc, ofs_loc, size_loc = arglocs + scale = get_scale(size_loc.value) + self._write_to_mem(value_loc, base_loc, ofs_loc, imm(scale), fcond) return fcond - emit_op_setfield_raw = emit_op_setfield_gc - emit_op_zero_ptr_field = emit_op_setfield_gc - - def _genop_getfield(self, op, arglocs, regalloc, fcond): - base_loc, ofs, res, size = arglocs - signed = op.getdescr().is_field_signed() - scale = get_scale(size.value) - self._load_from_mem(res, base_loc, ofs, imm(scale), signed, fcond) + def _emit_op_gc_load(self, op, arglocs, regalloc, fcond): + base_loc, ofs_loc, res_loc, nsize_loc = arglocs + nsize = nsize_loc.value + signed = (nsize < 0) + scale = get_scale(abs(nsize)) + self._load_from_mem(res_loc, base_loc, ofs_loc, imm(scale), + signed, fcond) return fcond - emit_op_getfield_gc_i = _genop_getfield - emit_op_getfield_gc_r = _genop_getfield - emit_op_getfield_gc_f = _genop_getfield - emit_op_getfield_gc_pure_i = _genop_getfield - emit_op_getfield_gc_pure_r = _genop_getfield - emit_op_getfield_gc_pure_f = _genop_getfield - emit_op_getfield_raw_i = _genop_getfield - emit_op_getfield_raw_f = _genop_getfield + emit_op_gc_load_i = _emit_op_gc_load + emit_op_gc_load_r = _emit_op_gc_load + emit_op_gc_load_f = _emit_op_gc_load def emit_op_increment_debug_counter(self, op, arglocs, regalloc, fcond): base_loc, value_loc = arglocs @@ -688,68 +680,21 @@ self.mc.STR_ri(value_loc.value, base_loc.value, 0, cond=fcond) return fcond - def _genop_getinteriorfield(self, op, arglocs, regalloc, fcond): - (base_loc, index_loc, res_loc, - ofs_loc, ofs, itemsize, fieldsize) = arglocs - scale = get_scale(fieldsize.value) - tmploc, save = self.get_tmp_reg([base_loc, ofs_loc]) - assert not save - self.mc.gen_load_int(tmploc.value, itemsize.value) - self.mc.MUL(tmploc.value, index_loc.value, tmploc.value) - descr = op.getdescr() - assert isinstance(descr, InteriorFieldDescr) - signed = descr.fielddescr.is_field_signed() - if ofs.value > 0: - if ofs_loc.is_imm(): - self.mc.ADD_ri(tmploc.value, tmploc.value, ofs_loc.value) - else: - self.mc.ADD_rr(tmploc.value, tmploc.value, ofs_loc.value) - ofs_loc = tmploc - self._load_from_mem(res_loc, base_loc, ofs_loc, - imm(scale), signed, fcond) - return fcond - - emit_op_getinteriorfield_gc_i = _genop_getinteriorfield - emit_op_getinteriorfield_gc_r = _genop_getinteriorfield - emit_op_getinteriorfield_gc_f = _genop_getinteriorfield - - def emit_op_setinteriorfield_gc(self, op, arglocs, regalloc, fcond): - (base_loc, index_loc, value_loc, - ofs_loc, ofs, itemsize, fieldsize) = arglocs - scale = get_scale(fieldsize.value) - tmploc, save = self.get_tmp_reg([base_loc, index_loc, value_loc, ofs_loc]) - assert not save - self.mc.gen_load_int(tmploc.value, itemsize.value) - self.mc.MUL(tmploc.value, index_loc.value, tmploc.value) - if ofs.value > 0: - if ofs_loc.is_imm(): - self.mc.ADD_ri(tmploc.value, tmploc.value, ofs_loc.value) - else: - self.mc.ADD_rr(tmploc.value, tmploc.value, ofs_loc.value) - self._write_to_mem(value_loc, base_loc, tmploc, imm(scale), fcond) - return fcond - emit_op_setinteriorfield_raw = emit_op_setinteriorfield_gc - - def emit_op_arraylen_gc(self, op, arglocs, regalloc, fcond): - res, base_loc, ofs = arglocs - self.load_reg(self.mc, res, base_loc, ofs.value) - return fcond - - def emit_op_setarrayitem_gc(self, op, arglocs, regalloc, fcond): - value_loc, base_loc, ofs_loc, scale, ofs = arglocs - assert ofs_loc.is_core_reg() - if scale.value > 0: - self.mc.LSL_ri(r.ip.value, ofs_loc.value, scale.value) - ofs_loc = r.ip - + def emit_op_gc_store_indexed(self, op, arglocs, regalloc, fcond): + value_loc, base_loc, index_loc, size_loc, ofs_loc = arglocs + assert index_loc.is_core_reg() # add the base offset - if ofs.value > 0: - self.mc.ADD_ri(r.ip.value, ofs_loc.value, imm=ofs.value) - ofs_loc = r.ip - self._write_to_mem(value_loc, base_loc, ofs_loc, scale, fcond) + if ofs_loc.value > 0: + self.mc.ADD_ri(r.ip.value, index_loc.value, imm=ofs_loc.value) + index_loc = r.ip + scale = get_scale(size_loc.value) + self._write_to_mem(value_loc, base_loc, index_loc, imm(scale), fcond) return fcond def _write_to_mem(self, value_loc, base_loc, ofs_loc, scale, fcond=c.AL): + # Write a value of size '1 << scale' at the address + # 'base_ofs + ofs_loc'. Note that 'scale' is not used to scale + # the offset! if scale.value == 3: assert value_loc.is_vfp_reg() # vstr only supports imm offsets @@ -789,43 +734,31 @@ else: assert 0 - emit_op_setarrayitem_raw = emit_op_setarrayitem_gc - - def emit_op_raw_store(self, op, arglocs, regalloc, fcond): - value_loc, base_loc, ofs_loc, scale, ofs = arglocs - assert ofs_loc.is_core_reg() - self._write_to_mem(value_loc, base_loc, ofs_loc, scale, fcond) + def _emit_op_gc_load_indexed(self, op, arglocs, regalloc, fcond): + res_loc, base_loc, index_loc, nsize_loc, ofs_loc = arglocs + assert index_loc.is_core_reg() + nsize = nsize_loc.value + signed = (nsize < 0) + # add the base offset + if ofs_loc.value > 0: + self.mc.ADD_ri(r.ip.value, index_loc.value, imm=ofs_loc.value) + index_loc = r.ip + # + scale = get_scale(abs(nsize)) + self._load_from_mem(res_loc, base_loc, index_loc, imm(scale), + signed, fcond) return fcond - def _genop_getarrayitem(self, op, arglocs, regalloc, fcond): - res_loc, base_loc, ofs_loc, scale, ofs = arglocs - assert ofs_loc.is_core_reg() - signed = op.getdescr().is_item_signed() - - # scale the offset as required - # XXX we should try to encode the scale inside the "shift" part of LDR - if scale.value > 0: - self.mc.LSL_ri(r.ip.value, ofs_loc.value, scale.value) - ofs_loc = r.ip - # add the base offset - if ofs.value > 0: - self.mc.ADD_ri(r.ip.value, ofs_loc.value, imm=ofs.value) - ofs_loc = r.ip - # - self._load_from_mem(res_loc, base_loc, ofs_loc, scale, signed, fcond) - return fcond - - emit_op_getarrayitem_gc_i = _genop_getarrayitem - emit_op_getarrayitem_gc_r = _genop_getarrayitem - emit_op_getarrayitem_gc_f = _genop_getarrayitem - emit_op_getarrayitem_gc_pure_i = _genop_getarrayitem - emit_op_getarrayitem_gc_pure_r = _genop_getarrayitem - emit_op_getarrayitem_gc_pure_f = _genop_getarrayitem - emit_op_getarrayitem_raw_i = _genop_getarrayitem - emit_op_getarrayitem_raw_f = _genop_getarrayitem + emit_op_gc_load_indexed_i = _emit_op_gc_load_indexed + emit_op_gc_load_indexed_r = _emit_op_gc_load_indexed + emit_op_gc_load_indexed_f = _emit_op_gc_load_indexed def _load_from_mem(self, res_loc, base_loc, ofs_loc, scale, signed=False, fcond=c.AL): + # Load a value of '1 << scale' bytes, from the memory location + # 'base_loc + ofs_loc'. Note that 'scale' is not used to scale + # the offset! + # if scale.value == 3: assert res_loc.is_vfp_reg() # vldr only supports imm offsets @@ -881,51 +814,6 @@ else: assert 0 - def _genop_raw_load(self, op, arglocs, regalloc, fcond): - res_loc, base_loc, ofs_loc, scale, ofs = arglocs - assert ofs_loc.is_core_reg() - # no base offset - assert ofs.value == 0 - signed = op.getdescr().is_item_signed() - self._load_from_mem(res_loc, base_loc, ofs_loc, scale, signed, fcond) - return fcond - - emit_op_raw_load_i = _genop_raw_load - emit_op_raw_load_f = _genop_raw_load - - def emit_op_strlen(self, op, arglocs, regalloc, fcond): - l0, l1, res = arglocs - if l1.is_imm(): - self.mc.LDR_ri(res.value, l0.value, l1.getint(), cond=fcond) - else: - self.mc.LDR_rr(res.value, l0.value, l1.value, cond=fcond) - return fcond - - def emit_op_strgetitem(self, op, arglocs, regalloc, fcond): - res, base_loc, ofs_loc, basesize = arglocs - if ofs_loc.is_imm(): - self.mc.ADD_ri(r.ip.value, base_loc.value, ofs_loc.getint(), - cond=fcond) - else: - self.mc.ADD_rr(r.ip.value, base_loc.value, ofs_loc.value, - cond=fcond) - - self.mc.LDRB_ri(res.value, r.ip.value, basesize.value, cond=fcond) - return fcond - - def emit_op_strsetitem(self, op, arglocs, regalloc, fcond): - value_loc, base_loc, ofs_loc, basesize = arglocs - if ofs_loc.is_imm(): - self.mc.ADD_ri(r.ip.value, base_loc.value, ofs_loc.getint(), - cond=fcond) - else: - self.mc.ADD_rr(r.ip.value, base_loc.value, ofs_loc.value, - cond=fcond) - - self.mc.STRB_ri(value_loc.value, r.ip.value, basesize.value, - cond=fcond) - return fcond - #from ../x86/regalloc.py:928 ff. def emit_op_copystrcontent(self, op, arglocs, regalloc, fcond): assert len(arglocs) == 0 @@ -1016,35 +904,6 @@ else: raise AssertionError("bad unicode item size") - emit_op_unicodelen = emit_op_strlen - - def emit_op_unicodegetitem(self, op, arglocs, regalloc, fcond): - res, base_loc, ofs_loc, scale, basesize, itemsize = arglocs - self.mc.ADD_rr(r.ip.value, base_loc.value, ofs_loc.value, cond=fcond, - imm=scale.value, shifttype=shift.LSL) - if scale.value == 2: - self.mc.LDR_ri(res.value, r.ip.value, basesize.value, cond=fcond) - elif scale.value == 1: - self.mc.LDRH_ri(res.value, r.ip.value, basesize.value, cond=fcond) - else: - assert 0, itemsize.value - return fcond - - def emit_op_unicodesetitem(self, op, arglocs, regalloc, fcond): - value_loc, base_loc, ofs_loc, scale, basesize, itemsize = arglocs - self.mc.ADD_rr(r.ip.value, base_loc.value, ofs_loc.value, cond=fcond, - imm=scale.value, shifttype=shift.LSL) - if scale.value == 2: - self.mc.STR_ri(value_loc.value, r.ip.value, basesize.value, - cond=fcond) - elif scale.value == 1: - self.mc.STRH_ri(value_loc.value, r.ip.value, basesize.value, - cond=fcond) - else: - assert 0, itemsize.value - - return fcond - def store_force_descr(self, op, fail_locs, frame_depth): pos = self.mc.currpos() guard_token = self.build_guard_token(op, frame_depth, fail_locs, pos, c.AL) diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -34,9 +34,6 @@ from rpython.rtyper.lltypesystem import lltype, rffi, rstr, llmemory from rpython.rtyper.lltypesystem.lloperation import llop from rpython.jit.codewriter.effectinfo import EffectInfo -from rpython.jit.backend.llsupport.descr import unpack_arraydescr -from rpython.jit.backend.llsupport.descr import unpack_fielddescr -from rpython.jit.backend.llsupport.descr import unpack_interiorfielddescr from rpython.rlib.rarithmetic import r_uint from rpython.jit.backend.llsupport.descr import CallDescr @@ -802,15 +799,12 @@ src_locations2, dst_locations2, vfptmploc) return [] - def prepare_op_setfield_gc(self, op, fcond): + def prepare_op_gc_store(self, op, fcond): boxes = op.getarglist() - ofs, size, sign = unpack_fielddescr(op.getdescr()) - return self._prepare_op_setfield(boxes, ofs, size) - - def _prepare_op_setfield(self, boxes, ofs, size): - a0, a1 = boxes - base_loc = self.make_sure_var_in_reg(a0, boxes) - value_loc = self.make_sure_var_in_reg(a1, boxes) + base_loc = self.make_sure_var_in_reg(boxes[0], boxes) + ofs = boxes[1].getint() + value_loc = self.make_sure_var_in_reg(boxes[2], boxes) + size = boxes[3].getint() ofs_size = default_imm_size if size < 8 else VMEM_imm_size if check_imm_arg(ofs, size=ofs_size): ofs_loc = imm(ofs) @@ -819,19 +813,13 @@ self.assembler.load(ofs_loc, imm(ofs)) return [value_loc, base_loc, ofs_loc, imm(size)] - prepare_op_setfield_raw = prepare_op_setfield_gc - - def prepare_op_zero_ptr_field(self, op, fcond): + def _prepare_op_gc_load(self, op, fcond): a0 = op.getarg(0) ofs = op.getarg(1).getint() - return self._prepare_op_setfield([a0, ConstInt(0)], ofs, WORD) - - def _prepare_op_getfield(self, op, fcond): - a0 = op.getarg(0) - ofs, size, sign = unpack_fielddescr(op.getdescr()) + nsize = op.getarg(2).getint() # negative for "signed" base_loc = self.make_sure_var_in_reg(a0) immofs = imm(ofs) - ofs_size = default_imm_size if size < 8 else VMEM_imm_size + ofs_size = default_imm_size if abs(nsize) < 8 else VMEM_imm_size if check_imm_arg(ofs, size=ofs_size): ofs_loc = immofs else: @@ -839,17 +827,12 @@ self.assembler.load(ofs_loc, immofs) self.possibly_free_vars_for_op(op) self.free_temp_vars() - res = self.force_allocate_reg(op) - return [base_loc, ofs_loc, res, imm(size)] + res_loc = self.force_allocate_reg(op) + return [base_loc, ofs_loc, res_loc, imm(nsize)] - prepare_op_getfield_gc_i = _prepare_op_getfield - prepare_op_getfield_gc_r = _prepare_op_getfield - prepare_op_getfield_gc_f = _prepare_op_getfield - prepare_op_getfield_raw_i = _prepare_op_getfield - prepare_op_getfield_raw_f = _prepare_op_getfield - prepare_op_getfield_gc_pure_i = _prepare_op_getfield - prepare_op_getfield_gc_pure_r = _prepare_op_getfield - prepare_op_getfield_gc_pure_f = _prepare_op_getfield + prepare_op_gc_load_i = _prepare_op_gc_load + prepare_op_gc_load_r = _prepare_op_gc_load + prepare_op_gc_load_f = _prepare_op_gc_load def prepare_op_increment_debug_counter(self, op, fcond): boxes = op.getarglist() @@ -859,188 +842,38 @@ self.free_temp_vars() return [base_loc, value_loc] - def _prepare_op_getinteriorfield(self, op, fcond): - t = unpack_interiorfielddescr(op.getdescr()) - ofs, itemsize, fieldsize, sign = t - args = op.getarglist() - base_loc = self.make_sure_var_in_reg(op.getarg(0), args) - index_loc = self.make_sure_var_in_reg(op.getarg(1), args) - immofs = imm(ofs) - ofs_size = default_imm_size if fieldsize < 8 else VMEM_imm_size - if check_imm_arg(ofs, size=ofs_size): - ofs_loc = immofs - else: - ofs_loc = self.get_scratch_reg(INT, args) - self.assembler.load(ofs_loc, immofs) + def prepare_op_gc_store_indexed(self, op, fcond): + boxes = op.getarglist() + base_loc = self.make_sure_var_in_reg(boxes[0], boxes) + value_loc = self.make_sure_var_in_reg(boxes[2], boxes) + index_loc = self.make_sure_var_in_reg(boxes[1], boxes) + assert boxes[3].getint() == 1 # scale + ofs = boxes[4].getint() + size = boxes[5].getint() + assert check_imm_arg(ofs) + return [value_loc, base_loc, index_loc, imm(size), imm(ofs)] + + def _prepare_op_gc_load_indexed(self, op, fcond): + boxes = op.getarglist() + base_loc = self.make_sure_var_in_reg(boxes[0], boxes) + index_loc = self.make_sure_var_in_reg(boxes[1], boxes) + assert boxes[2].getint() == 1 # scale + ofs = boxes[3].getint() + nsize = boxes[4].getint() + assert check_imm_arg(ofs) self.possibly_free_vars_for_op(op) self.free_temp_vars() - result_loc = self.force_allocate_reg(op) - return [base_loc, index_loc, result_loc, ofs_loc, imm(ofs), - imm(itemsize), imm(fieldsize)] + res_loc = self.force_allocate_reg(op) + return [res_loc, base_loc, index_loc, imm(nsize), imm(ofs)] - prepare_op_getinteriorfield_gc_i = _prepare_op_getinteriorfield - prepare_op_getinteriorfield_gc_r = _prepare_op_getinteriorfield - prepare_op_getinteriorfield_gc_f = _prepare_op_getinteriorfield - - def prepare_op_setinteriorfield_gc(self, op, fcond): - t = unpack_interiorfielddescr(op.getdescr()) - ofs, itemsize, fieldsize, sign = t - args = op.getarglist() - base_loc = self.make_sure_var_in_reg(op.getarg(0), args) - index_loc = self.make_sure_var_in_reg(op.getarg(1), args) - value_loc = self.make_sure_var_in_reg(op.getarg(2), args) - immofs = imm(ofs) - ofs_size = default_imm_size if fieldsize < 8 else VMEM_imm_size - if check_imm_arg(ofs, size=ofs_size): - ofs_loc = immofs - else: - ofs_loc = self.get_scratch_reg(INT, args) - self.assembler.load(ofs_loc, immofs) - return [base_loc, index_loc, value_loc, ofs_loc, imm(ofs), - imm(itemsize), imm(fieldsize)] - prepare_op_setinteriorfield_raw = prepare_op_setinteriorfield_gc - - def prepare_op_arraylen_gc(self, op, fcond): - arraydescr = op.getdescr() - assert isinstance(arraydescr, ArrayDescr) - ofs = arraydescr.lendescr.offset - arg = op.getarg(0) - base_loc = self.make_sure_var_in_reg(arg) - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - res = self.force_allocate_reg(op) - return [res, base_loc, imm(ofs)] - - def prepare_op_setarrayitem_gc(self, op, fcond): - size, ofs, _ = unpack_arraydescr(op.getdescr()) - scale = get_scale(size) - args = op.getarglist() - base_loc = self.make_sure_var_in_reg(args[0], args) - value_loc = self.make_sure_var_in_reg(args[2], args) - ofs_loc = self.make_sure_var_in_reg(args[1], args) - assert check_imm_arg(ofs) - return [value_loc, base_loc, ofs_loc, imm(scale), imm(ofs)] - prepare_op_setarrayitem_raw = prepare_op_setarrayitem_gc - prepare_op_raw_store = prepare_op_setarrayitem_gc - - def _prepare_op_getarrayitem(self, op, fcond): - boxes = op.getarglist() - size, ofs, _ = unpack_arraydescr(op.getdescr()) - scale = get_scale(size) - base_loc = self.make_sure_var_in_reg(boxes[0], boxes) - ofs_loc = self.make_sure_var_in_reg(boxes[1], boxes) - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - res = self.force_allocate_reg(op) - assert check_imm_arg(ofs) - return [res, base_loc, ofs_loc, imm(scale), imm(ofs)] - - prepare_op_getarrayitem_gc_i = _prepare_op_getarrayitem - prepare_op_getarrayitem_gc_r = _prepare_op_getarrayitem - prepare_op_getarrayitem_gc_f = _prepare_op_getarrayitem - prepare_op_getarrayitem_raw_i = _prepare_op_getarrayitem - prepare_op_getarrayitem_raw_f = _prepare_op_getarrayitem - prepare_op_getarrayitem_gc_pure_i = _prepare_op_getarrayitem - prepare_op_getarrayitem_gc_pure_r = _prepare_op_getarrayitem - prepare_op_getarrayitem_gc_pure_f = _prepare_op_getarrayitem - prepare_op_raw_load_i = _prepare_op_getarrayitem - prepare_op_raw_load_f = _prepare_op_getarrayitem - - def prepare_op_strlen(self, op, fcond): - args = op.getarglist() - l0 = self.make_sure_var_in_reg(op.getarg(0)) - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR, - self.cpu.translate_support_code) - immofs = imm(ofs_length) - if check_imm_arg(ofs_length): - l1 = immofs - else: - l1 = self.get_scratch_reg(INT, args) - self.assembler.load(l1, immofs) - - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - - res = self.force_allocate_reg(op) - self.possibly_free_var(op) - return [l0, l1, res] - - def prepare_op_strgetitem(self, op, fcond): - boxes = op.getarglist() - base_loc = self.make_sure_var_in_reg(boxes[0]) - - a1 = boxes[1] - imm_a1 = check_imm_box(a1) - if imm_a1: - ofs_loc = self.convert_to_imm(a1) - else: - ofs_loc = self.make_sure_var_in_reg(a1, boxes) - - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - res = self.force_allocate_reg(op) - - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR, - self.cpu.translate_support_code) - assert itemsize == 1 - return [res, base_loc, ofs_loc, imm(basesize)] - - def prepare_op_strsetitem(self, op, fcond): - boxes = op.getarglist() - base_loc = self.make_sure_var_in_reg(boxes[0], boxes) - ofs_loc = self.make_sure_var_in_reg(boxes[1], boxes) - value_loc = self.make_sure_var_in_reg(boxes[2], boxes) - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR, - self.cpu.translate_support_code) - assert itemsize == 1 - return [value_loc, base_loc, ofs_loc, imm(basesize)] + prepare_op_gc_load_indexed_i = _prepare_op_gc_load_indexed + prepare_op_gc_load_indexed_r = _prepare_op_gc_load_indexed + prepare_op_gc_load_indexed_f = _prepare_op_gc_load_indexed prepare_op_copystrcontent = void prepare_op_copyunicodecontent = void prepare_op_zero_array = void - def prepare_op_unicodelen(self, op, fcond): - l0 = self.make_sure_var_in_reg(op.getarg(0)) - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.UNICODE, - self.cpu.translate_support_code) - immofs = imm(ofs_length) - if check_imm_arg(ofs_length): - l1 = immofs - else: - l1 = self.get_scratch_reg(INT, [op.getarg(0)]) - self.assembler.load(l1, immofs) - - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - res = self.force_allocate_reg(op) - return [l0, l1, res] - - def prepare_op_unicodegetitem(self, op, fcond): - boxes = op.getarglist() - base_loc = self.make_sure_var_in_reg(boxes[0], boxes) - ofs_loc = self.make_sure_var_in_reg(boxes[1], boxes) - - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - res = self.force_allocate_reg(op) - - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.UNICODE, - self.cpu.translate_support_code) - scale = itemsize / 2 - return [res, base_loc, ofs_loc, - imm(scale), imm(basesize), imm(itemsize)] - - def prepare_op_unicodesetitem(self, op, fcond): - boxes = op.getarglist() - base_loc = self.make_sure_var_in_reg(boxes[0], boxes) - ofs_loc = self.make_sure_var_in_reg(boxes[1], boxes) - value_loc = self.make_sure_var_in_reg(boxes[2], boxes) - basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.UNICODE, - self.cpu.translate_support_code) - scale = itemsize / 2 - return [value_loc, base_loc, ofs_loc, - imm(scale), imm(basesize), imm(itemsize)] - def _prepare_op_same_as(self, op, fcond): arg = op.getarg(0) imm_arg = check_imm_box(arg) @@ -1142,8 +975,7 @@ def prepare_op_cond_call_gc_wb(self, op, fcond): # we force all arguments in a reg because it will be needed anyway by - # the following setfield_gc or setarrayitem_gc. It avoids loading it - # twice from the memory. + # the following gc_store. It avoids loading it twice from the memory. N = op.numargs() args = op.getarglist() arglocs = [self.make_sure_var_in_reg(op.getarg(i), args) diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py --- a/rpython/jit/backend/arm/runner.py +++ b/rpython/jit/backend/arm/runner.py @@ -29,6 +29,10 @@ float_regs = VFPRegisterManager.all_regs frame_reg = fp + # can an ISA instruction handle a factor to the offset? + # XXX should be: tuple(1 << i for i in range(31)) + load_supported_factors = (1,) + def __init__(self, rtyper, stats, opts=None, translate_support_code=False, gcdescr=None): AbstractLLCPU.__init__(self, rtyper, stats, opts, diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py --- a/rpython/jit/backend/llgraph/runner.py +++ b/rpython/jit/backend/llgraph/runner.py @@ -13,6 +13,7 @@ from rpython.rtyper.llinterp import LLInterpreter, LLException from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr +from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rtyper import rclass from rpython.rlib.clibffi import FFI_DEFAULT_ABI @@ -33,7 +34,7 @@ # front-end will mutate them under our feet again. We also # need to make sure things get freed. _cache={} - + def mapping(box): if isinstance(box, Const) or box is None: return box @@ -205,7 +206,7 @@ class ArrayDescr(AbstractDescr): all_interiorfielddescrs = None - + def __init__(self, A, runner): self.A = self.OUTERA = A self._is_pure = A._immutable_field(None) @@ -642,18 +643,9 @@ return array.getlength() def _bh_getarrayitem(self, a, index, descr, pure=False): + a = support.cast_arg(lltype.Ptr(descr.A), a) + array = a._obj assert index >= 0 - if descr.A is descr.OUTERA: - a = support.cast_arg(lltype.Ptr(descr.A), a) - else: - # we use rffi.cast instead of support.cast_arg because the types - # might not be "compatible" enough from the lltype point of - # view. In particular, this happens when we use - # str_storage_getitem, in which an rpy_string is casted to - # rpy_string_as_Signed (or similar) - a = rffi.cast(lltype.Ptr(descr.OUTERA), a) - a = getattr(a, descr.OUTERA._arrayfld) - array = a._obj return support.cast_result(descr.A.OF, array.getitem(index)) direct_getarrayitem_gc = _bh_getarrayitem @@ -722,6 +714,24 @@ else: return self.bh_raw_load_i(struct, offset, descr) + def bh_gc_load_indexed_i(self, struct, index, scale, base_ofs, bytes): + if bytes == 1: T = rffi.UCHAR + elif bytes == 2: T = rffi.USHORT + elif bytes == 4: T = rffi.UINT + elif bytes == 8: T = rffi.ULONGLONG + elif bytes == -1: T = rffi.SIGNEDCHAR + elif bytes == -2: T = rffi.SHORT + elif bytes == -4: T = rffi.INT + elif bytes == -8: T = rffi.LONGLONG + else: raise NotImplementedError(bytes) + x = llop.gc_load_indexed(T, struct, index, scale, base_ofs) + return lltype.cast_primitive(lltype.Signed, x) + + def bh_gc_load_indexed_f(self, struct, index, scale, base_ofs, bytes): + if bytes != 8: + raise Exception("gc_load_indexed_f is only for 'double'!") + return llop.gc_load_indexed(rffi.DOUBLE, struct, index, scale, base_ofs) + def bh_increment_debug_counter(self, addr): p = rffi.cast(rffi.CArrayPtr(lltype.Signed), addr) p[0] += 1 @@ -1048,7 +1058,7 @@ if box.datatype == INT: for i,a in enumerate(arg): if isinstance(a, bool): - arg[i] = int(a) + arg[i] = int(a) assert all([lltype.typeOf(a) == lltype.Signed for a in arg]) elif box.datatype == FLOAT: assert all([lltype.typeOf(a) == longlong.FLOATSTORAGE or \ diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -764,6 +764,16 @@ def bh_raw_load_f(self, addr, offset, descr): return self.read_float_at_mem(addr, offset) + def bh_gc_load_indexed_i(self, addr, index, scale, base_ofs, bytes): + offset = base_ofs + scale * index + return self.read_int_at_mem(addr, offset, abs(bytes), bytes < 0) + + def bh_gc_load_indexed_f(self, addr, index, scale, base_ofs, bytes): + # only for 'double'! + assert bytes == rffi.sizeof(lltype.Float) + offset = base_ofs + scale * index + return self.read_float_at_mem(addr, offset) + def bh_new(self, sizedescr): return self.gc_ll_descr.gc_malloc(sizedescr) diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py --- a/rpython/jit/backend/llsupport/rewrite.py +++ b/rpython/jit/backend/llsupport/rewrite.py @@ -1,6 +1,6 @@ from rpython.rlib import rgc from rpython.rlib.objectmodel import we_are_translated -from rpython.rlib.rarithmetic import ovfcheck +from rpython.rlib.rarithmetic import ovfcheck, highest_bit from rpython.rtyper.lltypesystem import llmemory, lltype, rstr from rpython.jit.metainterp import history from rpython.jit.metainterp.history import ConstInt, ConstPtr @@ -133,11 +133,11 @@ def emit_gc_store_or_indexed(self, op, ptr_box, index_box, value_box, itemsize, factor, offset): factor, offset, index_box = \ - self._emit_mul_add_if_factor_offset_not_supported(index_box, + self._emit_mul_if_factor_offset_not_supported(index_box, factor, offset) # - if factor == 1 and offset == 0: - args = [ptr_box, index_box, value_box, ConstInt(itemsize)] + if index_box is None: + args = [ptr_box, ConstInt(offset), value_box, ConstInt(itemsize)] newload = ResOperation(rop.GC_STORE, args) else: args = [ptr_box, index_box, value_box, ConstInt(factor), @@ -160,34 +160,31 @@ index_box = op.getarg(1) self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize, 1, ofs, sign) - def _emit_mul_add_if_factor_offset_not_supported(self, index_box, factor, offset): - orig_factor = factor - # factor - must_manually_load_const = False # offset != 0 and not self.cpu.load_constant_offset - if factor != 1 and (factor not in self.cpu.load_supported_factors or \ - (not index_box.is_constant() and must_manually_load_const)): - # enter here if the factor is supported by the cpu - # OR the index is not constant and a new resop must be emitted - # to add the offset - if isinstance(index_box, ConstInt): - index_box = ConstInt(index_box.value * factor) - else: - index_box = ResOperation(rop.INT_MUL, [index_box, ConstInt(factor)]) + def _emit_mul_if_factor_offset_not_supported(self, index_box, + factor, offset): + # Returns (factor, offset, index_box) where index_box is either + # a non-constant BoxInt or None. + if isinstance(index_box, ConstInt): + return 1, index_box.value * factor + offset, None + else: + if factor != 1 and factor not in self.cpu.load_supported_factors: + # the factor is supported by the cpu + # x & (x - 1) == 0 is a quick test for power of 2 + assert factor > 0 + if (factor & (factor - 1)) == 0: + index_box = ResOperation(rop.INT_LSHIFT, + [index_box, ConstInt(highest_bit(factor))]) + else: + index_box = ResOperation(rop.INT_MUL, + [index_box, ConstInt(factor)]) self.emit_op(index_box) - factor = 1 - # adjust the constant offset - #if must_manually_load_const: - # if isinstance(index_box, ConstInt): - # index_box = ConstInt(index_box.value + offset) - # else: - # index_box = ResOperation(rop.INT_ADD, [index_box, ConstInt(offset)]) - # self.emit_op(index_box) - # offset = 0 - return factor, offset, index_box + factor = 1 + return factor, offset, index_box - def emit_gc_load_or_indexed(self, op, ptr_box, index_box, itemsize, factor, offset, sign, type='i'): + def emit_gc_load_or_indexed(self, op, ptr_box, index_box, itemsize, + factor, offset, sign, type='i'): factor, offset, index_box = \ - self._emit_mul_add_if_factor_offset_not_supported(index_box, + self._emit_mul_if_factor_offset_not_supported(index_box, factor, offset) # if sign: @@ -197,8 +194,8 @@ optype = type if op is not None: optype = op.type - if factor == 1 and offset == 0: - args = [ptr_box, index_box, ConstInt(itemsize)] + if index_box is None: + args = [ptr_box, ConstInt(offset), ConstInt(itemsize)] newload = ResOperation(OpHelpers.get_gc_load(optype), args) else: args = [ptr_box, index_box, ConstInt(factor), @@ -303,13 +300,6 @@ self.cpu.translate_support_code) self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1), op.getarg(2), itemsize, itemsize, basesize) - elif op.getopnum() == rop.ZERO_PTR_FIELD: - ofs = op.getarg(1).getint() - size = WORD - index_box = ConstInt(0) - value_box = ConstInt(0) - self.emit_gc_store_or_indexed(op, op.getarg(0), index_box, value_box, - size, 1, ofs) return False @@ -548,7 +538,9 @@ return # the ZERO_ARRAY operation will be optimized according to what # SETARRAYITEM_GC we see before the next allocation operation. - # See emit_pending_zeros(). + # See emit_pending_zeros(). (This optimization is done by + # hacking the object 'o' in-place: e.g., o.getarg(1) may be + # replaced with another constant greater than 0.) o = ResOperation(rop.ZERO_ARRAY, [v_arr, self.c_zero, v_length], descr=arraydescr) self.emit_op(o) @@ -562,9 +554,8 @@ ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_depth) if sign: size = -size - args = [ConstInt(frame_info), ConstInt(0), ConstInt(1), - ConstInt(ofs), ConstInt(size)] - size = ResOperation(rop.GC_LOAD_INDEXED_I, args) + args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)] + size = ResOperation(rop.GC_LOAD_I, args) self.emit_op(size) frame = ResOperation(rop.NEW_ARRAY, [size], descr=descrs.arraydescr) @@ -575,9 +566,8 @@ ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_size) if sign: size = -size - args = [ConstInt(frame_info), ConstInt(0), ConstInt(1), - ConstInt(ofs), ConstInt(size)] - size = ResOperation(rop.GC_LOAD_INDEXED_I, args) + args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)] + size = ResOperation(rop.GC_LOAD_I, args) self.emit_op(size) frame = self.gen_malloc_nursery_varsize_frame(size) self.gen_initialize_tid(frame, descrs.arraydescr.tid) @@ -657,15 +647,12 @@ descr = self.cpu.getarraydescr_for_frame(arg.type) assert self.cpu.JITFRAME_FIXED_SIZE & 1 == 0 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit