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

Reply via email to