Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r92100:45907003511b Date: 2017-08-06 15:37 +0100 http://bitbucket.org/pypy/pypy/changeset/45907003511b/
Log: hg merge default 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 @@ -60,3 +60,16 @@ Small improvement to optimize list accesses with constant indexes better by throwing away information about them less eagerly. + + +.. branch: getarrayitem-into-bridges: + +More information is retained into a bridge: knowledge about the content of +arrays (at fixed indices) is stored in guards (and thus available at the +beginning of bridges). Also, some better feeding of information about known +fields of constant objects into bridges. + +.. branch: cpyext-leakchecking + +Add support for leakfinder in cpyext tests (disabled for now, due to too many +failures). diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1,4 +1,5 @@ import sys +import py from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES @@ -1271,8 +1272,22 @@ self.setitem(w_globals, w_key, self.builtin) return statement.exec_code(self, w_globals, w_locals) + @not_rpython + def appdef(self, source): + '''Create interp-level function object from app-level source. + + The source should be in the same format as for space.appexec(): + """(foo, bar): return 'baz'""" + ''' + source = source.lstrip() + assert source.startswith('('), "incorrect header in:\n%s" % (source,) + source = py.code.Source("def anonymous%s\n" % source) + w_glob = self.newdict(module=True) + self.exec_(str(source), w_glob, w_glob) + return self.getitem(w_glob, self.newtext('anonymous')) + @specialize.arg(2) - def appexec(self, posargs_w, source): + def appexec(self, posargs_w, source, cache=True): """ return value from executing given source at applevel. The source must look like '''(x, y): @@ -1280,7 +1295,11 @@ return result ''' """ - w_func = self.fromcache(AppExecCache).getorbuild(source) + if cache: + w_func = self.fromcache(AppExecCache).getorbuild(source) + else: + # NB: since appdef() is not-RPython, using cache=False also is. + w_func = self.appdef(source) args = Arguments(self, list(posargs_w)) return self.call_args(w_func, args) @@ -1817,15 +1836,7 @@ class AppExecCache(SpaceCache): @not_rpython def build(cache, source): - space = cache.space - # XXX will change once we have our own compiler - import py - source = source.lstrip() - assert source.startswith('('), "incorrect header in:\n%s" % (source,) - source = py.code.Source("def anonymous%s\n" % source) - w_glob = space.newdict(module=True) - space.exec_(str(source), w_glob, w_glob) - return space.getitem(w_glob, space.newtext('anonymous')) + return cache.space.appdef(source) # Table describing the regular part of the interface of object spaces, diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py --- a/pypy/module/cpyext/buffer.py +++ b/pypy/module/cpyext/buffer.py @@ -84,22 +84,27 @@ if self.needs_decref: if self.releasebufferproc: func_target = rffi.cast(releasebufferproc, self.releasebufferproc) - with lltype.scoped_alloc(Py_buffer) as pybuf: - pybuf.c_buf = self.ptr - pybuf.c_len = self.size - pybuf.c_ndim = cts.cast('int', self.ndim) - pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape) - pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides) - for i in range(self.ndim): - pybuf.c_shape[i] = self.shape[i] - pybuf.c_strides[i] = self.strides[i] - if self.format: - pybuf.c_format = rffi.str2charp(self.format) - else: - pybuf.c_format = rffi.str2charp("B") + size = rffi.sizeof(cts.gettype('Py_buffer')) + pybuf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True) + pybuf = cts.cast('Py_buffer*', pybuf) + pybuf.c_buf = self.ptr + pybuf.c_len = self.size + pybuf.c_ndim = cts.cast('int', self.ndim) + pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape) + pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides) + for i in range(self.ndim): + pybuf.c_shape[i] = self.shape[i] + pybuf.c_strides[i] = self.strides[i] + fmt = rffi.str2charp(self.format if self.format else "B") + try: + pybuf.c_format = fmt generic_cpy_call(self.space, func_target, self.pyobj, pybuf) + finally: + lltype.free(fmt, flavor='raw') + lltype.free(pybuf, flavor='raw') decref(self.space, self.pyobj) self.pyobj = lltype.nullptr(PyObject.TO) + self.w_obj = None else: #do not call twice return @@ -211,6 +216,7 @@ view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO) view.c_internal = lltype.nullptr(rffi.VOIDP.TO) +DEFAULT_FMT = rffi.str2charp("B") @cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t, lltype.Signed, lltype.Signed], rffi.INT, error=-1) @@ -233,7 +239,8 @@ rffi.setintfield(view, 'c_ndim', 1) view.c_format = lltype.nullptr(rffi.CCHARP.TO) if (flags & PyBUF_FORMAT) == PyBUF_FORMAT: - view.c_format = rffi.str2charp("B") + # NB: this needs to be a static string, because nothing frees it + view.c_format = DEFAULT_FMT view.c_shape = lltype.nullptr(Py_ssize_tP.TO) if (flags & PyBUF_ND) == PyBUF_ND: view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape) diff --git a/pypy/module/cpyext/memoryobject.py b/pypy/module/cpyext/memoryobject.py --- a/pypy/module/cpyext/memoryobject.py +++ b/pypy/module/cpyext/memoryobject.py @@ -73,7 +73,7 @@ readonly=widen(view.c_readonly)) # Ensure view.c_buf is released upon object finalization fq.register_finalizer(buf) - # Allow subclassing W_MemeoryView + # Allow subclassing W_MemoryView w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) w_obj = space.allocate_instance(W_MemoryView, w_type) w_obj.__init__(buf) @@ -178,11 +178,9 @@ return (_IsCContiguous(view) or _IsFortranContiguous(view)) return 0 -@cpython_api([PyObject], PyObject, result_is_ll=True) +@cpython_api([PyObject], PyObject) def PyMemoryView_FromObject(space, w_obj): - w_memview = space.call_method(space.builtin, "memoryview", w_obj) - py_memview = make_ref(space, w_memview, w_obj) - return py_memview + return space.call_method(space.builtin, "memoryview", w_obj) @cts.decl("""PyObject * PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags)""") @@ -207,6 +205,7 @@ # copy view into obj.c_view, without creating a new view.c_obj typedescr = get_typedescr(W_MemoryView.typedef) py_obj = typedescr.allocate(space, space.w_memoryview) + py_mem = rffi.cast(PyMemoryViewObject, py_obj) mview = py_mem.c_view mview.c_buf = view.c_buf diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -29,7 +29,7 @@ from pypy.module.cpyext.typeobject import subtype_dealloc return subtype_dealloc.api_func - def allocate(self, space, w_type, itemcount=0): + def allocate(self, space, w_type, itemcount=0, immortal=False): # typically called from PyType_GenericAlloc via typedescr.allocate # this returns a PyObject with ob_refcnt == 1. @@ -50,7 +50,7 @@ assert size >= rffi.sizeof(PyObject.TO) buf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True, - add_memory_pressure=True) + add_memory_pressure=True, immortal=immortal) pyobj = rffi.cast(PyObject, buf) if pytype.c_tp_itemsize: pyvarobj = rffi.cast(PyVarObject, pyobj) @@ -102,7 +102,7 @@ basestruct = tp_basestruct if tp_alloc: - def allocate(self, space, w_type, itemcount=0): + def allocate(self, space, w_type, itemcount=0, immortal=False): return tp_alloc(space, w_type, itemcount) if tp_dealloc: @@ -151,7 +151,7 @@ class InvalidPointerException(Exception): pass -def create_ref(space, w_obj, w_userdata=None): +def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given interpreter object. @@ -163,7 +163,7 @@ itemcount = space.len_w(w_obj) # PyBytesObject and subclasses else: itemcount = 0 - py_obj = typedescr.allocate(space, w_type, itemcount=itemcount) + py_obj = typedescr.allocate(space, w_type, itemcount=itemcount, immortal=immortal) track_reference(space, py_obj, w_obj) # # py_obj.c_ob_refcnt should be exactly REFCNT_FROM_PYPY + 1 here, @@ -227,7 +227,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) -def as_pyobj(space, w_obj, w_userdata=None): +def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. This doesn't give a new reference, but the returned 'PyObject *' @@ -239,7 +239,7 @@ assert not is_pyobj(w_obj) py_obj = rawrefcount.from_obj(PyObject, w_obj) if not py_obj: - py_obj = create_ref(space, w_obj, w_userdata) + py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal) return py_obj else: return lltype.nullptr(PyObject.TO) @@ -270,7 +270,7 @@ return hop.inputconst(lltype.Bool, hop.s_result.const) @specialize.ll() -def make_ref(space, obj, w_userdata=None): +def make_ref(space, obj, w_userdata=None, immortal=False): """Increment the reference counter of the PyObject and return it. Can be called with either a PyObject or a W_Root. """ @@ -278,7 +278,7 @@ pyobj = rffi.cast(PyObject, obj) at_least = 1 else: - pyobj = as_pyobj(space, obj, w_userdata) + pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal) at_least = rawrefcount.REFCNT_FROM_PYPY if pyobj: assert pyobj.c_ob_refcnt >= at_least diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py --- a/pypy/module/cpyext/sequence.py +++ b/pypy/module/cpyext/sequence.py @@ -48,7 +48,7 @@ m as the message text. If the conversion otherwise, fails, reraise the original exception""" if isinstance(w_obj, W_ListObject): - # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM + # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM w_obj.convert_to_cpy_strategy(space) return w_obj try: @@ -313,7 +313,7 @@ self) w_clone.switch_to_object_strategy() return w_clone - + def copy_into(self, w_list, w_other): w_list.switch_to_object_strategy() w_list.strategy.copy_into(w_list, w_other) @@ -378,7 +378,7 @@ def is_empty_strategy(self): return False - + PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True})) diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c --- a/pypy/module/cpyext/test/array.c +++ b/pypy/module/cpyext/test/array.c @@ -2322,10 +2322,12 @@ if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1) { int ii, nn; + PyObject *ret; int n = PyList_Size(obj1); PyObject *v = getarrayitem(obj2, 0); long i = PyLong_AsLong(v); // XXX: error checking? - PyObject * ret = PyList_New(n*i); + Py_DECREF(v); + ret = PyList_New(n*i); for (ii = 0; ii < i; ii++) for (nn = 0; nn < n; nn++) { @@ -2338,10 +2340,12 @@ else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1) { int ii, nn; + PyObject *ret; int n = PyList_Size(obj2); PyObject *v = getarrayitem(obj1, 0); long i = PyLong_AsLong(v); - PyObject * ret = PyList_New(n*i); + Py_DECREF(v); + ret = PyList_New(n*i); for (ii = 0; ii < i; ii++) for (nn = 0; nn < n; nn++) { @@ -2374,31 +2378,35 @@ if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1) { int nn; + PyObject *ret; int n = PyList_Size(obj1); PyObject * lhs, * out; PyObject * rhs = getarrayitem(obj2, 0); - PyObject * ret = PyList_New(n); + ret = PyList_New(n); for (nn = 0; nn < n; nn++) { lhs = PyList_GetItem(obj1, nn); out = lhs->ob_type->tp_as_number->nb_multiply(lhs, rhs); PyList_SetItem(ret, nn, out); } + Py_DECREF(rhs); return ret; } else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1) { int nn; + PyObject *ret; int n = PyList_Size(obj2); PyObject * rhs, * out; PyObject * lhs = getarrayitem(obj1, 0); - PyObject * ret = PyList_New(n); + ret = PyList_New(n); for (nn = 0; nn < n; nn++) { rhs = PyList_GetItem(obj2, nn); out = lhs->ob_type->tp_as_number->nb_multiply(lhs, rhs); PyList_SetItem(ret, nn, out); } + Py_DECREF(lhs); return ret; } else if(obj1->ob_type == &Arraytype) @@ -2904,6 +2912,16 @@ return PyLong_FromLong(buf_len); } +static PyObject * +same_dealloc(PyObject *self, PyObject *args) +{ + PyObject *obj1, *obj2; + if (!PyArg_ParseTuple(args, "OO", &obj1, &obj2)) { + return NULL; + } + return PyLong_FromLong(obj1->ob_type->tp_dealloc == obj2->ob_type->tp_dealloc); +} + /*********************** Install Module **************************/ static PyMethodDef a_methods[] = { @@ -2914,6 +2932,7 @@ {"get_releasebuffer_cnt", (PyCFunction)get_releasebuffer_cnt, METH_NOARGS, NULL}, {"create_and_release_buffer", (PyCFunction)create_and_release_buffer, METH_O, NULL}, {"write_buffer_len", write_buffer_len, METH_O, NULL}, + {"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/test_api.py b/pypy/module/cpyext/test/test_api.py --- a/pypy/module/cpyext/test/test_api.py +++ b/pypy/module/cpyext/test/test_api.py @@ -6,7 +6,8 @@ from pypy.module.cpyext.api import ( slot_function, cpython_api, copy_header_files, INTERPLEVEL_API, Py_ssize_t, Py_ssize_tP, PyObject, cts) -from pypy.module.cpyext.test.test_cpyext import freeze_refcnts, LeakCheckingTest +from pypy.module.cpyext.test.test_cpyext import ( + freeze_refcnts, LeakCheckingTest) from pypy.interpreter.error import OperationError from rpython.rlib import rawrefcount import os @@ -21,17 +22,7 @@ class BaseApiTest(LeakCheckingTest): def setup_class(cls): space = cls.space - # warm up reference counts: - # - the posix module allocates a HCRYPTPROV on Windows - # - writing to stdout and stderr allocates a file lock - space.getbuiltinmodule("cpyext") - space.getbuiltinmodule(os.name) - space.call_function(space.getattr(space.sys.get("stderr"), - space.wrap("write")), - space.wrap("")) - space.call_function(space.getattr(space.sys.get("stdout"), - space.wrap("write")), - space.wrap("")) + cls.preload_builtins(space) class CAPI: def __getattr__(self, name): @@ -39,9 +30,6 @@ cls.api = CAPI() CAPI.__dict__.update(INTERPLEVEL_API) - print 'DONT_FREE_ANY_MORE' - rawrefcount._dont_free_any_more() - def raises(self, space, api, expected_exc, f, *args): if not callable(f): raise Exception("%s is not callable" % (f,)) diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -1,3 +1,4 @@ +import pytest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from pypy.conftest import option @@ -99,6 +100,19 @@ res = [1, 2, 3] * arr assert res == [2, 4, 6] + @pytest.mark.xfail + def test_subclass_dealloc(self): + module = self.import_module(name='array') + class Sub(module.array): + pass + + arr = Sub('i', [2]) + module.readbuffer_as_string(arr) + class A(object): + pass + assert not module.same_dealloc(arr, module.array('i', [2])) + assert module.same_dealloc(arr, A()) + def test_string_buf(self): module = self.import_module(name='array') arr = module.array('u', '123') diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -4,8 +4,11 @@ from pypy.tool.cpyext.extbuild import SystemCompilationInfo, HERE from pypy.interpreter.gateway import unwrap_spec, interp2app +from pypy.interpreter.error import OperationError from rpython.rtyper.lltypesystem import lltype from pypy.module.cpyext import api +from pypy.module.cpyext.api import cts +from pypy.module.cpyext.pyobject import from_ref from pypy.module.cpyext.state import State from rpython.tool import leakfinder from rpython.rlib import rawrefcount @@ -75,18 +78,94 @@ def freeze_refcnts(self): rawrefcount._dont_free_any_more() +def preload(space, name): + from pypy.module.cpyext.pyobject import make_ref + if '.' not in name: + w_obj = space.builtin.getdictvalue(space, name) + else: + module, localname = name.rsplit('.', 1) + code = "(): import {module}; return {module}.{localname}" + code = code.format(**locals()) + w_obj = space.appexec([], code) + make_ref(space, w_obj) + +def preload_expr(space, expr): + from pypy.module.cpyext.pyobject import make_ref + code = "(): return {}".format(expr) + w_obj = space.appexec([], code) + make_ref(space, w_obj) + +def is_interned_string(space, w_obj): + try: + s = space.str_w(w_obj) + except OperationError: + return False + return space.is_interned_str(s) + +def is_allowed_to_leak(space, obj): + from pypy.module.cpyext.methodobject import W_PyCFunctionObject + try: + w_obj = from_ref(space, cts.cast('PyObject*', obj._as_ptr())) + except: + return False + if isinstance(w_obj, W_PyCFunctionObject): + return True + # It's OK to "leak" some interned strings: if the pyobj is created by + # the test, but the w_obj is referred to from elsewhere. + return is_interned_string(space, w_obj) + +def _get_w_obj(space, c_obj): + return from_ref(space, cts.cast('PyObject*', c_obj._as_ptr())) + +class CpyextLeak(leakfinder.MallocMismatch): + def __str__(self): + lines = [leakfinder.MallocMismatch.__str__(self), ''] + lines.append( + "These objects are attached to the following W_Root objects:") + for c_obj in self.args[0]: + try: + lines.append(" %s" % (_get_w_obj(self.args[1], c_obj),)) + except: + pass + return '\n'.join(lines) + + class LeakCheckingTest(object): """Base class for all cpyext tests.""" spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array', 'itertools', 'time', 'binascii', 'mmap', ]) + @classmethod + def preload_builtins(cls, space): + """ + Eagerly create pyobjs for various builtins so they don't look like + leaks. + """ + for name in [ + 'buffer', 'mmap.mmap', + 'types.FunctionType', 'types.CodeType', + 'types.TracebackType', 'types.FrameType']: + preload(space, name) + for expr in ['type(str.join)']: + preload_expr(space, expr) + def cleanup(self): self.space.getexecutioncontext().cleanup_cpyext_state() for _ in range(5): rawrefcount._collect() self.space.user_del_action._run_finalizers() - leakfinder.stop_tracking_allocations(check=False) + try: + # set check=True to actually enable leakfinder + leakfinder.stop_tracking_allocations(check=False) + except leakfinder.MallocMismatch as e: + result = e.args[0] + filtered_result = {} + for obj, value in result.iteritems(): + if not is_allowed_to_leak(self.space, obj): + filtered_result[obj] = value + if filtered_result: + raise CpyextLeak(filtered_result, self.space) assert not self.space.finalizer_queue.next_dead() @@ -127,6 +206,7 @@ def debug_collect(space): rawrefcount._collect() + class AppTestCpythonExtensionBase(LeakCheckingTest): def setup_class(cls): @@ -136,13 +216,8 @@ cls.w_runappdirect = space.wrap(cls.runappdirect) if not cls.runappdirect: cls.sys_info = get_cpyext_info(space) - space.getbuiltinmodule("cpyext") - # 'import os' to warm up reference counts - w_import = space.builtin.getdictvalue(space, '__import__') - space.call_function(w_import, space.wrap("os")) - #state = cls.space.fromcache(RefcountState) ZZZ - #state.non_heaptypes_w[:] = [] cls.w_debug_collect = space.wrap(interp2app(debug_collect)) + cls.preload_builtins(space) else: def w_import_module(self, name, init=None, body='', filename=None, include_dirs=None, PY_SSIZE_T_CLEAN=False): diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -112,74 +112,14 @@ PyDict_Update(space, w_d, w_d2) assert space.unwrap(w_d) == dict(a='b') # unchanged - def test_iter(self, space): - w_dict = space.sys.getdict(space) - py_dict = make_ref(space, w_dict) - - ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') - ppos[0] = 0 - pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - - try: - w_copy = space.newdict() - while PyDict_Next(space, w_dict, ppos, pkey, pvalue): - w_key = from_ref(space, pkey[0]) - w_value = from_ref(space, pvalue[0]) - space.setitem(w_copy, w_key, w_value) - finally: - lltype.free(ppos, flavor='raw') - lltype.free(pkey, flavor='raw') - lltype.free(pvalue, flavor='raw') - - decref(space, py_dict) # release borrowed references - - assert space.eq_w(space.len(w_copy), space.len(w_dict)) - assert space.eq_w(w_copy, w_dict) - - def test_iterkeys(self, space): - w_dict = space.sys.getdict(space) - py_dict = make_ref(space, w_dict) - - ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') - pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - - keys_w = [] - values_w = [] - try: - ppos[0] = 0 - while PyDict_Next(space, w_dict, ppos, pkey, None): - w_key = from_ref(space, pkey[0]) - keys_w.append(w_key) - ppos[0] = 0 - while PyDict_Next(space, w_dict, ppos, None, pvalue): - w_value = from_ref(space, pvalue[0]) - values_w.append(w_value) - finally: - lltype.free(ppos, flavor='raw') - lltype.free(pkey, flavor='raw') - lltype.free(pvalue, flavor='raw') - - decref(space, py_dict) # release borrowed references - - assert space.eq_w(space.newlist(keys_w), - space.call_function( - space.w_list, - space.call_method(w_dict, "keys"))) - assert space.eq_w(space.newlist(values_w), - space.call_function( - space.w_list, - space.call_method(w_dict, "values"))) - def test_dictproxy(self, space): - w_dict = space.sys.get('modules') + w_dict = space.appexec([], """(): return {1: 2, 3: 4}""") w_proxy = PyDictProxy_New(space, w_dict) - assert space.contains_w(w_proxy, space.wrap('sys')) + assert space.contains_w(w_proxy, space.newint(1)) raises(OperationError, space.setitem, - w_proxy, space.wrap('sys'), space.w_None) + w_proxy, space.newint(1), space.w_None) raises(OperationError, space.delitem, - w_proxy, space.wrap('sys')) + w_proxy, space.newint(1)) raises(OperationError, space.call_method, w_proxy, 'clear') assert PyDictProxy_Check(space, w_proxy) @@ -248,6 +188,59 @@ d = {"a": 1} raises(AttributeError, module.update, d, [("c", 2)]) + def test_iter(self): + module = self.import_extension('foo', [ + ("copy", "METH_O", + ''' + Py_ssize_t pos = 0; + PyObject *key, *value; + PyObject* copy = PyDict_New(); + while (PyDict_Next(args, &pos, &key, &value)) + { + if (PyDict_SetItem(copy, key, value) < 0) + { + Py_DecRef(copy); + return NULL; + } + } + return copy; + ''')]) + d = {1: 'xyz', 3: 'abcd'} + copy = module.copy(d) + assert len(copy) == len(d) + assert copy == d + + def test_iterkeys(self): + module = self.import_extension('foo', [ + ("keys_and_values", "METH_O", + ''' + Py_ssize_t pos = 0; + PyObject *key, *value, *values; + PyObject* keys = PyList_New(0); + while (PyDict_Next(args, &pos, &key, NULL)) + { + if (PyList_Append(keys, key) < 0) + { + Py_DecRef(keys); + return NULL; + } + } + pos = 0; + values = PyList_New(0); + while (PyDict_Next(args, &pos, NULL, &value)) + { + if (PyList_Append(values, value) < 0) + { + Py_DecRef(keys); + Py_DecRef(values); + return NULL; + } + } + return Py_BuildValue("(NN)", keys, values); + ''')]) + d = {1: 'xyz', 3: 'abcd'} + assert module.keys_and_values(d) == (list(d.keys()), list(d.values())) + def test_typedict2(self): module = self.import_extension('foo', [ ("get_type_dict", "METH_O", @@ -260,6 +253,7 @@ ]) d = module.get_type_dict(1) assert d['real'].__get__(1, 1) == 1 + def test_advanced(self): module = self.import_extension('foo', [ ("dict_len", "METH_O", @@ -271,7 +265,7 @@ ''' int ret; PyObject * dict = PyTuple_GetItem(args, 0); - if (PyTuple_Size(args) < 3 || !dict || + if (PyTuple_Size(args) < 3 || !dict || !dict->ob_type->tp_as_mapping || !dict->ob_type->tp_as_mapping->mp_ass_subscript) return PyLong_FromLong(-1); @@ -284,7 +278,7 @@ ''' int ret; PyObject * dict = PyTuple_GetItem(args, 0); - if (PyTuple_Size(args) < 2 || !dict || + if (PyTuple_Size(args) < 2 || !dict || !dict->ob_type->tp_as_mapping || !dict->ob_type->tp_as_mapping->mp_ass_subscript) return PyLong_FromLong(-1); diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py --- a/pypy/module/cpyext/test/test_eval.py +++ b/pypy/module/cpyext/test/test_eval.py @@ -279,6 +279,7 @@ assert module.call_method("text") == 2 def test_CompileString_and_Exec(self): + import sys module = self.import_extension('foo', [ ("compile_string", "METH_NOARGS", """ @@ -313,6 +314,9 @@ print(mod.__dict__) assert mod.f(42) == 47 + # Clean-up + del sys.modules['cpyext_test_modname'] + def test_merge_compiler_flags(self): import sys module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/test/test_floatobject.py b/pypy/module/cpyext/test/test_floatobject.py --- a/pypy/module/cpyext/test/test_floatobject.py +++ b/pypy/module/cpyext/test/test_floatobject.py @@ -104,6 +104,7 @@ PyFloatObject* pfo = (PyFloatObject*)pyobj; int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) && PyFloat_Check(pfo) && PyFloat_CheckExact(pfo); + Py_DecRef(pyobj); return PyLong_FromLong(res);"""), ]) assert module.test() == 1 diff --git a/pypy/module/cpyext/test/test_funcobject.py b/pypy/module/cpyext/test/test_funcobject.py --- a/pypy/module/cpyext/test/test_funcobject.py +++ b/pypy/module/cpyext/test/test_funcobject.py @@ -44,7 +44,7 @@ w_function = space.appexec([], """(): def func(x, y, z): return x return func - """) + """, cache=False) w_code = PyFunction_GetCode(space, w_function) assert w_code.co_name == "func" @@ -61,7 +61,7 @@ w_code = space.appexec([], """(): def func(%s): %s return func.__code__ - """ % (signature, body)) + """ % (signature, body), cache=False) ref = make_ref(space, w_code) co_flags = rffi.cast(PyCodeObject, ref).c_co_flags decref(space, ref) 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 @@ -329,6 +329,7 @@ ret = obj->ob_type->tp_as_number->nb_power(obj, one, one); else ret = PyLong_FromLong(-1); + Py_DECREF(one); Py_DECREF(obj); return ret; """)]) diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -6,7 +6,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from pypy.interpreter.buffer import SimpleView -from pypy.module.cpyext.pyobject import from_ref +from pypy.module.cpyext.pyobject import make_ref, from_ref from pypy.module.cpyext.memoryobject import PyMemoryViewObject only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" @@ -14,9 +14,9 @@ class TestMemoryViewObject(BaseApiTest): def test_frombuffer(self, space, api): w_view = SimpleView(StringBuffer("hello")).wrap(space) + w_memoryview = api.PyMemoryView_FromObject(w_view) c_memoryview = rffi.cast( - PyMemoryViewObject, api.PyMemoryView_FromObject(w_view)) - w_memoryview = from_ref(space, c_memoryview) + PyMemoryViewObject, make_ref(space, w_memoryview)) view = c_memoryview.c_view assert view.c_ndim == 1 f = rffi.charp2str(view.c_format) @@ -34,6 +34,7 @@ assert space.eq_w(space.getattr(w_mv, w_f), space.getattr(w_memoryview, w_f)) api.Py_DecRef(ref) + api.Py_DecRef(w_memoryview) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): @@ -64,7 +65,6 @@ """)]) result = module.fillinfo() assert b"hello, world." == result - del result def test_0d(self): module = self.import_extension('foo', [ @@ -195,8 +195,6 @@ # in <bound method ConcreteArray.__del__ ...> ignored def test_releasebuffer(self): - if not self.runappdirect: - skip("Fails due to ll2ctypes nonsense") module = self.import_extension('foo', [ ("create_test", "METH_NOARGS", """ diff --git a/pypy/module/cpyext/test/test_traceback.py b/pypy/module/cpyext/test/test_traceback.py --- a/pypy/module/cpyext/test/test_traceback.py +++ b/pypy/module/cpyext/test/test_traceback.py @@ -3,17 +3,19 @@ from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref from pypy.module.cpyext.pytraceback import PyTracebackObject from pypy.interpreter.pytraceback import PyTraceback -from pypy.interpreter.pyframe import PyFrame +from pypy.interpreter.baseobjspace import AppExecCache class TestPyTracebackObject(BaseApiTest): def test_traceback(self, space, api): - w_traceback = space.appexec([], """(): + src = """(): import sys try: 1/0 except: return sys.exc_info()[2] - """) + """ + w_traceback = space.appexec([], src) + py_obj = make_ref(space, w_traceback) py_traceback = rffi.cast(PyTracebackObject, py_obj) assert (from_ref(space, rffi.cast(PyObject, py_traceback.c_ob_type)) is @@ -38,3 +40,5 @@ assert lltype.normalizeptr(py_traceback) is None api.Py_DecRef(py_obj) + # hack to allow the code object to be freed + del space.fromcache(AppExecCache).content[src] diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py --- a/pypy/module/cpyext/test/test_tupleobject.py +++ b/pypy/module/cpyext/test/test_tupleobject.py @@ -24,6 +24,7 @@ def test_tuple_realize_refuses_nulls(self, space, api): py_tuple = api.PyTuple_New(1) py.test.raises(FatalError, from_ref, space, py_tuple) + api.Py_DecRef(py_tuple) def test_tuple_resize(self, space, api): w_42 = space.wrap(42) @@ -70,6 +71,7 @@ w_tuple = from_ref(space, py_tuple) assert space.eq_w(w_tuple, space.newtuple([space.wrap(42), space.wrap(43)])) + api.Py_DecRef(py_tuple) def test_getslice(self, space, api): w_tuple = space.newtuple([space.wrap(i) for i in range(10)]) @@ -174,6 +176,7 @@ res = PyTuple_SetItem(tuple, 0, one); if (res != 0) { + Py_DECREF(one); Py_DECREF(tuple); return NULL; } @@ -187,14 +190,13 @@ /* Do something that uses the tuple, but does not incref */ t2 = PyTuple_GetSlice(tuple, 0, 1); Py_DECREF(t2); - Py_INCREF(one); res = PyTuple_SetItem(tuple, 0, one); - Py_DECREF(tuple); if (res != 0) { - Py_DECREF(one); + Py_DECREF(tuple); return NULL; } + Py_DECREF(tuple); Py_INCREF(Py_None); return Py_None; """), @@ -205,4 +207,3 @@ raises(SystemError, module.set_after_use, s) else: module.set_after_use(s) - diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -143,6 +143,7 @@ old_ref = tupleobj.c_ob_item[index] if pyobj_has_w_obj(ref): # similar but not quite equal to ref.c_ob_refcnt != 1 on CPython + decref(space, py_obj) raise oefmt(space.w_SystemError, "PyTuple_SetItem called on tuple after" " use of tuple") tupleobj.c_ob_item[index] = py_obj # consumes a reference 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 @@ -194,6 +194,8 @@ py_getsetdef = make_GetSet(space, w_obj) assert space.isinstance_w(w_userdata, space.w_type) w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata) + # now w_obj.getset is py_getsetdef, which was freshly allocated + # XXX how is this ever released? # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset @@ -823,7 +825,9 @@ bases_w = [] else: bases_w = [from_ref(space, base_pyo)] - pto.c_tp_bases = make_ref(space, space.newtuple(bases_w)) + is_heaptype = bool(pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) + pto.c_tp_bases = make_ref(space, space.newtuple(bases_w), + immortal=not is_heaptype) def finish_type_2(space, pto, w_obj): """ diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py --- a/pypy/objspace/std/specialisedtupleobject.py +++ b/pypy/objspace/std/specialisedtupleobject.py @@ -74,8 +74,7 @@ elif typetuple[i] == int: # mimic cpythons behavior of a hash value of -2 for -1 y = value - if y == -1: - y = -2 + y -= (y == -1) # No explicit condition, to avoid JIT bridges elif typetuple[i] == float: # get the correct hash for float which is an # integer & other less frequent cases diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py b/pypy/objspace/std/test/test_specialisedtupleobject.py --- a/pypy/objspace/std/test/test_specialisedtupleobject.py +++ b/pypy/objspace/std/test/test_specialisedtupleobject.py @@ -37,6 +37,8 @@ self.space.eq(self.space.hash(N_w_tuple), self.space.hash(S_w_tuple))) + hash_test([-1, -1]) + hash_test([-1.0, -1.0]) hash_test([1, 2]) hash_test([1.5, 2.8]) hash_test([1.0, 2.0]) diff --git a/rpython/jit/metainterp/logger.py b/rpython/jit/metainterp/logger.py --- a/rpython/jit/metainterp/logger.py +++ b/rpython/jit/metainterp/logger.py @@ -13,10 +13,11 @@ self.guard_number = guard_number def log_loop_from_trace(self, trace, memo): + debug_start("jit-log-noopt") if not have_debug_prints(): + debug_stop("jit-log-noopt") return inputargs, ops = self._unpack_trace(trace) - debug_start("jit-log-noopt") debug_print("# Traced loop or bridge with", len(ops), "ops") logops = self._log_operations(inputargs, ops, None, memo) debug_stop("jit-log-noopt") diff --git a/rpython/jit/metainterp/optimizeopt/bridgeopt.py b/rpython/jit/metainterp/optimizeopt/bridgeopt.py --- a/rpython/jit/metainterp/optimizeopt/bridgeopt.py +++ b/rpython/jit/metainterp/optimizeopt/bridgeopt.py @@ -18,6 +18,10 @@ # (<box1> <descr> <box2>) length times, if getfield(box1, descr) == box2 # both boxes should be in the liveboxes # +# <length> +# (<box1> <index> <descr> <box2>) length times, if getarrayitem_gc(box1, index, descr) == box2 +# both boxes should be in the liveboxes +# # ---- @@ -82,18 +86,26 @@ # structs # XXX could be extended to arrays if optimizer.optheap: - triples = optimizer.optheap.serialize_optheap(available_boxes) + triples_struct, triples_array = optimizer.optheap.serialize_optheap(available_boxes) # can only encode descrs that have a known index into # metainterp_sd.all_descrs - triples = [triple for triple in triples if triple[1].descr_index != -1] - numb_state.append_int(len(triples)) - for box1, descr, box2 in triples: - index = descr.descr_index + triples_struct = [triple for triple in triples_struct if triple[1].descr_index != -1] + numb_state.append_int(len(triples_struct)) + for box1, descr, box2 in triples_struct: + descr_index = descr.descr_index + numb_state.append_short(tag_box(box1, liveboxes_from_env, memo)) + numb_state.append_int(descr_index) + numb_state.append_short(tag_box(box2, liveboxes_from_env, memo)) + numb_state.append_int(len(triples_array)) + for box1, index, descr, box2 in triples_array: + descr_index = descr.descr_index numb_state.append_short(tag_box(box1, liveboxes_from_env, memo)) numb_state.append_int(index) + numb_state.append_int(descr_index) numb_state.append_short(tag_box(box2, liveboxes_from_env, memo)) else: numb_state.append_int(0) + numb_state.append_int(0) def deserialize_optimizer_knowledge(optimizer, resumestorage, frontend_boxes, liveboxes): reader = resumecode.Reader(resumestorage.rd_numb) @@ -123,13 +135,24 @@ if not optimizer.optheap: return length = reader.next_item() - result = [] + result_struct = [] + for i in range(length): + tagged = reader.next_item() + box1 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu) + descr_index = reader.next_item() + descr = metainterp_sd.all_descrs[descr_index] + tagged = reader.next_item() + box2 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu) + result_struct.append((box1, descr, box2)) + length = reader.next_item() + result_array = [] for i in range(length): tagged = reader.next_item() box1 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu) index = reader.next_item() - descr = metainterp_sd.all_descrs[index] + descr_index = reader.next_item() + descr = metainterp_sd.all_descrs[descr_index] tagged = reader.next_item() box2 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu) - result.append((box1, descr, box2)) - optimizer.optheap.deserialize_optheap(result) + result_array.append((box1, index, descr, box2)) + optimizer.optheap.deserialize_optheap(result_struct, result_array) diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py --- a/rpython/jit/metainterp/optimizeopt/heap.py +++ b/rpython/jit/metainterp/optimizeopt/heap.py @@ -698,7 +698,7 @@ return self.emit(op) def serialize_optheap(self, available_boxes): - result = [] + result_getfield = [] for descr, cf in self.cached_fields.iteritems(): if cf._lazy_set: continue # XXX safe default for now @@ -706,27 +706,62 @@ if not parent_descr.is_object(): continue # XXX could be extended to non-instance objects for i, box1 in enumerate(cf.cached_structs): - if box1 not in available_boxes: + if not box1.is_constant() and box1 not in available_boxes: continue structinfo = cf.cached_infos[i] - box2 = structinfo.getfield(descr).get_box_replacement() - if isinstance(box2, Const) or box2 in available_boxes: - result.append((box1, descr, box2)) - return result + box2 = structinfo.getfield(descr) + if box2 is None: + # XXX this should not happen, as it is an invariant + # violation! yet it does if box1 is a constant + continue + box2 = box2.get_box_replacement() + if box2.is_constant() or box2 in available_boxes: + result_getfield.append((box1, descr, box2)) + result_array = [] + for descr, indexdict in self.cached_arrayitems.iteritems(): + for index, cf in indexdict.iteritems(): + if cf._lazy_set: + continue # XXX safe default for now + for i, box1 in enumerate(cf.cached_structs): + if not box1.is_constant() and box1 not in available_boxes: + continue + arrayinfo = cf.cached_infos[i] + box2 = arrayinfo.getitem(descr, index) + if box2 is None: + # XXX this should not happen, as it is an invariant + # violation! yet it does if box1 is a constant + continue + box2 = box2.get_box_replacement() + if box2.is_constant() or box2 in available_boxes: + result_array.append((box1, index, descr, box2)) + return result_getfield, result_array - def deserialize_optheap(self, triples): - for box1, descr, box2 in triples: + def deserialize_optheap(self, triples_struct, triples_array): + for box1, descr, box2 in triples_struct: parent_descr = descr.get_parent_descr() assert parent_descr.is_object() - structinfo = box1.get_forwarded() - if not isinstance(structinfo, info.AbstractVirtualPtrInfo): - structinfo = info.InstancePtrInfo(parent_descr) - structinfo.init_fields(parent_descr, descr.get_index()) - box1.set_forwarded(structinfo) - + if box1.is_constant(): + structinfo = info.ConstPtrInfo(box1) + else: + structinfo = box1.get_forwarded() + if not isinstance(structinfo, info.AbstractVirtualPtrInfo): + structinfo = info.InstancePtrInfo(parent_descr) + structinfo.init_fields(parent_descr, descr.get_index()) + box1.set_forwarded(structinfo) cf = self.field_cache(descr) structinfo.setfield(descr, box1, box2, optheap=self, cf=cf) + for box1, index, descr, box2 in triples_array: + if box1.is_constant(): + arrayinfo = info.ConstPtrInfo(box1) + else: + arrayinfo = box1.get_forwarded() + if not isinstance(arrayinfo, info.AbstractVirtualPtrInfo): + arrayinfo = info.ArrayPtrInfo(descr) + box1.set_forwarded(arrayinfo) + cf = self.arrayitem_cache(descr, index) + arrayinfo.setitem(descr, index, box1, box2, optheap=self, cf=cf) + dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_', default=OptHeap.emit) diff --git a/rpython/jit/metainterp/test/test_bridgeopt.py b/rpython/jit/metainterp/test/test_bridgeopt.py --- a/rpython/jit/metainterp/test/test_bridgeopt.py +++ b/rpython/jit/metainterp/test/test_bridgeopt.py @@ -61,7 +61,7 @@ serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None) - assert unpack_numbering(numb_state.create_numbering()) == [1, 0b010000, 0] + assert unpack_numbering(numb_state.create_numbering()) == [1, 0b010000, 0, 0] rbox1 = InputArgRef() rbox2 = InputArgRef() @@ -97,7 +97,7 @@ serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None) - assert len(numb_state.create_numbering().code) == 2 + math.ceil(len(refboxes) / 6.0) + assert len(numb_state.create_numbering().code) == 3 + math.ceil(len(refboxes) / 6.0) dct = {box: cls for box, known_class in boxes_known_classes @@ -143,11 +143,7 @@ def test_bridge_field_read(self): myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a']) class A(object): - def f(self): - return 1 - class B(A): - def f(self): - return 2 + pass class M(object): _immutable_fields_ = ['x'] def __init__(self, x): @@ -156,14 +152,12 @@ m1 = M(1) m2 = M(2) def f(x, y, n): + a = A() + a.n = n if x: - a = A() a.m = m1 - a.n = n else: - a = B() a.m = m2 - a.n = n a.x = 0 res = 0 while y > 0: @@ -186,3 +180,105 @@ self.check_resops(getfield_gc_i=4) # 3x a.x, 1x a.n self.check_resops(getfield_gc_r=1) # in main loop + def test_bridge_field_read_constants(self): + myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n']) + class A(object): + pass + class M(object): + _immutable_fields_ = ['x'] + def __init__(self, x): + self.x = x + + m1 = M(1) + m2 = M(2) + a = A() + a.m = m1 + a.n = 0 + def f(x, y, n): + if x: + a.m = m1 + a.n = n + else: + a.m = m2 + a.n = n + a.x = 0 + res = 0 + while y > 0: + myjitdriver.jit_merge_point(y=y, n=n, res=res) + n1 = a.n + m = jit.promote(a.m) + res += m.x + a.x += 1 + if y > n: + res += 1 + m = jit.promote(a.m) + res += m.x + res += n1 + a.n + y -= 1 + return res + res = self.meta_interp(f, [6, 32, 16]) + assert res == f(6, 32, 16) + self.check_trace_count(3) + self.check_resops(guard_value=1) + self.check_resops(getfield_gc_i=4) # 3x a.x, 1x a.n + self.check_resops(getfield_gc_r=1) # in main loop + + def test_bridge_array_read(self): + myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a']) + def f(x, y, n): + if x: + a = [1, n, 0] + else: + a = [2, n, 0] + res = 0 + while y > 0: + myjitdriver.jit_merge_point(y=y, n=n, res=res, a=a) + n1 = a[1] + m = jit.promote(a[0]) + res += m + a[2] += 1 + if y > n: + res += 1 + m = jit.promote(a[0]) + res += m + res += n1 + a[1] + y -= 1 + return res + res = self.meta_interp(f, [6, 32, 16]) + assert res == f(6, 32, 16) + self.check_trace_count(3) + self.check_resops(guard_value=1) + self.check_resops(getarrayitem_gc_i=4) + + def test_bridge_array_read_constant(self): + myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n']) + class A(object): + pass + a = A() + a.l = [1, -65, 0] + def f(x, y, n): + if x: + a.l[0] = 1 + else: + a.l[0] = 2 + a.l[1] = n + a.l[2] = 0 + res = 0 + while y > 0: + myjitdriver.jit_merge_point(y=y, n=n, res=res) + n1 = a.l[1] + m = jit.promote(a.l[0]) + res += m + a.l[2] += 1 + if y > n: + res += 1 + m = jit.promote(a.l[0]) + res += m + res += n1 + a.l[1] + y -= 1 + return res + res = self.meta_interp(f, [6, 32, 16]) + assert res == f(6, 32, 16) + self.check_trace_count(3) + self.check_resops(guard_value=1) + self.check_resops(getarrayitem_gc_i=5) diff --git a/rpython/rlib/rpath.py b/rpython/rlib/rpath.py --- a/rpython/rlib/rpath.py +++ b/rpython/rlib/rpath.py @@ -5,6 +5,7 @@ import os, stat from rpython.rlib import rposix from rpython.rlib.signature import signature +from rpython.rlib.rstring import assert_str0 from rpython.annotator.model import s_Str0 @@ -31,9 +32,11 @@ """Test whether a path is absolute""" return s.startswith('/') +@signature(s_Str0, returns=s_Str0) def _posix_rnormpath(path): """Normalize path, eliminating double slashes, etc.""" slash, dot = '/', '.' + assert_str0(dot) if path == '': return dot initial_slashes = path.startswith('/') @@ -56,6 +59,7 @@ path = slash.join(comps) if initial_slashes: path = slash*initial_slashes + path + assert_str0(path) return path or dot @signature(s_Str0, returns=s_Str0) @@ -66,6 +70,7 @@ if not _posix_risabs(path): cwd = os.getcwd() path = _posix_rjoin(cwd, path) + assert path is not None return _posix_rnormpath(path) except OSError: return path diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py --- a/rpython/rlib/rvmprof/cintf.py +++ b/rpython/rlib/rvmprof/cintf.py @@ -18,7 +18,7 @@ SHARED = SRC.join('shared') BACKTRACE = SHARED.join('libbacktrace') -compile_extra = ['-DRPYTHON_VMPROF', '-O3'] +compile_extra = ['-DRPYTHON_VMPROF'] separate_module_files = [ SHARED.join('symboltable.c'), SHARED.join('vmprof_unix.c') diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -2208,7 +2208,7 @@ return _ptr(Ptr(T), o, solid) @analyzer_for(malloc) -def ann_malloc(s_T, s_n=None, s_flavor=None, s_zero=None, +def ann_malloc(s_T, s_n=None, s_flavor=None, s_immortal=None, s_zero=None, s_track_allocation=None, s_add_memory_pressure=None, s_nonmovable=None): assert (s_n is None or s_n.knowntype == int diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py --- a/rpython/rtyper/rbuiltin.py +++ b/rpython/rtyper/rbuiltin.py @@ -347,19 +347,20 @@ # annotation of low-level types @typer_for(lltype.malloc) -def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None, - i_add_memory_pressure=None, i_nonmovable=None): +def rtype_malloc(hop, i_flavor=None, i_immortal=None, i_zero=None, + i_track_allocation=None, i_add_memory_pressure=None, i_nonmovable=None): assert hop.args_s[0].is_constant() vlist = [hop.inputarg(lltype.Void, arg=0)] opname = 'malloc' kwds_v = parse_kwds( hop, (i_flavor, lltype.Void), + (i_immortal, None), (i_zero, None), (i_track_allocation, None), (i_add_memory_pressure, None), (i_nonmovable, None)) - (v_flavor, v_zero, v_track_allocation, + (v_flavor, v_immortal, v_zero, v_track_allocation, v_add_memory_pressure, v_nonmovable) = kwds_v flags = {'flavor': 'gc'} if v_flavor is not None: diff --git a/rpython/rtyper/tool/rffi_platform.py b/rpython/rtyper/tool/rffi_platform.py --- a/rpython/rtyper/tool/rffi_platform.py +++ b/rpython/rtyper/tool/rffi_platform.py @@ -545,7 +545,7 @@ def question(self, ask_gcc): try: - ask_gcc(self.name + ';') + ask_gcc('(void)' + self.name + ';') return True except CompilationError: return False diff --git a/rpython/translator/c/src/signals.c b/rpython/translator/c/src/signals.c --- a/rpython/translator/c/src/signals.c +++ b/rpython/translator/c/src/signals.c @@ -31,11 +31,11 @@ # endif #endif +#define N_LONGBITS (8 * sizeof(long)) +#define N_LONGSIG ((NSIG - 1) / N_LONGBITS + 1) + struct pypysig_long_struct pypysig_counter = {0}; -static char volatile pypysig_flags[NSIG] = {0}; -static int volatile pypysig_occurred = 0; -/* pypysig_occurred is only an optimization: it tells if any - pypysig_flags could be set. */ +static long volatile pypysig_flags_bits[N_LONGSIG]; static int wakeup_fd = -1; static int wakeup_with_nul_byte = 1; @@ -73,12 +73,28 @@ #endif } +#ifdef _WIN32 +#define atomic_cas(ptr, oldv, newv) (InterlockedCompareExchange(ptr, \ + newv, oldv) == (oldv)) +#else +#define atomic_cas(ptr, oldv, newv) __sync_bool_compare_and_swap(ptr, \ + oldv, newv) +#endif + void pypysig_pushback(int signum) { if (0 <= signum && signum < NSIG) { - pypysig_flags[signum] = 1; - pypysig_occurred = 1; + int ok, index = signum / N_LONGBITS; + unsigned long bitmask = 1UL << (signum % N_LONGBITS); + do + { + long value = pypysig_flags_bits[index]; + if (value & bitmask) + break; /* already set */ + ok = atomic_cas(&pypysig_flags_bits[index], value, value | bitmask); + } while (!ok); + pypysig_counter.value = -1; } } @@ -161,19 +177,22 @@ int pypysig_poll(void) { - if (pypysig_occurred) - { - int i; - pypysig_occurred = 0; - for (i=0; i<NSIG; i++) - if (pypysig_flags[i]) - { - pypysig_flags[i] = 0; - pypysig_occurred = 1; /* maybe another signal is pending */ - return i; - } + int index; + for (index = 0; index < N_LONGSIG; index++) { + long value; + retry: + value = pypysig_flags_bits[index]; + if (value != 0L) { + int j = 0; + while ((value & (1UL << j)) == 0) + j++; + if (!atomic_cas(&pypysig_flags_bits[index], value, + value & ~(1UL << j))) + goto retry; + return index * N_LONGBITS + j; + } } - return -1; /* no pending signal */ + return -1; /* no pending signal */ } int pypysig_set_wakeup_fd(int fd, int with_nul_byte) diff --git a/rpython/translator/platform/__init__.py b/rpython/translator/platform/__init__.py --- a/rpython/translator/platform/__init__.py +++ b/rpython/translator/platform/__init__.py @@ -129,7 +129,7 @@ # some helpers which seem to be cross-platform enough def _execute_c_compiler(self, cc, args, outname, cwd=None): - log.execute(cc + ' ' + ' '.join(args)) + #log.execute(cc + ' ' + ' '.join(args)) # 'cc' can also contain some options for the C compiler; # e.g. it can be "gcc -m32". We handle it by splitting on ' '. cclist = cc.split() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit