[pypy-commit] pypy space-newtext: merge default

cfbolz pypy.commits at gmail.com
Mon Dec 19 08:47:44 EST 2016


Author: Carl Friedrich Bolz <cfbolz at 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
- at 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()
 
 
+ at 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)
+
+ at 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)
+
+ at 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)
+
+ at 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")
+
+ at 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]


More information about the pypy-commit mailing list