Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: space-newtext Changeset: r89180:8e2bcfb77e80 Date: 2016-12-19 14:47 +0100 http://bitbucket.org/pypy/pypy/changeset/8e2bcfb77e80/
Log: merge default diff too long, truncating to 2000 out of 3192 lines diff --git a/lib-python/2.7/weakref.py b/lib-python/2.7/weakref.py --- a/lib-python/2.7/weakref.py +++ b/lib-python/2.7/weakref.py @@ -213,10 +213,10 @@ if o is None: if args: return args[0] - raise KeyError, key + else: + raise KeyError, key else: return o - # The logic above was fixed in PyPy def setdefault(self, key, default=None): try: @@ -230,7 +230,6 @@ return default else: return o - # The logic above was fixed in PyPy def update(*args, **kwargs): if not args: diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -190,6 +190,12 @@ "make sure that all calls go through space.call_args", default=False), + BoolOption("disable_entrypoints", + "Disable external entry points, notably the" + " cpyext module and cffi's embedding mode.", + default=False, + requires=[("objspace.usemodules.cpyext", False)]), + OptionDescription("std", "Standard Object Space Options", [ BoolOption("withtproxy", "support transparent proxies", default=True), 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 @@ -56,3 +56,11 @@ The Cling-backend brings support for modern C++ (11, 14, etc.), dynamic template instantations, and improved integration with CFFI for better performance. It also provides interactive C++ (and bindings to that). + +.. branch: better-PyDict_Next + +Improve the performance of ``PyDict_Next``. When trying ``PyDict_Next`` on a +typedef dict, the test exposed a problem converting a ``GetSetProperty`` to a +``PyGetSetDescrObject``. The other direction seem to be fully implemented. +This branch made a minimal effort to convert the basic fields to avoid +segfaults, but trying to use the ``PyGetSetDescrObject`` will probably fail. diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -83,12 +83,18 @@ return 1 return exitcode + return entry_point, get_additional_entrypoints(space, w_initstdio) + + +def get_additional_entrypoints(space, w_initstdio): # register the minimal equivalent of running a small piece of code. This # should be used as sparsely as possible, just to register callbacks - from rpython.rlib.entrypoint import entrypoint_highlevel from rpython.rtyper.lltypesystem import rffi, lltype + if space.config.objspace.disable_entrypoints: + return {} + @entrypoint_highlevel('main', [rffi.CCHARP, rffi.INT], c_name='pypy_setup_home') def pypy_setup_home(ll_home, verbose): @@ -188,11 +194,11 @@ return -1 return 0 - return entry_point, {'pypy_execute_source': pypy_execute_source, - 'pypy_execute_source_ptr': pypy_execute_source_ptr, - 'pypy_init_threads': pypy_init_threads, - 'pypy_thread_attach': pypy_thread_attach, - 'pypy_setup_home': pypy_setup_home} + return {'pypy_execute_source': pypy_execute_source, + 'pypy_execute_source_ptr': pypy_execute_source_ptr, + 'pypy_init_threads': pypy_init_threads, + 'pypy_thread_attach': pypy_thread_attach, + 'pypy_setup_home': pypy_setup_home} # _____ Define and setup target ___ diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -4,6 +4,7 @@ import sys from rpython.rlib import jit from rpython.rlib.debug import make_sure_not_resized, check_nonneg +from rpython.rlib.debug import ll_assert_not_none from rpython.rlib.jit import hint from rpython.rlib.objectmodel import instantiate, specialize, we_are_translated from rpython.rlib.rarithmetic import intmask, r_uint @@ -298,7 +299,13 @@ # stack manipulation helpers def pushvalue(self, w_object): depth = self.valuestackdepth - self.locals_cells_stack_w[depth] = w_object + self.locals_cells_stack_w[depth] = ll_assert_not_none(w_object) + self.valuestackdepth = depth + 1 + + def pushvalue_none(self): + depth = self.valuestackdepth + # the entry is already None, and remains None + assert self.locals_cells_stack_w[depth] is None self.valuestackdepth = depth + 1 def _check_stack_index(self, index): @@ -311,6 +318,9 @@ return index >= stackstart def popvalue(self): + return ll_assert_not_none(self.popvalue_maybe_none()) + + def popvalue_maybe_none(self): depth = self.valuestackdepth - 1 assert self._check_stack_index(depth) assert depth >= 0 @@ -385,6 +395,9 @@ def peekvalue(self, index_from_top=0): # NOTE: top of the stack is peekvalue(0). # Contrast this with CPython where it's PEEK(-1). + return ll_assert_not_none(self.peekvalue_maybe_none(index_from_top)) + + def peekvalue_maybe_none(self, index_from_top=0): index_from_top = hint(index_from_top, promote=True) index = self.valuestackdepth + ~index_from_top assert self._check_stack_index(index) @@ -396,7 +409,7 @@ index = self.valuestackdepth + ~index_from_top assert self._check_stack_index(index) assert index >= 0 - self.locals_cells_stack_w[index] = w_object + self.locals_cells_stack_w[index] = ll_assert_not_none(w_object) @jit.unroll_safe def dropvaluesuntil(self, finaldepth): diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -1,6 +1,6 @@ import sys from pypy.interpreter.mixedmodule import MixedModule -from rpython.rlib import rdynload, clibffi, entrypoint +from rpython.rlib import rdynload, clibffi from rpython.rtyper.lltypesystem import rffi VERSION = "1.9.1" @@ -68,9 +68,14 @@ if has_stdcall: interpleveldefs['FFI_STDCALL'] = 'space.wrap(%d)' % FFI_STDCALL - def startup(self, space): - from pypy.module._cffi_backend import embedding - embedding.glob.space = space + def __init__(self, space, *args): + MixedModule.__init__(self, space, *args) + # + if not space.config.objspace.disable_entrypoints: + # import 'embedding', which has the side-effect of registering + # the 'pypy_init_embedded_cffi_module' entry point + from pypy.module._cffi_backend import embedding + embedding.glob.space = space def get_dict_rtld_constants(): @@ -85,11 +90,3 @@ for _name, _value in get_dict_rtld_constants().items(): Module.interpleveldefs[_name] = 'space.wrap(%d)' % _value - - -# write this entrypoint() here, to make sure it is registered early enough -@entrypoint.entrypoint_highlevel('main', [rffi.INT, rffi.VOIDP], - c_name='pypy_init_embedded_cffi_module') -def pypy_init_embedded_cffi_module(version, init_struct): - from pypy.module._cffi_backend import embedding - return embedding.pypy_init_embedded_cffi_module(version, init_struct) diff --git a/pypy/module/_cffi_backend/embedding.py b/pypy/module/_cffi_backend/embedding.py --- a/pypy/module/_cffi_backend/embedding.py +++ b/pypy/module/_cffi_backend/embedding.py @@ -1,4 +1,5 @@ import os +from rpython.rlib import entrypoint from rpython.rtyper.lltypesystem import lltype, rffi from rpython.translator.tool.cbuild import ExternalCompilationInfo @@ -46,6 +47,8 @@ glob = Global() +@entrypoint.entrypoint_highlevel('main', [rffi.INT, rffi.VOIDP], + c_name='pypy_init_embedded_cffi_module') def pypy_init_embedded_cffi_module(version, init_struct): # called from __init__.py name = "?" diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py --- a/pypy/module/_ssl/test/test_ssl.py +++ b/pypy/module/_ssl/test/test_ssl.py @@ -169,8 +169,8 @@ } def setup_method(self, method): - # https://www.verisign.net/ - ADDR = "www.verisign.net", 443 + # https://gmail.com/ + ADDR = "gmail.com", 443 self.w_s = self.space.appexec([self.space.wrap(ADDR)], """(ADDR): import socket diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -602,7 +602,7 @@ GLOBALS['%s#%s' % (cpyname, pypy_decl)] = ('PyTypeObject*', pypyexpr) for cpyname in '''PyMethodObject PyListObject PyLongObject - PyDictObject PyClassObject'''.split(): + PyClassObject'''.split(): FORWARD_DECLS.append('typedef struct { PyObject_HEAD } %s' % (cpyname, )) build_exported_objects() diff --git a/pypy/module/cpyext/bufferobject.py b/pypy/module/cpyext/bufferobject.py --- a/pypy/module/cpyext/bufferobject.py +++ b/pypy/module/cpyext/bufferobject.py @@ -31,7 +31,7 @@ dealloc=buffer_dealloc, realize=buffer_realize) -def buffer_attach(space, py_obj, w_obj): +def buffer_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyBufferObject with the given (str) buffer object. """ diff --git a/pypy/module/cpyext/bytearrayobject.py b/pypy/module/cpyext/bytearrayobject.py --- a/pypy/module/cpyext/bytearrayobject.py +++ b/pypy/module/cpyext/bytearrayobject.py @@ -7,7 +7,7 @@ PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL) from pypy.module.cpyext.pyerrors import PyErr_BadArgument from pypy.module.cpyext.pyobject import ( - PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference, + PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, make_typedescr, get_typedescr, Py_IncRef) # Type PyByteArrayObject represents a mutable array of bytes. # The Python API is that of a sequence; diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py --- a/pypy/module/cpyext/bytesobject.py +++ b/pypy/module/cpyext/bytesobject.py @@ -73,7 +73,7 @@ py_str.c_ob_sstate = rffi.cast(rffi.INT, 0) # SSTATE_NOT_INTERNED return py_str -def bytes_attach(space, py_obj, w_obj): +def bytes_attach(space, py_obj, w_obj, w_userdata=None): """ Copy RPython string object contents to a PyBytesObject. The c_ob_sval must not be modified. diff --git a/pypy/module/cpyext/complexobject.py b/pypy/module/cpyext/complexobject.py --- a/pypy/module/cpyext/complexobject.py +++ b/pypy/module/cpyext/complexobject.py @@ -29,7 +29,7 @@ attach=complex_attach, realize=complex_realize) -def complex_attach(space, py_obj, w_obj): +def complex_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyComplexObject with the given complex object. The value must not be modified. diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -1,11 +1,66 @@ from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.objectmodel import specialize +from pypy.interpreter.error import OperationError +from pypy.objspace.std.classdict import ClassDictStrategy +from pypy.interpreter.typedef import GetSetProperty from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, build_type_checkers, Py_ssize_t, - Py_ssize_tP, CONST_STRING) -from pypy.module.cpyext.pyobject import PyObject, PyObjectP, as_pyobj + Py_ssize_tP, CONST_STRING, PyObjectFields, cpython_struct, + bootstrap_function) +from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, as_pyobj, + make_typedescr, track_reference, create_ref, from_ref, decref, + Py_IncRef) +from pypy.module.cpyext.object import _dealloc from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall -from pypy.interpreter.error import OperationError -from rpython.rlib.objectmodel import specialize + +PyDictObjectStruct = lltype.ForwardReference() +PyDictObject = lltype.Ptr(PyDictObjectStruct) +PyDictObjectFields = PyObjectFields + \ + (("ob_keys", PyObject),) +cpython_struct("PyDictObject", PyDictObjectFields, PyDictObjectStruct) + +@bootstrap_function +def init_dictobject(space): + "Type description of PyDictObject" + make_typedescr(space.w_dict.layout.typedef, + basestruct=PyDictObject.TO, + attach=dict_attach, + dealloc=dict_dealloc, + realize=dict_realize) + +def dict_attach(space, py_obj, w_obj, w_userdata=None): + """ + Fills a newly allocated PyDictObject with the given dict object. + """ + py_dict = rffi.cast(PyDictObject, py_obj) + py_dict.c_ob_keys = lltype.nullptr(PyObject.TO) + # Problems: if this dict is a typedict, we may have unbound GetSetProperty + # functions in the dict. The corresponding PyGetSetDescrObject must be + # bound to a class, but the actual w_type will be unavailable later on. + # Solution: use the w_userdata argument when assigning a PyTypeObject's + # tp_dict slot to pass a w_type in, and force creation of the pair here + if not space.is_w(w_userdata, space.gettypefor(GetSetProperty)): + # do not do this for type dict of GetSetProperty, that would recurse + w_vals = space.call_method(space.w_dict, "values", w_obj) + vals = space.listview(w_vals) + for w_v in vals: + if isinstance(w_v, GetSetProperty): + pyobj = as_pyobj(space, w_v, w_userdata) + # refcnt will be REFCNT_FROM_PYPY, no need to inc or dec + +def dict_realize(space, py_obj): + """ + Creates the dict in the interpreter + """ + w_obj = space.newdict() + track_reference(space, py_obj, w_obj) + +@cpython_api([PyObject], lltype.Void, header=None) +def dict_dealloc(space, py_obj): + py_dict = rffi.cast(PyDictObject, py_obj) + decref(space, py_dict.c_ob_keys) + py_dict.c_ob_keys = lltype.nullptr(PyObject.TO) + _dealloc(space, py_obj) @cpython_api([], PyObject) def PyDict_New(space): @@ -181,9 +236,9 @@ } The dictionary p should not be mutated during iteration. It is safe - (since Python 2.1) to modify the values of the keys as you iterate over the - dictionary, but only so long as the set of keys does not change. For - example: + (since Python 2.1) to modify the values but not the keys as you iterate + over the dictionary, the keys must not change. + For example: PyObject *key, *value; Py_ssize_t pos = 0; @@ -199,34 +254,32 @@ } Py_DECREF(o); }""" + if w_dict is None: return 0 - # XXX XXX PyDict_Next is not efficient. Storing an iterator would probably - # work, but we can't work out how to not leak it if iteration does - # not complete. Alternatively, we could add some RPython-only - # dict-iterator method to move forward by N steps. - - w_dict.ensure_object_strategy() # make sure both keys and values can - # be borrwed - try: - w_iter = space.call_method(space.w_dict, "iteritems", w_dict) - pos = ppos[0] - while pos: - space.call_method(w_iter, "next") - pos -= 1 - - w_item = space.call_method(w_iter, "next") - w_key, w_value = space.fixedview(w_item, 2) - if pkey: - pkey[0] = as_pyobj(space, w_key) - if pvalue: - pvalue[0] = as_pyobj(space, w_value) - ppos[0] += 1 - except OperationError as e: - if not e.match(space, space.w_StopIteration): - raise + pos = ppos[0] + py_obj = as_pyobj(space, w_dict) + py_dict = rffi.cast(PyDictObject, py_obj) + if pos == 0: + # Store the current keys in the PyDictObject. + decref(space, py_dict.c_ob_keys) + w_keys = space.call_method(space.w_dict, "keys", w_dict) + py_dict.c_ob_keys = create_ref(space, w_keys) + Py_IncRef(space, py_dict.c_ob_keys) + else: + w_keys = from_ref(space, py_dict.c_ob_keys) + ppos[0] += 1 + if pos >= space.len_w(w_keys): + decref(space, py_dict.c_ob_keys) + py_dict.c_ob_keys = lltype.nullptr(PyObject.TO) return 0 + w_key = space.listview(w_keys)[pos] + w_value = space.getitem(w_dict, w_key) + if pkey: + pkey[0] = as_pyobj(space, w_key) + if pvalue: + pvalue[0] = as_pyobj(space, w_value) return 1 @specialize.memo() diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py --- a/pypy/module/cpyext/floatobject.py +++ b/pypy/module/cpyext/floatobject.py @@ -22,7 +22,7 @@ attach=float_attach, realize=float_realize) -def float_attach(space, py_obj, w_obj): +def float_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyFloatObject with the given float object. The value must not be modified. diff --git a/pypy/module/cpyext/frameobject.py b/pypy/module/cpyext/frameobject.py --- a/pypy/module/cpyext/frameobject.py +++ b/pypy/module/cpyext/frameobject.py @@ -30,7 +30,7 @@ dealloc=frame_dealloc, realize=frame_realize) -def frame_attach(space, py_obj, w_obj): +def frame_attach(space, py_obj, w_obj, w_userdata=None): "Fills a newly allocated PyFrameObject with a frame object" frame = space.interp_w(PyFrame, w_obj) py_frame = rffi.cast(PyFrameObject, py_obj) diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py --- a/pypy/module/cpyext/funcobject.py +++ b/pypy/module/cpyext/funcobject.py @@ -51,7 +51,7 @@ PyMethod_Check, PyMethod_CheckExact = build_type_checkers("Method", Method) PyCode_Check, PyCode_CheckExact = build_type_checkers("Code", PyCode) -def function_attach(space, py_obj, w_obj): +def function_attach(space, py_obj, w_obj, w_userdata=None): py_func = rffi.cast(PyFunctionObject, py_obj) assert isinstance(w_obj, Function) py_func.c_func_name = make_ref(space, space.newtext(w_obj.name)) @@ -63,7 +63,7 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) -def code_attach(space, py_obj, w_obj): +def code_attach(space, py_obj, w_obj, w_userdata=None): py_code = rffi.cast(PyCodeObject, py_obj) assert isinstance(w_obj, PyCode) py_code.c_co_name = make_ref(space, space.newtext(w_obj.co_name)) diff --git a/pypy/module/cpyext/import_.py b/pypy/module/cpyext/import_.py --- a/pypy/module/cpyext/import_.py +++ b/pypy/module/cpyext/import_.py @@ -1,6 +1,6 @@ from pypy.interpreter import module from pypy.module.cpyext.api import ( - generic_cpy_call, cpython_api, PyObject, CONST_STRING) + generic_cpy_call, cpython_api, PyObject, CONST_STRING, CANNOT_FAIL) from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import OperationError from pypy.interpreter.module import Module @@ -124,3 +124,22 @@ w_mod = importing.add_module(space, w_name) space.setattr(w_mod, space.newtext('__file__'), space.newtext(pathname)) return importing.exec_code_module(space, w_mod, code, w_name) + +@cpython_api([], lltype.Void, error=CANNOT_FAIL) +def _PyImport_AcquireLock(space): + """Locking primitive to prevent parallel imports of the same module + in different threads to return with a partially loaded module. + These calls are serialized by the global interpreter lock.""" + try: + space.call_method(space.getbuiltinmodule('imp'), 'acquire_lock') + except OperationError as e: + e.write_unraisable(space, "_PyImport_AcquireLock") + +@cpython_api([], rffi.INT_real, error=CANNOT_FAIL) +def _PyImport_ReleaseLock(space): + try: + space.call_method(space.getbuiltinmodule('imp'), 'release_lock') + return 1 + except OperationError as e: + e.write_unraisable(space, "_PyImport_ReleaseLock") + return -1 diff --git a/pypy/module/cpyext/include/dictobject.h b/pypy/module/cpyext/include/dictobject.h --- a/pypy/module/cpyext/include/dictobject.h +++ b/pypy/module/cpyext/include/dictobject.h @@ -7,6 +7,10 @@ extern "C" { #endif +typedef struct { + PyObject_HEAD + PyObject *ob_keys; /* a private place to put keys during PyDict_Next */ +} PyDictObject; #ifdef __cplusplus } diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py --- a/pypy/module/cpyext/intobject.py +++ b/pypy/module/cpyext/intobject.py @@ -24,7 +24,7 @@ attach=int_attach, realize=int_realize) -def int_attach(space, py_obj, w_obj): +def int_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyIntObject with the given int object. The value must not be modified. diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -44,7 +44,7 @@ attach=cfunction_attach, dealloc=cfunction_dealloc) -def cfunction_attach(space, py_obj, w_obj): +def cfunction_attach(space, py_obj, w_obj, w_userdata=None): assert isinstance(w_obj, W_PyCFunctionObject) py_func = rffi.cast(PyCFunctionObject, py_obj) py_func.c_m_ml = w_obj.ml diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -61,7 +61,7 @@ pyobj.c_ob_type = pytype return pyobj - def attach(self, space, pyobj, w_obj): + def attach(self, space, pyobj, w_obj, w_userdata=None): pass def realize(self, space, obj): @@ -111,8 +111,8 @@ return tp_dealloc.api_func if tp_attach: - def attach(self, space, pyobj, w_obj): - tp_attach(space, pyobj, w_obj) + def attach(self, space, pyobj, w_obj, w_userdata=None): + tp_attach(space, pyobj, w_obj, w_userdata) if tp_realize: def realize(self, space, ref): @@ -152,7 +152,7 @@ class InvalidPointerException(Exception): pass -def create_ref(space, w_obj): +def create_ref(space, w_obj, w_userdata=None): """ Allocates a PyObject, and fills its fields with info from the given interpreter object. @@ -173,7 +173,7 @@ assert py_obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY py_obj.c_ob_refcnt -= 1 # - typedescr.attach(space, py_obj, w_obj) + typedescr.attach(space, py_obj, w_obj, w_userdata) return py_obj def track_reference(space, py_obj, w_obj): @@ -228,7 +228,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) -def as_pyobj(space, w_obj): +def as_pyobj(space, w_obj, w_userdata=None): """ Returns a 'PyObject *' representing the given intepreter object. This doesn't give a new reference, but the returned 'PyObject *' @@ -240,7 +240,7 @@ assert not is_pyobj(w_obj) py_obj = rawrefcount.from_obj(PyObject, w_obj) if not py_obj: - py_obj = create_ref(space, w_obj) + py_obj = create_ref(space, w_obj, w_userdata) return py_obj else: return lltype.nullptr(PyObject.TO) @@ -269,14 +269,14 @@ return hop.inputconst(lltype.Bool, hop.s_result.const) @specialize.ll() -def make_ref(space, obj): +def make_ref(space, obj, w_userdata=None): """Increment the reference counter of the PyObject and return it. Can be called with either a PyObject or a W_Root. """ if is_pyobj(obj): pyobj = rffi.cast(PyObject, obj) else: - pyobj = as_pyobj(space, obj) + pyobj = as_pyobj(space, obj, w_userdata) if pyobj: assert pyobj.c_ob_refcnt > 0 pyobj.c_ob_refcnt += 1 diff --git a/pypy/module/cpyext/pytraceback.py b/pypy/module/cpyext/pytraceback.py --- a/pypy/module/cpyext/pytraceback.py +++ b/pypy/module/cpyext/pytraceback.py @@ -28,7 +28,7 @@ dealloc=traceback_dealloc) -def traceback_attach(space, py_obj, w_obj): +def traceback_attach(space, py_obj, w_obj, w_userdata=None): py_traceback = rffi.cast(PyTracebackObject, py_obj) traceback = space.interp_w(PyTraceback, w_obj) if traceback.next is None: diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py --- a/pypy/module/cpyext/sliceobject.py +++ b/pypy/module/cpyext/sliceobject.py @@ -25,7 +25,7 @@ attach=slice_attach, dealloc=slice_dealloc) -def slice_attach(space, py_obj, w_obj): +def slice_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PySliceObject with the given slice object. The fields must not be modified. diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -1,7 +1,7 @@ import py from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.test.test_api import BaseApiTest -from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP +from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP, PyTypeObjectPtr from pypy.module.cpyext.pyobject import make_ref, from_ref from pypy.interpreter.error import OperationError from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase @@ -181,6 +181,27 @@ raises(OperationError, space.call_method, w_proxy, 'clear') assert api.PyDictProxy_Check(w_proxy) + def test_typedict1(self, space, api): + py_type = make_ref(space, space.w_int) + py_dict = rffi.cast(PyTypeObjectPtr, py_type).c_tp_dict + ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') + + ppos[0] = 0 + pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw') + pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw') + try: + w_copy = space.newdict() + while api.PyDict_Next(py_dict, ppos, pkey, pvalue): + w_key = from_ref(space, pkey[0]) + w_value = from_ref(space, pvalue[0]) + space.setitem(w_copy, w_key, w_value) + finally: + lltype.free(ppos, flavor='raw') + lltype.free(pkey, flavor='raw') + lltype.free(pvalue, flavor='raw') + api.Py_DecRef(py_type) # release borrowed references + # do something with w_copy ? + class AppTestDictObject(AppTestCpythonExtensionBase): def test_dictproxytype(self): module = self.import_extension('foo', [ @@ -225,3 +246,16 @@ d = {"a": 1} raises(AttributeError, module.update, d, [("c", 2)]) + def test_typedict2(self): + module = self.import_extension('foo', [ + ("get_type_dict", "METH_O", + ''' + PyObject* value = args->ob_type->tp_dict; + if (value == NULL) value = Py_None; + Py_INCREF(value); + return value; + '''), + ]) + d = module.get_type_dict(1) + assert d['real'].__get__(1, 1) == 1 + diff --git a/pypy/module/cpyext/test/test_import.py b/pypy/module/cpyext/test/test_import.py --- a/pypy/module/cpyext/test/test_import.py +++ b/pypy/module/cpyext/test/test_import.py @@ -37,6 +37,14 @@ stat = api.PyImport_ReloadModule(stat) assert space.getattr(stat, space.wrap("S_IMODE")) + def test_lock(self, space, api): + # "does not crash" + api._PyImport_AcquireLock() + api._PyImport_AcquireLock() + api._PyImport_ReleaseLock() + api._PyImport_ReleaseLock() + + class AppTestImportLogic(AppTestCpythonExtensionBase): def test_import_logic(self): import sys, os diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -63,7 +63,7 @@ p[i] = lltype.nullptr(PyObject.TO) return py_obj -def tuple_attach(space, py_obj, w_obj): +def tuple_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyTupleObject with the given tuple object. The buffer must not be modified. diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -32,7 +32,7 @@ from pypy.module.cpyext.state import State from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne from pypy.module.cpyext.typeobjectdefs import ( - PyGetSetDef, PyMemberDef, newfunc, + PyGetSetDef, PyMemberDef, newfunc, getter, setter, PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs) from pypy.objspace.std.typeobject import W_TypeObject, find_best_base @@ -61,6 +61,7 @@ self.w_type = w_type doc = set = get = None if doc: + # XXX dead code? doc = rffi.charp2str(getset.c_doc) if getset.c_get: get = GettersAndSetters.getter.im_func @@ -73,6 +74,21 @@ def PyDescr_NewGetSet(space, getset, w_type): return W_GetSetPropertyEx(getset, w_type) +def make_GetSet(space, getsetprop): + py_getsetdef = lltype.malloc(PyGetSetDef, flavor='raw') + doc = getsetprop.doc + if doc: + py_getsetdef.c_doc = rffi.str2charp(doc) + else: + py_getsetdef.c_doc = rffi.cast(rffi.CCHARP, 0) + py_getsetdef.c_name = rffi.str2charp(getsetprop.getname(space)) + # XXX FIXME - actually assign these !!! + py_getsetdef.c_get = rffi.cast(getter, 0) + py_getsetdef.c_set = rffi.cast(setter, 0) + py_getsetdef.c_closure = rffi.cast(rffi.VOIDP, 0) + return py_getsetdef + + class W_MemberDescr(GetSetProperty): name = 'member_descriptor' def __init__(self, member, w_type): @@ -160,7 +176,7 @@ realize=methoddescr_realize, ) -def memberdescr_attach(space, py_obj, w_obj): +def memberdescr_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr object. The values must not be modified. @@ -179,17 +195,21 @@ track_reference(space, obj, w_obj) return w_obj -def getsetdescr_attach(space, py_obj, w_obj): +def getsetdescr_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyGetSetDescrObject with the given W_GetSetPropertyEx object. The values must not be modified. """ py_getsetdescr = rffi.cast(PyGetSetDescrObject, py_obj) + if isinstance(w_obj, GetSetProperty): + py_getsetdef = make_GetSet(space, w_obj) + assert space.isinstance_w(w_userdata, space.w_type) + w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata) # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset -def methoddescr_attach(space, py_obj, w_obj): +def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): py_methoddescr = rffi.cast(PyMethodDescrObject, py_obj) # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_PyCFunctionObject) @@ -665,7 +685,7 @@ return rffi.cast(PyObject, heaptype) -def type_attach(space, py_obj, w_type): +def type_attach(space, py_obj, w_type, w_userdata=None): """ Fills a newly allocated PyTypeObject from an existing type. """ @@ -892,7 +912,9 @@ if w_obj.is_cpytype(): Py_DecRef(space, pto.c_tp_dict) w_dict = w_obj.getdict(space) - pto.c_tp_dict = make_ref(space, w_dict) + # pass in the w_obj to convert any values that are + # unbound GetSetProperty into bound PyGetSetDescrObject + pto.c_tp_dict = make_ref(space, w_dict, w_obj) @cpython_api([PyTypeObjectPtr, PyTypeObjectPtr], rffi.INT_real, error=CANNOT_FAIL) def PyType_IsSubtype(space, a, b): diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -62,7 +62,7 @@ py_uni.c_defenc = lltype.nullptr(PyObject.TO) return py_uni -def unicode_attach(space, py_obj, w_obj): +def unicode_attach(space, py_obj, w_obj, w_userdata=None): "Fills a newly allocated PyUnicodeObject with a unicode string" py_unicode = rffi.cast(PyUnicodeObject, py_obj) py_unicode.c_length = len(space.unicode_w(w_obj)) diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py --- a/pypy/module/micronumpy/compile.py +++ b/pypy/module/micronumpy/compile.py @@ -66,7 +66,7 @@ w_SystemExit = W_TypeObject("SystemExit") w_KeyboardInterrupt = W_TypeObject("KeyboardInterrupt") w_VisibleDeprecationWarning = W_TypeObject("VisibleDeprecationWarning") - w_None = None + w_None = W_Root() w_bool = W_TypeObject("bool") w_int = W_TypeObject("int") diff --git a/pypy/objspace/std/callmethod.py b/pypy/objspace/std/callmethod.py --- a/pypy/objspace/std/callmethod.py +++ b/pypy/objspace/std/callmethod.py @@ -80,14 +80,14 @@ if w_value is None: w_value = space.getattr(w_obj, w_name) f.pushvalue(w_value) - f.pushvalue(None) + f.pushvalue_none() @jit.unroll_safe def CALL_METHOD(f, oparg, *ignored): # opargs contains the arg, and kwarg count, excluding the implicit 'self' n_args = oparg & 0xff n_kwargs = (oparg >> 8) & 0xff - w_self = f.peekvalue(n_args + (2 * n_kwargs)) + w_self = f.peekvalue_maybe_none(n_args + (2 * n_kwargs)) n = n_args + (w_self is not None) if not n_kwargs: @@ -115,7 +115,7 @@ arguments, keywords, keywords_w, None, None, methodcall=w_self is not None) if w_self is None: - f.popvalue() # removes w_self, which is None + f.popvalue_maybe_none() # removes w_self, which is None w_callable = f.popvalue() if f.get_is_being_profiled() and function.is_builtin_code(w_callable): w_result = f.space.call_args_and_c_profile(f, w_callable, args) diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -231,6 +231,12 @@ v = graph.getreturnvar() if v.annotation is None: self.setbinding(v, s_ImpossibleValue) + v = graph.exceptblock.inputargs[1] + if v.annotation is not None and v.annotation.can_be_none(): + raise annmodel.AnnotatorError( + "%r is found by annotation to possibly raise None, " + "but the None was not suppressed by the flow space" % + (graph,)) def validate(self): """Check that the annotation results are valid""" diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py --- a/rpython/annotator/model.py +++ b/rpython/annotator/model.py @@ -484,6 +484,9 @@ def __init__(self, classdefs): self.classdefs = classdefs + def can_be_none(self): + return False + def as_SomeInstance(self): return unionof(*[SomeInstance(cdef) for cdef in self.classdefs]) diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4652,6 +4652,17 @@ assert ('string formatting requires a constant string/unicode' in str(e.value)) + def test_cannot_raise_none(self): + def f(x): + s = None + if x > 5: + s = ValueError() + raise s + a = self.RPythonAnnotator() + a.build_types(f, [int]) + s_exc = a.binding(graphof(a, f).exceptblock.inputargs[1]) + assert not s_exc.can_be_none() + def g(n): return [0, 1, 2, n] diff --git a/rpython/flowspace/flowcontext.py b/rpython/flowspace/flowcontext.py --- a/rpython/flowspace/flowcontext.py +++ b/rpython/flowspace/flowcontext.py @@ -597,6 +597,9 @@ Returns an FSException object whose w_value is an instance of w_type. """ + from rpython.rlib.debug import ll_assert_not_none + + check_not_none = False w_is_type = op.isinstance(w_arg1, const(type)).eval(self) if self.guessbool(w_is_type): # this is for all cases of the form (Class, something) @@ -608,6 +611,7 @@ if self.guessbool(op.issubtype(w_valuetype, w_arg1).eval(self)): # raise Type, Instance: let etype be the exact type of value w_value = w_arg2 + check_not_none = True else: # raise Type, X: assume X is the constructor argument w_value = op.simple_call(w_arg1, w_arg2).eval(self) @@ -618,6 +622,10 @@ "separate value") raise Raise(const(exc)) w_value = w_arg1 + check_not_none = True + if check_not_none: + w_value = op.simple_call(const(ll_assert_not_none), + w_value).eval(self) w_type = op.type(w_value).eval(self) return FSException(w_type, w_value) 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 @@ -283,6 +283,12 @@ def rewrite_op_jit_record_exact_class(self, op): return SpaceOperation("record_exact_class", [op.args[0], op.args[1]], None) + def rewrite_op_debug_assert_not_none(self, op): + if isinstance(op.args[0], Variable): + return SpaceOperation('assert_not_none', [op.args[0]], None) + else: + return [] + def rewrite_op_cast_bool_to_int(self, op): pass def rewrite_op_cast_bool_to_uint(self, op): pass def rewrite_op_cast_char_to_int(self, op): pass diff --git a/rpython/jit/codewriter/test/test_flatten.py b/rpython/jit/codewriter/test/test_flatten.py --- a/rpython/jit/codewriter/test/test_flatten.py +++ b/rpython/jit/codewriter/test/test_flatten.py @@ -402,7 +402,7 @@ self.encoding_test(f, [65], """ raise $<* struct object> - """) + """, transform=True) def test_exc_raise_2(self): def g(i): @@ -466,6 +466,14 @@ int_return $True """, transform=True) + def test_assert_disappears(self): + def f(i): + assert i > 5 + return i + self.encoding_test(f, [7], """ + int_return %i0 + """) + def test_int_floordiv_ovf_zer(self): def f(i, j): assert i >= 0 diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -563,6 +563,10 @@ ll_assert((i & 1) == 1, "bhimpl_cast_int_to_ptr: not an odd int") return lltype.cast_int_to_ptr(llmemory.GCREF, i) + @arguments("r") + def bhimpl_assert_not_none(a): + assert a + @arguments("r", "i") def bhimpl_record_exact_class(a, b): pass 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 @@ -5,6 +5,7 @@ from rpython.rlib.rarithmetic import ovfcheck, r_longlong, is_valid_int from rpython.rlib.unroll import unrolling_iterable from rpython.rlib.objectmodel import specialize +from rpython.rlib.debug import fatalerror from rpython.jit.metainterp.history import check_descr from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID, AbstractDescr from rpython.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr @@ -321,6 +322,10 @@ def do_keepalive(cpu, _, x): pass +def do_assert_not_none(cpu, _, box): + if not box.getref_base(): + fatalerror("found during JITting: ll_assert_not_none() failed") + # ____________________________________________________________ diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py --- a/rpython/jit/metainterp/heapcache.py +++ b/rpython/jit/metainterp/heapcache.py @@ -230,7 +230,8 @@ opnum != rop.PTR_EQ and opnum != rop.PTR_NE and opnum != rop.INSTANCE_PTR_EQ and - opnum != rop.INSTANCE_PTR_NE): + opnum != rop.INSTANCE_PTR_NE and + opnum != rop.ASSERT_NOT_NONE): for box in argboxes: self._escape_box(box) @@ -263,7 +264,8 @@ opnum == rop.SETFIELD_RAW or opnum == rop.SETARRAYITEM_RAW or opnum == rop.SETINTERIORFIELD_RAW or - opnum == rop.RAW_STORE): + opnum == rop.RAW_STORE or + opnum == rop.ASSERT_NOT_NONE): return if (rop._OVF_FIRST <= opnum <= rop._OVF_LAST or rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST or @@ -371,7 +373,7 @@ def class_now_known(self, box): if isinstance(box, Const): return - self._set_flag(box, HF_KNOWN_CLASS) + self._set_flag(box, HF_KNOWN_CLASS | HF_KNOWN_NULLITY) def is_nullity_known(self, box): if isinstance(box, Const): @@ -401,7 +403,8 @@ def new(self, box): assert isinstance(box, RefFrontendOp) self.update_version(box) - add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | HF_IS_UNESCAPED) + add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | HF_IS_UNESCAPED + | HF_KNOWN_NULLITY) def new_array(self, box, lengthbox): self.new(box) diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py --- a/rpython/jit/metainterp/optimizeopt/rewrite.py +++ b/rpython/jit/metainterp/optimizeopt/rewrite.py @@ -499,6 +499,9 @@ box = self.get_box_replacement(op.getarg(0)) self.make_constant(box, CONST_0) + def optimize_ASSERT_NOT_NONE(self, op): + self.make_nonnull(op.getarg(0)) + def optimize_RECORD_EXACT_CLASS(self, op): opinfo = self.getptrinfo(op.getarg(0)) expectedclassbox = op.getarg(1) diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py --- a/rpython/jit/metainterp/optimizeopt/simplify.py +++ b/rpython/jit/metainterp/optimizeopt/simplify.py @@ -42,6 +42,9 @@ # but it's a bit hard to implement robustly if heap.py is also run pass + def optimize_ASSERT_NOT_NONE(self, op): + pass + def optimize_RECORD_EXACT_CLASS(self, op): pass diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py @@ -5595,6 +5595,19 @@ """ self.optimize_loop(ops, expected) + def test_assert_not_none(self): + ops = """ + [p0] + assert_not_none(p0) + guard_nonnull(p0) [] + finish() + """ + expected = """ + [p0] + finish() + """ + self.optimize_loop(ops, expected) + class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin): pass 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 @@ -275,12 +275,18 @@ def opimpl_ptr_iszero(self, box): return self.execute(rop.PTR_EQ, box, history.CONST_NULL) + @arguments("box") + def opimpl_assert_not_none(self, box): + if self.metainterp.heapcache.is_nullity_known(box): + return + self.execute(rop.ASSERT_NOT_NONE, box) + self.metainterp.heapcache.nullity_now_known(box) + @arguments("box", "box") def opimpl_record_exact_class(self, box, clsbox): from rpython.rtyper.lltypesystem import llmemory if self.metainterp.heapcache.is_class_known(box): return - adr = clsbox.getaddr() self.execute(rop.RECORD_EXACT_CLASS, box, clsbox) self.metainterp.heapcache.class_now_known(box) 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 @@ -1143,6 +1143,7 @@ 'COPYSTRCONTENT/5/n', # src, dst, srcstart, dststart, length 'COPYUNICODECONTENT/5/n', 'QUASIIMMUT_FIELD/1d/n', # [objptr], descr=SlowMutateDescr + 'ASSERT_NOT_NONE/1/n', # [objptr] 'RECORD_EXACT_CLASS/2/n', # [objptr, clsptr] 'KEEPALIVE/1/n', 'SAVE_EXCEPTION/0/r', diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py --- a/rpython/jit/metainterp/test/test_ajit.py +++ b/rpython/jit/metainterp/test/test_ajit.py @@ -3510,6 +3510,7 @@ self.check_resops(call_f=1) def test_look_inside_iff_virtual(self): + from rpython.rlib.debug import ll_assert_not_none # There's no good reason for this to be look_inside_iff, but it's a test! @look_inside_iff(lambda arg, n: isvirtual(arg)) def f(arg, n): @@ -3529,7 +3530,7 @@ if n == 0: i += f(a, n) else: - i += f(A(2), n) + i += f(ll_assert_not_none(A(2)), n) res = self.meta_interp(main, [0], enable_opts='') assert res == main(0) self.check_resops(call_i=1, getfield_gc_i=0) @@ -4585,3 +4586,30 @@ assert res == -42 res = self.interp_operations(f, [0, 200]) assert res == 205 + + def test_ll_assert_not_none(self): + # the presence of ll_assert_not_none(), even in cases where it + # doesn't influence the annotation, is a hint for the JIT + from rpython.rlib.debug import ll_assert_not_none + class X: + pass + class Y(X): + pass + def g(x, check): + if check: + x = ll_assert_not_none(x) + return isinstance(x, Y) + @dont_look_inside + def make(i): + if i == 1: + return X() + if i == 2: + return Y() + return None + def f(a, b, check): + return g(make(a), check) + g(make(b), check) * 10 + res = self.interp_operations(f, [1, 2, 1]) + assert res == 10 + self.check_operations_history(guard_nonnull=0, guard_nonnull_class=0, + guard_class=2, + assert_not_none=2) # before optimization diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py --- a/rpython/jit/metainterp/test/test_heapcache.py +++ b/rpython/jit/metainterp/test/test_heapcache.py @@ -83,6 +83,19 @@ assert not h.is_nullity_known(box1) assert not h.is_nullity_known(box2) + def test_known_nullity_more_cases(self): + h = HeapCache() + box1 = RefFrontendOp(1) + box2 = RefFrontendOp(2) + h.class_now_known(box1) + assert h.is_nullity_known(box1) + + h.new(box2) + assert h.is_nullity_known(box2) + + h.reset() + assert not h.is_nullity_known(box1) + assert not h.is_nullity_known(box2) def test_nonstandard_virtualizable(self): h = HeapCache() diff --git a/rpython/rlib/debug.py b/rpython/rlib/debug.py --- a/rpython/rlib/debug.py +++ b/rpython/rlib/debug.py @@ -11,7 +11,8 @@ # Expose these here (public interface) from rpython.rtyper.debug import ( - ll_assert, FatalError, fatalerror, fatalerror_notb, debug_print_traceback) + ll_assert, FatalError, fatalerror, fatalerror_notb, debug_print_traceback, + ll_assert_not_none) class DebugLog(list): diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -1141,6 +1141,9 @@ """ Assure the JIT that value is an instance of cls. This is a precise class check, like a guard_class. + + See also debug.ll_assert_not_none(x), which asserts that x is not None + and also assures the JIT that it is the case. """ assert type(value) is cls diff --git a/rpython/rtyper/debug.py b/rpython/rtyper/debug.py --- a/rpython/rtyper/debug.py +++ b/rpython/rtyper/debug.py @@ -20,6 +20,23 @@ hop.exception_cannot_occur() hop.genop('debug_assert', vlist) +def ll_assert_not_none(x): + """assert x is not None""" + assert x is not None, "ll_assert_not_none(%r)" % (x,) + return x + +class Entry(ExtRegistryEntry): + _about_ = ll_assert_not_none + + def compute_result_annotation(self, s_x): + return s_x.nonnoneify() + + def specialize_call(self, hop): + [v0] = hop.inputargs(hop.args_r[0]) + hop.exception_cannot_occur() + hop.genop('debug_assert_not_none', [v0]) + return v0 + class FatalError(Exception): pass diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -521,6 +521,10 @@ if not x: raise LLAssertFailure(msg) + def op_debug_assert_not_none(self, x): + if not x: + raise LLAssertFailure("ll_assert_not_none() failed") + def op_debug_fatalerror(self, ll_msg, ll_exc=None): msg = ''.join(ll_msg.chars) if ll_exc is None: diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -78,7 +78,8 @@ def is_pure(self, args_v): if self.canfold: # canfold => pure operation return True - if self is llop.debug_assert: # debug_assert is pure enough + if (self is llop.debug_assert or # debug_assert is pure enough + self is llop.debug_assert_not_none): return True # reading from immutable if self is llop.getfield or self is llop.getarrayitem: @@ -552,6 +553,7 @@ 'debug_offset': LLOp(canrun=True), 'debug_flush': LLOp(canrun=True), 'debug_assert': LLOp(tryfold=True), + 'debug_assert_not_none': LLOp(tryfold=True), 'debug_fatalerror': LLOp(canrun=True), 'debug_llinterpcall': LLOp(canraise=(Exception,)), # Python func call 'res=arg[0](*arg[1:])' diff --git a/rpython/translator/backendopt/mallocv.py b/rpython/translator/backendopt/mallocv.py deleted file mode 100644 --- a/rpython/translator/backendopt/mallocv.py +++ /dev/null @@ -1,1055 +0,0 @@ -from rpython.flowspace.model import Variable, Constant, Block, Link -from rpython.flowspace.model import SpaceOperation, copygraph -from rpython.flowspace.model import checkgraph -from rpython.translator.backendopt.support import log -from rpython.translator.simplify import join_blocks -from rpython.translator.unsimplify import varoftype -from rpython.rtyper.lltypesystem.lltype import getfunctionptr -from rpython.rtyper.lltypesystem import lltype -from rpython.rtyper.lltypesystem.lloperation import llop - - -def virtualize_mallocs(translator, graphs, verbose=False): - newgraphs = graphs[:] - mallocv = MallocVirtualizer(newgraphs, translator.rtyper, verbose) - while mallocv.remove_mallocs_once(): - pass - for graph in newgraphs: - checkgraph(graph) - join_blocks(graph) - assert newgraphs[:len(graphs)] == graphs - del newgraphs[:len(graphs)] - translator.graphs.extend(newgraphs) - -# ____________________________________________________________ - - -class MallocTypeDesc(object): - - def __init__(self, MALLOCTYPE): - if not isinstance(MALLOCTYPE, lltype.GcStruct): - raise CannotRemoveThisType - self.MALLOCTYPE = MALLOCTYPE - self.check_no_destructor() - self.names_and_types = [] - self.name2index = {} - self.name2subtype = {} - self.initialize_type(MALLOCTYPE) - #self.immutable_struct = MALLOCTYPE._hints.get('immutable') - - def check_no_destructor(self): - STRUCT = self.MALLOCTYPE - try: - rttiptr = lltype.getRuntimeTypeInfo(STRUCT) - except ValueError: - return # ok - destr_ptr = getattr(rttiptr._obj, 'destructor_funcptr', None) - if destr_ptr: - raise CannotRemoveThisType - - def initialize_type(self, TYPE): - fieldnames = TYPE._names - firstname, FIRSTTYPE = TYPE._first_struct() - if FIRSTTYPE is not None: - self.initialize_type(FIRSTTYPE) - fieldnames = fieldnames[1:] - for name in fieldnames: - FIELDTYPE = TYPE._flds[name] - if isinstance(FIELDTYPE, lltype.ContainerType): - raise CannotRemoveThisType("inlined substructure") - self.name2index[name] = len(self.names_and_types) - self.names_and_types.append((name, FIELDTYPE)) - self.name2subtype[name] = TYPE - - -class SpecNode(object): - pass - - -class RuntimeSpecNode(SpecNode): - - def __init__(self, name, TYPE): - self.name = name - self.TYPE = TYPE - - def newvar(self): - v = Variable(self.name) - v.concretetype = self.TYPE - return v - - def getfrozenkey(self, memo): - return 'R' - - def accumulate_nodes(self, rtnodes, vtnodes): - rtnodes.append(self) - - def copy(self, memo, flagreadonly): - return RuntimeSpecNode(self.name, self.TYPE) - - def bind_rt_nodes(self, memo, newnodes_iter): - return newnodes_iter.next() - - -class VirtualSpecNode(SpecNode): - - def __init__(self, typedesc, fields, readonly=False): - self.typedesc = typedesc - self.fields = fields # list of SpecNodes - self.readonly = readonly - - def getfrozenkey(self, memo): - if self in memo: - return memo[self] - else: - memo[self] = len(memo) - result = [self.typedesc, self.readonly] - for subnode in self.fields: - result.append(subnode.getfrozenkey(memo)) - return tuple(result) - - def accumulate_nodes(self, rtnodes, vtnodes): - if self in vtnodes: - return - vtnodes[self] = True - for subnode in self.fields: - subnode.accumulate_nodes(rtnodes, vtnodes) - - def copy(self, memo, flagreadonly): - if self in memo: - return memo[self] - readonly = self.readonly or self in flagreadonly - newnode = VirtualSpecNode(self.typedesc, [], readonly) - memo[self] = newnode - for subnode in self.fields: - newnode.fields.append(subnode.copy(memo, flagreadonly)) - return newnode - - def bind_rt_nodes(self, memo, newnodes_iter): - if self in memo: - return memo[self] - newnode = VirtualSpecNode(self.typedesc, [], self.readonly) - memo[self] = newnode - for subnode in self.fields: - newnode.fields.append(subnode.bind_rt_nodes(memo, newnodes_iter)) - return newnode - - -class VirtualFrame(object): - - def __init__(self, sourceblock, nextopindex, - allnodes, callerframe=None, calledgraphs={}): - if isinstance(allnodes, dict): - self.varlist = vars_alive_through_op(sourceblock, nextopindex) - self.nodelist = [allnodes[v] for v in self.varlist] - else: - assert nextopindex == 0 - self.varlist = sourceblock.inputargs - self.nodelist = allnodes[:] - self.sourceblock = sourceblock - self.nextopindex = nextopindex - self.callerframe = callerframe - self.calledgraphs = calledgraphs - - def get_nodes_in_use(self): - return dict(zip(self.varlist, self.nodelist)) - - def shallowcopy(self): - newframe = VirtualFrame.__new__(VirtualFrame) - newframe.varlist = self.varlist - newframe.nodelist = self.nodelist - newframe.sourceblock = self.sourceblock - newframe.nextopindex = self.nextopindex - newframe.callerframe = self.callerframe - newframe.calledgraphs = self.calledgraphs - return newframe - - def copy(self, memo, flagreadonly={}): - newframe = self.shallowcopy() - newframe.nodelist = [node.copy(memo, flagreadonly) - for node in newframe.nodelist] - if newframe.callerframe is not None: - newframe.callerframe = newframe.callerframe.copy(memo, - flagreadonly) - return newframe - - def enum_call_stack(self): - frame = self - while frame is not None: - yield frame - frame = frame.callerframe - - def getfrozenkey(self): - memo = {} - key = [] - for frame in self.enum_call_stack(): - key.append(frame.sourceblock) - key.append(frame.nextopindex) - for node in frame.nodelist: - key.append(node.getfrozenkey(memo)) - return tuple(key) - - def find_all_nodes(self): - rtnodes = [] - vtnodes = {} - for frame in self.enum_call_stack(): - for node in frame.nodelist: - node.accumulate_nodes(rtnodes, vtnodes) - return rtnodes, vtnodes - - def find_rt_nodes(self): - rtnodes, vtnodes = self.find_all_nodes() - return rtnodes - - def find_vt_nodes(self): - rtnodes, vtnodes = self.find_all_nodes() - return vtnodes - - -def copynodes(nodelist, flagreadonly={}): - memo = {} - return [node.copy(memo, flagreadonly) for node in nodelist] - -def find_all_nodes(nodelist): - rtnodes = [] - vtnodes = {} - for node in nodelist: - node.accumulate_nodes(rtnodes, vtnodes) - return rtnodes, vtnodes - -def is_trivial_nodelist(nodelist): - for node in nodelist: - if not isinstance(node, RuntimeSpecNode): - return False - return True - -def bind_rt_nodes(srcnodelist, newnodes_list): - """Return srcnodelist with all RuntimeNodes replaced by nodes - coming from newnodes_list. - """ - memo = {} - newnodes_iter = iter(newnodes_list) - result = [node.bind_rt_nodes(memo, newnodes_iter) for node in srcnodelist] - rest = list(newnodes_iter) - assert rest == [], "too many nodes in newnodes_list" - return result - - -class CannotVirtualize(Exception): - pass - -class ForcedInline(Exception): - pass - -class CannotRemoveThisType(Exception): - pass - -# ____________________________________________________________ - - -class MallocVirtualizer(object): - - def __init__(self, graphs, rtyper, verbose=False): - self.graphs = graphs - self.rtyper = rtyper - self.excdata = rtyper.exceptiondata - self.graphbuilders = {} - self.specialized_graphs = {} - self.specgraphorigin = {} - self.inline_and_remove = {} # {graph: op_to_remove} - self.inline_and_remove_seen = {} # set of (graph, op_to_remove) - self.malloctypedescs = {} - self.count_virtualized = 0 - self.verbose = verbose - self.EXCTYPE_to_vtable = self.build_obscure_mapping() - - def build_obscure_mapping(self): - result = {} - for rinstance in self.rtyper.instance_reprs.values(): - result[rinstance.lowleveltype.TO] = rinstance.rclass.getvtable() - return result - - def report_result(self, progress): - if progress: - log.mallocv('removed %d mallocs so far' % self.count_virtualized) - else: - log.mallocv('done') - - def enum_all_mallocs(self, graph): - for block in graph.iterblocks(): - for op in block.operations: - if op.opname == 'malloc': - MALLOCTYPE = op.result.concretetype.TO - try: - self.getmalloctypedesc(MALLOCTYPE) - except CannotRemoveThisType: - pass - else: - yield (block, op) - elif op.opname == 'direct_call': - graph = graph_called_by(op) - if graph in self.inline_and_remove: - yield (block, op) - - def remove_mallocs_once(self): - self.flush_failed_specializations() - prev = self.count_virtualized - count_inline_and_remove = len(self.inline_and_remove) - for graph in self.graphs: - seen = {} - while True: - for block, op in self.enum_all_mallocs(graph): - if op.result not in seen: - seen[op.result] = True - if self.try_remove_malloc(graph, block, op): - break # graph mutated, restart enum_all_mallocs() - else: - break # enum_all_mallocs() exhausted, graph finished - progress1 = self.count_virtualized - prev - progress2 = len(self.inline_and_remove) - count_inline_and_remove - progress = progress1 or bool(progress2) - self.report_result(progress) - return progress - - def flush_failed_specializations(self): - for key, (mode, specgraph) in self.specialized_graphs.items(): - if mode == 'fail': - del self.specialized_graphs[key] - - def fixup_except_block(self, exceptblock): - # hack: this block's inputargs may be missing concretetypes... - e1, v1 = exceptblock.inputargs - e1.concretetype = self.excdata.lltype_of_exception_type - v1.concretetype = self.excdata.lltype_of_exception_value - - def getmalloctypedesc(self, MALLOCTYPE): - try: - dsc = self.malloctypedescs[MALLOCTYPE] - except KeyError: - dsc = self.malloctypedescs[MALLOCTYPE] = MallocTypeDesc(MALLOCTYPE) - return dsc - - def try_remove_malloc(self, graph, block, op): - if (graph, op) in self.inline_and_remove_seen: - return False # no point in trying again - graphbuilder = GraphBuilder(self, graph) - if graph in self.graphbuilders: - graphbuilder.initialize_from_old_builder(self.graphbuilders[graph]) - graphbuilder.start_from_a_malloc(graph, block, op.result) - try: - graphbuilder.propagate_specializations() - except CannotVirtualize as e: - self.logresult(op, 'failed', e) - return False - except ForcedInline as e: - self.logresult(op, 'forces inlining', e) - self.inline_and_remove[graph] = op - self.inline_and_remove_seen[graph, op] = True - return False - else: - self.logresult(op, 'removed') - graphbuilder.finished_removing_malloc() - self.graphbuilders[graph] = graphbuilder - self.count_virtualized += 1 - return True - - def logresult(self, op, msg, exc=None): # only for nice log outputs - if self.verbose: - if exc is None: - exc = '' - else: - exc = ': %s' % (exc,) - chain = [] - while True: - chain.append(str(op.result)) - if op.opname != 'direct_call': - break - fobj = op.args[0].value._obj - op = self.inline_and_remove[fobj.graph] - log.mallocv('%s %s%s' % ('->'.join(chain), msg, exc)) - elif exc is None: - log.dot() - - def get_specialized_graph(self, graph, nodelist): - assert len(graph.getargs()) == len(nodelist) - if is_trivial_nodelist(nodelist): - return 'trivial', graph - if graph in self.specgraphorigin: - orggraph, orgnodelist = self.specgraphorigin[graph] - nodelist = bind_rt_nodes(orgnodelist, nodelist) - graph = orggraph - virtualframe = VirtualFrame(graph.startblock, 0, nodelist) - key = virtualframe.getfrozenkey() - try: - return self.specialized_graphs[key] - except KeyError: - self.build_specialized_graph(graph, key, nodelist) - return self.specialized_graphs[key] - - def build_specialized_graph(self, graph, key, nodelist): - graph2 = copygraph(graph) - virtualframe = VirtualFrame(graph2.startblock, 0, nodelist) - graphbuilder = GraphBuilder(self, graph2) - specblock = graphbuilder.start_from_virtualframe(virtualframe) - specgraph = graph2 - specgraph.name += '_mallocv' - specgraph.startblock = specblock - self.specialized_graphs[key] = ('call', specgraph) - try: - graphbuilder.propagate_specializations() - except ForcedInline as e: - if self.verbose: - log.mallocv('%s inlined: %s' % (graph.name, e)) - self.specialized_graphs[key] = ('inline', None) - except CannotVirtualize as e: - if self.verbose: - log.mallocv('%s failing: %s' % (graph.name, e)) - self.specialized_graphs[key] = ('fail', None) - else: - self.graphbuilders[specgraph] = graphbuilder - self.specgraphorigin[specgraph] = graph, nodelist - self.graphs.append(specgraph) - - -class GraphBuilder(object): - - def __init__(self, mallocv, graph): - self.mallocv = mallocv - self.graph = graph - self.specialized_blocks = {} - self.pending_specializations = [] - - def initialize_from_old_builder(self, oldbuilder): - self.specialized_blocks.update(oldbuilder.specialized_blocks) - - def start_from_virtualframe(self, startframe): - spec = BlockSpecializer(self) - spec.initialize_renamings(startframe) - self.pending_specializations.append(spec) - return spec.specblock - - def start_from_a_malloc(self, graph, block, v_result): - assert v_result in [op.result for op in block.operations] - nodelist = [] - for v in block.inputargs: - nodelist.append(RuntimeSpecNode(v, v.concretetype)) - trivialframe = VirtualFrame(block, 0, nodelist) - spec = BlockSpecializer(self, v_result) - spec.initialize_renamings(trivialframe, keep_inputargs=True) - self.pending_specializations.append(spec) - self.pending_patch = (block, spec.specblock) - - def finished_removing_malloc(self): - (srcblock, specblock) = self.pending_patch - srcblock.inputargs = specblock.inputargs - srcblock.operations = specblock.operations - srcblock.exitswitch = specblock.exitswitch - srcblock.recloseblock(*specblock.exits) - - def create_outgoing_link(self, currentframe, targetblock, - nodelist, renamings, v_expand_malloc=None): - assert len(nodelist) == len(targetblock.inputargs) - # - if is_except(targetblock): - v_expand_malloc = None - while currentframe.callerframe is not None: - currentframe = currentframe.callerframe - newlink = self.handle_catch(currentframe, nodelist, renamings) - if newlink: - return newlink - else: - targetblock = self.exception_escapes(nodelist, renamings) - assert len(nodelist) == len(targetblock.inputargs) - - if (currentframe.callerframe is None and - is_trivial_nodelist(nodelist)): - # there is no more VirtualSpecNodes being passed around, - # so we can stop specializing - rtnodes = nodelist - specblock = targetblock - else: - if is_return(targetblock): - v_expand_malloc = None - newframe = self.return_to_caller(currentframe, nodelist[0]) - else: - targetnodes = dict(zip(targetblock.inputargs, nodelist)) - newframe = VirtualFrame(targetblock, 0, targetnodes, - callerframe=currentframe.callerframe, - calledgraphs=currentframe.calledgraphs) - rtnodes = newframe.find_rt_nodes() - specblock = self.get_specialized_block(newframe, v_expand_malloc) - - linkargs = [renamings[rtnode] for rtnode in rtnodes] - return Link(linkargs, specblock) - - def return_to_caller(self, currentframe, retnode): - callerframe = currentframe.callerframe - if callerframe is None: - raise ForcedInline("return block") - nodelist = callerframe.nodelist - callerframe = callerframe.shallowcopy() - callerframe.nodelist = [] - for node in nodelist: - if isinstance(node, FutureReturnValue): - node = retnode - callerframe.nodelist.append(node) - return callerframe - - def handle_catch(self, catchingframe, nodelist, renamings): - if not self.has_exception_catching(catchingframe): - return None - [exc_node, exc_value_node] = nodelist - v_exc_type = renamings.get(exc_node) - if isinstance(v_exc_type, Constant): - exc_type = v_exc_type.value - elif isinstance(exc_value_node, VirtualSpecNode): - EXCTYPE = exc_value_node.typedesc.MALLOCTYPE - exc_type = self.mallocv.EXCTYPE_to_vtable[EXCTYPE] - else: - raise CannotVirtualize("raising non-constant exc type") - excdata = self.mallocv.excdata - assert catchingframe.sourceblock.exits[0].exitcase is None - for catchlink in catchingframe.sourceblock.exits[1:]: - if excdata.fn_exception_match(exc_type, catchlink.llexitcase): - # Match found. Follow this link. - mynodes = catchingframe.get_nodes_in_use() - for node, attr in zip(nodelist, - ['last_exception', 'last_exc_value']): - v = getattr(catchlink, attr) - if isinstance(v, Variable): - mynodes[v] = node - # - nodelist = [] - for v in catchlink.args: - if isinstance(v, Variable): - node = mynodes[v] - else: - node = getconstnode(v, renamings) - nodelist.append(node) - return self.create_outgoing_link(catchingframe, - catchlink.target, - nodelist, renamings) - else: - # No match at all, propagate the exception to the caller - return None - - def has_exception_catching(self, catchingframe): - if not catchingframe.sourceblock.canraise: - return False - else: - operations = catchingframe.sourceblock.operations - assert 1 <= catchingframe.nextopindex <= len(operations) - return catchingframe.nextopindex == len(operations) - - def exception_escapes(self, nodelist, renamings): - # the exception escapes - if not is_trivial_nodelist(nodelist): - # start of hacks to help handle_catch() - [exc_node, exc_value_node] = nodelist - v_exc_type = renamings.get(exc_node) - if isinstance(v_exc_type, Constant): - # cannot improve: handle_catch() would already be happy - # by seeing the exc_type as a constant - pass - elif isinstance(exc_value_node, VirtualSpecNode): - # can improve with a strange hack: we pretend that - # the source code jumps to a block that itself allocates - # the exception, sets all fields, and raises it by - # passing a constant type. - typedesc = exc_value_node.typedesc - return self.get_exc_reconstruction_block(typedesc) - else: - # cannot improve: handle_catch() will have no clue about - # the exception type - pass - raise CannotVirtualize("except block") - targetblock = self.graph.exceptblock - self.mallocv.fixup_except_block(targetblock) - return targetblock - - def get_exc_reconstruction_block(self, typedesc): - exceptblock = self.graph.exceptblock - self.mallocv.fixup_except_block(exceptblock) - TEXC = exceptblock.inputargs[0].concretetype - TVAL = exceptblock.inputargs[1].concretetype - # - v_ignored_type = varoftype(TEXC) - v_incoming_value = varoftype(TVAL) - block = Block([v_ignored_type, v_incoming_value]) - # - c_EXCTYPE = Constant(typedesc.MALLOCTYPE, lltype.Void) - v = varoftype(lltype.Ptr(typedesc.MALLOCTYPE)) - c_flavor = Constant({'flavor': 'gc'}, lltype.Void) - op = SpaceOperation('malloc', [c_EXCTYPE, c_flavor], v) - block.operations.append(op) - # - for name, FIELDTYPE in typedesc.names_and_types: - EXACTPTR = lltype.Ptr(typedesc.name2subtype[name]) - c_name = Constant(name) - c_name.concretetype = lltype.Void - # - v_in = varoftype(EXACTPTR) - op = SpaceOperation('cast_pointer', [v_incoming_value], v_in) - block.operations.append(op) - # - v_field = varoftype(FIELDTYPE) - op = SpaceOperation('getfield', [v_in, c_name], v_field) - block.operations.append(op) - # - v_out = varoftype(EXACTPTR) - op = SpaceOperation('cast_pointer', [v], v_out) - block.operations.append(op) - # - v0 = varoftype(lltype.Void) - op = SpaceOperation('setfield', [v_out, c_name, v_field], v0) - block.operations.append(op) - # - v_exc_value = varoftype(TVAL) - op = SpaceOperation('cast_pointer', [v], v_exc_value) - block.operations.append(op) - # - exc_type = self.mallocv.EXCTYPE_to_vtable[typedesc.MALLOCTYPE] - c_exc_type = Constant(exc_type, TEXC) - block.closeblock(Link([c_exc_type, v_exc_value], exceptblock)) - return block - - def get_specialized_block(self, virtualframe, v_expand_malloc=None): - key = virtualframe.getfrozenkey() - specblock = self.specialized_blocks.get(key) - if specblock is None: - orgblock = virtualframe.sourceblock - assert len(orgblock.exits) != 0 - spec = BlockSpecializer(self, v_expand_malloc) - spec.initialize_renamings(virtualframe) - self.pending_specializations.append(spec) - specblock = spec.specblock - self.specialized_blocks[key] = specblock - return specblock - - def propagate_specializations(self): - while self.pending_specializations: - spec = self.pending_specializations.pop() - spec.specialize_operations() - spec.follow_exits() - - -class BlockSpecializer(object): - - def __init__(self, graphbuilder, v_expand_malloc=None): - self.graphbuilder = graphbuilder - self.v_expand_malloc = v_expand_malloc - self.specblock = Block([]) - - def initialize_renamings(self, virtualframe, keep_inputargs=False): - # we make a copy of the original 'virtualframe' because the - # specialize_operations() will mutate some of its content. - virtualframe = virtualframe.copy({}) - self.virtualframe = virtualframe - self.nodes = virtualframe.get_nodes_in_use() - self.renamings = {} # {RuntimeSpecNode(): Variable()} - if keep_inputargs: - assert virtualframe.varlist == virtualframe.sourceblock.inputargs - specinputargs = [] - for i, rtnode in enumerate(virtualframe.find_rt_nodes()): - if keep_inputargs: - v = virtualframe.varlist[i] - assert v.concretetype == rtnode.TYPE - else: - v = rtnode.newvar() - self.renamings[rtnode] = v - specinputargs.append(v) - self.specblock.inputargs = specinputargs - - def setnode(self, v, node): - assert v not in self.nodes - self.nodes[v] = node - - def getnode(self, v): - if isinstance(v, Variable): - return self.nodes[v] _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit