Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: py3k
Changeset: r85869:bb148d9bb7e9
Date: 2016-07-26 20:40 +0100
http://bitbucket.org/pypy/pypy/changeset/bb148d9bb7e9/

Log:    hg merge default

diff --git a/dotviewer/graphparse.py b/dotviewer/graphparse.py
--- a/dotviewer/graphparse.py
+++ b/dotviewer/graphparse.py
@@ -85,10 +85,11 @@
     pass
 
 def splitline(line, re_word = re.compile(r'[^\s"]\S*|["]["]|["].*?[^\\]["]')):
+    import ast
     result = []
     for word in re_word.findall(line):
         if word.startswith('"'):
-            word = eval(word)
+            word = ast.literal_eval(word)
         result.append(word)
     return result
 
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -198,10 +198,13 @@
     return tp._alignmentofinstances()
 
 @builtinify
-def byref(cdata):
+def byref(cdata, offset=0):
     # "pointer" is imported at the end of this module to avoid circular
     # imports
-    return pointer(cdata)
+    ptr = pointer(cdata)
+    if offset != 0:
+        ptr._buffer[0] += offset
+    return ptr
 
 def cdata_from_address(self, address):
     # fix the address: turn it into as unsigned, in case it's a negative number
diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst
--- a/pypy/doc/faq.rst
+++ b/pypy/doc/faq.rst
@@ -335,3 +335,58 @@
 
 This will disable SELinux's protection and allow PyPy to configure correctly.
 Be sure to enable it again if you need it!
+
+
+How should I report a bug?
+--------------------------
+
+Our bug tracker is here: https://bitbucket.org/pypy/pypy/issues/
+
+Missing features or incompatibilities with CPython are considered
+bugs, and they are welcome.  (See also our list of `known
+incompatibilities`__.)
+
+.. __: http://pypy.org/compat.html
+
+For bugs of the kind "I'm getting a PyPy crash or a strange
+exception", please note that: **We can't do anything without
+reproducing the bug ourselves**.  We cannot do anything with
+tracebacks from gdb, or core dumps.  This is not only because the
+standard PyPy is compiled without debug symbols.  The real reason is
+that a C-level traceback is usually of no help at all in PyPy.
+Debugging PyPy can be annoying.
+
+In more details:
+
+* First, please give the exact PyPy version, and the OS.
+
+* It might help focus our search if we know if the bug can be
+  reproduced on a "``pypy --jit off``" or not.  If "``pypy --jit
+  off``" always works, then the problem might be in the JIT.
+  Otherwise, we know we can ignore that part.
+
+* If you got the bug using only Open Source components, please give a
+  step-by-step guide that we can follow to reproduce the problem
+  ourselves.  Don't assume we know anything about any program other
+  than PyPy.  We would like a guide that we can follow point by point
+  (without guessing or having to figure things out)
+  on a machine similar to yours, starting from a bare PyPy, until we
+  see the same problem.  (If you can, you can try to reduce the number
+  of steps and the time it needs to run, but that is not mandatory.)
+
+* If the bug involves Closed Source components, or just too many Open
+  Source components to install them all ourselves, then maybe you can
+  give us some temporary ssh access to a machine where the bug can be
+  reproduced.
+
+* If giving us access would require us to sign a NDA, then we can
+  consider a commerical support contract for a small sum of money.
+
+* If even that is not possible for you, then sorry, we can't help.
+
+Of course, you can try to debug the problem yourself, and we can help
+you get started if you ask on the #pypy IRC channel, but be prepared:
+debugging an annoying PyPy problem usually involves quite a lot of gdb
+in auto-generated C code, and at least some knowledge about the
+various components involved, from PyPy's own RPython source code to
+the GC and possibly the JIT.
diff --git a/pypy/module/_cffi_backend/ctypestruct.py 
b/pypy/module/_cffi_backend/ctypestruct.py
--- a/pypy/module/_cffi_backend/ctypestruct.py
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -90,7 +90,7 @@
         self.force_lazy_struct()
         space = self.space
         try:
-            cfield = self._fields_dict[fieldname]
+            cfield = self._getcfield_const(fieldname)
         except KeyError:
             raise OperationError(space.w_KeyError, space.wrap(fieldname))
         if cfield.bitshift >= 0:
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
@@ -2366,6 +2366,60 @@
     (binaryfunc)NULL, /* nb_divide */
 };
 
+static PyObject*
+array_base_multiply(PyObject* obj1, PyObject* obj2)
+{
+    if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' 
&& Py_SIZE(obj2) == 1)
+    {
+        int nn;
+        int n = PyList_Size(obj1);
+        PyObject *v = getarrayitem(obj2, 0);
+        int i = ((PyIntObject*)v)->ob_ival;
+        PyObject * ret = PyList_New(n);
+        for (nn = 0; nn < n; nn++)
+        {
+            v = PyList_GetItem(obj1, nn);
+            if (PyInt_Check(v))
+                PyList_SetItem(ret, nn, PyLong_FromLong(i * 
((PyIntObject*)v)->ob_ival));
+            else
+                PyList_SetItem(ret, nn, v);
+        }
+        return ret;
+    }
+    else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 
'i' && Py_SIZE(obj1) == 1)
+    {
+        int nn;
+        int n = PyList_Size(obj2);
+        PyObject *v = getarrayitem(obj1, 0);
+        int i = ((PyIntObject*)v)->ob_ival;
+        PyObject * ret = PyList_New(n);
+        for (nn = 0; nn < n; nn++)
+        {
+            v = PyList_GetItem(obj2, nn);
+            if (PyInt_Check(v))
+                PyList_SetItem(ret, nn, PyLong_FromLong(i * 
((PyIntObject*)v)->ob_ival));
+            else
+                PyList_SetItem(ret, nn, v);
+        }
+        return ret;
+    }
+    else if(obj1->ob_type == &Arraytype)
+        fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
+            ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); 
+    else if(obj2->ob_type == &Arraytype)
+        fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
+            ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); 
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+static PyNumberMethods array_base_as_number = {
+    (binaryfunc)NULL, /* nb_add*/
+    (binaryfunc)NULL, /* nb_subtract */
+    (binaryfunc)array_base_multiply, /* nb_multiply */
+    (binaryfunc)NULL, /* nb_divide */
+};
+
 static PyMappingMethods array_as_mapping = {
     (lenfunc)array_length,
     (binaryfunc)array_subscr,
@@ -2624,6 +2678,49 @@
 
 static PyObject *array_iter(arrayobject *ao);
 
+static PyTypeObject ArrayBasetype = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "array.basearray",
+    sizeof(arrayobject),
+    0,
+    (destructor)array_dealloc,                  /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_compare */
+    (reprfunc)array_repr,                       /* tp_repr */
+    &array_base_as_number,                      /* tp_as_number*/
+    &array_as_sequence,                         /* tp_as_sequence*/
+    &array_as_mapping,                          /* tp_as_mapping*/
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    0,                                          /* tp_setattro */
+    &array_as_buffer,                           /* tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 
+    Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_CHECKTYPES,  /* tp_flags */
+    arraytype_doc,                              /* tp_doc */
+    0,                                          /* tp_traverse */
+    0,                                          /* tp_clear */
+    array_richcompare,                          /* tp_richcompare */
+    offsetof(arrayobject, weakreflist),         /* tp_weaklistoffset */
+    (getiterfunc)array_iter,                    /* tp_iter */
+    0,                                          /* tp_iternext */
+    array_methods,                              /* tp_methods */
+    0,                                          /* tp_members */
+    array_getsets,                              /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    PyType_GenericAlloc,                        /* tp_alloc */
+    array_new,                                  /* tp_new */
+    PyObject_Del,                               /* tp_free */
+};
+
 static PyTypeObject Arraytype = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "array.array",
@@ -2791,6 +2888,8 @@
     register Py_UNICODE *p;
     struct arraydescr *descr;
 
+    ArrayBasetype.ob_type = &PyType_Type;
+    Arraytype.tp_base = &ArrayBasetype;
     if (PyType_Ready(&Arraytype) < 0)
         return NULL;
     Py_TYPE(&PyArrayIter_Type) = &PyType_Type;
@@ -2798,10 +2897,11 @@
     if (m == NULL)
         return NULL;
 
-    Py_INCREF((PyObject *)&Arraytype);
+    if (PyType_Ready(&ArrayBasetype) < 0)
+        return;
     PyModule_AddObject(m, "ArrayType", (PyObject *)&Arraytype);
-    Py_INCREF((PyObject *)&Arraytype);
     PyModule_AddObject(m, "array", (PyObject *)&Arraytype);
+    PyModule_AddObject(m, "arraybase", (PyObject *)&ArrayBasetype);
 
     for (descr=descriptors; descr->typecode != '\0'; descr++) {
         size++;
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -747,6 +747,7 @@
     if (PyType_Ready(&UnicodeSubtype3) < 0)
         INITERROR;
 
+    TupleLike.tp_flags = Py_TPFLAGS_DEFAULT;
     TupleLike.tp_base = &PyTuple_Type;
     if (PyType_Ready(&TupleLike) < 0)
         INITERROR;
diff --git a/pypy/module/cpyext/test/test_bytesobject.py 
b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -113,9 +113,10 @@
         module = self.import_extension('foo', [
             ("getbytes", "METH_NOARGS",
              """
-                 PyObject* s1 = PyBytes_FromStringAndSize("test", 4);
-                 char* c = PyBytes_AsString(s1);
-                 PyObject* s2 = PyBytes_FromStringAndSize(c, 4);
+                 char *c;
+                 PyObject* s2, *s1 = PyBytes_FromStringAndSize("test", 4);
+                 c = PyBytes_AsString(s1);
+                 s2 = PyBytes_FromStringAndSize(c, 4);
                  Py_DECREF(s1);
                  return s2;
              """),
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
@@ -78,7 +78,7 @@
     else:
         libraries = []
         if sys.platform.startswith('linux'):
-            compile_extra = ["-Werror", "-g", "-O0", "-fPIC"]
+            compile_extra = ["-Werror", "-g", "-O0", "-Wp,-U_FORTIFY_SOURCE", 
"-fPIC"]
             link_extra = ["-g"]
         else:
             compile_extra = link_extra = None
@@ -129,6 +129,7 @@
     return str(soname)
 
 def freeze_refcnts(self):
+    rawrefcount._dont_free_any_more()
     return #ZZZ
     state = self.space.fromcache(RefcountState)
     self.frozen_refcounts = {}
diff --git a/pypy/module/itertools/interp_itertools.py 
b/pypy/module/itertools/interp_itertools.py
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -803,7 +803,7 @@
     data before the other iterator, it is faster to use list() instead
     of tee()
 
-    Equivalent to :
+    If iter(iterable) has no method __copy__(), this is equivalent to:
 
     def tee(iterable, n=2):
         def gen(next, data={}, cnt=[0]):
@@ -816,17 +816,22 @@
                 yield item
         it = iter(iterable)
         return tuple([gen(it.next) for i in range(n)])
+
+    If iter(iterable) has a __copy__ method, though, we just return
+    a tuple t = (iterable, t[0].__copy__(), t[1].__copy__(), ...).
     """
     if n < 0:
         raise oefmt(space.w_ValueError, "n must be >= 0")
 
-    if isinstance(w_iterable, W_TeeIterable):     # optimization only
-        w_chained_list = w_iterable.w_chained_list
-        w_iterator = w_iterable.w_iterator
+    if space.findattr(w_iterable, space.wrap("__copy__")) is not None:
+        # In this case, we don't instantiate any W_TeeIterable.
+        # We just rely on doing repeated __copy__().  This case
+        # includes the situation where w_iterable is already
+        # a W_TeeIterable itself.
         iterators_w = [w_iterable] * n
         for i in range(1, n):
-            iterators_w[i] = space.wrap(W_TeeIterable(space, w_iterator,
-                                                      w_chained_list))
+            w_iterable = space.call_method(w_iterable, "__copy__")
+            iterators_w[i] = w_iterable
     else:
         w_iterator = space.iter(w_iterable)
         w_chained_list = W_TeeChainedListNode(space)
@@ -905,6 +910,11 @@
         self.w_chained_list = w_chained_list.w_next
         return w_obj
 
+    def copy_w(self):
+        space = self.space
+        tee_iter = W_TeeIterable(space, self.w_iterator, self.chained_list)
+        return space.wrap(tee_iter)
+
     def reduce_w(self):
         return self.space.newtuple([self.space.gettypefor(W_TeeIterable),
                                     
self.space.newtuple([self.space.newtuple([])]),
@@ -943,6 +953,7 @@
         __new__ = interp2app(W_TeeIterable___new__),
         __iter__ = interp2app(W_TeeIterable.iter_w),
         __next__ = interp2app(W_TeeIterable.next_w),
+        __copy__ = interp2app(W_TeeIterable.copy_w),
         __weakref__ = make_weakref_descr(W_TeeIterable),
         __reduce__ = interp2app(W_TeeIterable.reduce_w),
         __setstate__ = interp2app(W_TeeIterable.setstate_w)
diff --git a/pypy/module/itertools/test/test_itertools.py 
b/pypy/module/itertools/test/test_itertools.py
--- a/pypy/module/itertools/test/test_itertools.py
+++ b/pypy/module/itertools/test/test_itertools.py
@@ -577,6 +577,48 @@
         x = next(d)
         assert x == 'b'
 
+    def test_tee_defines_copy(self):
+        import itertools
+        a, b = itertools.tee('abc')
+        c = b.__copy__()
+        assert list(a) == ['a', 'b', 'c']
+        assert list(b) == ['a', 'b', 'c']
+        assert list(c) == ['a', 'b', 'c']
+        a, = itertools.tee('abc', 1)
+        x = a.next()
+        assert x == 'a'
+        b = a.__copy__()
+        x = a.next()
+        assert x == 'b'
+        x = b.next()
+        assert x == 'b'
+
+    def test_tee_function_uses_copy(self):
+        import itertools
+        class MyIterator(object):
+            def __iter__(self):
+                return self
+            def next(self):
+                raise NotImplementedError
+            def __copy__(self):
+                return iter('def')
+        my = MyIterator()
+        a, = itertools.tee(my, 1)
+        assert a is my
+        a, b = itertools.tee(my)
+        assert a is my
+        assert b is not my
+        assert list(b) == ['d', 'e', 'f']
+        # this gives AttributeError because it tries to call
+        # my.__copy__().__copy__() and there isn't one
+        raises(AttributeError, itertools.tee, my, 3)
+
+    def test_tee_function_empty(self):
+        import itertools
+        assert itertools.tee('abc', 0) == ()
+        a, = itertools.tee('abc', 1)
+        assert itertools.tee(a, 0) == ()
+
 
 class AppTestItertools26:
     spaceconfig = dict(usemodules=['itertools'])
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py 
b/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_pointers.py
@@ -265,3 +265,10 @@
         class A(object):
             _byref = byref
         A._byref(c_int(5))
+
+    def test_byref_with_offset(self):
+        c = c_int()
+        d = byref(c)
+        base = cast(d, c_void_p).value
+        for i in [0, 1, 4, 1444, -10293]:
+            assert cast(byref(c, i), c_void_p).value == base + i
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -205,6 +205,9 @@
     def newbytes(self, x):
         return w_some_obj()
 
+    def newunicode(self, x):
+        return w_some_obj()
+
     def wrap(self, x):
         if not we_are_translated():
             if isinstance(x, gateway.interp2app):
diff --git a/rpython/jit/backend/arm/test/test_assembler.py 
b/rpython/jit/backend/arm/test/test_assembler.py
--- a/rpython/jit/backend/arm/test/test_assembler.py
+++ b/rpython/jit/backend/arm/test/test_assembler.py
@@ -1,6 +1,5 @@
 from rpython.jit.backend.arm import conditions as c
 from rpython.jit.backend.arm import registers as r
-from rpython.jit.backend.arm.support import arm_int_div
 from rpython.jit.backend.arm.assembler import AssemblerARM
 from rpython.jit.backend.arm.locations import imm
 from rpython.jit.backend.arm.test.support import run_asm
@@ -180,19 +179,6 @@
         self.a.gen_func_epilog()
         assert run_asm(self.a) == 133
 
-    def test_division(self):
-        self.a.gen_func_prolog()
-        self.a.mc.MOV_ri(r.r0.value, 123)
-        self.a.mc.MOV_ri(r.r1.value, 2)
-
-        # call to div
-        self.a.mc.PUSH(range(2, 12))
-        div_addr = rffi.cast(lltype.Signed, arm_int_div)
-        self.a.mc.BL(div_addr)
-        self.a.mc.POP(range(2, 12))
-        self.a.gen_func_epilog()
-        assert run_asm(self.a) == 61
-
     def test_bl_with_conditional_exec(self):
         functype = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed))
         call_addr = rffi.cast(lltype.Signed, llhelper(functype, callme))
diff --git a/rpython/jit/backend/arm/test/test_regalloc.py 
b/rpython/jit/backend/arm/test/test_regalloc.py
--- a/rpython/jit/backend/arm/test/test_regalloc.py
+++ b/rpython/jit/backend/arm/test/test_regalloc.py
@@ -545,23 +545,6 @@
         self.interpret(ops, [s, 1234567890])
         assert s[1] == 1234567890
 
-    def test_division_optimized(self):
-        ops = '''
-        [i7, i6]
-        label(i7, i6, descr=targettoken)
-        i18 = int_floordiv(i7, i6)
-        i19 = int_xor(i7, i6)
-        i21 = int_lt(i19, 0)
-        i22 = int_mod(i7, i6)
-        i23 = int_is_true(i22)
-        i24 = int_eq(i6, 4)
-        guard_false(i24) [i18]
-        jump(i18, i6, descr=targettoken)
-        '''
-        self.interpret(ops, [10, 4])
-        assert self.getint(0) == 2
-        # FIXME: Verify that i19 - i23 are removed
-
 
 class TestRegallocFloats(CustomBaseTestRegalloc):
     def test_float_add(self):
diff --git a/rpython/jit/backend/llsupport/test/test_gc_integration.py 
b/rpython/jit/backend/llsupport/test/test_gc_integration.py
--- a/rpython/jit/backend/llsupport/test/test_gc_integration.py
+++ b/rpython/jit/backend/llsupport/test/test_gc_integration.py
@@ -330,6 +330,13 @@
                 expected_size = 2
                 idx = 1
                 fixed_size -= 32
+            if self.cpu.backend_name.startswith('zarch') or \
+               self.cpu.backend_name.startswith('ppc'):
+                # the allocation always allocates the register
+                # into the return register. (e.g. r3 on ppc)
+                # the next malloc_nursery will move r3 to the
+                # frame manager, thus the two bits will be on the frame
+                fixed_size += 4
             assert len(frame.jf_gcmap) == expected_size
             # check that we have two bits set, and that they are in two
             # registers (p0 and p1 are moved away when doing p2, but not
diff --git a/rpython/jit/backend/zarch/arch.py 
b/rpython/jit/backend/zarch/arch.py
--- a/rpython/jit/backend/zarch/arch.py
+++ b/rpython/jit/backend/zarch/arch.py
@@ -45,10 +45,7 @@
 #     +------------------------------+ <- assembler begin
 #     |  SAVE CONTEXT                |
 #     +------------------------------+
-# +--+|  BRANCH (saves addr of pool  |
-# |   |  in r13)                     |
-# |   +------------------------------+
-# |   |  ...                         |
+#start|  ...                         |
 # |   |  LITERAL POOL                | <---+
 # |   |  ...                         | <-+ |
 # +-->+------------------------------+   | |
diff --git a/rpython/jit/backend/zarch/assembler.py 
b/rpython/jit/backend/zarch/assembler.py
--- a/rpython/jit/backend/zarch/assembler.py
+++ b/rpython/jit/backend/zarch/assembler.py
@@ -309,17 +309,15 @@
 
         # signature of this _frame_realloc_slowpath function:
         #   * on entry, r0 is the new size
-        #   * on entry, r1 is the gcmap
         #   * no managed register must be modified
 
-        ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap')
-        mc.STG(r.SCRATCH, l.addr(ofs2, r.SPP))
+        # caller already did push_gcmap(store=True)
 
         self._push_core_regs_to_jitframe(mc, r.MANAGED_REGS)
         self._push_fp_regs_to_jitframe(mc)
 
 
-        # First argument is SPP (= r31), which is the jitframe
+        # First argument is SPP, which is the jitframe
         mc.LGR(r.r2, r.SPP)
 
         # no need to move second argument (frame_depth),
@@ -347,6 +345,7 @@
             mc.load(r.r5, r.r5, 0)
             mc.store(r.r2, r.r5, -WORD)
 
+        self.pop_gcmap(mc) # cancel the push_gcmap(store=True) in the caller
         self._pop_core_regs_from_jitframe(mc, r.MANAGED_REGS)
         self._pop_fp_regs_from_jitframe(mc)
 
@@ -386,14 +385,12 @@
         # signature of these cond_call_slowpath functions:
         #   * on entry, r11 contains the function to call
         #   * r2, r3, r4, r5 contain arguments for the call
-        #   * r0 is the gcmap
+        #   * gcmap is pushed
         #   * the old value of these regs must already be stored in the 
jitframe
         #   * on exit, all registers are restored from the jitframe
 
         mc = InstrBuilder()
         self.mc = mc
-        ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap')
-        mc.STG(r.SCRATCH2, l.addr(ofs2,r.SPP))
         mc.store_link()
         mc.push_std_frame()
 
@@ -411,6 +408,7 @@
                        reg is not r.r4 and
                        reg is not r.r5 and
                        reg is not r.r11]
+        # the caller already did push_gcmap(store=True)
         self._push_core_regs_to_jitframe(mc, regs)
         if supports_floats:
             self._push_fp_regs_to_jitframe(mc)
@@ -420,6 +418,7 @@
         # Finish
         self._reload_frame_if_necessary(mc)
 
+        self.pop_gcmap(mc) # cancel the push_gcmap(store=True) in the caller
         self._pop_core_regs_from_jitframe(mc, saved_regs)
         if supports_floats:
             self._pop_fp_regs_from_jitframe(mc)
@@ -449,12 +448,11 @@
         mc.store_link()
         mc.push_std_frame()
         #
-        ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap')
-        mc.STG(r.r1, l.addr(ofs2, r.SPP))
         saved_regs = [reg for reg in r.MANAGED_REGS
                           if reg is not r.RES and reg is not r.RSZ]
         self._push_core_regs_to_jitframe(mc, saved_regs)
         self._push_fp_regs_to_jitframe(mc)
+        # the caller already did push_gcmap(store=True)
         #
         if kind == 'fixed':
             addr = self.cpu.gc_ll_descr.get_malloc_slowpath_addr()
@@ -588,7 +586,7 @@
         #       sum -> (14 bytes)
         mc.write('\x00'*14)
         mc.load_imm(r.RETURN, self._frame_realloc_slowpath)
-        self.load_gcmap(mc, r.r1, gcmap)
+        self.push_gcmap(mc, gcmap, store=True)
         mc.raw_call()
 
         self.frame_depth_to_patch.append((patch_pos, mc.currpos()))
@@ -685,6 +683,8 @@
         #    name = "Loop # %s: %s" % (looptoken.number, loopname)
         #    self.cpu.profile_agent.native_code_written(name,
         #                                               rawstart, full_size)
+        #print(hex(rawstart+looppos))
+        #import pdb; pdb.set_trace()
         return AsmInfo(ops_offset, rawstart + looppos,
                        size_excluding_failure_stuff - looppos, rawstart)
 
@@ -867,6 +867,10 @@
         ofs = self.cpu.get_ofs_of_frame_field('jf_gcmap')
         mc.STG(r.SCRATCH, l.addr(ofs, r.SPP))
 
+    def pop_gcmap(self, mc):
+        ofs = self.cpu.get_ofs_of_frame_field('jf_gcmap')
+        mc.LG(r.SCRATCH, l.addr(ofs, r.SPP))
+
     def break_long_loop(self):
         # If the loop is too long, the guards in it will jump forward
         # more than 32 KB.  We use an approximate hack to know if we
@@ -1339,7 +1343,7 @@
 
         # new value of nursery_free_adr in RSZ and the adr of the new object
         # in RES.
-        self.load_gcmap(mc, r.r1, gcmap)
+        self.push_gcmap(mc, gcmap, store=True)
         mc.branch_absolute(self.malloc_slowpath)
 
         # here r1 holds nursery_free_addr
@@ -1375,7 +1379,7 @@
 
         # new value of nursery_free_adr in RSZ and the adr of the new object
         # in RES.
-        self.load_gcmap(mc, r.r1, gcmap)
+        self.push_gcmap(mc, gcmap, store=True)
         mc.branch_absolute(self.malloc_slowpath)
 
         offset = mc.currpos() - fast_jmp_pos
@@ -1468,7 +1472,7 @@
         pmc.overwrite()
         #
         # save the gcmap
-        self.load_gcmap(mc, r.r1, gcmap)
+        self.push_gcmap(mc, gcmap, store=True)
         #
         # load the function into r14 and jump
         if kind == rewrite.FLAG_ARRAY:
diff --git a/rpython/jit/backend/zarch/opassembler.py 
b/rpython/jit/backend/zarch/opassembler.py
--- a/rpython/jit/backend/zarch/opassembler.py
+++ b/rpython/jit/backend/zarch/opassembler.py
@@ -387,8 +387,7 @@
                 if reg in self._COND_CALL_SAVE_REGS]
         self._push_core_regs_to_jitframe(self.mc, should_be_saved)
 
-        # load gc map into unusual location: r0
-        self.load_gcmap(self.mc, r.SCRATCH2, regalloc.get_gcmap())
+        self.push_gcmap(self.mc, regalloc.get_gcmap())
         #
         # load the 0-to-4 arguments into these registers, with the address of
         # the function to call into r11
diff --git a/rpython/jit/backend/zarch/test/test_runner.py 
b/rpython/jit/backend/zarch/test/test_runner.py
--- a/rpython/jit/backend/zarch/test/test_runner.py
+++ b/rpython/jit/backend/zarch/test/test_runner.py
@@ -26,4 +26,4 @@
 
     add_loop_instructions = "lg; lgr; larl; agr; cgfi; jge; j;$"
     bridge_loop_instructions = "lg; cgfi; jnl; lghi; " \
-                               "(lgfi|iilf);( iihf;)? (lgfi|iilf);( iihf;)? 
basr; larl; (lgfi|iilf);( iihf;)? br;$"
+                               "(lgfi|iilf);( iihf;)? (lgfi|iilf);( iihf;)? 
stg; basr; larl; (lgfi|iilf);( iihf;)? br;$"
diff --git a/rpython/jit/metainterp/blackhole.py 
b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1143,45 +1143,35 @@
 
     @arguments("cpu", "i", "R", "d", returns="i")
     def bhimpl_residual_call_r_i(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, None, args_r, None, calldescr)
     @arguments("cpu", "i", "R", "d", returns="r")
     def bhimpl_residual_call_r_r(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, None, args_r, None, calldescr)
     @arguments("cpu", "i", "R", "d")
     def bhimpl_residual_call_r_v(cpu, func, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, None, args_r, None, calldescr)
 
     @arguments("cpu", "i", "I", "R", "d", returns="i")
     def bhimpl_residual_call_ir_i(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, args_i, args_r, None, calldescr)
     @arguments("cpu", "i", "I", "R", "d", returns="r")
     def bhimpl_residual_call_ir_r(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, args_i, args_r, None, calldescr)
     @arguments("cpu", "i", "I", "R", "d")
     def bhimpl_residual_call_ir_v(cpu, func, args_i, args_r, calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, args_i, args_r, None, calldescr)
 
     @arguments("cpu", "i", "I", "R", "F", "d", returns="i")
     def bhimpl_residual_call_irf_i(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_i(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d", returns="r")
     def bhimpl_residual_call_irf_r(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_r(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d", returns="f")
     def bhimpl_residual_call_irf_f(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_f(func, args_i, args_r, args_f, calldescr)
     @arguments("cpu", "i", "I", "R", "F", "d")
     def bhimpl_residual_call_irf_v(cpu, func, args_i,args_r,args_f,calldescr):
-        workaround2200.active = True
         return cpu.bh_call_v(func, args_i, args_r, args_f, calldescr)
 
     # conditional calls - note that they cannot return stuff
@@ -1209,54 +1199,44 @@
 
     @arguments("cpu", "j", "R", returns="i")
     def bhimpl_inline_call_r_i(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "R", returns="r")
     def bhimpl_inline_call_r_r(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "R")
     def bhimpl_inline_call_r_v(cpu, jitcode, args_r):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              None, args_r, None, jitcode.calldescr)
 
     @arguments("cpu", "j", "I", "R", returns="i")
     def bhimpl_inline_call_ir_i(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", returns="r")
     def bhimpl_inline_call_ir_r(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R")
     def bhimpl_inline_call_ir_v(cpu, jitcode, args_i, args_r):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, None, jitcode.calldescr)
 
     @arguments("cpu", "j", "I", "R", "F", returns="i")
     def bhimpl_inline_call_irf_i(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_i(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F", returns="r")
     def bhimpl_inline_call_irf_r(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_r(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F", returns="f")
     def bhimpl_inline_call_irf_f(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_f(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
     @arguments("cpu", "j", "I", "R", "F")
     def bhimpl_inline_call_irf_v(cpu, jitcode, args_i, args_r, args_f):
-        workaround2200.active = True
         return cpu.bh_call_v(jitcode.get_fnaddr_as_int(),
                              args_i, args_r, args_f, jitcode.calldescr)
 
@@ -1543,8 +1523,6 @@
             if not self.nextblackholeinterp:
                 self._exit_frame_with_exception(current_exc)
             return current_exc
-        finally:
-            workaround2200.active = False
         #
         # pass the frame's return value to the caller
         caller = self.nextblackholeinterp
@@ -1717,10 +1695,3 @@
     #
     _run_forever(firstbh, current_exc)
 convert_and_run_from_pyjitpl._dont_inline_ = True
-
-# ____________________________________________________________
-
-class WorkaroundIssue2200(object):
-    pass
-workaround2200 = WorkaroundIssue2200()
-workaround2200.active = False
diff --git a/rpython/jit/metainterp/compile.py 
b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -620,23 +620,28 @@
         raise jitexc.DoneWithThisFrameVoid()
 
 class DoneWithThisFrameDescrInt(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_int_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.INT
-        result = metainterp_sd.cpu.get_int_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameInt(result)
+        cpu = metainterp_sd.cpu
+        raise jitexc.DoneWithThisFrameInt(self.get_result(cpu, deadframe))
 
 class DoneWithThisFrameDescrRef(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_ref_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.REF
         cpu = metainterp_sd.cpu
-        result = cpu.get_ref_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameRef(cpu, result)
+        raise jitexc.DoneWithThisFrameRef(cpu, self.get_result(cpu, deadframe))
 
 class DoneWithThisFrameDescrFloat(_DoneWithThisFrameDescr):
+    def get_result(self, cpu, deadframe):
+        return cpu.get_float_value(deadframe, 0)
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         assert jitdriver_sd.result_type == history.FLOAT
-        result = metainterp_sd.cpu.get_float_value(deadframe, 0)
-        raise jitexc.DoneWithThisFrameFloat(result)
+        cpu = metainterp_sd.cpu
+        raise jitexc.DoneWithThisFrameFloat(self.get_result(cpu, deadframe))
 
 class ExitFrameWithExceptionDescrRef(_DoneWithThisFrameDescr):
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
diff --git a/rpython/jit/metainterp/heapcache.py 
b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -57,10 +57,16 @@
         self.cache_anything = {}
         self.cache_seen_allocation = {}
 
+        # set of boxes that we've seen a quasi-immut for the field on. cleared
+        # on writes to the field.
+        self.quasiimmut_seen = None
+
     def _clear_cache_on_write(self, seen_allocation_of_target):
         if not seen_allocation_of_target:
             self.cache_seen_allocation.clear()
         self.cache_anything.clear()
+        if self.quasiimmut_seen is not None:
+            self.quasiimmut_seen.clear()
 
     def _seen_alloc(self, ref_box):
         if not isinstance(ref_box, RefFrontendOp):
@@ -92,6 +98,8 @@
     def invalidate_unescaped(self):
         self._invalidate_unescaped(self.cache_anything)
         self._invalidate_unescaped(self.cache_seen_allocation)
+        if self.quasiimmut_seen is not None:
+            self.quasiimmut_seen.clear()
 
     def _invalidate_unescaped(self, d):
         for ref_box in d.keys():
@@ -484,3 +492,18 @@
         if isinstance(oldbox, FrontendOp) and isinstance(newbox, Const):
             assert newbox.same_constant(constant_from_op(oldbox))
             oldbox.set_replaced_with_const()
+
+    def is_quasi_immut_known(self, fielddescr, box):
+        cache = self.heap_cache.get(fielddescr, None)
+        if cache is not None and cache.quasiimmut_seen is not None:
+            return box in cache.quasiimmut_seen
+        return False
+
+    def quasi_immut_now_known(self, fielddescr, box):
+        cache = self.heap_cache.get(fielddescr, None)
+        if cache is None:
+            cache = self.heap_cache[fielddescr] = CacheEntry(self)
+        if cache.quasiimmut_seen is not None:
+            cache.quasiimmut_seen[box] = None
+        else:
+            cache.quasiimmut_seen = {box: None}
diff --git a/rpython/jit/metainterp/optimizeopt/intdiv.py 
b/rpython/jit/metainterp/optimizeopt/intdiv.py
--- a/rpython/jit/metainterp/optimizeopt/intdiv.py
+++ b/rpython/jit/metainterp/optimizeopt/intdiv.py
@@ -1,5 +1,5 @@
 from rpython.rlib.rarithmetic import LONG_BIT, intmask, r_uint
-from rpython.rlib.rbigint import rbigint, ONERBIGINT
+
 
 from rpython.jit.metainterp.history import ConstInt
 from rpython.jit.metainterp.resoperation import ResOperation, rop
@@ -17,10 +17,19 @@
     while (r_uint(1) << (i+1)) < r_uint(m):
         i += 1
 
-    # k = 2**(64+i) // m + 1, computed manually using rbigint
-    #                         because that's the easiest
-    k1 = ONERBIGINT.lshift(LONG_BIT + i).floordiv(rbigint.fromint(m))
-    k = k1.touint() + r_uint(1)
+    # quotient = 2**(64+i) // m
+    high_word_dividend = r_uint(1) << i
+    quotient = r_uint(0)
+    for bit in range(LONG_BIT-1, -1, -1):
+        t = quotient + (r_uint(1) << bit)
+        # check: is 't * m' small enough to be < 2**(64+i), or not?
+        # note that we're really computing (2**(64+i)-1) // m, but the result
+        # is the same, because powers of two are not multiples of m.
+        if unsigned_mul_high(t, r_uint(m)) < high_word_dividend:
+            quotient = t     # yes, small enough
+
+    # k = 2**(64+i) // m + 1
+    k = quotient + r_uint(1)
 
     assert k != r_uint(0)
     # Proof that k < 2**64 holds in all cases, even with the "+1":
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -829,8 +829,11 @@
                                        mutatefielddescr, orgpc):
         from rpython.jit.metainterp.quasiimmut import QuasiImmutDescr
         cpu = self.metainterp.cpu
+        if self.metainterp.heapcache.is_quasi_immut_known(fielddescr, box):
+            return
         descr = QuasiImmutDescr(cpu, box.getref_base(), fielddescr,
                                 mutatefielddescr)
+        self.metainterp.heapcache.quasi_immut_now_known(fielddescr, box)
         self.metainterp.history.record(rop.QUASIIMMUT_FIELD, [box],
                                        None, descr=descr)
         self.metainterp.generate_guard(rop.GUARD_NOT_INVALIDATED,
@@ -2540,7 +2543,13 @@
             elif box.type == history.REF: args.append(box.getref_base())
             elif box.type == history.FLOAT: args.append(box.getfloatstorage())
             else: assert 0
-        self.jitdriver_sd.warmstate.execute_assembler(loop_token, *args)
+        res = self.jitdriver_sd.warmstate.execute_assembler(loop_token, *args)
+        kind = history.getkind(lltype.typeOf(res))
+        if kind == 'void':  raise jitexc.DoneWithThisFrameVoid()
+        if kind == 'int':   raise jitexc.DoneWithThisFrameInt(res)
+        if kind == 'ref':   raise jitexc.DoneWithThisFrameRef(self.cpu, res)
+        if kind == 'float': raise jitexc.DoneWithThisFrameFloat(res)
+        raise AssertionError(kind)
 
     def prepare_resume_from_failure(self, deadframe, inputargs, resumedescr):
         exception = self.cpu.grab_exc_value(deadframe)
diff --git a/rpython/jit/metainterp/test/test_ajit.py 
b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4470,6 +4470,28 @@
 
         self.meta_interp(f, [])
 
+    def test_issue2335_recursion(self):
+        # Reproduces issue #2335: same as issue #2200, but the workaround
+        # in c4c54cb69aba was not enough.
+        driver = JitDriver(greens=["level"], reds=["i"])
+        def enter(level, i):
+            if level == 0:
+                f(1)     # recursive call
+            driver.can_enter_jit(level=level, i=i)
+        def f(level):
+            i = 0 if level == 0 else 298
+            while True:
+                driver.jit_merge_point(level=level, i=i)
+                i += 1
+                if i >= 300:
+                    return i
+                promote(i + 1)   # a failing guard
+                enter(level, i)
+        def main():
+            set_param(None, 'trace_eagerness', 999999)
+            f(0)
+        self.meta_interp(main, [])
+
     def test_pending_setarrayitem_with_indirect_constant_index(self):
         driver = JitDriver(greens=[], reds='auto')
         class X:
diff --git a/rpython/jit/metainterp/test/test_heapcache.py 
b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -797,3 +797,46 @@
         h.class_now_known(box1)     # interaction of the two families of flags
         assert not h.is_unescaped(box1)
         assert h.is_likely_virtual(box1)
+
+    def test_quasiimmut_seen(self):
+        h = HeapCache()
+        box1 = RefFrontendOp(1)
+        box2 = RefFrontendOp(2)
+        box3 = RefFrontendOp(3)
+        box4 = RefFrontendOp(4)
+        assert not h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr2, box3)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr2, box4)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr2, box3)
+        assert h.is_quasi_immut_known(descr2, box4)
+
+        # invalidate the descr1 cache
+
+        h.setfield(box1, box3, descr1)
+        assert not h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+
+        # a call invalidates everything
+        h.invalidate_caches(
+            rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), [])
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
diff --git a/rpython/jit/metainterp/test/test_tracingopts.py 
b/rpython/jit/metainterp/test/test_tracingopts.py
--- a/rpython/jit/metainterp/test/test_tracingopts.py
+++ b/rpython/jit/metainterp/test/test_tracingopts.py
@@ -512,6 +512,32 @@
         assert res == 4 * 7
         self.check_operations_history(getfield_gc_i=2, getfield_gc_r=2)
 
+    def test_heap_caching_quasi_immutable(self):
+        class A:
+            _immutable_fields_ = ['x?']
+        a1 = A()
+        a1.x = 5
+        a2 = A()
+        a2.x = 7
+
+        @jit.elidable
+        def get(n):
+            if n > 0:
+                return a1
+            return a2
+
+        def g(a):
+            return a.x
+
+        def fn(n):
+            jit.promote(n)
+            a = get(n)
+            return g(a) + a.x
+        res = self.interp_operations(fn, [7])
+        assert res == 10
+        self.check_operations_history(quasiimmut_field=1)
+
+
     def test_heap_caching_multiple_tuples(self):
         class Gbl(object):
             pass
diff --git a/rpython/jit/metainterp/warmspot.py 
b/rpython/jit/metainterp/warmspot.py
--- a/rpython/jit/metainterp/warmspot.py
+++ b/rpython/jit/metainterp/warmspot.py
@@ -529,7 +529,7 @@
     def make_enter_function(self, jd):
         from rpython.jit.metainterp.warmstate import WarmEnterState
         state = WarmEnterState(self, jd)
-        maybe_compile_and_run = state.make_entry_point()
+        maybe_compile_and_run, EnterJitAssembler = state.make_entry_point()
         jd.warmstate = state
 
         def crash_in_jit(e):
@@ -560,6 +560,7 @@
         maybe_enter_jit._always_inline_ = True
         jd._maybe_enter_jit_fn = maybe_enter_jit
         jd._maybe_compile_and_run_fn = maybe_compile_and_run
+        jd._EnterJitAssembler = EnterJitAssembler
 
     def make_driverhook_graphs(self):
         s_Str = annmodel.SomeString()
@@ -842,12 +843,8 @@
         #           while 1:
         #               try:
         #                   return portal(*args)
-        #               except ContinueRunningNormally, e:
-        #                   *args = *e.new_args
-        #               except DoneWithThisFrame, e:
-        #                   return e.return
-        #               except ExitFrameWithException, e:
-        #                   raise Exception, e.value
+        #               except JitException, e:
+        #                   return handle_jitexception(e)
         #
         #       def portal(*args):
         #           while 1:
@@ -884,90 +881,79 @@
         rtyper = self.translator.rtyper
         RESULT = PORTALFUNC.RESULT
         result_kind = history.getkind(RESULT)
+        assert result_kind.startswith(jd.result_type)
         ts = self.cpu.ts
         state = jd.warmstate
         maybe_compile_and_run = jd._maybe_compile_and_run_fn
+        EnterJitAssembler = jd._EnterJitAssembler
 
         def ll_portal_runner(*args):
-            start = True
-            while 1:
-                try:
-                    # maybe enter from the function's start.  Note that the
-                    # 'start' variable is constant-folded away because it's
-                    # the first statement in the loop.
-                    if start:
-                        maybe_compile_and_run(
-                            state.increment_function_threshold, *args)
-                    #
-                    # then run the normal portal function, i.e. the
-                    # interpreter's main loop.  It might enter the jit
-                    # via maybe_enter_jit(), which typically ends with
-                    # handle_fail() being called, which raises on the
-                    # following exceptions --- catched here, because we
-                    # want to interrupt the whole interpreter loop.
-                    return support.maybe_on_top_of_llinterp(rtyper,
-                                                      portal_ptr)(*args)
-                except jitexc.ContinueRunningNormally as e:
+            try:
+                # maybe enter from the function's start.
+                maybe_compile_and_run(
+                    state.increment_function_threshold, *args)
+                #
+                # then run the normal portal function, i.e. the
+                # interpreter's main loop.  It might enter the jit
+                # via maybe_enter_jit(), which typically ends with
+                # handle_fail() being called, which raises on the
+                # following exceptions --- catched here, because we
+                # want to interrupt the whole interpreter loop.
+                return support.maybe_on_top_of_llinterp(rtyper,
+                                                  portal_ptr)(*args)
+            except jitexc.JitException as e:
+                result = handle_jitexception(e)
+                if result_kind != 'void':
+                    result = specialize_value(RESULT, result)
+                return result
+
+        def handle_jitexception(e):
+            # XXX there are too many exceptions all around...
+            while True:
+                if isinstance(e, EnterJitAssembler):
+                    try:
+                        return e.execute()
+                    except jitexc.JitException as e:
+                        continue
+                #
+                if isinstance(e, jitexc.ContinueRunningNormally):
                     args = ()
                     for ARGTYPE, attrname, count in portalfunc_ARGS:
                         x = getattr(e, attrname)[count]
                         x = specialize_value(ARGTYPE, x)
                         args = args + (x,)
-                    start = False
-                    continue
-                except jitexc.DoneWithThisFrameVoid:
-                    assert result_kind == 'void'
-                    return
-                except jitexc.DoneWithThisFrameInt as e:
-                    assert result_kind == 'int'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.DoneWithThisFrameRef as e:
-                    assert result_kind == 'ref'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.DoneWithThisFrameFloat as e:
-                    assert result_kind == 'float'
-                    return specialize_value(RESULT, e.result)
-                except jitexc.ExitFrameWithExceptionRef as e:
+                    try:
+                        result = support.maybe_on_top_of_llinterp(rtyper,
+                                                            portal_ptr)(*args)
+                    except jitexc.JitException as e:
+                        continue
+                    if result_kind != 'void':
+                        result = unspecialize_value(result)
+                    return result
+                #
+                if result_kind == 'void':
+                    if isinstance(e, jitexc.DoneWithThisFrameVoid):
+                        return None
+                if result_kind == 'int':
+                    if isinstance(e, jitexc.DoneWithThisFrameInt):
+                        return e.result
+                if result_kind == 'ref':
+                    if isinstance(e, jitexc.DoneWithThisFrameRef):
+                        return e.result
+                if result_kind == 'float':
+                    if isinstance(e, jitexc.DoneWithThisFrameFloat):
+                        return e.result
+                #
+                if isinstance(e, jitexc.ExitFrameWithExceptionRef):
                     value = ts.cast_to_baseclass(e.value)
                     if not we_are_translated():
                         raise LLException(ts.get_typeptr(value), value)
                     else:
                         value = cast_base_ptr_to_instance(Exception, value)
+                        assert value is not None
                         raise value
-
-        def handle_jitexception(e):
-            # XXX the bulk of this function is mostly a copy-paste from above
-            try:
-                raise e
-            except jitexc.ContinueRunningNormally as e:
-                args = ()
-                for ARGTYPE, attrname, count in portalfunc_ARGS:
-                    x = getattr(e, attrname)[count]
-                    x = specialize_value(ARGTYPE, x)
-                    args = args + (x,)
-                result = ll_portal_runner(*args)
-                if result_kind != 'void':
-                    result = unspecialize_value(result)
-                return result
-            except jitexc.DoneWithThisFrameVoid:
-                assert result_kind == 'void'
-                return
-            except jitexc.DoneWithThisFrameInt as e:
-                assert result_kind == 'int'
-                return e.result
-            except jitexc.DoneWithThisFrameRef as e:
-                assert result_kind == 'ref'
-                return e.result
-            except jitexc.DoneWithThisFrameFloat as e:
-                assert result_kind == 'float'
-                return e.result
-            except jitexc.ExitFrameWithExceptionRef as e:
-                value = ts.cast_to_baseclass(e.value)
-                if not we_are_translated():
-                    raise LLException(ts.get_typeptr(value), value)
-                else:
-                    value = cast_base_ptr_to_instance(Exception, value)
-                    raise value
+                #
+                raise AssertionError("all cases should have been handled")
 
         jd._ll_portal_runner = ll_portal_runner # for debugging
         jd.portal_runner_ptr = self.helper_func(jd._PTR_PORTAL_FUNCTYPE,
diff --git a/rpython/jit/metainterp/warmstate.py 
b/rpython/jit/metainterp/warmstate.py
--- a/rpython/jit/metainterp/warmstate.py
+++ b/rpython/jit/metainterp/warmstate.py
@@ -2,7 +2,7 @@
 import weakref
 
 from rpython.jit.codewriter import support, heaptracker, longlong
-from rpython.jit.metainterp import resoperation, history
+from rpython.jit.metainterp import resoperation, history, jitexc
 from rpython.rlib.debug import debug_start, debug_stop, debug_print
 from rpython.rlib.debug import have_debug_prints_for
 from rpython.rlib.jit import PARAMETERS
@@ -348,8 +348,9 @@
 
     def make_entry_point(self):
         "NOT_RPYTHON"
-        if hasattr(self, 'maybe_compile_and_run'):
-            return self.maybe_compile_and_run
+        from rpython.jit.metainterp import compile
+        if hasattr(self, 'entry_point_fns'):
+            return self.entry_point_fns
 
         warmrunnerdesc = self.warmrunnerdesc
         metainterp_sd = warmrunnerdesc.metainterp_sd
@@ -362,6 +363,8 @@
         confirm_enter_jit = self.confirm_enter_jit
         range_red_args = unrolling_iterable(
             range(num_green_args, num_green_args + jitdriver_sd.num_red_args))
+        name_red_args = unrolling_iterable(
+            [(i, 'arg%d' % i) for i in range(jitdriver_sd.num_red_args)])
         # get a new specialized copy of the method
         ARGS = []
         for kind in jitdriver_sd.red_args_types:
@@ -376,6 +379,7 @@
         func_execute_token = self.cpu.make_execute_token(*ARGS)
         cpu = self.cpu
         jitcounter = self.warmrunnerdesc.jitcounter
+        result_type = jitdriver_sd.result_type
 
         def execute_assembler(loop_token, *args):
             # Call the backend to run the 'looptoken' with the given
@@ -396,8 +400,23 @@
             #
             # Handle the failure
             fail_descr = cpu.get_latest_descr(deadframe)
+            # First, a fast path to avoid raising and immediately catching
+            # a DoneWithThisFrame exception
+            if result_type == history.VOID:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrVoid):
+                    return None
+            if result_type == history.INT:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrInt):
+                    return fail_descr.get_result(cpu, deadframe)
+            if result_type == history.REF:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrRef):
+                    return fail_descr.get_result(cpu, deadframe)
+            if result_type == history.FLOAT:
+                if isinstance(fail_descr, compile.DoneWithThisFrameDescrFloat):
+                    return fail_descr.get_result(cpu, deadframe)
+            #
+            # General case
             fail_descr.handle_fail(deadframe, metainterp_sd, jitdriver_sd)
-            #
             assert 0, "should have raised"
 
         def bound_reached(hash, cell, *args):
@@ -419,7 +438,8 @@
 
         def maybe_compile_and_run(increment_threshold, *args):
             """Entry point to the JIT.  Called at the point with the
-            can_enter_jit() hint.
+            can_enter_jit() hint, and at the start of a function
+            with a different threshold.
             """
             # Look for the cell corresponding to the current greenargs.
             # Search for the JitCell that is of the correct subclass of
@@ -439,14 +459,6 @@
                     bound_reached(hash, None, *args)
                 return
 
-            # Workaround for issue #2200, maybe temporary.  This is not
-            # a proper fix, but only a hack that should work well enough
-            # for PyPy's main jitdriver...  See test_issue2200_recursion
-            from rpython.jit.metainterp.blackhole import workaround2200
-            if workaround2200.active:
-                workaround2200.active = False
-                return
-
             # Here, we have found 'cell'.
             #
             if cell.flags & (JC_TRACING | JC_TEMPORARY):
@@ -484,14 +496,27 @@
             execute_args = ()
             for i in range_red_args:
                 execute_args += (unspecialize_value(args[i]), )
-            # run it!  this executes until interrupted by an exception
-            execute_assembler(procedure_token, *execute_args)
-            assert 0, "should not reach this point"
+            # run it, but from outside in ll_portal_runner, not from here
+            # (this avoids RPython-level recursion with no corresponding
+            # app-level recursion, as shown by issues 2200 and 2335)
+            raise EnterJitAssembler(procedure_token, *execute_args)
+
+        class EnterJitAssembler(jitexc.JitException):
+            def __init__(self, procedure_token, *args):
+                self.procedure_token = procedure_token
+                for i, argname in name_red_args:
+                    setattr(self, argname, args[i])
+            def execute(self):
+                args = ()
+                for i, argname in name_red_args:
+                    args += (getattr(self, argname), )
+                return execute_assembler(self.procedure_token, *args)
 
         maybe_compile_and_run._dont_inline_ = True
-        self.maybe_compile_and_run = maybe_compile_and_run
         self.execute_assembler = execute_assembler
-        return maybe_compile_and_run
+        self.entry_point_fns = (maybe_compile_and_run,
+                                EnterJitAssembler)
+        return self.entry_point_fns
 
     # ----------
 
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -593,11 +593,6 @@
 HASH_MALLOC_SIZE = EVP_MD_SIZE + EVP_MD_CTX_SIZE \
         + rffi.sizeof(EVP_MD) * 2 + 208
 
-OBJ_NAME_CALLBACK = lltype.Ptr(lltype.FuncType(
-        [OBJ_NAME, rffi.VOIDP], lltype.Void))
-OBJ_NAME_do_all = external(
-    'OBJ_NAME_do_all', [rffi.INT, OBJ_NAME_CALLBACK, rffi.VOIDP], lltype.Void)
-
 def init_ssl():
     libssl_SSL_load_error_strings()
     libssl_SSL_library_init()
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -220,7 +220,7 @@
             pass
 
 if _WIN32:
-    includes = ['io.h', 'sys/utime.h', 'sys/types.h']
+    includes = ['io.h', 'sys/utime.h', 'sys/types.h', 'process.h']
     libraries = []
 else:
     if sys.platform.startswith(('darwin', 'netbsd', 'openbsd')):
@@ -704,10 +704,10 @@
 c_execve = external('execve',
                     [rffi.CCHARP, rffi.CCHARPP, rffi.CCHARPP], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)
-c_spawnv = external('spawnv',
+c_spawnv = external(UNDERSCORE_ON_WIN32 + 'spawnv',
                     [rffi.INT, rffi.CCHARP, rffi.CCHARPP], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)
-c_spawnve = external('spawnve',
+c_spawnve = external(UNDERSCORE_ON_WIN32 + 'spawnve',
                     [rffi.INT, rffi.CCHARP, rffi.CCHARPP, rffi.CCHARPP],
                      rffi.INT,
                      save_err=rffi.RFFI_SAVE_ERRNO)
diff --git a/rpython/rlib/test/test_rsocket.py 
b/rpython/rlib/test/test_rsocket.py
--- a/rpython/rlib/test/test_rsocket.py
+++ b/rpython/rlib/test/test_rsocket.py
@@ -313,8 +313,10 @@
     assert isinstance(lst, list)
     found = False
     for family, socktype, protocol, canonname, addr in lst:
-        if addr.get_host() == '104.130.43.121':
+        if addr.get_host() in ('104.130.43.121', '23.253.135.79'):
             found = True
+        elif family == AF_INET:
+            print 'pydotorg changed to', addr.get_host()
     result[i] += found
 
 def test_getaddrinfo_pydotorg():
diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py 
b/rpython/rtyper/lltypesystem/ll2ctypes.py
--- a/rpython/rtyper/lltypesystem/ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/ll2ctypes.py
@@ -171,10 +171,7 @@
             _length_ = 2
             @property
             def value(self):
-                if sys.byteorder == 'little':
-                    res = self[0] | (self[1] << 64)
-                else:
-                    res = self[1] | (self[0] << 64)
+                res = self[0] | (self[1] << 64)
                 if res >= (1 << 127):
                     res -= 1 << 128
                 return res
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to