Author: Matti Picus <[email protected]>
Branch: better-PyDict_Next
Changeset: r88985:7c55b879f853
Date: 2016-12-08 20:16 +0200
http://bitbucket.org/pypy/pypy/changeset/7c55b879f853/

Log:    merge default into branch

diff too long, truncating to 2000 out of 5162 lines

diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py 
b/lib-python/2.7/distutils/sysconfig_pypy.py
--- a/lib-python/2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/2.7/distutils/sysconfig_pypy.py
@@ -12,7 +12,6 @@
 
 import sys
 import os
-import shlex
 import imp
 
 from distutils.errors import DistutilsPlatformError
@@ -62,13 +61,32 @@
 def _init_posix():
     """Initialize the module as appropriate for POSIX systems."""
     g = {}
+    g['CC'] = "gcc -pthread"
+    g['CXX'] = "g++ -pthread"
+    g['OPT'] = "-DNDEBUG -O2"
+    g['CFLAGS'] = "-DNDEBUG -O2"
+    g['CCSHARED'] = "-fPIC"
+    g['LDSHARED'] = "gcc -pthread -shared"
+    g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0]
+    g['AR'] = "ar"
+    g['ARFLAGS'] = "rc"
     g['EXE'] = ""
-    g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0]
     g['LIBDIR'] = os.path.join(sys.prefix, 'lib')
-    g['CC'] = "gcc -pthread" # -pthread might not be valid on OS/X, check
-    g['OPT'] = "" 
     g['VERSION'] = get_python_version()
 
+    if sys.platform[:6] == "darwin":
+        import platform
+        if platform.machine() == 'i386':
+            if platform.architecture()[0] == '32bit':
+                arch = 'i386'
+            else:
+                arch = 'x86_64'
+        else:
+            # just a guess
+            arch = platform.machine()
+        g['LDSHARED'] += ' -undefined dynamic_lookup'
+        g['CC'] += ' -arch %s' % (arch,)
+
     global _config_vars
     _config_vars = g
 
@@ -104,6 +122,12 @@
         _config_vars['prefix'] = PREFIX
         _config_vars['exec_prefix'] = EXEC_PREFIX
 
+        # OS X platforms require special customization to handle
+        # multi-architecture, multi-os-version installers
+        if sys.platform == 'darwin':
+            import _osx_support
+            _osx_support.customize_config_vars(_config_vars)
+
     if args:
         vals = []
         for name in args:
@@ -119,30 +143,80 @@
     """
     return get_config_vars().get(name)
 
+
 def customize_compiler(compiler):
-    """Dummy method to let some easy_install packages that have
-    optional C speedup components.
+    """Do any platform-specific customization of a CCompiler instance.
+
+    Mainly needed on Unix, so we can plug in the information that
+    varies across Unices and is stored in Python's Makefile (CPython)
+    or hard-coded in _init_posix() (PyPy).
     """
-    def customize(executable, flags):
-        command = compiler.executables[executable] + flags
-        setattr(compiler, executable, command)
+    if compiler.compiler_type == "unix":
+        if sys.platform == "darwin":
+            # Perform first-time customization of compiler-related
+            # config vars on OS X now that we know we need a compiler.
+            # This is primarily to support Pythons from binary
+            # installers.  The kind and paths to build tools on
+            # the user system may vary significantly from the system
+            # that Python itself was built on.  Also the user OS
+            # version and build tools may not support the same set
+            # of CPU architectures for universal builds.
+            global _config_vars
+            # Use get_config_var() to ensure _config_vars is initialized.
+            if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
+                import _osx_support
+                _osx_support.customize_compiler(_config_vars)
+                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
 
-    if compiler.compiler_type == "unix":
-        # compiler_so can be c++ which has no -Wimplicit
-        #compiler.compiler_so.extend(['-O2', '-fPIC', '-Wimplicit'])
-        compiler.compiler_so.extend(['-O2', '-fPIC'])
-        compiler.shared_lib_extension = get_config_var('SO')
-        if "CPPFLAGS" in os.environ:
-            cppflags = shlex.split(os.environ["CPPFLAGS"])
-            for executable in ('compiler', 'compiler_so', 'linker_so'):
-                customize(executable, cppflags)
-        if "CFLAGS" in os.environ:
-            cflags = shlex.split(os.environ["CFLAGS"])
-            for executable in ('compiler', 'compiler_so', 'linker_so'):
-                customize(executable, cflags)
-        if "LDFLAGS" in os.environ:
-            ldflags = shlex.split(os.environ["LDFLAGS"])
-            customize('linker_so', ldflags)
+        (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
+            get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
+                            'CCSHARED', 'LDSHARED', 'SO', 'AR',
+                            'ARFLAGS')
+
+        if 'CC' in os.environ:
+            newcc = os.environ['CC']
+            if (sys.platform == 'darwin'
+                    and 'LDSHARED' not in os.environ
+                    and ldshared.startswith(cc)):
+                # On OS X, if CC is overridden, use that as the default
+                #       command for LDSHARED as well
+                ldshared = newcc + ldshared[len(cc):]
+            cc = newcc
+        if 'CXX' in os.environ:
+            cxx = os.environ['CXX']
+        if 'LDSHARED' in os.environ:
+            ldshared = os.environ['LDSHARED']
+        if 'CPP' in os.environ:
+            cpp = os.environ['CPP']
+        else:
+            cpp = cc + " -E"           # not always
+        if 'LDFLAGS' in os.environ:
+            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
+        if 'CFLAGS' in os.environ:
+            cflags = opt + ' ' + os.environ['CFLAGS']
+            ldshared = ldshared + ' ' + os.environ['CFLAGS']
+        if 'CPPFLAGS' in os.environ:
+            cpp = cpp + ' ' + os.environ['CPPFLAGS']
+            cflags = cflags + ' ' + os.environ['CPPFLAGS']
+            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
+        if 'AR' in os.environ:
+            ar = os.environ['AR']
+        if 'ARFLAGS' in os.environ:
+            archiver = ar + ' ' + os.environ['ARFLAGS']
+        else:
+            archiver = ar + ' ' + ar_flags
+
+        cc_cmd = cc + ' ' + cflags
+        compiler.set_executables(
+            preprocessor=cpp,
+            compiler=cc_cmd,
+            compiler_so=cc_cmd + ' ' + ccshared,
+            compiler_cxx=cxx,
+            linker_so=ldshared,
+            linker_exe=cc,
+            archiver=archiver)
+
+        compiler.shared_lib_extension = so_ext
 
 
 from sysconfig_cpython import (
diff --git a/lib_pypy/_pypy_wait.py b/lib_pypy/_pypy_wait.py
--- a/lib_pypy/_pypy_wait.py
+++ b/lib_pypy/_pypy_wait.py
@@ -1,3 +1,4 @@
+import os
 from resource import ffi, lib, _make_struct_rusage
 
 __all__ = ["wait3", "wait4"]
@@ -7,6 +8,9 @@
     status = ffi.new("int *")
     ru = ffi.new("struct rusage *")
     pid = lib.wait3(status, options, ru)
+    if pid == -1:
+        errno = ffi.errno
+        raise OSError(errno, os.strerror(errno))
 
     rusage = _make_struct_rusage(ru)
 
@@ -16,6 +20,9 @@
     status = ffi.new("int *")
     ru = ffi.new("struct rusage *")
     pid = lib.wait4(pid, status, options, ru)
+    if pid == -1:
+        errno = ffi.errno
+        raise OSError(errno, os.strerror(errno))
 
     rusage = _make_struct_rusage(ru)
 
diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst
--- a/pypy/doc/project-ideas.rst
+++ b/pypy/doc/project-ideas.rst
@@ -71,8 +71,11 @@
 Analyzing performance of applications is always tricky. We have various
 tools, for example a `jitviewer`_ that help us analyze performance.
 
-The jitviewer shows the code generated by the PyPy JIT in a hierarchical way,
-as shown by the screenshot below:
+The old tool was partly rewritten and combined with vmprof. The service is
+hosted at `vmprof.com`_.
+
+The following shows an old image of the jitviewer.
+The code generated by the PyPy JIT in a hierarchical way:
 
   - at the bottom level, it shows the Python source code of the compiled loops
 
@@ -84,13 +87,17 @@
 
 .. image:: image/jitviewer.png
 
-The jitviewer is a web application based on flask and jinja2 (and jQuery on
-the client): if you have great web developing skills and want to help PyPy,
+The jitviewer is a web application based on django and angularjs:
+if you have great web developing skills and want to help PyPy,
 this is an ideal task to get started, because it does not require any deep
-knowledge of the internals.
+knowledge of the internals. Head over to `vmprof-python`_, `vmprof-server`_ and
+`vmprof-integration`_ to find open issues and documentation.
 
-.. _jitviewer: http://bitbucket.org/pypy/jitviewer
-
+.. _jitviewer: http://vmprof.com
+.. _vmprof.com: http://vmprof.com
+.. _vmprof-python: https://github.com/vmprof/vmprof-python
+.. _vmprof-server: https://github.com/vmprof/vmprof-server
+.. _vmprof-integration: https://github.com/vmprof/vmprof-integration
 
 Optimized Unicode Representation
 --------------------------------
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
@@ -5,6 +5,15 @@
 .. this is a revision shortly after release-pypy2.7-v5.6
 .. startrev: 7e9787939641
 
+
+Since a while now, PyPy preserves the order of dictionaries and sets.
+However, the set literal syntax ``{x, y, z}`` would by mistake build a
+set with the opposite order: ``set([z, y, x])``.  This has been fixed.
+Note that CPython is inconsistent too: in 2.7.12, ``{5, 5.0}`` would be
+``set([5.0])``, but in 2.7.trunk it is ``set([5])``.  PyPy's behavior
+changed in exactly the same way because of this fix.
+
+
 .. branch: rpython-error-to-systemerror
 
 Any uncaught RPython exception (from a PyPy bug) is turned into an
@@ -20,3 +29,19 @@
 .. branch: clean-exported-state
 
 Clean-ups in the jit optimizeopt
+
+.. branch: conditional_call_value_4
+
+Add jit.conditional_call_elidable(), a way to tell the JIT "conditonally
+call this function" returning a result.
+
+.. branch: desc-specialize
+
+Refactor FunctionDesc.specialize() and related code (RPython annotator).
+
+.. branch: raw-calloc
+
+.. branch: issue2446
+
+Assign ``tp_doc`` to the new TypeObject's type dictionary ``__doc__`` key
+so it will be picked up by app-level objects of that type
diff --git a/pypy/goal/targetpypystandalone.py 
b/pypy/goal/targetpypystandalone.py
--- a/pypy/goal/targetpypystandalone.py
+++ b/pypy/goal/targetpypystandalone.py
@@ -298,6 +298,12 @@
         if config.translation.sandbox:
             config.objspace.lonepycfiles = False
 
+        if config.objspace.usemodules.cpyext:
+            if config.translation.gc != 'incminimark':
+                raise Exception("The 'cpyext' module requires the 
'incminimark'"
+                                " GC.  You need either 
'targetpypystandalone.py"
+                                " --withoutmod-cpyext' or '--gc=incminimark'")
+
         config.translating = True
 
         import translate
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -183,6 +183,14 @@
                 assert self._finalize_.im_func is not W_Root._finalize_.im_func
             space.finalizer_queue.register_finalizer(self)
 
+    def may_unregister_rpython_finalizer(self, space):
+        """Optimization hint only: if there is no user-defined __del__()
+        method, pass the hint ``don't call any finalizer'' to rgc.
+        """
+        if not self.getclass(space).hasuserdel:
+            from rpython.rlib import rgc
+            rgc.may_ignore_finalizer(self)
+
     # hooks that the mapdict implementations needs:
     def _get_mapdict_map(self):
         return None
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -439,6 +439,7 @@
                                           space.wrap(msg))
         return OperationError(exc, w_error)
 
[email protected](3)
 def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
                   w_exception_class=None):
     assert isinstance(e, OSError)
@@ -466,8 +467,8 @@
         w_error = space.call_function(exc, space.wrap(errno),
                                       space.wrap(msg))
     return OperationError(exc, w_error)
-wrap_oserror2._annspecialcase_ = 'specialize:arg(3)'
 
[email protected](3)
 def wrap_oserror(space, e, filename=None, exception_name='w_OSError',
                  w_exception_class=None):
     if filename is not None:
@@ -478,7 +479,6 @@
         return wrap_oserror2(space, e, None,
                              exception_name=exception_name,
                              w_exception_class=w_exception_class)
-wrap_oserror._annspecialcase_ = 'specialize:arg(3)'
 
 def exception_from_saved_errno(space, w_type):
     from rpython.rlib.rposix import get_saved_errno
diff --git a/pypy/interpreter/executioncontext.py 
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -547,6 +547,8 @@
 
     @jit.dont_look_inside
     def _run_finalizers(self):
+        # called by perform() when we have to "perform" this action,
+        # and also directly at the end of gc.collect).
         while True:
             w_obj = self.space.finalizer_queue.next_dead()
             if w_obj is None:
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.pyopcode import LoopBlock
 from pypy.interpreter.pycode import CO_YIELD_INSIDE_TRY
-from rpython.rlib import jit
+from rpython.rlib import jit, rgc
 
 
 class GeneratorIterator(W_Root):
@@ -103,11 +103,11 @@
                 w_result = frame.execute_frame(w_arg, operr)
             except OperationError:
                 # errors finish a frame
-                self.frame = None
+                self.frame_is_finished()
                 raise
             # if the frame is now marked as finished, it was RETURNed from
             if frame.frame_finished_execution:
-                self.frame = None
+                self.frame_is_finished()
                 raise OperationError(space.w_StopIteration, space.w_None)
             else:
                 return w_result     # YIELDed
@@ -209,7 +209,7 @@
             finally:
                 frame.f_backref = jit.vref_None
                 self.running = False
-                self.frame = None
+                self.frame_is_finished()
         return unpack_into
     unpack_into = _create_unpack_into()
     unpack_into_w = _create_unpack_into()
@@ -228,6 +228,10 @@
                     break
                 block = block.previous
 
+    def frame_is_finished(self):
+        self.frame = None
+        rgc.may_ignore_finalizer(self)
+
 
 def get_printable_location_genentry(bytecode):
     return '%s <generator>' % (bytecode.get_repr(),)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1295,9 +1295,10 @@
     @jit.unroll_safe
     def BUILD_SET(self, itemcount, next_instr):
         w_set = self.space.newset()
-        for i in range(itemcount):
-            w_item = self.popvalue()
+        for i in range(itemcount-1, -1, -1):
+            w_item = self.peekvalue(i)
             self.space.call_method(w_set, 'add', w_item)
+        self.popvalues(itemcount)
         self.pushvalue(w_set)
 
     def STORE_MAP(self, oparg, next_instr):
diff --git a/pypy/interpreter/test/test_compiler.py 
b/pypy/interpreter/test/test_compiler.py
--- a/pypy/interpreter/test/test_compiler.py
+++ b/pypy/interpreter/test/test_compiler.py
@@ -729,6 +729,10 @@
 
 
 class AppTestCompiler:
+    def setup_class(cls):
+        cls.w_host_is_pypy = cls.space.wrap(
+            '__pypy__' in sys.builtin_module_names)
+
     def test_bom_with_future(self):
         s = '\xef\xbb\xbffrom __future__ import division\nx = 1/2'
         ns = {}
@@ -771,6 +775,18 @@
         assert math.copysign(1., c[0]) == -1.0
         assert math.copysign(1., c[1]) == -1.0
 
+    def test_dict_and_set_literal_order(self):
+        x = 1
+        l1 = list({1:'a', 3:'b', 2:'c', 4:'d'})
+        l2 = list({1, 3, 2, 4})
+        l3 = list({x:'a', 3:'b', 2:'c', 4:'d'})
+        l4 = list({x, 3, 2, 4})
+        if not self.host_is_pypy:
+            # the full test relies on the host Python providing ordered dicts
+            assert set(l1) == set(l2) == set(l3) == set(l4) == {1, 3, 2, 4}
+        else:
+            assert l1 == l2 == l3 == l4 == [1, 3, 2, 4]
+
 
 ##class TestPythonAstCompiler(BaseTestCompiler):
 ##    def setup_method(self, method):
diff --git a/pypy/interpreter/test/test_special.py 
b/pypy/interpreter/test/test_special.py
--- a/pypy/interpreter/test/test_special.py
+++ b/pypy/interpreter/test/test_special.py
@@ -4,9 +4,11 @@
     def test_Ellipsis(self):
         assert Ellipsis == Ellipsis
         assert repr(Ellipsis) == 'Ellipsis'
+        assert Ellipsis.__class__.__name__ == 'ellipsis'
     
     def test_NotImplemented(self):
         def f():
             return NotImplemented
         assert f() == NotImplemented 
         assert repr(NotImplemented) == 'NotImplemented'
+        assert NotImplemented.__class__.__name__ == 'NotImplementedType'
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -770,12 +770,12 @@
 )
 assert not Cell.typedef.acceptable_as_base_class  # no __new__
 
-Ellipsis.typedef = TypeDef("Ellipsis",
+Ellipsis.typedef = TypeDef("ellipsis",
     __repr__ = interp2app(Ellipsis.descr__repr__),
 )
 assert not Ellipsis.typedef.acceptable_as_base_class  # no __new__
 
-NotImplemented.typedef = TypeDef("NotImplemented",
+NotImplemented.typedef = TypeDef("NotImplementedType",
     __repr__ = interp2app(NotImplemented.descr__repr__),
 )
 assert not NotImplemented.typedef.acceptable_as_base_class  # no __new__
diff --git a/pypy/module/_cffi_backend/cdataobj.py 
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -397,7 +397,7 @@
         space = self.space
         if space.is_none(w_destructor):
             if isinstance(self, W_CDataGCP):
-                self.w_destructor = None
+                self.detach_destructor()
                 return space.w_None
             raise oefmt(space.w_TypeError,
                         "Can remove destructor only on a object "
@@ -604,6 +604,10 @@
             self.w_destructor = None
             self.space.call_function(w_destructor, self.w_original_cdata)
 
+    def detach_destructor(self):
+        self.w_destructor = None
+        self.may_unregister_rpython_finalizer(self.space)
+
 
 W_CData.typedef = TypeDef(
     '_cffi_backend.CData',
diff --git a/pypy/module/_cffi_backend/cdlopen.py 
b/pypy/module/_cffi_backend/cdlopen.py
--- a/pypy/module/_cffi_backend/cdlopen.py
+++ b/pypy/module/_cffi_backend/cdlopen.py
@@ -55,6 +55,7 @@
         if not libhandle:
             raise oefmt(self.ffi.w_FFIError, "library '%s' is already closed",
                         self.libname)
+        self.may_unregister_rpython_finalizer(self.ffi.space)
 
         # Clear the dict to force further accesses to do cdlopen_fetch()
         # again, and fail because the library was closed.  Note that the
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py 
b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -401,7 +401,8 @@
             retries += 1
             assert retries <= 5
             import gc; gc.collect()
-        assert seen == [40, 40, raw1, raw2]
+        assert (seen == [40, 40, raw1, raw2] or
+                seen == [40, 40, raw2, raw1])
         assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
         assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
 
diff --git a/pypy/module/_collections/interp_deque.py 
b/pypy/module/_collections/interp_deque.py
--- a/pypy/module/_collections/interp_deque.py
+++ b/pypy/module/_collections/interp_deque.py
@@ -1,4 +1,5 @@
 import sys
+from rpython.rlib.objectmodel import specialize
 from pypy.interpreter import gateway
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.typedef import TypeDef, make_weakref_descr
@@ -6,7 +7,6 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.error import OperationError, oefmt
 from rpython.rlib.debug import check_nonneg
-from rpython.rlib.objectmodel import specialize
 
 
 # A `dequeobject` is composed of a doubly-linked list of `block` nodes.
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -172,6 +172,7 @@
             self.newlines = self.stream.getnewlines()
             self.stream = None
             self.fd = -1
+            self.may_unregister_rpython_finalizer(self.space)
             openstreams = getopenstreams(self.space)
             try:
                 del openstreams[stream]
diff --git a/pypy/module/_socket/interp_socket.py 
b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -233,6 +233,7 @@
         except SocketError:
             # cpython doesn't return any errors on close
             pass
+        self.may_unregister_rpython_finalizer(space)
 
     def connect_w(self, space, w_addr):
         """connect(address)
diff --git a/pypy/module/_weakref/interp__weakref.py 
b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -217,7 +217,7 @@
             return self.space.w_None
         return w_obj
 
-    def descr__eq__(self, space, w_ref2):
+    def compare(self, space, w_ref2, invert):
         if not isinstance(w_ref2, W_Weakref):
             return space.w_NotImplemented
         ref1 = self
@@ -225,11 +225,18 @@
         w_obj1 = ref1.dereference()
         w_obj2 = ref2.dereference()
         if w_obj1 is None or w_obj2 is None:
-            return space.is_(ref1, ref2)
-        return space.eq(w_obj1, w_obj2)
+            w_res = space.is_(ref1, ref2)
+        else:
+            w_res = space.eq(w_obj1, w_obj2)
+        if invert:
+            w_res = space.not_(w_res)
+        return w_res
+
+    def descr__eq__(self, space, w_ref2):
+        return self.compare(space, w_ref2, invert=False)
 
     def descr__ne__(self, space, w_ref2):
-        return space.not_(space.eq(self, w_ref2))
+        return self.compare(space, w_ref2, invert=True)
 
 def getlifeline(space, w_obj):
     lifeline = w_obj.getweakref()
diff --git a/pypy/module/_weakref/test/test_weakref.py 
b/pypy/module/_weakref/test/test_weakref.py
--- a/pypy/module/_weakref/test/test_weakref.py
+++ b/pypy/module/_weakref/test/test_weakref.py
@@ -150,6 +150,14 @@
         assert not (ref1 == [])
         assert ref1 != []
 
+    def test_ne(self):
+        import _weakref
+        class X(object):
+            pass
+        ref1 = _weakref.ref(X())
+        assert ref1.__eq__(X()) is NotImplemented
+        assert ref1.__ne__(X()) is NotImplemented
+
     def test_getweakrefs(self):
         import _weakref, gc
         class A(object):
diff --git a/pypy/module/cppyy/test/test_zjit.py 
b/pypy/module/cppyy/test/test_zjit.py
--- a/pypy/module/cppyy/test/test_zjit.py
+++ b/pypy/module/cppyy/test/test_zjit.py
@@ -124,13 +124,13 @@
         assert isinstance(w_obj, FakeFloat)
         return w_obj.val
 
+    @specialize.arg(1)
     def interp_w(self, RequiredClass, w_obj, can_be_None=False):
         if can_be_None and w_obj is None:
             return None
         if not isinstance(w_obj, RequiredClass):
             raise TypeError
         return w_obj
-    interp_w._annspecialcase_ = 'specialize:arg(1)'
 
     def getarg_w(self, code, w_obj):    # for retrieving buffers
         return FakeBuffer(w_obj)
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
@@ -36,8 +36,6 @@
 from rpython.rlib.objectmodel import specialize
 from pypy.module import exceptions
 from pypy.module.exceptions import interp_exceptions
-# CPython 2.4 compatibility
-from py.builtin import BaseException
 from rpython.tool.sourcetools import func_with_new_name
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rlib import rawrefcount
@@ -985,7 +983,7 @@
         py_type_ready(space, get_capsule_type())
     INIT_FUNCTIONS.append(init_types)
     from pypy.module.posix.interp_posix import add_fork_hook
-    _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [], 
+    _reinit_tls = rffi.llexternal('%sThread_ReInitTLS' % prefix, [],
                                   lltype.Void, compilation_info=eci)
     def reinit_tls(space):
         _reinit_tls()
@@ -1614,9 +1612,8 @@
     miniglobals = {'__name__':    __name__, # for module name propagation
                    }
     exec source.compile() in miniglobals
-    call_external_function = miniglobals['cpy_call_external']
+    call_external_function = specialize.ll()(miniglobals['cpy_call_external'])
     call_external_function._dont_inline_ = True
-    call_external_function._annspecialcase_ = 'specialize:ll'
     call_external_function._gctransformer_hint_close_stack_ = True
     # don't inline, as a hack to guarantee that no GC pointer is alive
     # anywhere in call_external_function
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -62,12 +62,14 @@
     position must be positive, indexing from the end of the list is not
     supported.  If pos is out of bounds, return NULL and set an
     IndexError exception."""
+    from pypy.module.cpyext.sequence import CPyListStrategy
     if not isinstance(w_list, W_ListObject):
         PyErr_BadInternalCall(space)
     if index < 0 or index >= w_list.length():
         raise oefmt(space.w_IndexError, "list index out of range")
-    w_list.ensure_object_strategy()  # make sure we can return a borrowed obj
-    # XXX ^^^ how does this interact with CPyListStrategy?
+    cpy_strategy = space.fromcache(CPyListStrategy)
+    if w_list.strategy is not cpy_strategy:
+        w_list.ensure_object_strategy() # make sure we can return a borrowed 
obj
     w_res = w_list.getitem(index)
     return w_res     # borrowed ref
 
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
@@ -15,6 +15,7 @@
 from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib import rawrefcount
+from rpython.rlib.debug import fatalerror
 
 
 #________________________________________________________
@@ -192,6 +193,8 @@
     rawrefcount.create_link_pypy(w_obj, py_obj)
 
 
+w_marker_deallocating = W_Root()
+
 def from_ref(space, ref):
     """
     Finds the interpreter object corresponding to the given reference.  If the
@@ -202,7 +205,23 @@
         return None
     w_obj = rawrefcount.to_obj(W_Root, ref)
     if w_obj is not None:
-        return w_obj
+        if w_obj is not w_marker_deallocating:
+            return w_obj
+        fatalerror(
+            "*** Invalid usage of a dying CPython object ***\n"
+            "\n"
+            "cpyext, the emulation layer, detected that while it is calling\n"
+            "an object's tp_dealloc, the C code calls back a function that\n"
+            "tries to recreate the PyPy version of the object.  Usually it\n"
+            "means that tp_dealloc calls some general PyXxx() API.  It is\n"
+            "a dangerous and potentially buggy thing to do: even in CPython\n"
+            "the PyXxx() function could, in theory, cause a reference to the\n"
+            "object to be taken and stored somewhere, for an amount of time\n"
+            "exceeding tp_dealloc itself.  Afterwards, the object will be\n"
+            "freed, making that reference point to garbage.\n"
+            ">>> PyPy could contain some workaround to still work if\n"
+            "you are lucky, but it is not done so far; better fix the bug in\n"
+            "the CPython extension.")
 
     # This reference is not yet a real interpreter object.
     # Realize it.
@@ -233,7 +252,8 @@
 INTERPLEVEL_API['as_pyobj'] = as_pyobj
 
 def pyobj_has_w_obj(pyobj):
-    return rawrefcount.to_obj(W_Root, pyobj) is not None
+    w_obj = rawrefcount.to_obj(W_Root, pyobj)
+    return w_obj is not None and w_obj is not w_marker_deallocating
 INTERPLEVEL_API['pyobj_has_w_obj'] = staticmethod(pyobj_has_w_obj)
 
 
@@ -335,6 +355,7 @@
     pto = obj.c_ob_type
     #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \
     #      "'s type which is", rffi.charp2str(pto.c_tp_name)
+    rawrefcount.mark_deallocating(w_marker_deallocating, obj)
     generic_cpy_call(space, pto.c_tp_dealloc, obj)
 
 @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
diff --git a/pypy/module/cpyext/test/test_typeobject.py 
b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -142,7 +142,7 @@
         assert fuu2(u"abc").baz().escape()
         raises(TypeError, module.fooType.object_member.__get__, 1)
 
-    def test_multiple_inheritance(self):
+    def test_multiple_inheritance1(self):
         module = self.import_module(name='foo')
         obj = module.UnicodeSubtype(u'xyz')
         obj2 = module.UnicodeSubtype2()
@@ -422,7 +422,7 @@
         assert space.int_w(space.getattr(w_class, w_name)) == 1
         space.delitem(w_dict, w_name)
 
-    def test_multiple_inheritance(self, space, api):
+    def test_multiple_inheritance2(self, space, api):
         w_class = space.appexec([], """():
             class A(object):
                 pass
@@ -1167,3 +1167,38 @@
             __metaclass__ = FooType
         print repr(X)
         X()
+
+    def test_multiple_inheritance3(self):
+        module = self.import_extension('foo', [
+           ("new_obj", "METH_NOARGS",
+            '''
+                PyObject *obj;
+                PyTypeObject *Base1, *Base2, *Base12;
+                Base1 =  (PyTypeObject*)PyType_Type.tp_alloc(&PyType_Type, 0);
+                Base2 =  (PyTypeObject*)PyType_Type.tp_alloc(&PyType_Type, 0);
+                Base12 =  (PyTypeObject*)PyType_Type.tp_alloc(&PyType_Type, 0);
+                Base1->tp_name = "Base1";
+                Base2->tp_name = "Base2";
+                Base12->tp_name = "Base12";
+                Base1->tp_basicsize = sizeof(PyHeapTypeObject);
+                Base2->tp_basicsize = sizeof(PyHeapTypeObject);
+                Base12->tp_basicsize = sizeof(PyHeapTypeObject);
+                Base1->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 
Py_TPFLAGS_HEAPTYPE;
+                Base2->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 
Py_TPFLAGS_HEAPTYPE;
+                Base12->tp_flags = Py_TPFLAGS_DEFAULT;
+                Base12->tp_base = Base1;
+                Base12->tp_bases = PyTuple_Pack(2, Base1, Base2); 
+                Base12->tp_doc = "The Base12 type or object";
+                if (PyType_Ready(Base1) < 0) return NULL;
+                if (PyType_Ready(Base2) < 0) return NULL;
+                if (PyType_Ready(Base12) < 0) return NULL;
+                obj = PyObject_New(PyObject, Base12);
+                return obj;
+            '''
+            )])
+        obj = module.new_obj()
+        assert 'Base12' in str(obj)
+        assert type(obj).__doc__ == "The Base12 type or object"
+        assert obj.__doc__ == "The Base12 type or object"
+
+
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
@@ -327,6 +327,8 @@
         w_obj = W_PyCWrapperObject(space, pto, method_name, wrapper_func,
                 wrapper_func_kwds, doc, func_voidp, offset=offset)
         dict_w[method_name] = space.wrap(w_obj)
+    if pto.c_tp_doc:
+        dict_w['__doc__'] = space.newbytes(rffi.charp2str(pto.c_tp_doc))
     if pto.c_tp_new:
         add_tp_new_wrapper(space, dict_w, pto)
 
@@ -463,13 +465,17 @@
         convert_member_defs(space, dict_w, pto.c_tp_members, self)
 
         name = rffi.charp2str(pto.c_tp_name)
-        new_layout = (pto.c_tp_basicsize > rffi.sizeof(PyObject.TO) or
-                      pto.c_tp_itemsize > 0)
+        flag_heaptype = pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE
+        if flag_heaptype:
+            minsize = rffi.sizeof(PyHeapTypeObject.TO)
+        else:
+            minsize = rffi.sizeof(PyObject.TO)
+        new_layout = (pto.c_tp_basicsize > minsize or pto.c_tp_itemsize > 0)
 
         W_TypeObject.__init__(self, space, name,
             bases_w or [space.w_object], dict_w, force_new_layout=new_layout)
         self.flag_cpytype = True
-        self.flag_heaptype = pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE
+        self.flag_heaptype = flag_heaptype
         # if a sequence or a mapping, then set the flag to force it
         if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item:
             self.flag_map_or_seq = 'S'
diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py
--- a/pypy/module/gc/interp_gc.py
+++ b/pypy/module/gc/interp_gc.py
@@ -14,7 +14,28 @@
     cache.clear()
     cache = space.fromcache(MapAttrCache)
     cache.clear()
+
     rgc.collect()
+
+    # if we are running in gc.disable() mode but gc.collect() is called,
+    # we should still call the finalizers now.  We do this as an attempt
+    # to get closer to CPython's behavior: in Py3.5 some tests
+    # specifically rely on that.  This is similar to how, in CPython, an
+    # explicit gc.collect() will invoke finalizers from cycles and fully
+    # ignore the gc.disable() mode.
+    temp_reenable = not space.user_del_action.enabled_at_app_level
+    if temp_reenable:
+        enable_finalizers(space)
+    try:
+        # fetch the pending finalizers from the queue, where they are
+        # likely to have been added by rgc.collect() above, and actually
+        # run them now.  This forces them to run before this function
+        # returns, and also always in the enable_finalizers() mode.
+        space.user_del_action._run_finalizers()
+    finally:
+        if temp_reenable:
+            disable_finalizers(space)
+
     return space.wrap(0)
 
 def enable(space):
diff --git a/pypy/module/gc/test/test_gc.py b/pypy/module/gc/test/test_gc.py
--- a/pypy/module/gc/test/test_gc.py
+++ b/pypy/module/gc/test/test_gc.py
@@ -70,6 +70,19 @@
         gc.enable()
         assert gc.isenabled()
 
+    def test_gc_collect_overrides_gc_disable(self):
+        import gc
+        deleted = []
+        class X(object):
+            def __del__(self):
+                deleted.append(1)
+        assert gc.isenabled()
+        gc.disable()
+        X()
+        gc.collect()
+        assert deleted == [1]
+        gc.enable()
+
 
 class AppTestGcDumpHeap(object):
     pytestmark = py.test.mark.xfail(run=False)
diff --git a/pypy/module/micronumpy/test/test_zjit.py 
b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -374,17 +374,7 @@
     def test_sum(self):
         result = self.run("sum")
         assert result == sum(range(30))
-        self.check_vectorized(1, 1)
-
-    def define_sum():
-        return """
-        a = |30|
-        sum(a)
-        """
-    def test_sum(self):
-        result = self.run("sum")
-        assert result == sum(range(30))
-        self.check_vectorized(1, 1)
+        self.check_vectorized(1, 0)
 
     def define_sum_int():
         return """
@@ -408,7 +398,7 @@
     def test_sum_multi(self):
         result = self.run("sum_multi")
         assert result == sum(range(30)) + sum(range(60))
-        self.check_vectorized(1, 1)
+        self.check_vectorized(1, 0)
 
     def define_sum_float_to_int16():
         return """
@@ -490,7 +480,7 @@
         assert retval == sum(range(1,11))
         # check that we got only one loop
         assert len(get_stats().loops) == 1
-        self.check_vectorized(2, 1)
+        self.check_vectorized(2, 0)
 
     def test_reduce_axis_compile_only_once(self):
         self.compile_graph()
@@ -501,7 +491,7 @@
         retval = self.interp.eval_graph(self.graph, [i])
         # check that we got only one loop
         assert len(get_stats().loops) == 1
-        self.check_vectorized(3, 1)
+        self.check_vectorized(3, 0)
 
     def define_prod():
         return """
@@ -518,12 +508,10 @@
     def test_prod(self):
         result = self.run("prod")
         assert int(result) == 576
-        self.check_vectorized(1, 1)
 
     def test_prod_zero(self):
         result = self.run("prod_zero")
         assert int(result) == 0
-        self.check_vectorized(1, 1)
 
 
     def define_max():
diff --git a/pypy/module/posix/interp_posix.py 
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -307,7 +307,8 @@
     def __init__(self, space):
         self.stat_float_times = True
 
-def stat_float_times(space, w_value=None):
+@unwrap_spec(newval=int)
+def stat_float_times(space, newval=-1):
     """stat_float_times([newval]) -> oldval
 
 Determine whether os.[lf]stat represents time stamps as float objects.
@@ -317,10 +318,10 @@
 """
     state = space.fromcache(StatState)
 
-    if w_value is None:
+    if newval == -1:
         return space.wrap(state.stat_float_times)
     else:
-        state.stat_float_times = space.bool_w(w_value)
+        state.stat_float_times = (newval != 0)
 
 
 @unwrap_spec(fd=c_int)
diff --git a/pypy/module/posix/test/test_posix2.py 
b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -159,11 +159,14 @@
             st = posix.stat(path)
             assert isinstance(st.st_mtime, float)
             assert st[7] == int(st.st_atime)
+            assert posix.stat_float_times(-1) is True
 
             posix.stat_float_times(False)
             st = posix.stat(path)
             assert isinstance(st.st_mtime, (int, long))
             assert st[7] == st.st_atime
+            assert posix.stat_float_times(-1) is False
+
         finally:
             posix.stat_float_times(current)
 
diff --git a/pypy/module/pyexpat/interp_pyexpat.py 
b/pypy/module/pyexpat/interp_pyexpat.py
--- a/pypy/module/pyexpat/interp_pyexpat.py
+++ b/pypy/module/pyexpat/interp_pyexpat.py
@@ -3,11 +3,11 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
 from pypy.interpreter.error import OperationError, oefmt
 from rpython.rlib import rgc, jit
+from rpython.rlib.objectmodel import specialize
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rtyper.tool import rffi_platform
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.translator.platform import platform
-from rpython.rlib.objectmodel import specialize
 
 import sys
 import weakref
diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py 
b/pypy/module/pypyjit/test_pypy_c/test_containers.py
--- a/pypy/module/pypyjit/test_pypy_c/test_containers.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py
@@ -67,7 +67,8 @@
             p10 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i5, 
descr=<Callr . i EF=3>)
             guard_no_exception(descr=...)
             guard_nonnull(p10, descr=...)
-            i12 = call_i(ConstClass(_ll_strhash__rpy_stringPtr), p10, 
descr=<Calli . r EF=0>)
+            i99 = strhash(p10)
+            i12 = cond_call_value_i(i99, 
ConstClass(_ll_strhash__rpy_stringPtr), p10, descr=<Calli . r EF=2>)
             p13 = new(descr=...)
             p15 = new_array_clear(16, descr=<ArrayU 1>)
             {{{
@@ -86,6 +87,7 @@
             call_n(ConstClass(_ll_dict_setitem_lookup_done_trampoline), p13, 
p10, p20, i12, i17, descr=<Callv 0 rrrii EF=5>)
             setfield_gc(p20, i5, descr=<FieldS .*W_IntObject.inst_intval .* 
pure>)
             guard_no_exception(descr=...)
+            i98 = strhash(p10)
             i23 = call_i(ConstClass(ll_call_lookup_function), p13, p10, i12, 
0, descr=<Calli . rrii EF=5 OS=4>)
             guard_no_exception(descr=...)
             i27 = int_lt(i23, 0)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py 
b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
--- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
@@ -75,8 +75,6 @@
 
     arith_comb = [
         ('sum','int', 1742, 1742, 1),
-        ('sum','float', 2581, 2581, 1),
-        ('prod','float', 1, 3178, 1),
         ('prod','int', 1, 3178, 1),
         ('any','int', 1, 2239, 1),
         ('any','int', 0, 4912, 0),
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py 
b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -387,7 +387,8 @@
     def test_long_comparison(self):
         def main(n):
             while n:
-                12345L > 123L  # ID: long_op
+                x = 12345L
+                x > 123L  # ID: long_op
                 n -= 1
 
         log = self.run(main, [300])
diff --git a/pypy/module/select/interp_epoll.py 
b/pypy/module/select/interp_epoll.py
--- a/pypy/module/select/interp_epoll.py
+++ b/pypy/module/select/interp_epoll.py
@@ -79,6 +79,7 @@
 
 class W_Epoll(W_Root):
     def __init__(self, space, epfd):
+        self.space = space
         self.epfd = epfd
         self.register_finalizer(space)
 
@@ -113,6 +114,7 @@
         if not self.get_closed():
             socketclose(self.epfd)
             self.epfd = -1
+            self.may_unregister_rpython_finalizer(self.space)
 
     def epoll_ctl(self, space, ctl, w_fd, eventmask, ignore_ebadf=False):
         fd = space.c_filedescriptor_w(w_fd)
diff --git a/pypy/module/select/interp_kqueue.py 
b/pypy/module/select/interp_kqueue.py
--- a/pypy/module/select/interp_kqueue.py
+++ b/pypy/module/select/interp_kqueue.py
@@ -108,6 +108,7 @@
 
 class W_Kqueue(W_Root):
     def __init__(self, space, kqfd):
+        self.space = space
         self.kqfd = kqfd
         self.register_finalizer(space)
 
@@ -132,6 +133,7 @@
             kqfd = self.kqfd
             self.kqfd = -1
             socketclose_no_errno(kqfd)
+            self.may_unregister_rpython_finalizer(self.space)
 
     def check_closed(self, space):
         if self.get_closed():
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
@@ -494,3 +494,15 @@
     def test_negative_array_size(self):
         ffi = FFI()
         py.test.raises(ValueError, ffi.cast, "int[-5]", 0)
+
+    def test_cannot_instantiate_manually(self):
+        ffi = FFI()
+        ct = type(ffi.typeof("void *"))
+        py.test.raises(TypeError, ct)
+        py.test.raises(TypeError, ct, ffi.NULL)
+        for cd in [type(ffi.cast("void *", 0)),
+                   type(ffi.new("char[]", 3)),
+                   type(ffi.gc(ffi.NULL, lambda x: None))]:
+            py.test.raises(TypeError, cd)
+            py.test.raises(TypeError, cd, ffi.NULL)
+            py.test.raises(TypeError, cd, ffi.typeof("void *"))
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
@@ -361,7 +361,8 @@
         retries += 1
         assert retries <= 5
         import gc; gc.collect()
-    assert seen == [40, 40, raw1, raw2]
+    assert (seen == [40, 40, raw1, raw2] or
+            seen == [40, 40, raw2, raw1])
     assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
     assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
 
diff --git a/pypy/module/test_lib_pypy/test_os_wait.py 
b/pypy/module/test_lib_pypy/test_os_wait.py
--- a/pypy/module/test_lib_pypy/test_os_wait.py
+++ b/pypy/module/test_lib_pypy/test_os_wait.py
@@ -34,3 +34,7 @@
         assert os.WEXITSTATUS(status) == exit_status
         assert isinstance(rusage.ru_utime, float)
         assert isinstance(rusage.ru_maxrss, int)
+
+def test_errors():
+    py.test.raises(OSError, _pypy_wait.wait3, -999)
+    py.test.raises(OSError, _pypy_wait.wait4, -999, -999)
diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py
--- a/pypy/module/zlib/interp_zlib.py
+++ b/pypy/module/zlib/interp_zlib.py
@@ -202,6 +202,7 @@
                 if mode == rzlib.Z_FINISH:    # release the data structures now
                     rzlib.deflateEnd(self.stream)
                     self.stream = rzlib.null_stream
+                    self.may_unregister_rpython_finalizer(space)
             finally:
                 self.unlock()
         except rzlib.RZlibError as e:
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -853,7 +853,7 @@
                 if not e.match(space, space.w_TypeError):
                     raise
                 raise oefmt(space.w_TypeError,
-                            "%(specialname)s returned non-%(targetname)s "
+                            "%(specialname)s returned non-string "
                             "(type '%%T')", w_result)
             else:
                 # re-wrap the result as a real string
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -66,15 +66,16 @@
 class W_MyType(W_MyObject):
     name = "foobar"
     flag_map_or_seq = '?'
+    hasuserdel = False
 
     def __init__(self):
         self.mro_w = [w_some_obj(), w_some_obj()]
         self.dict_w = {'__str__': w_some_obj()}
+        self.hasuserdel = True
 
     def get_module(self):
         return w_some_obj()
 
-
     def getname(self, space):
         return self.name
 
@@ -202,6 +203,7 @@
     def newunicode(self, x):
         return w_some_obj()
 
+    @specialize.argtype(1)
     def wrap(self, x):
         if not we_are_translated():
             if isinstance(x, gateway.interp2app):
@@ -215,7 +217,6 @@
                 return w_some_obj()
             self._wrap_not_rpython(x)
         return w_some_obj()
-    wrap._annspecialcase_ = "specialize:argtype(1)"
 
     def _wrap_not_rpython(self, x):
         "NOT_RPYTHON"
@@ -305,10 +306,10 @@
         is_root(w_complex)
         return 1.1, 2.2
 
+    @specialize.arg(1)
     def allocate_instance(self, cls, w_subtype):
         is_root(w_subtype)
         return instantiate(cls)
-    allocate_instance._annspecialcase_ = "specialize:arg(1)"
 
     def decode_index(self, w_index_or_slice, seqlength):
         is_root(w_index_or_slice)
diff --git a/pypy/objspace/fake/test/test_checkmodule.py 
b/pypy/objspace/fake/test/test_checkmodule.py
--- a/pypy/objspace/fake/test/test_checkmodule.py
+++ b/pypy/objspace/fake/test/test_checkmodule.py
@@ -9,9 +9,9 @@
 
 def make_checker():
     check = []
+    @specialize.memo()
     def see():
         check.append(True)
-    see._annspecialcase_ = 'specialize:memo'
     return see, check
 
 def test_wrap_interp2app():
diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py
--- a/pypy/objspace/std/formatting.py
+++ b/pypy/objspace/std/formatting.py
@@ -2,12 +2,12 @@
 import sys
 
 from rpython.rlib import jit
+from rpython.rlib.objectmodel import specialize
 from rpython.rlib.rarithmetic import INT_MAX
 from rpython.rlib.rfloat import DTSF_ALT, formatd, isnan, isinf
 from rpython.rlib.rstring import StringBuilder, UnicodeBuilder
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.tool.sourcetools import func_with_new_name
-from rpython.rlib.objectmodel import specialize
 
 from pypy.interpreter.error import OperationError, oefmt
 
diff --git a/pypy/objspace/test/test_descroperation.py 
b/pypy/objspace/test/test_descroperation.py
--- a/pypy/objspace/test/test_descroperation.py
+++ b/pypy/objspace/test/test_descroperation.py
@@ -315,7 +315,8 @@
                 assert operate(A()) == "world" * n
             assert type(operate(A())) is str
             answer = 42
-            raises(TypeError, operate, A())
+            excinfo = raises(TypeError, operate, A())
+            assert "returned non-string (type 'int')" in str(excinfo.value)
 
     def test_missing_getattribute(self):
         class X(object):
diff --git a/pypy/tool/cpyext/extbuild.py b/pypy/tool/cpyext/extbuild.py
--- a/pypy/tool/cpyext/extbuild.py
+++ b/pypy/tool/cpyext/extbuild.py
@@ -204,6 +204,10 @@
         pass
     from distutils.ccompiler import new_compiler
     from distutils import sysconfig
+
+    # XXX for Darwin running old versions of CPython 2.7.x
+    sysconfig.get_config_vars()
+
     compiler = new_compiler(force=1)
     sysconfig.customize_compiler(compiler)  # XXX
     objects = []
diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -2,6 +2,7 @@
 
 import types
 from collections import defaultdict
+from contextlib import contextmanager
 
 from rpython.tool.ansi_print import AnsiLogger
 from rpython.tool.pairtype import pair
@@ -83,22 +84,17 @@
         annmodel.TLS.check_str_without_nul = (
             self.translator.config.translation.check_str_without_nul)
 
-        flowgraph, inputs_s = self.get_call_parameters(function, args_s, 
policy)
+        with self.using_policy(policy):
+            flowgraph, inputs_s = self.get_call_parameters(function, args_s)
 
         if main_entry_point:
             self.translator.entry_point_graph = flowgraph
         return self.build_graph_types(flowgraph, inputs_s, 
complete_now=complete_now)
 
-    def get_call_parameters(self, function, args_s, policy):
-        desc = self.bookkeeper.getdesc(function)
-        prevpolicy = self.policy
-        self.policy = policy
-        self.bookkeeper.enter(None)
-        try:
+    def get_call_parameters(self, function, args_s):
+        with self.bookkeeper.at_position(None):
+            desc = self.bookkeeper.getdesc(function)
             return desc.get_call_parameters(args_s)
-        finally:
-            self.bookkeeper.leave()
-            self.policy = prevpolicy
 
     def annotate_helper(self, function, args_s, policy=None):
         if policy is None:
@@ -107,21 +103,29 @@
             # XXX hack
             annmodel.TLS.check_str_without_nul = (
                 self.translator.config.translation.check_str_without_nul)
-        graph, inputcells = self.get_call_parameters(function, args_s, policy)
-        self.build_graph_types(graph, inputcells, complete_now=False)
-        self.complete_helpers(policy)
+        with self.using_policy(policy):
+            graph, inputcells = self.get_call_parameters(function, args_s)
+            self.build_graph_types(graph, inputcells, complete_now=False)
+            self.complete_helpers()
         return graph
 
-    def complete_helpers(self, policy):
-        saved = self.policy, self.added_blocks
-        self.policy = policy
+    def complete_helpers(self):
+        saved = self.added_blocks
+        self.added_blocks = {}
         try:
-            self.added_blocks = {}
             self.complete()
             # invoke annotation simplifications for the new blocks
             self.simplify(block_subset=self.added_blocks)
         finally:
-            self.policy, self.added_blocks = saved
+            self.added_blocks = saved
+
+    @contextmanager
+    def using_policy(self, policy):
+        """A context manager that temporarily replaces the annotator policy"""
+        old_policy = self.policy
+        self.policy = policy
+        yield
+        self.policy = old_policy
 
     def build_graph_types(self, flowgraph, inputcells, complete_now=True):
         checkgraph(flowgraph)
diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -9,6 +9,7 @@
 from collections import OrderedDict
 
 from rpython.flowspace.model import Constant
+from rpython.flowspace.bytecode import cpython_code_signature
 from rpython.annotator.model import (
     SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance,
     SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint,
@@ -21,6 +22,7 @@
 from rpython.annotator import description
 from rpython.annotator.signature import annotationoftype
 from rpython.annotator.argument import simple_args
+from rpython.annotator.specialize import memo
 from rpython.rlib.objectmodel import r_dict, r_ordereddict, Symbolic
 from rpython.tool.algo.unionfind import UnionFind
 from rpython.rtyper import extregistry
@@ -358,7 +360,7 @@
             return self.descs[obj_key]
         except KeyError:
             if isinstance(pyobj, types.FunctionType):
-                result = description.FunctionDesc(self, pyobj)
+                result = self.newfuncdesc(pyobj)
             elif isinstance(pyobj, (type, types.ClassType)):
                 if pyobj is object:
                     raise Exception("ClassDesc for object not supported")
@@ -403,6 +405,23 @@
             self.descs[obj_key] = result
             return result
 
+    def newfuncdesc(self, pyfunc):
+        name = pyfunc.__name__
+        if hasattr(pyfunc, '_generator_next_method_of_'):
+            from rpython.flowspace.argument import Signature
+            signature = Signature(['entry'])     # haaaaaack
+            defaults = ()
+        else:
+            signature = cpython_code_signature(pyfunc.func_code)
+            defaults = pyfunc.func_defaults
+        # get the specializer based on the tag of the 'pyobj'
+        # (if any), according to the current policy
+        tag = getattr(pyfunc, '_annspecialcase_', None)
+        specializer = self.annotator.policy.get_specializer(tag)
+        if specializer is memo:
+            return description.MemoDesc(self, pyfunc, name, signature, 
defaults, specializer)
+        return description.FunctionDesc(self, pyfunc, name, signature, 
defaults, specializer)
+
     def getfrozen(self, pyobj):
         return description.FrozenDesc(self, pyobj)
 
diff --git a/rpython/annotator/classdesc.py b/rpython/annotator/classdesc.py
--- a/rpython/annotator/classdesc.py
+++ b/rpython/annotator/classdesc.py
@@ -608,7 +608,7 @@
             if mixin:
                 # make a new copy of the FunctionDesc for this class,
                 # but don't specialize further for all subclasses
-                funcdesc = FunctionDesc(self.bookkeeper, value)
+                funcdesc = self.bookkeeper.newfuncdesc(value)
                 self.classdict[name] = funcdesc
                 return
             # NB. if value is, say, AssertionError.__init__, then we
diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py
--- a/rpython/annotator/description.py
+++ b/rpython/annotator/description.py
@@ -3,11 +3,10 @@
 from rpython.annotator.signature import (
     enforce_signature_args, enforce_signature_return, finish_type)
 from rpython.flowspace.model import FunctionGraph
-from rpython.flowspace.bytecode import cpython_code_signature
 from rpython.annotator.argument import rawshape, ArgErr, simple_args
 from rpython.tool.sourcetools import valid_identifier
 from rpython.tool.pairtype import extendabletype
-from rpython.annotator.model import AnnotatorError, s_ImpossibleValue
+from rpython.annotator.model import AnnotatorError, s_ImpossibleValue, unionof
 
 class CallFamily(object):
     """A family of Desc objects that could be called from common call sites.
@@ -117,7 +116,6 @@
         self.s_value = s_ImpossibleValue    # union of possible values
 
     def update(self, other):
-        from rpython.annotator.model import unionof
         self.descs.update(other.descs)
         self.read_locations.update(other.read_locations)
         self.s_value = unionof(self.s_value, other.s_value)
@@ -192,24 +190,12 @@
 class FunctionDesc(Desc):
     knowntype = types.FunctionType
 
-    def __init__(self, bookkeeper, pyobj=None,
-                 name=None, signature=None, defaults=None,
+    def __init__(self, bookkeeper, pyobj, name, signature, defaults,
                  specializer=None):
         super(FunctionDesc, self).__init__(bookkeeper, pyobj)
-        if name is None:
-            name = pyobj.func_name
-        if signature is None:
-            if hasattr(pyobj, '_generator_next_method_of_'):
-                from rpython.flowspace.argument import Signature
-                signature = Signature(['entry'])     # haaaaaack
-                defaults = ()
-            else:
-                signature = cpython_code_signature(pyobj.func_code)
-        if defaults is None:
-            defaults = pyobj.func_defaults
         self.name = name
         self.signature = signature
-        self.defaults = defaults or ()
+        self.defaults = defaults if defaults is not None else ()
         # 'specializer' is a function with the following signature:
         #      specializer(funcdesc, args_s) => graph
         #                                 or => s_result (overridden/memo 
cases)
@@ -288,12 +274,43 @@
                 getattr(self.bookkeeper, "position_key", None) is not None):
             _, block, i = self.bookkeeper.position_key
             op = block.operations[i]
-        if self.specializer is None:
-            # get the specializer based on the tag of the 'pyobj'
-            # (if any), according to the current policy
-            tag = getattr(self.pyobj, '_annspecialcase_', None)
-            policy = self.bookkeeper.annotator.policy
-            self.specializer = policy.get_specializer(tag)
+        self.normalize_args(inputcells)
+        if getattr(self.pyobj, '_annspecialcase_', 
'').endswith("call_location"):
+            return self.specializer(self, inputcells, op)
+        else:
+            return self.specializer(self, inputcells)
+
+    def pycall(self, whence, args, s_previous_result, op=None):
+        inputcells = self.parse_arguments(args)
+        graph = self.specialize(inputcells, op)
+        assert isinstance(graph, FunctionGraph)
+        # if that graph has a different signature, we need to re-parse
+        # the arguments.
+        # recreate the args object because inputcells may have been changed
+        new_args = args.unmatch_signature(self.signature, inputcells)
+        inputcells = self.parse_arguments(new_args, graph)
+        annotator = self.bookkeeper.annotator
+        result = annotator.recursivecall(graph, whence, inputcells)
+        signature = getattr(self.pyobj, '_signature_', None)
+        if signature:
+            sigresult = enforce_signature_return(self, signature[1], result)
+            if sigresult is not None:
+                annotator.addpendingblock(
+                    graph, graph.returnblock, [sigresult])
+                result = sigresult
+        # Some specializations may break the invariant of returning
+        # annotations that are always more general than the previous time.
+        # We restore it here:
+        result = unionof(result, s_previous_result)
+        return result
+
+    def normalize_args(self, inputs_s):
+        """
+        Canonicalize argument annotations into the exact parameter
+        annotations of a specific specialized graph.
+
+        Note: this method has no return value but mutates its argument instead.
+        """
         enforceargs = getattr(self.pyobj, '_annenforceargs_', None)
         signature = getattr(self.pyobj, '_signature_', None)
         if enforceargs and signature:
@@ -304,39 +321,9 @@
                 from rpython.annotator.signature import Sig
                 enforceargs = Sig(*enforceargs)
                 self.pyobj._annenforceargs_ = enforceargs
-            enforceargs(self, inputcells)  # can modify inputcells in-place
+            enforceargs(self, inputs_s)  # can modify inputs_s in-place
         if signature:
-            enforce_signature_args(self, signature[0], inputcells)  # mutates 
inputcells
-        if getattr(self.pyobj, '_annspecialcase_', 
'').endswith("call_location"):
-            return self.specializer(self, inputcells, op)
-        else:
-            return self.specializer(self, inputcells)
-
-    def pycall(self, whence, args, s_previous_result, op=None):
-        inputcells = self.parse_arguments(args)
-        result = self.specialize(inputcells, op)
-        if isinstance(result, FunctionGraph):
-            graph = result         # common case
-            annotator = self.bookkeeper.annotator
-            # if that graph has a different signature, we need to re-parse
-            # the arguments.
-            # recreate the args object because inputcells may have been changed
-            new_args = args.unmatch_signature(self.signature, inputcells)
-            inputcells = self.parse_arguments(new_args, graph)
-            result = annotator.recursivecall(graph, whence, inputcells)
-            signature = getattr(self.pyobj, '_signature_', None)
-            if signature:
-                sigresult = enforce_signature_return(self, signature[1], 
result)
-                if sigresult is not None:
-                    annotator.addpendingblock(
-                        graph, graph.returnblock, [sigresult])
-                    result = sigresult
-        # Some specializations may break the invariant of returning
-        # annotations that are always more general than the previous time.
-        # We restore it here:
-        from rpython.annotator.model import unionof
-        result = unionof(result, s_previous_result)
-        return result
+            enforce_signature_args(self, signature[0], inputs_s)  # mutates 
inputs_s
 
     def get_graph(self, args, op):
         inputs_s = self.parse_arguments(args)
@@ -405,6 +392,16 @@
 
             return s_sigs
 
+class MemoDesc(FunctionDesc):
+    def pycall(self, whence, args, s_previous_result, op=None):
+        inputcells = self.parse_arguments(args)
+        s_result = self.specialize(inputcells, op)
+        if isinstance(s_result, FunctionGraph):
+            s_result = s_result.getreturnvar().annotation
+        s_result = unionof(s_result, s_previous_result)
+        return s_result
+
+
 class MethodDesc(Desc):
     knowntype = types.MethodType
 
diff --git a/rpython/annotator/specialize.py b/rpython/annotator/specialize.py
--- a/rpython/annotator/specialize.py
+++ b/rpython/annotator/specialize.py
@@ -3,11 +3,13 @@
 
 from rpython.tool.sourcetools import func_with_new_name
 from rpython.tool.algo.unionfind import UnionFind
-from rpython.flowspace.model import Block, Link, Variable, SpaceOperation
+from rpython.flowspace.model import Block, Link, Variable
 from rpython.flowspace.model import checkgraph
 from rpython.flowspace.operation import op
 from rpython.annotator import model as annmodel
 from rpython.flowspace.argument import Signature
+from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool
+from rpython.annotator.model import unionof
 
 def flatten_star_args(funcdesc, args_s):
     argnames, vararg, kwarg = funcdesc.signature
@@ -127,7 +129,6 @@
     def finish(self):
         if self.do_not_process:
             return
-        from rpython.annotator.model import unionof
         assert self.graph is None, "MemoTable already finished"
         # list of which argument positions can take more than one value
         example_args, example_value = self.table.iteritems().next()
@@ -246,34 +247,36 @@
             args_s.append(unionof(*values_s))
         annotator.addpendinggraph(self.graph, args_s)
 
+def all_values(s):
+    """Return the exhaustive list of possible values matching annotation `s`.
 
-def memo(funcdesc, arglist_s):
-    from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool
-    from rpython.annotator.model import unionof
+    Raises `AnnotatorError` if no such (reasonably small) finite list exists.
+    """
+    if s.is_constant():
+        return [s.const]
+    elif isinstance(s, SomePBC):
+        values = []
+        assert not s.can_be_None, "memo call: cannot mix None and PBCs"
+        for desc in s.descriptions:
+            if desc.pyobj is None:
+                raise annmodel.AnnotatorError(
+                    "memo call with a class or PBC that has no "
+                    "corresponding Python object (%r)" % (desc,))
+            values.append(desc.pyobj)
+        return values
+    elif isinstance(s, SomeImpossibleValue):
+        return []
+    elif isinstance(s, SomeBool):
+        return [False, True]
+    else:
+        raise annmodel.AnnotatorError("memo call: argument must be a class "
+                                        "or a frozen PBC, got %r" % (s,))
+
+def memo(funcdesc, args_s):
     # call the function now, and collect possible results
-    argvalues = []
-    for s in arglist_s:
-        if s.is_constant():
-            values = [s.const]
-        elif isinstance(s, SomePBC):
-            values = []
-            assert not s.can_be_None, "memo call: cannot mix None and PBCs"
-            for desc in s.descriptions:
-                if desc.pyobj is None:
-                    raise annmodel.AnnotatorError(
-                        "memo call with a class or PBC that has no "
-                        "corresponding Python object (%r)" % (desc,))
-                values.append(desc.pyobj)
-        elif isinstance(s, SomeImpossibleValue):
-            return s    # we will probably get more possible args later
-        elif isinstance(s, SomeBool):
-            values = [False, True]
-        else:
-            raise annmodel.AnnotatorError("memo call: argument must be a class 
"
-                                          "or a frozen PBC, got %r" % (s,))
-        argvalues.append(values)
+
     # the list of all possible tuples of arguments to give to the memo function
-    possiblevalues = cartesian_product(argvalues)
+    possiblevalues = cartesian_product([all_values(s_arg) for s_arg in args_s])
 
     # a MemoTable factory -- one MemoTable per family of arguments that can
     # be called together, merged via a UnionFind.
diff --git a/rpython/jit/backend/arm/assembler.py 
b/rpython/jit/backend/arm/assembler.py
--- a/rpython/jit/backend/arm/assembler.py
+++ b/rpython/jit/backend/arm/assembler.py
@@ -268,12 +268,15 @@
         """
         mc = InstrBuilder(self.cpu.cpuinfo.arch_version)
         #
-        self._push_all_regs_to_jitframe(mc, [], self.cpu.supports_floats, 
callee_only)
+        # We don't save/restore r4; instead the return value (if any)
+        # will be stored there.
+        self._push_all_regs_to_jitframe(mc, [r.r4], self.cpu.supports_floats, 
callee_only)
         ## args are in their respective positions
         mc.PUSH([r.ip.value, r.lr.value])
         mc.BLX(r.r4.value)
+        mc.MOV_rr(r.r4.value, r.r0.value)
         self._reload_frame_if_necessary(mc)
-        self._pop_all_regs_from_jitframe(mc, [], supports_floats,
+        self._pop_all_regs_from_jitframe(mc, [r.r4], supports_floats,
                                       callee_only)
         # return
         mc.POP([r.ip.value, r.pc.value])
diff --git a/rpython/jit/backend/arm/opassembler.py 
b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -357,7 +357,13 @@
         return fcond
 
     def emit_op_cond_call(self, op, arglocs, regalloc, fcond):
-        [call_loc] = arglocs
+        call_loc = arglocs[0]
+        if len(arglocs) == 2:
+            res_loc = arglocs[1]     # cond_call_value
+        else:
+            res_loc = None           # cond_call
+        # useless to list res_loc in the gcmap, because if the call is
+        # done it means res_loc was initially NULL
         gcmap = regalloc.get_gcmap([call_loc])
 
         assert call_loc is r.r4
@@ -378,8 +384,13 @@
                 floats = True
         cond_call_adr = self.cond_call_slowpath[floats * 2 + callee_only]
         self.mc.BL(cond_call_adr)
+        # if this is a COND_CALL_VALUE, we need to move the result in place
+        # from its current location (which is, unusually, in r4: see
+        # cond_call_slowpath)
+        if res_loc is not None and res_loc is not r.r4:
+            self.mc.MOV_rr(res_loc.value, r.r4.value)
+        #
         self.pop_gcmap(self.mc)
-        # never any result value
         cond = c.get_opposite_of(self.guard_success_cc)
         self.guard_success_cc = c.cond_none
         pmc = OverwritingBuilder(self.mc, jmp_adr, WORD)
@@ -389,6 +400,9 @@
         self.previous_cond_call_jcond = jmp_adr, cond
         return fcond
 
+    emit_op_cond_call_value_i = emit_op_cond_call
+    emit_op_cond_call_value_r = emit_op_cond_call
+
     def emit_op_jump(self, op, arglocs, regalloc, fcond):
         target_token = op.getdescr()
         assert isinstance(target_token, TargetToken)
diff --git a/rpython/jit/backend/arm/regalloc.py 
b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -1004,7 +1004,6 @@
     def prepare_op_cond_call(self, op, fcond):
         # XXX don't force the arguments to be loaded in specific
         # locations before knowing if we can take the fast path
-        # XXX add cond_call_value support
         assert 2 <= op.numargs() <= 4 + 2
         tmpreg = self.get_scratch_reg(INT, selected_reg=r.r4)
         v = op.getarg(1)
@@ -1017,8 +1016,33 @@
             arg = op.getarg(i)
             self.make_sure_var_in_reg(arg, args_so_far, selected_reg=reg)
             args_so_far.append(arg)
-        self.load_condition_into_cc(op.getarg(0))
-        return [tmpreg]
+
+        if op.type == 'v':
+            # a plain COND_CALL.  Calls the function when args[0] is
+            # true.  Often used just after a comparison operation.
+            self.load_condition_into_cc(op.getarg(0))
+            return [tmpreg]
+        else:
+            # COND_CALL_VALUE_I/R.  Calls the function when args[0]
+            # is equal to 0 or NULL.  Returns the result from the
+            # function call if done, or args[0] if it was not 0/NULL.
+            # Implemented by forcing the result to live in the same
+            # register as args[0], and overwriting it if we really do
+            # the call.
+
+            # Load the register for the result.  Possibly reuse 'args[0]'.
+            # But the old value of args[0], if it survives, is first
+            # spilled away.  We can't overwrite any of op.args[2:] here.
+            args = op.getarglist()
+            resloc = self.rm.force_result_in_reg(op, args[0],
+                                                 forbidden_vars=args[2:])
+            # Test the register for the result.
+            self.assembler.mc.CMP_ri(resloc.value, 0)
+            self.assembler.guard_success_cc = c.EQ
+            return [tmpreg, resloc]
+
+    prepare_op_cond_call_value_i = prepare_op_cond_call
+    prepare_op_cond_call_value_r = prepare_op_cond_call
 
     def prepare_op_force_token(self, op, fcond):
         # XXX for now we return a regular reg
diff --git a/rpython/jit/backend/llgraph/runner.py 
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -15,11 +15,12 @@
 from rpython.rtyper.llinterp import LLInterpreter, LLException
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
 from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rtyper.annlowlevel import hlstr, hlunicode
 from rpython.rtyper import rclass
 
 from rpython.rlib.clibffi import FFI_DEFAULT_ABI
 from rpython.rlib.rarithmetic import ovfcheck, r_uint, r_ulonglong, intmask
-from rpython.rlib.objectmodel import Symbolic
+from rpython.rlib.objectmodel import Symbolic, compute_hash
 
 class LLAsmInfo(object):
     def __init__(self, lltrace):
@@ -326,7 +327,6 @@
     supports_longlong = r_uint is not r_ulonglong
     supports_singlefloats = True
     supports_guard_gc_type = True
-    supports_cond_call_value = True
     translate_support_code = False
     is_llgraph = True
     vector_ext = VectorExt()
@@ -789,6 +789,10 @@
         assert 0 <= dststart <= dststart + length <= len(dst.chars)
         rstr.copy_string_contents(src, dst, srcstart, dststart, length)
 
+    def bh_strhash(self, s):
+        lls = s._obj.container
+        return compute_hash(hlstr(lls._as_ptr()))
+
     def bh_newunicode(self, length):
         return lltype.cast_opaque_ptr(llmemory.GCREF,
                                       lltype.malloc(rstr.UNICODE, length,
@@ -811,6 +815,10 @@
         assert 0 <= dststart <= dststart + length <= len(dst.chars)
         rstr.copy_unicode_contents(src, dst, srcstart, dststart, length)
 
+    def bh_unicodehash(self, s):
+        lls = s._obj.container
+        return compute_hash(hlunicode(lls._as_ptr()))
+
     def bh_new(self, sizedescr):
         return lltype.cast_opaque_ptr(llmemory.GCREF,
                                       lltype.malloc(sizedescr.S, zero=True))
@@ -1120,7 +1128,7 @@
                 value = sum(value)
             elif info.accum_operation == '*':
                 def prod(acc, x): return acc * x
-                value = reduce(prod, value, 1)
+                value = reduce(prod, value, 1.0)
             else:
                 raise NotImplementedError("accum operator in fail guard")
             values[i] = value
diff --git a/rpython/jit/backend/llsupport/llmodel.py 
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -3,8 +3,9 @@
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.llinterp import LLInterpreter
 from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator
+from rpython.rtyper.annlowlevel import hlstr, hlunicode
 from rpython.rtyper.llannotation import lltype_to_annotation
-from rpython.rlib.objectmodel import we_are_translated, specialize
+from rpython.rlib.objectmodel import we_are_translated, specialize, 
compute_hash
 from rpython.jit.metainterp import history, compile
 from rpython.jit.metainterp.optimize import SpeculativeError
 from rpython.jit.codewriter import heaptracker, longlong
@@ -663,6 +664,14 @@
         u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string)
         return len(u.chars)
 
+    def bh_strhash(self, string):
+        s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string)
+        return compute_hash(hlstr(s))
+
+    def bh_unicodehash(self, string):
+        u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string)
+        return compute_hash(hlunicode(u))
+
     def bh_strgetitem(self, string, index):
         s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string)
         return ord(s.chars[index])
diff --git a/rpython/jit/backend/llsupport/rewrite.py 
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -9,9 +9,9 @@
 from rpython.jit.metainterp.typesystem import rd_eq, rd_hash
 from rpython.jit.codewriter import heaptracker
 from rpython.jit.backend.llsupport.symbolic import (WORD,
-        get_array_token)
+        get_field_token, get_array_token)
 from rpython.jit.backend.llsupport.descr import SizeDescr, ArrayDescr,\
-     FLAG_POINTER, CallDescr
+     FLAG_POINTER
 from rpython.jit.metainterp.history import JitCellToken
 from rpython.jit.backend.llsupport.descr import (unpack_arraydescr,
         unpack_fielddescr, unpack_interiorfielddescr)
@@ -262,6 +262,18 @@
                                                  
self.cpu.translate_support_code)
             self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
                                          WORD, 1, ofs_length, NOT_SIGNED)
+        elif opnum == rop.STRHASH:
+            offset, size = get_field_token(rstr.STR,
+                                        'hash', 
self.cpu.translate_support_code)
+            assert size == WORD
+            self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
+                                         WORD, 1, offset, sign=True)
+        elif opnum == rop.UNICODEHASH:
+            offset, size = get_field_token(rstr.UNICODE,
+                                        'hash', 
self.cpu.translate_support_code)
+            assert size == WORD
+            self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
+                                         WORD, 1, offset, sign=True)
         elif opnum == rop.STRGETITEM:
             basesize, itemsize, ofs_length = get_array_token(rstr.STR,
                                                  
self.cpu.translate_support_code)
@@ -347,9 +359,7 @@
                     self.consider_setfield_gc(op)
                 elif op.getopnum() == rop.SETARRAYITEM_GC:
                     self.consider_setarrayitem_gc(op)
-            # ---------- calls -----------
-            if OpHelpers.is_plain_call(op.getopnum()):
-                self.expand_call_shortcut(op)
+            # ---------- call assembler -----------
             if OpHelpers.is_call_assembler(op.getopnum()):
                 self.handle_call_assembler(op)
                 continue
@@ -595,33 +605,6 @@
         self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value,
                                       size, 1, ofs)
 
-    def expand_call_shortcut(self, op):
-        if not self.cpu.supports_cond_call_value:
-            return
-        descr = op.getdescr()
-        if descr is None:
-            return
-        assert isinstance(descr, CallDescr)
-        effectinfo = descr.get_extra_info()
-        if effectinfo is None or effectinfo.call_shortcut is None:
-            return
-        if op.type == 'r':
-            cond_call_opnum = rop.COND_CALL_VALUE_R
-        elif op.type == 'i':
-            cond_call_opnum = rop.COND_CALL_VALUE_I
-        else:
-            return
-        cs = effectinfo.call_shortcut
-        ptr_box = op.getarg(1 + cs.argnum)
-        if cs.fielddescr is not None:
-            value_box = self.emit_getfield(ptr_box, descr=cs.fielddescr,
-                                           raw=(ptr_box.type == 'i'))
-        else:
-            value_box = ptr_box
-        self.replace_op_with(op, ResOperation(cond_call_opnum,
-                                              [value_box] + op.getarglist(),
-                                              descr=descr))
-
     def handle_call_assembler(self, op):
         descrs = self.gc_ll_descr.getframedescrs(self.cpu)
         loop_token = op.getdescr()
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py 
b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -1,8 +1,7 @@
 import py
 from rpython.jit.backend.llsupport.descr import get_size_descr,\
      get_field_descr, get_array_descr, ArrayDescr, FieldDescr,\
-     SizeDescr, get_interiorfield_descr, get_call_descr
-from rpython.jit.codewriter.effectinfo import EffectInfo, CallShortcut
+     SizeDescr, get_interiorfield_descr
 from rpython.jit.backend.llsupport.gc import GcLLDescr_boehm,\
      GcLLDescr_framework
 from rpython.jit.backend.llsupport import jitframe
@@ -81,21 +80,6 @@
                                      lltype.malloc(T, zero=True))
         self.myT = myT
         #
-        call_shortcut = CallShortcut(0, tzdescr)
-        effectinfo = EffectInfo(None, None, None, None, None, None,
-                                EffectInfo.EF_RANDOM_EFFECTS,
-                                call_shortcut=call_shortcut)
-        call_shortcut_descr = get_call_descr(self.gc_ll_descr,
-            [lltype.Ptr(T)], lltype.Signed,
-            effectinfo)
-        call_shortcut_2 = CallShortcut(0, None)
-        effectinfo_2 = EffectInfo(None, None, None, None, None, None,
-                                EffectInfo.EF_RANDOM_EFFECTS,
-                                call_shortcut=call_shortcut_2)
-        call_shortcut_descr_2 = get_call_descr(self.gc_ll_descr,
-            [lltype.Signed], lltype.Signed,
-            effectinfo_2)
-        #
         A = lltype.GcArray(lltype.Signed)
         adescr = get_array_descr(self.gc_ll_descr, A)
         adescr.tid = 4321
@@ -216,7 +200,6 @@
 
     load_constant_offset = True
     load_supported_factors = (1,2,4,8)
-    supports_cond_call_value = True
 
     translate_support_code = None
 
@@ -1239,6 +1222,10 @@
                        'i3 = gc_load_i(p0,'
                                  '%(strlendescr.offset)s,'
                                  '%(strlendescr.field_size)s)'],
+        [True,  (1,),  'i3 = strhash(p0)' '->'
+                       'i3 = gc_load_i(p0,'
+                                 '%(strhashdescr.offset)s,'
+                                 '-%(strhashdescr.field_size)s)'],
         #[False, (1,),  'i3 = unicodelen(p0)' '->'
         #               'i3 = gc_load_i(p0,'
         #                       '%(unicodelendescr.offset)s,'
@@ -1247,7 +1234,10 @@
                        'i3 = gc_load_i(p0,'
                                '%(unicodelendescr.offset)s,'
                                '%(unicodelendescr.field_size)s)'],
-
+        [True,  (1,),  'i3 = unicodehash(p0)' '->'
+                       'i3 = gc_load_i(p0,'
+                                 '%(unicodehashdescr.offset)s,'
+                                 '-%(unicodehashdescr.field_size)s)'],
         ## getitem str/unicode
         [True,  (2,4), 'i3 = unicodegetitem(p0,i1)' '->'
                        'i3 = gc_load_indexed_i(p0,i1,'
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to