[pypy-commit] pypy vecopt-merge: merged default

plan_rich noreply at buildbot.pypy.org
Tue Oct 13 12:14:06 CEST 2015


Author: Richard Plangger <planrichi at gmail.com>
Branch: vecopt-merge
Changeset: r80157:1e544babac6b
Date: 2015-10-13 12:14 +0200
http://bitbucket.org/pypy/pypy/changeset/1e544babac6b/

Log:	merged 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
@@ -69,6 +69,12 @@
 
 Remove some remnants of the old ootypesystem vs lltypesystem dichotomy.
 
+.. branch: cffi-handle-lifetime
+
+ffi.new_handle() returns handles that work more like CPython's: they
+remain valid as long as the target exists (unlike the previous
+version, where handles become invalid *before* the __del__ is called).
+
 .. branch: vecopt
 .. branch: vecopt-merge
 
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -1,14 +1,14 @@
 """
 Callbacks.
 """
-import sys, os
+import sys, os, py
 
-from rpython.rlib import clibffi, jit, jit_libffi
+from rpython.rlib import clibffi, jit, jit_libffi, rgc, objectmodel
 from rpython.rlib.objectmodel import keepalive_until_here
-from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 
 from pypy.interpreter.error import OperationError, oefmt
-from pypy.module._cffi_backend import cerrno, misc, handle
+from pypy.module._cffi_backend import cerrno, misc
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, W_CTypeFunc
 from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
@@ -19,6 +19,23 @@
 # ____________________________________________________________
 
 
+ at jit.dont_look_inside
+def make_callback(space, ctype, w_callable, w_error, w_onerror):
+    # Allocate a callback as a nonmovable W_CDataCallback instance, which
+    # we can cast to a plain VOIDP.  As long as the object is not freed,
+    # we can cast the VOIDP back to a W_CDataCallback in reveal_callback().
+    cdata = objectmodel.instantiate(W_CDataCallback, nonmovable=True)
+    gcref = rgc.cast_instance_to_gcref(cdata)
+    raw_cdata = rgc.hide_nonmovable_gcref(gcref)
+    cdata.__init__(space, ctype, w_callable, w_error, w_onerror, raw_cdata)
+    return cdata
+
+def reveal_callback(raw_ptr):
+    addr = rffi.cast(llmemory.Address, raw_ptr)
+    gcref = rgc.reveal_gcref(addr)
+    return rgc.try_cast_gcref_to_instance(W_CDataCallback, gcref)
+
+
 class Closure(object):
     """This small class is here to have a __del__ outside any cycle."""
 
@@ -37,7 +54,8 @@
     _immutable_fields_ = ['key_pycode']
     w_onerror = None
 
-    def __init__(self, space, ctype, w_callable, w_error, w_onerror):
+    def __init__(self, space, ctype, w_callable, w_error, w_onerror,
+                 raw_cdata):
         raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc())
         self._closure = Closure(raw_closure)
         W_CData.__init__(self, space, raw_closure, ctype)
@@ -72,8 +90,6 @@
             from pypy.module.thread.os_thread import setup_threads
             setup_threads(space)
         #
-        handle_index = handle.get_handles(space).reserve_next_handle_index()
-        #
         cif_descr = self.getfunctype().cif_descr
         if not cif_descr:
             raise oefmt(space.w_NotImplementedError,
@@ -81,16 +97,13 @@
                         "return type or with '...'", self.getfunctype().name)
         with self as ptr:
             closure_ptr = rffi.cast(clibffi.FFI_CLOSUREP, ptr)
-            unique_id = rffi.cast(rffi.VOIDP, handle_index)
+            unique_id = rffi.cast(rffi.VOIDP, raw_cdata)
             res = clibffi.c_ffi_prep_closure(closure_ptr, cif_descr.cif,
                                              invoke_callback,
                                              unique_id)
         if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
             raise OperationError(space.w_SystemError,
                 space.wrap("libffi failed to build this callback"))
-        #
-        _current_space.space = space
-        handle.get_handles(space).store_handle(handle_index, self)
 
     def _repr_extra(self):
         space = self.space
@@ -221,12 +234,6 @@
     except OperationError, e:
         _handle_applevel_exception(callback, e, ll_res, extra_line)
 
-class CurrentSpace:
-    def _cleanup_(self):
-        if hasattr(self, 'space'):
-            del self.space
-_current_space = CurrentSpace()
-
 def _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
     """ Callback specification.
     ffi_cif - something ffi specific, don't care
@@ -236,10 +243,8 @@
                   (what the real callback is for example), casted to VOIDP
     """
     ll_res = rffi.cast(rffi.CCHARP, ll_res)
-    unique_id = rffi.cast(lltype.Signed, ll_userdata)
-    space = _current_space.space
-    callback = handle.get_handles(space).fetch_handle(unique_id)
-    if callback is None or not isinstance(callback, W_CDataCallback):
+    callback = reveal_callback(ll_userdata)
+    if callback is None:
         # oups!
         try:
             os.write(STDERR, "SystemError: invoking a callback "
@@ -251,6 +256,7 @@
         misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
         return
     #
+    space = callback.space
     must_leave = False
     try:
         must_leave = space.threadlocals.try_enter_thread(space)
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -294,9 +294,9 @@
                                          CONSIDER_FN_AS_FNPTR)
         space = self.space
         if not space.is_none(w_python_callable):
-            return ccallback.W_CDataCallback(space, w_ctype,
-                                             w_python_callable, w_error,
-                                             w_onerror)
+            return ccallback.make_callback(space, w_ctype,
+                                           w_python_callable, w_error,
+                                           w_onerror)
         else:
             # decorator mode: returns a single-argument function
             return space.appexec([w_ctype, w_error, w_onerror],
diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -24,8 +24,8 @@
 
 @unwrap_spec(w_ctype=ctypeobj.W_CType)
 def callback(space, w_ctype, w_callable, w_error=None, w_onerror=None):
-    from pypy.module._cffi_backend.ccallback import W_CDataCallback
-    return W_CDataCallback(space, w_ctype, w_callable, w_error, w_onerror)
+    from pypy.module._cffi_backend.ccallback import make_callback
+    return make_callback(space, w_ctype, w_callable, w_error, w_onerror)
 
 # ____________________________________________________________
 
diff --git a/pypy/module/_cffi_backend/handle.py b/pypy/module/_cffi_backend/handle.py
--- a/pypy/module/_cffi_backend/handle.py
+++ b/pypy/module/_cffi_backend/handle.py
@@ -1,24 +1,24 @@
+import py
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.baseobjspace import W_Root
 from pypy.module._cffi_backend import ctypeobj, ctypeptr, cdataobj
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib import rweaklist
-
-
-class CffiHandles(rweaklist.RWeakListMixin):
-    def __init__(self, space):
-        self.initialize()
-
-def get_handles(space):
-    return space.fromcache(CffiHandles)
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rlib import rgc, objectmodel, jit
 
 # ____________________________________________________________
 
+ at jit.dont_look_inside
 def _newp_handle(space, w_ctype, w_x):
-    index = get_handles(space).reserve_next_handle_index()
-    _cdata = rffi.cast(rffi.CCHARP, index + 1)
-    new_cdataobj = cdataobj.W_CDataHandle(space, _cdata, w_ctype, w_x)
-    get_handles(space).store_handle(index, new_cdataobj)
+    # Allocate a handle as a nonmovable W_CDataHandle instance, which
+    # we can cast to a plain CCHARP.  As long as the object is not freed,
+    # we can cast the CCHARP back to a W_CDataHandle with reveal_gcref().
+    new_cdataobj = objectmodel.instantiate(cdataobj.W_CDataHandle,
+                                           nonmovable=True)
+    gcref = rgc.cast_instance_to_gcref(new_cdataobj)
+    _cdata = rgc.hide_nonmovable_gcref(gcref)
+    _cdata = rffi.cast(rffi.CCHARP, _cdata)
+    cdataobj.W_CDataHandle.__init__(new_cdataobj, space, _cdata, w_ctype, w_x)
     return new_cdataobj
 
 @unwrap_spec(w_ctype=ctypeobj.W_CType)
@@ -38,14 +38,17 @@
                     "expected a 'cdata' object with a 'void *' out of "
                     "new_handle(), got '%s'", ctype.name)
     with w_cdata as ptr:
-        index = rffi.cast(lltype.Signed, ptr)
-        original_cdataobj = get_handles(space).fetch_handle(index - 1)
-    #
-    if isinstance(original_cdataobj, cdataobj.W_CDataHandle):
-        return original_cdataobj.w_keepalive
-    else:
-        if index == 0:
-            msg = "cannot use from_handle() on NULL pointer"
-        else:
-            msg = "'void *' value does not correspond to any object"
-        raise OperationError(space.w_RuntimeError, space.wrap(msg))
+        return _reveal(space, ptr)
+
+ at jit.dont_look_inside
+def _reveal(space, ptr):
+    addr = rffi.cast(llmemory.Address, ptr)
+    gcref = rgc.reveal_gcref(addr)
+    if not gcref:
+        raise oefmt(space.w_RuntimeError,
+                    "cannot use from_handle() on NULL pointer")
+    cd = rgc.try_cast_gcref_to_instance(cdataobj.W_CDataHandle, gcref)
+    if cd is None:
+        raise oefmt(space.w_SystemError,
+                    "ffi.from_handle(): dead or bogus object handle")
+    return cd.w_keepalive
diff --git a/pypy/module/_cffi_backend/test/test_handle.py b/pypy/module/_cffi_backend/test/test_handle.py
deleted file mode 100644
--- a/pypy/module/_cffi_backend/test/test_handle.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import random
-from pypy.module._cffi_backend.handle import CffiHandles
-
-
-class PseudoWeakRef(object):
-    _content = 42
-
-    def __call__(self):
-        return self._content
-
-
-def test_cffi_handles_1():
-    ch = CffiHandles(None)
-    expected_content = {}
-    for i in range(10000):
-        index = ch.reserve_next_handle_index()
-        assert 0 <= index < len(ch.handles)
-        assert ch.handles[index]() is None
-        pwr = PseudoWeakRef()
-        expected_content[index] = pwr
-        ch.handles[index] = pwr
-    assert len(ch.handles) <= 16384
-    for index, pwr in expected_content.items():
-        assert ch.handles[index] is pwr
-
-def test_cffi_handles_2():
-    ch = CffiHandles(None)
-    expected_content = {}
-    for i in range(10000):
-        index = ch.reserve_next_handle_index()
-        assert 0 <= index < len(ch.handles)
-        assert ch.handles[index]() is None
-        pwr = PseudoWeakRef()
-        expected_content[index] = pwr
-        ch.handles[index] = pwr
-        #
-        if len(expected_content) > 20:
-            r = random.choice(list(expected_content))
-            pwr = expected_content.pop(r)
-            pwr._content = None
-        #
-    assert len(ch.handles) < 100
-    for index, pwr in expected_content.items():
-        assert ch.handles[index] is pwr
diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py
--- a/rpython/annotator/builtin.py
+++ b/rpython/annotator/builtin.py
@@ -290,7 +290,7 @@
     return SomeInteger(knowntype=rpython.rlib.rarithmetic.r_longlong)
 
 @analyzer_for(rpython.rlib.objectmodel.instantiate)
-def robjmodel_instantiate(s_clspbc):
+def robjmodel_instantiate(s_clspbc, s_nonmovable=None):
     assert isinstance(s_clspbc, SomePBC)
     clsdef = None
     more_than_one = len(s_clspbc.descriptions) > 1
diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -508,6 +508,21 @@
         self._store_and_reset_exception(self.mc, resloc)
         return fcond
 
+    def emit_op_save_exc_class(self, op, arglocs, regalloc, fcond):
+        resloc = arglocs[0]
+        self.mc.gen_load_int(r.ip.value, self.cpu.pos_exception())
+        self.load_reg(self.mc, resloc, r.ip)
+        return fcond
+
+    def emit_op_save_exception(self, op, arglocs, regalloc, fcond):
+        resloc = arglocs[0]
+        self._store_and_reset_exception(self.mc, resloc)
+        return fcond
+
+    def emit_op_restore_exception(self, op, arglocs, regalloc, fcond):
+        self._restore_exception(self.mc, arglocs[1], arglocs[0])
+        return fcond
+
     def emit_op_debug_merge_point(self, op, arglocs, regalloc, fcond):
         return fcond
     emit_op_jit_debug = emit_op_debug_merge_point
diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -707,6 +707,17 @@
                     [loc, loc1, resloc, pos_exc_value, pos_exception])
         return arglocs
 
+    def prepare_op_save_exception(self, op, fcond):
+        resloc = self.force_allocate_reg(op)
+        return [resloc]
+    prepare_op_save_exc_class = prepare_op_save_exception
+
+    def prepare_op_restore_exception(self, op, fcond):
+        boxes = op.getarglist()
+        loc0 = self.make_sure_var_in_reg(op.getarg(0), boxes)  # exc class
+        loc1 = self.make_sure_var_in_reg(op.getarg(1), boxes)  # exc instance
+        return [loc0, loc1]
+
     def prepare_op_guard_no_exception(self, op, fcond):
         loc = self.make_sure_var_in_reg(ConstInt(self.cpu.pos_exception()))
         arglocs = self._prepare_guard(op, [loc])
diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -52,8 +52,6 @@
                 # we don't care about the value 13 here, because we gonna
                 # fish it from the extra slot on frame anyway
                 op.getdescr().make_a_counter_per_value(op, 13)
-            elif opnum == rop.BRIDGE_EXCEPTION:
-                assert len(self.operations) == 0   # must be first
             if op.getdescr() is not None:
                 if op.is_guard() or op.getopnum() == rop.FINISH:
                     newdescr = op.getdescr()
@@ -1097,8 +1095,9 @@
         self._accumulate(descr, self.current_op.getfailargs(), values)
         if hasattr(descr, '_llgraph_bridge'):
             if propagate_exception:
-                assert (descr._llgraph_bridge.operations[0].opnum ==
-                        rop.BRIDGE_EXCEPTION)
+                assert (descr._llgraph_bridge.operations[0].opnum in
+                        (rop.SAVE_EXC_CLASS, rop.GUARD_EXCEPTION,
+                         rop.GUARD_NO_EXCEPTION))
             target = (descr._llgraph_bridge, -1)
             values = [value for value in values if value is not None]
             raise Jump(target, values)
@@ -1430,8 +1429,32 @@
     def execute_keepalive(self, descr, x):
         pass
 
-    def execute_bridge_exception(self, descr):
-        pass
+    def execute_save_exc_class(self, descr):
+        lle = self.last_exception
+        if lle is None:
+            return 0
+        else:
+            return support.cast_to_int(lle.args[0])
+
+    def execute_save_exception(self, descr):
+        lle = self.last_exception
+        if lle is None:
+            res = lltype.nullptr(llmemory.GCREF.TO)
+        else:
+            res = lltype.cast_opaque_ptr(llmemory.GCREF, lle.args[1])
+        self.last_exception = None
+        return res
+
+    def execute_restore_exception(self, descr, kls, e):
+        kls = heaptracker.int2adr(kls)
+        if e:
+            value = lltype.cast_opaque_ptr(rclass.OBJECTPTR, e)
+            assert llmemory.cast_ptr_to_adr(value.typeptr) == kls
+            lle = LLException(value.typeptr, e)
+        else:
+            assert kls == llmemory.NULL
+            lle = None
+        self.last_exception = lle
 
 
 def _getdescr(op):
diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -119,6 +119,7 @@
         # barriers.  We do this on each "basic block" of operations, which in
         # this case means between CALLs or unknown-size mallocs.
         #
+        operations = self.remove_bridge_exception(operations)
         for i in range(len(operations)):
             op = operations[i]
             assert op.get_forwarded() is None
@@ -168,9 +169,6 @@
                 continue
             if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH:
                 self.emit_pending_zeros()
-            if op.getopnum() == rop.BRIDGE_EXCEPTION:
-                self.remove_bridge_exception(operations, i)
-                continue
             #
             self.emit_op(op)
         return self._newops
@@ -686,13 +684,17 @@
             size = max(size, 2 * WORD)
             return (size + WORD-1) & ~(WORD-1)     # round up
 
-    def remove_bridge_exception(self, operations, i):
-        """Check that the 'bridge_exception' operation occurs at the
-        start of the bridge."""
-        if i == 0:
-            return     # first operation, ok
-        if i == 1 and operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER:
-            return     # 2nd operation after INCREMENT_DEBUG_COUNTER, ok
-        # not ok!
-        assert we_are_translated()
-        raise BridgeExceptionNotFirst
+    def remove_bridge_exception(self, operations):
+        """Check a common case: 'save_exception' immediately followed by
+        'restore_exception' at the start of the bridge."""
+        # XXX should check if the boxes are used later; but we just assume
+        # they aren't for now
+        start = 0
+        if operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER:
+            start = 1
+        if len(operations) >= start + 3:
+            if (operations[start+0].getopnum() == rop.SAVE_EXC_CLASS and
+                operations[start+1].getopnum() == rop.SAVE_EXCEPTION and
+                operations[start+2].getopnum() == rop.RESTORE_EXCEPTION):
+                return operations[:start] + operations[start+3:]
+        return operations
diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -2099,6 +2099,60 @@
         excvalue = self.cpu.grab_exc_value(deadframe)
         assert not excvalue
 
+    def test_save_restore_exceptions(self):
+        exc_tp = None
+        exc_ptr = None
+        def func(i):
+            if hasattr(self.cpu, '_exception_emulator'):
+                assert not self.cpu._exception_emulator[0]
+                assert not self.cpu._exception_emulator[1]
+            called.append(i)
+            if i:
+                raise LLException(exc_tp, exc_ptr)
+
+        ops = '''
+        [i0]
+        i1 = same_as_i(1)
+        call_n(ConstClass(fptr), i0, descr=calldescr)
+        i2 = save_exc_class()
+        p2 = save_exception()
+        call_n(ConstClass(fptr), 0, descr=calldescr)
+        restore_exception(i2, p2)
+        p0 = guard_exception(ConstClass(xtp)) [i1]
+        finish(p0)
+        '''
+        FPTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Void))
+        fptr = llhelper(FPTR, func)
+        calldescr = self.cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
+                                         EffectInfo.MOST_GENERAL)
+
+        xtp = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
+        xtp.subclassrange_min = 1
+        xtp.subclassrange_max = 3
+        X = lltype.GcStruct('X', ('parent', rclass.OBJECT),
+                            hints={'vtable':  xtp._obj})
+        xx = lltype.malloc(X)
+        xx.parent.typeptr = xtp
+        xptr = lltype.cast_opaque_ptr(llmemory.GCREF, xx)
+
+        exc_tp = xtp
+        exc_ptr = xptr
+        loop = parse(ops, self.cpu, namespace=locals())
+        looptoken = JitCellToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+        called = []
+        deadframe = self.cpu.execute_token(looptoken, 5)
+        assert called == [5, 0]
+        assert self.cpu.get_ref_value(deadframe, 0) == xptr
+        excvalue = self.cpu.grab_exc_value(deadframe)
+        assert not excvalue
+        called = []
+        deadframe = self.cpu.execute_token(looptoken, 0)
+        assert called == [0, 0]
+        assert self.cpu.get_int_value(deadframe, 0) == 1
+        excvalue = self.cpu.grab_exc_value(deadframe)
+        assert not excvalue
+
     def test_cond_call_gc_wb(self):
         def func_void(a):
             record.append(rffi.cast(lltype.Signed, a))
diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -1698,6 +1698,15 @@
         self.implement_guard(guard_token)
         self._store_and_reset_exception(self.mc, resloc)
 
+    def genop_save_exc_class(self, op, arglocs, resloc):
+        self.mc.MOV(resloc, heap(self.cpu.pos_exception()))
+
+    def genop_save_exception(self, op, arglocs, resloc):
+        self._store_and_reset_exception(self.mc, resloc)
+
+    def genop_discard_restore_exception(self, op, arglocs):
+        self._restore_exception(self.mc, arglocs[1], arglocs[0])
+
     def _store_and_reset_exception(self, mc, excvalloc=None, exctploc=None,
                                    tmploc=None):
         """ Resest the exception. If excvalloc is None, then store it on the
diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -465,6 +465,17 @@
         self.perform_guard(op, [loc, loc1], resloc)
         self.rm.possibly_free_var(box)
 
+    def consider_save_exception(self, op):
+        resloc = self.rm.force_allocate_reg(op)
+        self.perform(op, [], resloc)
+    consider_save_exc_class = consider_save_exception
+
+    def consider_restore_exception(self, op):
+        args = op.getarglist()
+        loc0 = self.rm.make_sure_var_in_reg(op.getarg(0), args)  # exc class
+        loc1 = self.rm.make_sure_var_in_reg(op.getarg(1), args)  # exc instance
+        self.perform_discard(op, [loc0, loc1])
+
     consider_guard_no_overflow = consider_guard_no_exception
     consider_guard_overflow    = consider_guard_no_exception
     consider_guard_not_forced  = consider_guard_no_exception
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -915,10 +915,13 @@
         return [op0, op1]
 
     def rewrite_op_malloc(self, op):
-        if op.args[1].value['flavor'] == 'raw':
+        d = op.args[1].value
+        if d.get('nonmovable', False):
+            raise UnsupportedMallocFlags(d)
+        if d['flavor'] == 'raw':
             return self._rewrite_raw_malloc(op, 'raw_malloc_fixedsize', [])
         #
-        if op.args[1].value.get('zero', False):
+        if d.get('zero', False):
             zero = True
         else:
             zero = False
diff --git a/rpython/jit/metainterp/executor.py b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -386,7 +386,9 @@
                          rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME,
                          rop.NURSERY_PTR_INCREMENT,
                          rop.LABEL,
-                         rop.BRIDGE_EXCEPTION,
+                         rop.SAVE_EXC_CLASS,
+                         rop.SAVE_EXCEPTION,
+                         rop.RESTORE_EXCEPTION,
                          rop.VEC_RAW_LOAD_I,
                          rop.VEC_RAW_LOAD_F,
                          rop.VEC_RAW_STORE,
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
@@ -2487,17 +2487,28 @@
             # 'test_guard_no_exception_incorrectly_removed_from_bridge'
             # shows a corner case in which just putting GuARD_NO_EXCEPTION
             # here is a bad idea: the optimizer might remove it too.
-            # So we put a pair BRIDGE_EXCEPTION / GUARD_(NO)_EXCEPTION.
-            # The BRIDGE_EXCEPTION is meant to re-raise the exception
-            # caught before the bridge, but in reality it must end up
-            # as the first operation and thus is a no-op for the backends
-            # (it is removed in rewrite.py).  Its real purpose is only to
-            # pass through the optimizer unmodified, so that the following
-            # GUARD_NO_EXCEPTION is not killed.
-            self.history.record(rop.BRIDGE_EXCEPTION, [], None)
-            if exception:
-                self.execute_ll_raised(lltype.cast_opaque_ptr(rclass.OBJECTPTR,
-                                                              exception))
+            # So we put a SAVE_EXCEPTION at the start, and a
+            # RESTORE_EXCEPTION just before the guard.  (rewrite.py will
+            # remove the two if they end up consecutive.)
+
+            # XXX too much jumps between older and newer models; clean up
+            # by killing SAVE_EXC_CLASS, RESTORE_EXCEPTION and GUARD_EXCEPTION
+
+            exception_obj = lltype.cast_opaque_ptr(rclass.OBJECTPTR, exception)
+            if exception_obj:
+                exc_class = heaptracker.adr2int(
+                    llmemory.cast_ptr_to_adr(exception_obj.typeptr))
+            else:
+                exc_class = 0
+            i = len(self.history.operations)
+            op1 = self.history.record(rop.SAVE_EXC_CLASS, [], exc_class)
+            op2 = self.history.record(rop.SAVE_EXCEPTION, [], exception)
+            assert op1 is self.history.operations[i]
+            assert op2 is self.history.operations[i + 1]
+            self.history.operations = [op1, op2] + self.history.operations[:i]
+            self.history.record(rop.RESTORE_EXCEPTION, [op1, op2], None)
+            if exception_obj:
+                self.execute_ll_raised(exception_obj)
             else:
                 self.clear_exception()
             try:
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -978,7 +978,7 @@
     'GUARD_SUBCLASS/2d/n',      # only if supports_guard_gc_type
     '_GUARD_FOLDABLE_LAST',
     'GUARD_NO_EXCEPTION/0d/n',   # may be called with an exception currently set
-    'GUARD_EXCEPTION/1d/r',     # may be called with an exception currently set
+    'GUARD_EXCEPTION/1d/r',     # XXX kill me, use only SAVE_EXCEPTION
     'GUARD_NO_OVERFLOW/0d/n',
     'GUARD_OVERFLOW/0d/n',
     'GUARD_NOT_FORCED/0d/n',      # may be called with an exception currently set
@@ -1158,7 +1158,9 @@
     'QUASIIMMUT_FIELD/1d/n',    # [objptr], descr=SlowMutateDescr
     'RECORD_EXACT_CLASS/2/n',   # [objptr, clsptr]
     'KEEPALIVE/1/n',
-    'BRIDGE_EXCEPTION/0/n',     # pyjitpl: prepare_resume_from_failure()
+    'SAVE_EXCEPTION/0/r',
+    'SAVE_EXC_CLASS/0/i',       # XXX kill me
+    'RESTORE_EXCEPTION/2/n',    # XXX kill me
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
     '_CALL_FIRST',
diff --git a/rpython/memory/gc/base.py b/rpython/memory/gc/base.py
--- a/rpython/memory/gc/base.py
+++ b/rpython/memory/gc/base.py
@@ -172,6 +172,9 @@
     def can_move(self, addr):
         return False
 
+    def malloc_fixedsize_nonmovable(self, typeid):
+        raise MemoryError
+
     def pin(self, addr):
         return False
 
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -597,7 +597,7 @@
         if needs_finalizer and not is_finalizer_light:
             ll_assert(not contains_weakptr,
                      "'needs_finalizer' and 'contains_weakptr' both specified")
-            obj = self.external_malloc(typeid, 0, can_make_young=False)
+            obj = self.external_malloc(typeid, 0, alloc_young=False)
             self.objects_with_finalizers.append(obj)
         #
         # If totalsize is greater than nonlarge_max (which should never be
@@ -606,7 +606,7 @@
         elif rawtotalsize > self.nonlarge_max:
             ll_assert(not contains_weakptr,
                       "'contains_weakptr' specified for a large object")
-            obj = self.external_malloc(typeid, 0)
+            obj = self.external_malloc(typeid, 0, alloc_young=True)
             #
         else:
             # If totalsize is smaller than minimal_size_in_nursery, round it
@@ -659,7 +659,7 @@
             # If the total size of the object would be larger than
             # 'nonlarge_max', then allocate it externally.  We also
             # go there if 'length' is actually negative.
-            obj = self.external_malloc(typeid, length)
+            obj = self.external_malloc(typeid, length, alloc_young=True)
             #
         else:
             # With the above checks we know now that totalsize cannot be more
@@ -692,6 +692,11 @@
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
+    def malloc_fixedsize_nonmovable(self, typeid):
+        obj = self.external_malloc(typeid, 0, alloc_young=True)
+        return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+
+
     def collect(self, gen=2):
         """Do a minor (gen=0), start a major (gen=1), or do a full
         major (gen>=2) collection."""
@@ -808,7 +813,7 @@
     collect_and_reserve._dont_inline_ = True
 
 
-    def external_malloc(self, typeid, length, can_make_young=True):
+    def external_malloc(self, typeid, length, alloc_young):
         """Allocate a large object using the ArenaCollection or
         raw_malloc(), possibly as an object with card marking enabled,
         if it has gc pointers in its var-sized part.  'length' should be
@@ -862,7 +867,9 @@
             # we should get a MemoryError from major_collection_step().
         #
         # Check if the object would fit in the ArenaCollection.
-        if raw_malloc_usage(totalsize) <= self.small_request_threshold:
+        # Also, an object allocated from ArenaCollection must be old.
+        if (raw_malloc_usage(totalsize) <= self.small_request_threshold
+            and not alloc_young):
             #
             # Yes.  Round up 'totalsize' (it cannot overflow and it
             # must remain <= self.small_request_threshold.)
@@ -874,10 +881,6 @@
             # Allocate from the ArenaCollection.  Don't clear it.
             result = self.ac.malloc(totalsize)
             #
-            # An object allocated from ArenaCollection is always old, even
-            # if 'can_make_young'.  The interesting case of 'can_make_young'
-            # is for large objects, bigger than the 'large_objects' threshold,
-            # which are raw-malloced but still young.
             extra_flags = GCFLAG_TRACK_YOUNG_PTRS
             #
         else:
@@ -897,11 +900,11 @@
                 extra_words = self.card_marking_words_for_length(length)
                 cardheadersize = WORD * extra_words
                 extra_flags = GCFLAG_HAS_CARDS | GCFLAG_TRACK_YOUNG_PTRS
-                # if 'can_make_young', then we also immediately set
+                # if 'alloc_young', then we also immediately set
                 # GCFLAG_CARDS_SET, but without adding the object to
                 # 'old_objects_with_cards_set'.  In this way it should
                 # never be added to that list as long as it is young.
-                if can_make_young:
+                if alloc_young:
                     extra_flags |= GCFLAG_CARDS_SET
             #
             # Detect very rare cases of overflows
@@ -939,7 +942,7 @@
             # Record the newly allocated object and its full malloced size.
             # The object is young or old depending on the argument.
             self.rawmalloced_total_size += r_uint(allocsize)
-            if can_make_young:
+            if alloc_young:
                 if not self.young_rawmalloced_objects:
                     self.young_rawmalloced_objects = self.AddressDict()
                 self.young_rawmalloced_objects.add(result + size_gc_header)
diff --git a/rpython/memory/gc/minimark.py b/rpython/memory/gc/minimark.py
--- a/rpython/memory/gc/minimark.py
+++ b/rpython/memory/gc/minimark.py
@@ -519,7 +519,7 @@
         if needs_finalizer and not is_finalizer_light:
             ll_assert(not contains_weakptr,
                      "'needs_finalizer' and 'contains_weakptr' both specified")
-            obj = self.external_malloc(typeid, 0, can_make_young=False)
+            obj = self.external_malloc(typeid, 0, alloc_young=False)
             self.objects_with_finalizers.append(obj)
         #
         # If totalsize is greater than nonlarge_max (which should never be
@@ -528,7 +528,7 @@
         elif rawtotalsize > self.nonlarge_max:
             ll_assert(not contains_weakptr,
                       "'contains_weakptr' specified for a large object")
-            obj = self.external_malloc(typeid, 0)
+            obj = self.external_malloc(typeid, 0, alloc_young=True)
             #
         else:
             # If totalsize is smaller than minimal_size_in_nursery, round it
@@ -581,7 +581,7 @@
             # If the total size of the object would be larger than
             # 'nonlarge_max', then allocate it externally.  We also
             # go there if 'length' is actually negative.
-            obj = self.external_malloc(typeid, length)
+            obj = self.external_malloc(typeid, length, alloc_young=True)
             #
         else:
             # With the above checks we know now that totalsize cannot be more
@@ -614,6 +614,11 @@
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
+    def malloc_fixedsize_nonmovable(self, typeid):
+        obj = self.external_malloc(typeid, 0, alloc_young=True)
+        return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+
+
     def collect(self, gen=1):
         """Do a minor (gen=0) or major (gen>0) collection."""
         self.minor_collection()
@@ -671,7 +676,7 @@
     collect_and_reserve._dont_inline_ = True
 
 
-    def external_malloc(self, typeid, length, can_make_young=True):
+    def external_malloc(self, typeid, length, alloc_young):
         """Allocate a large object using the ArenaCollection or
         raw_malloc(), possibly as an object with card marking enabled,
         if it has gc pointers in its var-sized part.  'length' should be
@@ -711,7 +716,9 @@
             self.major_collection(raw_malloc_usage(totalsize))
         #
         # Check if the object would fit in the ArenaCollection.
-        if raw_malloc_usage(totalsize) <= self.small_request_threshold:
+        # Also, an object allocated from ArenaCollection must be old.
+        if (raw_malloc_usage(totalsize) <= self.small_request_threshold
+            and not alloc_young):
             #
             # Yes.  Round up 'totalsize' (it cannot overflow and it
             # must remain <= self.small_request_threshold.)
@@ -724,10 +731,6 @@
             result = self.ac.malloc(totalsize)
             llmemory.raw_memclear(result, totalsize)
             #
-            # An object allocated from ArenaCollection is always old, even
-            # if 'can_make_young'.  The interesting case of 'can_make_young'
-            # is for large objects, bigger than the 'large_objects' threshold,
-            # which are raw-malloced but still young.
             extra_flags = GCFLAG_TRACK_YOUNG_PTRS
             #
         else:
@@ -747,11 +750,11 @@
                 extra_words = self.card_marking_words_for_length(length)
                 cardheadersize = WORD * extra_words
                 extra_flags = GCFLAG_HAS_CARDS | GCFLAG_TRACK_YOUNG_PTRS
-                # if 'can_make_young', then we also immediately set
+                # if 'alloc_young', then we also immediately set
                 # GCFLAG_CARDS_SET, but without adding the object to
                 # 'old_objects_with_cards_set'.  In this way it should
                 # never be added to that list as long as it is young.
-                if can_make_young:
+                if alloc_young:
                     extra_flags |= GCFLAG_CARDS_SET
             #
             # Detect very rare cases of overflows
@@ -787,7 +790,7 @@
             # Record the newly allocated object and its full malloced size.
             # The object is young or old depending on the argument.
             self.rawmalloced_total_size += r_uint(allocsize)
-            if can_make_young:
+            if alloc_young:
                 if not self.young_rawmalloced_objects:
                     self.young_rawmalloced_objects = self.AddressDict()
                 self.young_rawmalloced_objects.add(result + size_gc_header)
diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py
--- a/rpython/memory/gc/test/test_direct.py
+++ b/rpython/memory/gc/test/test_direct.py
@@ -565,8 +565,8 @@
         tid = self.get_type_id(VAR)
         largeobj_size =  self.gc.nonlarge_max + 1
         self.gc.next_major_collection_threshold = 99999.0
-        addr_src = self.gc.external_malloc(tid, largeobj_size)
-        addr_dst = self.gc.external_malloc(tid, largeobj_size)
+        addr_src = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
+        addr_dst = self.gc.external_malloc(tid, largeobj_size, alloc_young=True)
         hdr_src = self.gc.header(addr_src)
         hdr_dst = self.gc.header(addr_dst)
         #
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -531,6 +531,9 @@
                                              getfn(func,
                                                    [SomeAddress()],
                                                    annmodel.s_None)
+        self.malloc_nonmovable_ptr = getfn(GCClass.malloc_fixedsize_nonmovable,
+                                           [s_gc, s_typeid16],
+                                           s_gcref)
 
     def create_custom_trace_funcs(self, gc, rtyper):
         custom_trace_funcs = tuple(rtyper.custom_trace_funcs)
@@ -757,7 +760,12 @@
         c_has_light_finalizer = rmodel.inputconst(lltype.Bool,
                                                   has_light_finalizer)
 
-        if not op.opname.endswith('_varsize') and not flags.get('varsize'):
+        if flags.get('nonmovable'):
+            assert op.opname == 'malloc'
+            assert not flags.get('varsize')
+            malloc_ptr = self.malloc_nonmovable_ptr
+            args = [self.c_const_gc, c_type_id]
+        elif not op.opname.endswith('_varsize') and not flags.get('varsize'):
             zero = flags.get('zero', False)
             if (self.malloc_fast_ptr is not None and
                 not c_has_finalizer.value and
diff --git a/rpython/memory/test/test_transformed_gc.py b/rpython/memory/test/test_transformed_gc.py
--- a/rpython/memory/test/test_transformed_gc.py
+++ b/rpython/memory/test/test_transformed_gc.py
@@ -1247,6 +1247,26 @@
         res = self.runner('nursery_hash_base')
         assert res([]) >= 195
 
+    def define_instantiate_nonmovable(cls):
+        from rpython.rlib import objectmodel
+        from rpython.rtyper import annlowlevel
+        class A:
+            pass
+        def fn():
+            a1 = A()
+            a = objectmodel.instantiate(A, nonmovable=True)
+            a.next = a1  # 'a' is known young here, so no write barrier emitted
+            res = rgc.can_move(annlowlevel.cast_instance_to_base_ptr(a))
+            rgc.collect()
+            objectmodel.keepalive_until_here(a)
+            return res
+        return fn
+
+    def test_instantiate_nonmovable(self):
+        res = self.runner('instantiate_nonmovable')
+        assert res([]) == 0
+
+
 class TestIncrementalMiniMarkGC(TestMiniMarkGC):
     gcname = "incminimark"
 
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -276,7 +276,7 @@
 
 # ____________________________________________________________
 
-def instantiate(cls):
+def instantiate(cls, nonmovable=False):
     "Create an empty instance of 'cls'."
     if isinstance(cls, type):
         return cls.__new__(cls)
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -480,7 +480,7 @@
 
 class _GcRef(object):
     # implementation-specific: there should not be any after translation
-    __slots__ = ['_x']
+    __slots__ = ['_x', '_handle']
     def __init__(self, x):
         self._x = x
     def __hash__(self):
@@ -529,6 +529,48 @@
         return None
 try_cast_gcref_to_instance._annspecialcase_ = 'specialize:arg(0)'
 
+_ffi_cache = None
+def _fetch_ffi():
+    global _ffi_cache
+    if _ffi_cache is None:
+        try:
+            import _cffi_backend
+            _ffi_cache = _cffi_backend.FFI()
+        except (ImportError, AttributeError):
+            import py
+            py.test.skip("need CFFI >= 1.0")
+    return _ffi_cache
+
+ at jit.dont_look_inside
+def hide_nonmovable_gcref(gcref):
+    from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+    if we_are_translated():
+        assert lltype.typeOf(gcref) == llmemory.GCREF
+        assert not can_move(gcref)
+        return rffi.cast(llmemory.Address, gcref)
+    else:
+        assert isinstance(gcref, _GcRef)
+        x = gcref._x
+        ffi = _fetch_ffi()
+        if not hasattr(x, '__handle'):
+            x.__handle = ffi.new_handle(x)
+        addr = int(ffi.cast("intptr_t", x.__handle))
+        return rffi.cast(llmemory.Address, addr)
+
+ at jit.dont_look_inside
+def reveal_gcref(addr):
+    from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+    assert lltype.typeOf(addr) == llmemory.Address
+    if we_are_translated():
+        return rffi.cast(llmemory.GCREF, addr)
+    else:
+        addr = rffi.cast(lltype.Signed, addr)
+        if addr == 0:
+            return lltype.nullptr(llmemory.GCREF.TO)
+        ffi = _fetch_ffi()
+        x = ffi.from_handle(ffi.cast("void *", addr))
+        return _GcRef(x)
+
 # ------------------- implementation -------------------
 
 _cache_s_list_of_gcrefs = None
diff --git a/rpython/rlib/rthread.py b/rpython/rlib/rthread.py
--- a/rpython/rlib/rthread.py
+++ b/rpython/rlib/rthread.py
@@ -79,6 +79,7 @@
 
 @specialize.arg(0)
 def ll_start_new_thread(func):
+    _check_thread_enabled()
     ident = c_thread_start(func)
     if ident == -1:
         raise error("can't start new thread")
@@ -170,6 +171,18 @@
     def _cleanup_(self):
         raise Exception("seeing a prebuilt rpython.rlib.rthread.Lock instance")
 
+def _check_thread_enabled():
+    pass
+class Entry(ExtRegistryEntry):
+    _about_ = _check_thread_enabled
+    def compute_result_annotation(self):
+        translator = self.bookkeeper.annotator.translator
+        if not translator.config.translation.thread:
+            raise Exception(
+                "this RPython program uses threads: translate with '--thread'")
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+
 # ____________________________________________________________
 #
 # Stack size
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
@@ -92,12 +92,13 @@
         PLT = ""
         size_decl = ""
         type_decl = ""
+        extra_align = ""
     else:
         PLT = "@PLT"
         type_decl = "\t.type\t%s, @function" % (tramp_name,)
         size_decl = "\t.size\t%s, .-%s" % (
             tramp_name, tramp_name)
-
+        extra_align = "\t.cfi_def_cfa_offset 8"
 
     assert detect_cpu.autodetect().startswith(detect_cpu.MODEL_X86_64), (
         "rvmprof only supports x86-64 CPUs for now")
@@ -132,7 +133,7 @@
 \t.cfi_def_cfa_offset 16
 \tcall %(cont_name)s%(PLT)s
 \taddq\t$8, %%rsp
-\t.cfi_def_cfa_offset 8
+%(extra_align)s
 \tret
 \t.cfi_endproc
 %(size_decl)s
diff --git a/rpython/rlib/rvmprof/src/vmprof_main.h b/rpython/rlib/rvmprof/src/vmprof_main.h
--- a/rpython/rlib/rvmprof/src/vmprof_main.h
+++ b/rpython/rlib/rvmprof/src/vmprof_main.h
@@ -31,7 +31,11 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include "vmprof_getpc.h"
+#ifdef __APPLE__
+#include "libunwind.h"
+#else
 #include "vmprof_unwind.h"
+#endif
 #include "vmprof_mt.h"
 
 
@@ -39,10 +43,12 @@
 
 // functions copied from libunwind using dlopen
 
+#ifndef __APPLE__ // should be linux only probably
 static int (*unw_get_reg)(unw_cursor_t*, int, unw_word_t*) = NULL;
 static int (*unw_step)(unw_cursor_t*) = NULL;
 static int (*unw_init_local)(unw_cursor_t *, unw_context_t *) = NULL;
 static int (*unw_get_proc_info)(unw_cursor_t *, unw_proc_info_t *) = NULL;
+#endif
 
 static int profile_file = -1;
 static long prepare_interval_usec;
@@ -67,6 +73,7 @@
         return "bad value for 'interval'";
     prepare_interval_usec = (int)(interval * 1000000.0);
 
+#ifndef __APPLE__
     if (!unw_get_reg) {
         void *libhandle;
 
@@ -81,6 +88,7 @@
         if (!(unw_step = dlsym(libhandle, UNW_PREFIX  "_step")))
             goto error;
     }
+#endif
     if (prepare_concurrent_bufs() < 0)
         return "out of memory";
 
@@ -206,7 +214,12 @@
     void *ip;
     int n = 0;
     unw_cursor_t cursor;
+#ifdef __APPLE__
+    unw_context_t uc;
+    unw_getcontext(&uc);
+#else
     unw_context_t uc = *ucontext;
+#endif
 
     int ret = unw_init_local(&cursor, &uc);
     assert(ret >= 0);
diff --git a/rpython/rlib/rvmprof/src/vmprof_unwind.h b/rpython/rlib/rvmprof/src/vmprof_unwind.h
--- a/rpython/rlib/rvmprof/src/vmprof_unwind.h
+++ b/rpython/rlib/rvmprof/src/vmprof_unwind.h
@@ -64,8 +64,7 @@
 typedef struct unw_cursor
   {
     unw_word_t opaque[UNW_TDEP_CURSOR_LEN];
-  }
-unw_cursor_t;
+  } unw_cursor_t;
 
 #define UNW_REG_IP UNW_X86_64_RIP
 #define UNW_REG_SP UNW_X86_64_RSP
@@ -84,7 +83,7 @@
     int format;			/* unwind-info format (arch-specific) */
     int unwind_info_size;	/* size of the information (if applicable) */
     void *unwind_info;		/* unwind-info (arch-specific) */
-  }
-unw_proc_info_t;
+  } unw_proc_info_t;
 
 // end of copy
+
diff --git a/rpython/rlib/rvmprof/test/test_rvmprof.py b/rpython/rlib/rvmprof/test/test_rvmprof.py
--- a/rpython/rlib/rvmprof/test/test_rvmprof.py
+++ b/rpython/rlib/rvmprof/test/test_rvmprof.py
@@ -2,6 +2,7 @@
 from rpython.tool.udir import udir
 from rpython.rlib import rvmprof
 from rpython.translator.c.test.test_genc import compile
+from rpython.rlib.objectmodel import we_are_translated
 
 
 def test_vmprof_execute_code_1():
@@ -96,7 +97,12 @@
     @rvmprof.vmprof_execute_code("xcode1", lambda code, num: code)
     def main(code, num):
         print num
-        return 42
+        s = 0
+        for i in range(num):
+            s += (i << 1)
+            if s % 32423423423 == 0:
+                print s
+        return s
 
     tmpfilename = str(udir.join('test_rvmprof'))
 
@@ -104,16 +110,37 @@
         code = MyCode()
         rvmprof.register_code(code, get_name)
         fd = os.open(tmpfilename, os.O_WRONLY | os.O_CREAT, 0666)
-        rvmprof.enable(fd, 0.5)
-        res = main(code, 5)
-        assert res == 42
+        if we_are_translated():
+            num = 100000000
+            period = 0.0001
+        else:
+            num = 10000
+            period = 0.9
+        rvmprof.enable(fd, period)
+        res = main(code, num)
+        #assert res == 499999500000
         rvmprof.disable()
         os.close(fd)
         return 0
 
+    def check_profile(filename):
+        from vmprof import read_profile
+
+        prof = read_profile(filename)
+        assert prof.get_tree().name.startswith("py:")
+        assert prof.get_tree().count
+
     assert f() == 0
     assert os.path.exists(tmpfilename)
     fn = compile(f, [], gcpolicy="minimark")
-    os.unlink(tmpfilename)
     assert fn() == 0
-    assert os.path.exists(tmpfilename)
+    try:
+        import vmprof
+    except ImportError:
+        py.test.skip("vmprof unimportable")
+    else:
+        check_profile(tmpfilename)
+    finally:
+        assert os.path.exists(tmpfilename)
+        os.unlink(tmpfilename)
+       
\ No newline at end of file
diff --git a/rpython/rtyper/lltypesystem/rtagged.py b/rpython/rtyper/lltypesystem/rtagged.py
--- a/rpython/rtyper/lltypesystem/rtagged.py
+++ b/rpython/rtyper/lltypesystem/rtagged.py
@@ -27,7 +27,8 @@
                     self.classdef, flds))
             self.specialfieldname = flds[0]
 
-    def new_instance(self, llops, classcallhop=None):
+    def new_instance(self, llops, classcallhop=None, nonmovable=False):
+        assert not nonmovable
         if self.is_parent:
             raise TyperError("don't instantiate %r, it is a parent of an "
                              "UnboxedValue class" % (self.classdef,))
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -693,18 +693,24 @@
     return hop.args_r[0].rtype_isinstance(hop)
 
 @typer_for(objectmodel.instantiate)
-def rtype_instantiate(hop):
+def rtype_instantiate(hop, i_nonmovable=None):
     hop.exception_cannot_occur()
     s_class = hop.args_s[0]
     assert isinstance(s_class, annmodel.SomePBC)
+    v_nonmovable, = parse_kwds(hop, (i_nonmovable, None))
+    nonmovable = (i_nonmovable is not None and v_nonmovable.value)
     if len(s_class.descriptions) != 1:
         # instantiate() on a variable class
+        if nonmovable:
+            raise TyperError("instantiate(x, nonmovable=True) cannot be used "
+                             "if x is not a constant class")
         vtypeptr, = hop.inputargs(rclass.get_type_repr(hop.rtyper))
         r_class = hop.args_r[0]
         return r_class._instantiate_runtime_class(hop, vtypeptr,
                                                   hop.r_result.lowleveltype)
     classdef = s_class.any_description().getuniqueclassdef()
-    return rclass.rtype_new_instance(hop.rtyper, classdef, hop.llops)
+    return rclass.rtype_new_instance(hop.rtyper, classdef, hop.llops,
+                                     nonmovable=nonmovable)
 
 
 @typer_for(hasattr)
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -684,10 +684,12 @@
             rbase = rbase.rbase
         return False
 
-    def new_instance(self, llops, classcallhop=None):
+    def new_instance(self, llops, classcallhop=None, nonmovable=False):
         """Build a new instance, without calling __init__."""
         flavor = self.gcflavor
         flags = {'flavor': flavor}
+        if nonmovable:
+            flags['nonmovable'] = True
         ctype = inputconst(Void, self.object_type)
         cflags = inputconst(Void, flags)
         vlist = [ctype, cflags]
@@ -1031,9 +1033,10 @@
 
 # ____________________________________________________________
 
-def rtype_new_instance(rtyper, classdef, llops, classcallhop=None):
+def rtype_new_instance(rtyper, classdef, llops, classcallhop=None,
+                       nonmovable=False):
     rinstance = getinstancerepr(rtyper, classdef)
-    return rinstance.new_instance(llops, classcallhop)
+    return rinstance.new_instance(llops, classcallhop, nonmovable=nonmovable)
 
 def ll_inst_hash(ins):
     if not ins:
diff --git a/rpython/rtyper/test/test_rbuiltin.py b/rpython/rtyper/test/test_rbuiltin.py
--- a/rpython/rtyper/test/test_rbuiltin.py
+++ b/rpython/rtyper/test/test_rbuiltin.py
@@ -432,6 +432,14 @@
         res = self.interpret(f, [2])
         assert self.class_name(res) == 'B'
 
+    def test_instantiate_nonmovable(self):
+        class A:
+            pass
+        def f():
+            return instantiate(A, nonmovable=True)   # no effect before GC
+        res = self.interpret(f, [])
+        assert self.class_name(res) == 'A'
+
     def test_os_path_join(self):
         def fn(a, b):
             return os.path.join(a, b)


More information about the pypy-commit mailing list