Author: Richard Plangger <planri...@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 @@ # ____________________________________________________________ +@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 # ____________________________________________________________ +@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) + +@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 + +@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) + +@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) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit