[pypy-commit] pypy numpy-impicit-convert: merge default

snus_mumrik noreply at buildbot.pypy.org
Tue Jul 5 20:16:48 CEST 2011


Author: Ilya Osadchiy <osadchiy.ilya at gmail.com>
Branch: numpy-impicit-convert
Changeset: r45359:418e5dfaa542
Date: 2011-07-05 21:23 +0300
http://bitbucket.org/pypy/pypy/changeset/418e5dfaa542/

Log:	merge default

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,6 +1,7 @@
 syntax: glob
 *.py[co]
 *~
+.*.swp
 
 syntax: regexp
 ^testresult$
@@ -38,6 +39,8 @@
 ^pypy/translator/benchmark/shootout_benchmarks$
 ^pypy/translator/goal/pypy-translation-snapshot$
 ^pypy/translator/goal/pypy-c
+^pypy/translator/goal/pypy-jvm
+^pypy/translator/goal/pypy-jvm.jar
 ^pypy/translator/goal/.+\.exe$
 ^pypy/translator/goal/.+\.dll$
 ^pypy/translator/goal/target.+-c$
diff --git a/lib-python/modified-2.7/opcode.py b/lib-python/modified-2.7/opcode.py
--- a/lib-python/modified-2.7/opcode.py
+++ b/lib-python/modified-2.7/opcode.py
@@ -189,7 +189,6 @@
 def_op('MAP_ADD', 147)
 
 # pypy modification, experimental bytecode
-def_op('CALL_LIKELY_BUILTIN', 200)    # #args + (#kwargs << 8)
 def_op('LOOKUP_METHOD', 201)          # Index in name list
 hasname.append(201)
 def_op('CALL_METHOD', 202)            # #args not including 'self'
diff --git a/lib-python/modified-2.7/pickle.py b/lib-python/modified-2.7/pickle.py
--- a/lib-python/modified-2.7/pickle.py
+++ b/lib-python/modified-2.7/pickle.py
@@ -873,7 +873,7 @@
 
 # Unpickling machinery
 
-class Unpickler:
+class Unpickler(object):
 
     def __init__(self, file):
         """This takes a file-like object for reading a pickle data stream.
diff --git a/lib-python/modified-2.7/test/test_descr.py b/lib-python/modified-2.7/test/test_descr.py
--- a/lib-python/modified-2.7/test/test_descr.py
+++ b/lib-python/modified-2.7/test/test_descr.py
@@ -4399,13 +4399,10 @@
         self.assertTrue(l.__add__ != [5].__add__)
         self.assertTrue(l.__add__ != l.__mul__)
         self.assertTrue(l.__add__.__name__ == '__add__')
-        if hasattr(l.__add__, '__self__'):
-            # CPython
-            self.assertTrue(l.__add__.__self__ is l)
+        self.assertTrue(l.__add__.__self__ is l)
+        if hasattr(l.__add__, '__objclass__'):   # CPython
             self.assertTrue(l.__add__.__objclass__ is list)
-        else:
-            # Python implementations where [].__add__ is a normal bound method
-            self.assertTrue(l.__add__.im_self is l)
+        else:                                    # PyPy
             self.assertTrue(l.__add__.im_class is list)
         self.assertEqual(l.__add__.__doc__, list.__add__.__doc__)
         try:
diff --git a/lib-python/modified-2.7/test/test_dis.py b/lib-python/modified-2.7/test/test_dis.py
deleted file mode 100644
--- a/lib-python/modified-2.7/test/test_dis.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Minimal tests for dis module
-
-from test.test_support import run_unittest
-import unittest
-import sys
-import dis
-import StringIO
-
-
-def _f(a):
-    print a
-    return 1
-
-dis_f = """\
- %-4d         0 LOAD_FAST                0 (a)
-              3 PRINT_ITEM
-              4 PRINT_NEWLINE
-
- %-4d         5 LOAD_CONST               1 (1)
-              8 RETURN_VALUE
-"""%(_f.func_code.co_firstlineno + 1,
-     _f.func_code.co_firstlineno + 2)
-
-
-# we "call" rangexxx() instead of range() to disable the
-# pypy optimization that turns it into CALL_LIKELY_BUILTIN.
-def bug708901():
-    for res in rangexxx(1,
-                        10):
-        pass
-
-dis_bug708901 = """\
- %-4d         0 SETUP_LOOP              23 (to 26)
-              3 LOAD_GLOBAL              0 (rangexxx)
-              6 LOAD_CONST               1 (1)
-
- %-4d         9 LOAD_CONST               2 (10)
-             12 CALL_FUNCTION            2
-             15 GET_ITER
-        >>   16 FOR_ITER                 6 (to 25)
-             19 STORE_FAST               0 (res)
-
- %-4d        22 JUMP_ABSOLUTE           16
-        >>   25 POP_BLOCK
-        >>   26 LOAD_CONST               0 (None)
-             29 RETURN_VALUE
-"""%(bug708901.func_code.co_firstlineno + 1,
-     bug708901.func_code.co_firstlineno + 2,
-     bug708901.func_code.co_firstlineno + 3)
-
-
-def bug1333982(x=[]):
-    assert 0, ([s for s in x] +
-              1)
-    pass
-
-dis_bug1333982 = """\
- %-4d         0 LOAD_CONST               1 (0)
-              3 POP_JUMP_IF_TRUE        38
-              6 LOAD_GLOBAL              0 (AssertionError)
-              9 BUILD_LIST               0
-             12 LOAD_FAST                0 (x)
-             15 GET_ITER
-        >>   16 FOR_ITER                12 (to 31)
-             19 STORE_FAST               1 (s)
-             22 LOAD_FAST                1 (s)
-             25 LIST_APPEND              2
-             28 JUMP_ABSOLUTE           16
-
- %-4d   >>   31 LOAD_CONST               2 (1)
-             34 BINARY_ADD
-             35 RAISE_VARARGS            2
-
- %-4d   >>   38 LOAD_CONST               0 (None)
-             41 RETURN_VALUE
-"""%(bug1333982.func_code.co_firstlineno + 1,
-     bug1333982.func_code.co_firstlineno + 2,
-     bug1333982.func_code.co_firstlineno + 3)
-
-_BIG_LINENO_FORMAT = """\
-%3d           0 LOAD_GLOBAL              0 (spam)
-              3 POP_TOP
-              4 LOAD_CONST               0 (None)
-              7 RETURN_VALUE
-"""
-
-class DisTests(unittest.TestCase):
-    def do_disassembly_test(self, func, expected):
-        s = StringIO.StringIO()
-        save_stdout = sys.stdout
-        sys.stdout = s
-        dis.dis(func)
-        sys.stdout = save_stdout
-        got = s.getvalue()
-        # Trim trailing blanks (if any).
-        lines = got.split('\n')
-        lines = [line.rstrip() for line in lines]
-        expected = expected.split("\n")
-        import difflib
-        if expected != lines:
-            self.fail(
-                "events did not match expectation:\n" +
-                "\n".join(difflib.ndiff(expected,
-                                        lines)))
-
-    def test_opmap(self):
-        self.assertEqual(dis.opmap["STOP_CODE"], 0)
-        self.assertIn(dis.opmap["LOAD_CONST"], dis.hasconst)
-        self.assertIn(dis.opmap["STORE_NAME"], dis.hasname)
-
-    def test_opname(self):
-        self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST")
-
-    def test_boundaries(self):
-        self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG)
-        self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT)
-
-    def test_dis(self):
-        self.do_disassembly_test(_f, dis_f)
-
-    def test_bug_708901(self):
-        self.do_disassembly_test(bug708901, dis_bug708901)
-
-    def test_bug_1333982(self):
-        # This one is checking bytecodes generated for an `assert` statement,
-        # so fails if the tests are run with -O.  Skip this test then.
-        if __debug__:
-            self.do_disassembly_test(bug1333982, dis_bug1333982)
-
-    def test_big_linenos(self):
-        def func(count):
-            namespace = {}
-            func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"])
-            exec func in namespace
-            return namespace['foo']
-
-        # Test all small ranges
-        for i in xrange(1, 300):
-            expected = _BIG_LINENO_FORMAT % (i + 2)
-            self.do_disassembly_test(func(i), expected)
-
-        # Test some larger ranges too
-        for i in xrange(300, 5000, 10):
-            expected = _BIG_LINENO_FORMAT % (i + 2)
-            self.do_disassembly_test(func(i), expected)
-
-def test_main():
-    run_unittest(DisTests)
-
-
-if __name__ == "__main__":
-    test_main()
diff --git a/lib-python/modified-2.7/test/test_weakref.py b/lib-python/modified-2.7/test/test_weakref.py
--- a/lib-python/modified-2.7/test/test_weakref.py
+++ b/lib-python/modified-2.7/test/test_weakref.py
@@ -993,13 +993,13 @@
         self.assertTrue(len(weakdict) == 2)
         k, v = weakdict.popitem()
         self.assertTrue(len(weakdict) == 1)
-        if k is key1:
+        if k == key1:
             self.assertTrue(v is value1)
         else:
             self.assertTrue(v is value2)
         k, v = weakdict.popitem()
         self.assertTrue(len(weakdict) == 0)
-        if k is key1:
+        if k == key1:
             self.assertTrue(v is value1)
         else:
             self.assertTrue(v is value2)
diff --git a/lib_pypy/_ctypes/__init__.py b/lib_pypy/_ctypes/__init__.py
--- a/lib_pypy/_ctypes/__init__.py
+++ b/lib_pypy/_ctypes/__init__.py
@@ -18,7 +18,16 @@
 if _os.name in ("nt", "ce"):
     from _rawffi import FormatError
     from _rawffi import check_HRESULT as _check_HRESULT
-    CopyComPointer = None # XXX
+
+    def CopyComPointer(src, dst):
+        from ctypes import c_void_p, cast
+        if src:
+            hr = src[0][0].AddRef(src)
+            if hr & 0x80000000:
+                return hr
+        dst[0] = cast(src, c_void_p).value
+        return 0
+
     LoadLibrary = dlopen
 
 from _rawffi import FUNCFLAG_STDCALL, FUNCFLAG_CDECL, FUNCFLAG_PYTHONAPI
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -139,7 +139,10 @@
         return buffer(self._buffer)
 
     def _get_b_base(self):
-        return self._base
+        try:
+            return self._base
+        except AttributeError:
+            return None
     _b_base_ = property(_get_b_base)
     _b_needsfree_ = False
 
@@ -218,5 +221,7 @@
     'z' : _ffi.types.void_p,
     'O' : _ffi.types.void_p,
     'Z' : _ffi.types.void_p,
+    'X' : _ffi.types.void_p,
+    'v' : _ffi.types.sshort,
     }
 
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -322,20 +322,18 @@
                           RuntimeWarning, stacklevel=2)
 
         if self._com_index:
-            assert False, 'TODO2'
             from ctypes import cast, c_void_p, POINTER
             if not args:
                 raise ValueError(
                     "native COM method call without 'this' parameter"
                     )
-            thisarg = cast(args[0], POINTER(POINTER(c_void_p))).contents
-            argtypes = [c_void_p] + list(argtypes)
-            args = list(args)
-            args[0] = args[0].value
+            thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
+            newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs)
+            newargs.insert(0, args[0].value)
+            argtypes.insert(0, c_void_p)
         else:
             thisarg = None
-            
-        newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
+            newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
 
         funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
         result = self._call_funcptr(funcptr, *newargs)
@@ -343,6 +341,11 @@
 
         if not outargs:
             return result
+
+        simple_cdata = type(c_void_p()).__bases__[0]
+        outargs = [x.value if type(x).__bases__[0] is simple_cdata else x
+                   for x in outargs]
+
         if len(outargs) == 1:
             return outargs[0]
         return tuple(outargs)
@@ -398,10 +401,10 @@
             # extract the address from the object's virtual table
             if not thisarg:
                 raise ValueError("COM method call without VTable")
-            ptr = thisarg[self._com_index - 0x1000]
-            argshapes = [arg._ffiargshape for arg in argtypes]
-            resshape = restype._ffiargshape
-            return _rawffi.FuncPtr(ptr, argshapes, resshape, self._flags_)
+            ptr = thisarg[0][self._com_index - 0x1000]
+            ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
+            ffires = restype.get_ffi_argtype()
+            return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires)
         
         cdll = self.dll._handle
         try:
@@ -468,11 +471,7 @@
         newargtypes = []
         total = len(args)
         paramflags = self._paramflags
-
-        if self._com_index:
-            inargs_idx = 1
-        else:
-            inargs_idx = 0
+        inargs_idx = 0
 
         if not paramflags and total < len(argtypes):
             raise TypeError("not enough arguments")
@@ -587,13 +586,7 @@
 
         retval = None
 
-        if self._com_index:
-            if resbuffer[0] & 0x80000000:
-                raise get_com_error(resbuffer[0],
-                                    self._com_iid, argsandobjs[0])
-            else:
-                retval = int(resbuffer[0])
-        elif restype is not None:
+        if restype is not None:
             checker = getattr(self.restype, '_check_retval_', None)
             if checker:
                 val = restype(result)
@@ -601,7 +594,13 @@
                 # classes defining a new type, and their subclasses
                 if '_type_' in restype.__dict__:
                     val = val.value
-                retval = checker(val)
+                # XXX Raise a COMError when restype is HRESULT and
+                # checker(val) fails.  How to check for restype == HRESULT?
+                if self._com_index:
+                    if result & 0x80000000:
+                        raise get_com_error(result, None, None)
+                else:
+                    retval = checker(val)
             elif not isinstance(restype, _CDataMeta):
                 retval = restype(result)
             else:
diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py
--- a/lib_pypy/_ctypes/primitive.py
+++ b/lib_pypy/_ctypes/primitive.py
@@ -216,10 +216,15 @@
             result.value = property(_getvalue, _setvalue)
 
         elif tp == 'X':
-            from ctypes import windll
-            SysAllocStringLen = windll.oleaut32.SysAllocStringLen
-            SysStringLen = windll.oleaut32.SysStringLen
-            SysFreeString = windll.oleaut32.SysFreeString
+            from ctypes import WinDLL
+            # Use WinDLL("oleaut32") instead of windll.oleaut32
+            # because the latter is a shared (cached) object; and
+            # other code may set their own restypes. We need out own
+            # restype here.
+            oleaut32 = WinDLL("oleaut32")
+            SysAllocStringLen = oleaut32.SysAllocStringLen
+            SysStringLen = oleaut32.SysStringLen
+            SysFreeString = oleaut32.SysFreeString
             def _getvalue(self):
                 addr = self._buffer[0]
                 if addr == 0:
diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py
--- a/lib_pypy/pwd.py
+++ b/lib_pypy/pwd.py
@@ -16,6 +16,7 @@
 
 from ctypes_support import standard_c_lib as libc
 from ctypes import Structure, POINTER, c_int, c_char_p, c_long
+from _structseq import structseqtype, structseqfield
 
 try: from __pypy__ import builtinify
 except ImportError: builtinify = lambda f: f
@@ -68,7 +69,7 @@
             yield self.pw_dir
             yield self.pw_shell
 
-class struct_passwd(tuple):
+class struct_passwd:
     """
     pwd.struct_passwd: Results from getpw*() routines.
 
@@ -76,15 +77,15 @@
       (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)
     or via the object attributes as named in the above tuple.
     """
-    def __init__(self, passwd):
-        self.pw_name = passwd.pw_name
-        self.pw_passwd = passwd.pw_passwd
-        self.pw_uid = passwd.pw_uid
-        self.pw_gid = passwd.pw_gid
-        self.pw_gecos = passwd.pw_gecos
-        self.pw_dir = passwd.pw_dir
-        self.pw_shell = passwd.pw_shell
-        tuple.__init__(self, passwd)
+    __metaclass__ = structseqtype
+    name = "pwd.struct_passwd"
+    pw_name = structseqfield(0)
+    pw_passwd = structseqfield(1)
+    pw_uid = structseqfield(2)
+    pw_gid = structseqfield(3)
+    pw_gecos = structseqfield(4)
+    pw_dir = structseqfield(5)
+    pw_shell = structseqfield(6)
 
 passwd_p = POINTER(passwd)
 
diff --git a/pypy/annotation/bookkeeper.py b/pypy/annotation/bookkeeper.py
--- a/pypy/annotation/bookkeeper.py
+++ b/pypy/annotation/bookkeeper.py
@@ -299,12 +299,13 @@
             listdef.generalize_range_step(flags['range_step'])
         return SomeList(listdef)
 
-    def getdictdef(self, is_r_dict=False):
+    def getdictdef(self, is_r_dict=False, force_non_null=False):
         """Get the DictDef associated with the current position."""
         try:
             dictdef = self.dictdefs[self.position_key]
         except KeyError:
-            dictdef = DictDef(self, is_r_dict=is_r_dict)
+            dictdef = DictDef(self, is_r_dict=is_r_dict,
+                              force_non_null=force_non_null)
             self.dictdefs[self.position_key] = dictdef
         return dictdef
 
diff --git a/pypy/annotation/builtin.py b/pypy/annotation/builtin.py
--- a/pypy/annotation/builtin.py
+++ b/pypy/annotation/builtin.py
@@ -311,8 +311,14 @@
 def robjmodel_we_are_translated():
     return immutablevalue(True)
 
-def robjmodel_r_dict(s_eqfn, s_hashfn):
-    dictdef = getbookkeeper().getdictdef(is_r_dict=True)
+def robjmodel_r_dict(s_eqfn, s_hashfn, s_force_non_null=None):
+    if s_force_non_null is None:
+        force_non_null = False
+    else:
+        assert s_force_non_null.is_constant()
+        force_non_null = s_force_non_null.const
+    dictdef = getbookkeeper().getdictdef(is_r_dict=True,
+                                         force_non_null=force_non_null)
     dictdef.dictkey.update_rdict_annotations(s_eqfn, s_hashfn)
     return SomeDict(dictdef)
 
@@ -351,17 +357,6 @@
 def llmemory_cast_int_to_adr(s):
     return SomeAddress()
 
-
-##def rarith_ovfcheck(s_obj):
-##    if isinstance(s_obj, SomeInteger) and s_obj.unsigned:
-##        getbookkeeper().warning("ovfcheck on unsigned")
-##    return s_obj
-
-##def rarith_ovfcheck_lshift(s_obj1, s_obj2):
-##    if isinstance(s_obj1, SomeInteger) and s_obj1.unsigned:
-##        getbookkeeper().warning("ovfcheck_lshift with unsigned")
-##    return SomeInteger()
-
 def unicodedata_decimal(s_uchr):
     raise TypeError, "unicodedate.decimal() calls should not happen at interp-level"    
 
@@ -379,8 +374,6 @@
         original = getattr(__builtin__, name[8:])
         BUILTIN_ANALYZERS[original] = value
 
-##BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.ovfcheck] = rarith_ovfcheck
-##BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.ovfcheck_lshift] = rarith_ovfcheck_lshift
 BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.intmask] = rarith_intmask
 BUILTIN_ANALYZERS[pypy.rlib.objectmodel.instantiate] = robjmodel_instantiate
 BUILTIN_ANALYZERS[pypy.rlib.objectmodel.we_are_translated] = (
diff --git a/pypy/annotation/dictdef.py b/pypy/annotation/dictdef.py
--- a/pypy/annotation/dictdef.py
+++ b/pypy/annotation/dictdef.py
@@ -85,12 +85,14 @@
 
     def __init__(self, bookkeeper, s_key = s_ImpossibleValue,
                                  s_value = s_ImpossibleValue,
-                               is_r_dict = False):
+                               is_r_dict = False,
+                           force_non_null = False):
         self.dictkey = DictKey(bookkeeper, s_key, is_r_dict)
         self.dictkey.itemof[self] = True
         self.dictvalue = DictValue(bookkeeper, s_value)
         self.dictvalue.itemof[self] = True
         self.bookkeeper = bookkeeper
+        self.force_non_null = force_non_null
 
     def read_key(self, position_key=None):
         if position_key is None:
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -129,9 +129,6 @@
                  cmdline='--objspace -o'),
 
     OptionDescription("opcodes", "opcodes to enable in the interpreter", [
-        BoolOption("CALL_LIKELY_BUILTIN", "emit a special bytecode for likely calls to builtin functions",
-                   default=False,
-                   requires=[("translation.stackless", False)]),
         BoolOption("CALL_METHOD", "emit a special bytecode for expr.name()",
                    default=False),
         ]),
@@ -266,13 +263,7 @@
         BoolOption("withcelldict",
                    "use dictionaries that are optimized for being used as module dicts",
                    default=False,
-                   requires=[("objspace.opcodes.CALL_LIKELY_BUILTIN", False),
-                             ("objspace.honor__builtins__", False)]),
-
-        BoolOption("withdictmeasurement",
-                   "create huge files with masses of information "
-                   "about dictionaries",
-                   default=False),
+                   requires=[("objspace.honor__builtins__", False)]),
 
         BoolOption("withmapdict",
                    "make instances really small but slow without the JIT",
@@ -355,8 +346,6 @@
     backend = config.translation.backend
 
     # all the good optimizations for PyPy should be listed here
-    if level in ['2', '3']:
-        config.objspace.opcodes.suggest(CALL_LIKELY_BUILTIN=True)
     if level in ['2', '3', 'jit']:
         config.objspace.opcodes.suggest(CALL_METHOD=True)
         config.objspace.std.suggest(withrangelist=True)
diff --git a/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt b/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt
deleted file mode 100644
--- a/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Introduce a new opcode called ``CALL_LIKELY_BUILTIN``. It is used when something
-is called, that looks like a builtin function (but could in reality be shadowed
-by a name in the module globals). For all module globals dictionaries it is
-then tracked which builtin name is shadowed in this module. If the
-``CALL_LIKELY_BUILTIN`` opcode is executed, it is checked whether the builtin is
-shadowed. If not, the corresponding builtin is called. Otherwise the object that
-is shadowing it is called instead. If no shadowing is happening, this saves two
-dictionary lookups on calls to builtins.
-
-For more information, see the section in `Standard Interpreter Optimizations`_.
-
-.. _`Standard Interpreter Optimizations`: ../interpreter-optimizations.html#call-likely-builtin
diff --git a/pypy/doc/config/objspace.std.withdictmeasurement.txt b/pypy/doc/config/objspace.std.withdictmeasurement.txt
deleted file mode 100644
--- a/pypy/doc/config/objspace.std.withdictmeasurement.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Internal option.
-
-.. internal
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -248,5 +248,7 @@
   never a dictionary as it sometimes is in CPython. Assigning to
   ``__builtins__`` has no effect.
 
+* object identity of immutable keys in dictionaries is not necessarily preserved.
+  Never compare immutable objects with ``is``.
+
 .. include:: _ref.txt
-
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -17,7 +17,7 @@
         self.varargname = varargname
         self.kwargname = kwargname
 
-    @jit.purefunction
+    @jit.elidable
     def find_argname(self, name):
         try:
             return self.argnames.index(name)
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -655,9 +655,6 @@
 def _compute_CALL_FUNCTION_VAR_KW(arg):
     return -_num_args(arg) - 2
 
-def _compute_CALL_LIKELY_BUILTIN(arg):
-    return -(arg & 0xFF) + 1
-
 def _compute_CALL_METHOD(arg):
     return -_num_args(arg) - 1
 
diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -12,7 +12,6 @@
 from pypy.interpreter.pyparser.error import SyntaxError
 from pypy.tool import stdlib_opcode as ops
 from pypy.interpreter.error import OperationError
-from pypy.module.__builtin__.__init__ import BUILTIN_TO_INDEX
 
 
 def compile_ast(space, module, info):
@@ -134,7 +133,7 @@
 
     def accept_comp_iteration(self, codegen, index):
         self.elt.walkabout(codegen)
-        codegen.emit_op_arg(ops.SET_ADD, index)
+        codegen.emit_op_arg(ops.SET_ADD, index + 1)
 
 
 class __extend__(ast.DictComp):
@@ -148,7 +147,7 @@
     def accept_comp_iteration(self, codegen, index):
         self.value.walkabout(codegen)
         self.key.walkabout(codegen)
-        codegen.emit_op_arg(ops.MAP_ADD, index)
+        codegen.emit_op_arg(ops.MAP_ADD, index + 1)
 
 
 # These are frame blocks.
@@ -942,8 +941,7 @@
 
     def visit_Call(self, call):
         self.update_position(call.lineno)
-        if self._optimize_builtin_call(call) or \
-                self._optimize_method_call(call):
+        if self._optimize_method_call(call):
             return
         call.func.walkabout(self)
         arg = 0
@@ -977,28 +975,6 @@
     def _call_has_simple_args(self, call):
         return self._call_has_no_star_args(call) and not call.keywords
 
-    def _optimize_builtin_call(self, call):
-        if not self.space.config.objspace.opcodes.CALL_LIKELY_BUILTIN or \
-                not self._call_has_simple_args(call) or \
-                not isinstance(call.func, ast.Name):
-            return False
-        func_name = call.func
-        assert isinstance(func_name, ast.Name)
-        name_scope = self.scope.lookup(func_name.id)
-        if name_scope == symtable.SCOPE_GLOBAL_IMPLICIT or \
-                name_scope == symtable.SCOPE_UNKNOWN:
-            builtin_index = BUILTIN_TO_INDEX.get(func_name.id, -1)
-            if builtin_index != -1:
-                if call.args:
-                    args_count = len(call.args)
-                    self.visit_sequence(call.args)
-                else:
-                    args_count = 0
-                arg = builtin_index << 8 | args_count
-                self.emit_op_arg(ops.CALL_LIKELY_BUILTIN, arg)
-                return True
-        return False
-
     def _optimize_method_call(self, call):
         if not self.space.config.objspace.opcodes.CALL_METHOD or \
                 not self._call_has_no_star_args(call) or \
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -237,7 +237,7 @@
 
 class ObjSpace(object):
     """Base class for the interpreter-level implementations of object spaces.
-    http://codespeak.net/pypy/dist/pypy/doc/objspace.html"""
+    http://pypy.readthedocs.org/en/latest/objspace.html"""
 
     full_exceptions = True  # full support for exceptions (normalization & more)
 
@@ -311,9 +311,6 @@
             mod = self.interpclass_w(w_mod)
             if isinstance(mod, Module) and mod.startup_called:
                 mod.shutdown(self)
-        if self.config.objspace.std.withdictmeasurement:
-            from pypy.objspace.std.dictmultiobject import report
-            report()
         if self.config.objspace.logbytecodes:
             self.reportbytecodecounts()
         if self.config.objspace.std.logspaceoptypes:
diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py
--- a/pypy/interpreter/eval.py
+++ b/pypy/interpreter/eval.py
@@ -100,12 +100,12 @@
 
     @jit.dont_look_inside
     def fast2locals(self):
-        # Copy values from self.fastlocals_w to self.w_locals
+        # Copy values from the fastlocals to self.w_locals
         if self.w_locals is None:
             self.w_locals = self.space.newdict()
         varnames = self.getcode().getvarnames()
         fastscope_w = self.getfastscope()
-        for i in range(min(len(varnames), len(fastscope_w))):
+        for i in range(min(len(varnames), self.getfastscopelength())):
             name = varnames[i]
             w_value = fastscope_w[i]
             if w_value is not None:
@@ -114,7 +114,7 @@
 
     @jit.dont_look_inside
     def locals2fast(self):
-        # Copy values from self.w_locals to self.fastlocals_w
+        # Copy values from self.w_locals to the fastlocals
         assert self.w_locals is not None
         varnames = self.getcode().getvarnames()
         numlocals = self.getfastscopelength()
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -16,7 +16,7 @@
 
 funccallunrolling = unrolling_iterable(range(4))
 
- at jit.purefunction_promote()
+ at jit.elidable_promote()
 def _get_immutable_code(func):
     assert not func.can_change_code
     return func.code
@@ -63,7 +63,7 @@
         if jit.we_are_jitted():
             if not self.can_change_code:
                 return _get_immutable_code(self)
-            return jit.hint(self.code, promote=True)
+            return jit.promote(self.code)
         return self.code
 
     def funccall(self, *args_w): # speed hack
@@ -98,7 +98,7 @@
                                                    self.closure)
                 for i in funccallunrolling:
                     if i < nargs:
-                        new_frame.fastlocals_w[i] = args_w[i]
+                        new_frame.locals_stack_w[i] = args_w[i]
                 return new_frame.run()
         elif nargs >= 1 and fast_natural_arity == Code.PASSTHROUGHARGS1:
             assert isinstance(code, gateway.BuiltinCodePassThroughArguments1)
@@ -158,7 +158,7 @@
                                                    self.closure)
         for i in xrange(nargs):
             w_arg = frame.peekvalue(nargs-1-i)
-            new_frame.fastlocals_w[i] = w_arg
+            new_frame.locals_stack_w[i] = w_arg
 
         return new_frame.run()
 
@@ -169,13 +169,13 @@
                                                    self.closure)
         for i in xrange(nargs):
             w_arg = frame.peekvalue(nargs-1-i)
-            new_frame.fastlocals_w[i] = w_arg
+            new_frame.locals_stack_w[i] = w_arg
 
         ndefs = len(self.defs_w)
         start = ndefs - defs_to_load
         i = nargs
         for j in xrange(start, ndefs):
-            new_frame.fastlocals_w[i] = self.defs_w[j]
+            new_frame.locals_stack_w[i] = self.defs_w[j]
             i += 1
         return new_frame.run()
 
@@ -465,19 +465,23 @@
                 space.abstract_isinstance_w(w_firstarg, self.w_class)):
             pass  # ok
         else:
-            myname = self.getname(space,"")
-            clsdescr = self.w_class.getname(space,"")
+            myname = self.getname(space, "")
+            clsdescr = self.w_class.getname(space, "")
             if clsdescr:
-                clsdescr+=" "
+                clsdescr += " instance"
+            else:
+                clsdescr = "instance"
             if w_firstarg is None:
                 instdescr = "nothing"
             else:
-                instname = space.abstract_getclass(w_firstarg).getname(space,"")
+                instname = space.abstract_getclass(w_firstarg).getname(space,
+                                                                       "")
                 if instname:
-                    instname += " "
-                instdescr = "%sinstance" %instname
-            msg = ("unbound method %s() must be called with %s"
-                   "instance as first argument (got %s instead)")
+                    instdescr = instname + " instance"
+                else:
+                    instdescr = "instance"
+            msg = ("unbound method %s() must be called with %s "
+                   "as first argument (got %s instead)")
             raise operationerrfmt(space.w_TypeError, msg,
                                   myname, clsdescr, instdescr)
         return space.call_args(self.w_function, args)
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -62,7 +62,7 @@
             raise operr
         # XXX it's not clear that last_instr should be promoted at all
         # but as long as it is necessary for call_assembler, let's do it early
-        last_instr = jit.hint(frame.last_instr, promote=True)
+        last_instr = jit.promote(frame.last_instr)
         if last_instr == -1:
             if w_arg and not space.is_w(w_arg, space.w_None):
                 msg = "can't send non-None value to a just-started generator"
diff --git a/pypy/interpreter/nestedscope.py b/pypy/interpreter/nestedscope.py
--- a/pypy/interpreter/nestedscope.py
+++ b/pypy/interpreter/nestedscope.py
@@ -170,7 +170,7 @@
         for i in range(len(args_to_copy)):
             argnum = args_to_copy[i]
             if argnum >= 0:
-                self.cells[i].set(self.fastlocals_w[argnum])
+                self.cells[i].set(self.locals_stack_w[argnum])
 
     def getfreevarname(self, index):
         freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -63,6 +63,7 @@
         the pypy compiler"""
         self.space = space
         eval.Code.__init__(self, name)
+        assert nlocals >= 0
         self.co_argcount = argcount
         self.co_nlocals = nlocals
         self.co_stacksize = stacksize
@@ -202,7 +203,7 @@
         # speed hack
         fresh_frame = jit.hint(frame, access_directly=True,
                                       fresh_virtualizable=True)
-        args_matched = args.parse_into_scope(None, fresh_frame.fastlocals_w,
+        args_matched = args.parse_into_scope(None, fresh_frame.locals_stack_w,
                                              func.name,
                                              sig, func.defs_w)
         fresh_frame.init_cells()
@@ -215,7 +216,7 @@
         # speed hack
         fresh_frame = jit.hint(frame, access_directly=True,
                                       fresh_virtualizable=True)
-        args_matched = args.parse_into_scope(w_obj, fresh_frame.fastlocals_w,
+        args_matched = args.parse_into_scope(w_obj, fresh_frame.locals_stack_w,
                                              func.name,
                                              sig, func.defs_w)
         fresh_frame.init_cells()
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -9,7 +9,7 @@
 from pypy.interpreter import pytraceback
 from pypy.rlib.objectmodel import we_are_translated, instantiate
 from pypy.rlib.jit import hint
-from pypy.rlib.debug import make_sure_not_resized
+from pypy.rlib.debug import make_sure_not_resized, check_nonneg
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib import jit
 from pypy.tool import stdlib_opcode
@@ -56,16 +56,18 @@
         assert isinstance(code, pycode.PyCode)
         self.pycode = code
         eval.Frame.__init__(self, space, w_globals)
-        self.valuestack_w = [None] * code.co_stacksize
-        self.valuestackdepth = 0
+        self.locals_stack_w = [None] * (code.co_nlocals + code.co_stacksize)
+        self.nlocals = code.co_nlocals
+        self.valuestackdepth = code.co_nlocals
         self.lastblock = None
+        make_sure_not_resized(self.locals_stack_w)
+        check_nonneg(self.nlocals)
+        #
         if space.config.objspace.honor__builtins__:
             self.builtin = space.builtin.pick_builtin(w_globals)
         # regular functions always have CO_OPTIMIZED and CO_NEWLOCALS.
         # class bodies only have CO_NEWLOCALS.
         self.initialize_frame_scopes(closure, code)
-        self.fastlocals_w = [None] * code.co_nlocals
-        make_sure_not_resized(self.fastlocals_w)
         self.f_lineno = code.co_firstlineno
 
     def mark_as_escaped(self):
@@ -184,14 +186,14 @@
     # stack manipulation helpers
     def pushvalue(self, w_object):
         depth = self.valuestackdepth
-        self.valuestack_w[depth] = w_object
+        self.locals_stack_w[depth] = w_object
         self.valuestackdepth = depth + 1
 
     def popvalue(self):
         depth = self.valuestackdepth - 1
-        assert depth >= 0, "pop from empty value stack"
-        w_object = self.valuestack_w[depth]
-        self.valuestack_w[depth] = None
+        assert depth >= self.nlocals, "pop from empty value stack"
+        w_object = self.locals_stack_w[depth]
+        self.locals_stack_w[depth] = None
         self.valuestackdepth = depth
         return w_object
 
@@ -217,24 +219,24 @@
     def peekvalues(self, n):
         values_w = [None] * n
         base = self.valuestackdepth - n
-        assert base >= 0
+        assert base >= self.nlocals
         while True:
             n -= 1
             if n < 0:
                 break
-            values_w[n] = self.valuestack_w[base+n]
+            values_w[n] = self.locals_stack_w[base+n]
         return values_w
 
     @jit.unroll_safe
     def dropvalues(self, n):
         n = hint(n, promote=True)
         finaldepth = self.valuestackdepth - n
-        assert finaldepth >= 0, "stack underflow in dropvalues()"        
+        assert finaldepth >= self.nlocals, "stack underflow in dropvalues()"
         while True:
             n -= 1
             if n < 0:
                 break
-            self.valuestack_w[finaldepth+n] = None
+            self.locals_stack_w[finaldepth+n] = None
         self.valuestackdepth = finaldepth
 
     @jit.unroll_safe
@@ -261,30 +263,30 @@
         # Contrast this with CPython where it's PEEK(-1).
         index_from_top = hint(index_from_top, promote=True)
         index = self.valuestackdepth + ~index_from_top
-        assert index >= 0, "peek past the bottom of the stack"
-        return self.valuestack_w[index]
+        assert index >= self.nlocals, "peek past the bottom of the stack"
+        return self.locals_stack_w[index]
 
     def settopvalue(self, w_object, index_from_top=0):
         index_from_top = hint(index_from_top, promote=True)
         index = self.valuestackdepth + ~index_from_top
-        assert index >= 0, "settop past the bottom of the stack"
-        self.valuestack_w[index] = w_object
+        assert index >= self.nlocals, "settop past the bottom of the stack"
+        self.locals_stack_w[index] = w_object
 
     @jit.unroll_safe
     def dropvaluesuntil(self, finaldepth):
         depth = self.valuestackdepth - 1
         finaldepth = hint(finaldepth, promote=True)
         while depth >= finaldepth:
-            self.valuestack_w[depth] = None
+            self.locals_stack_w[depth] = None
             depth -= 1
         self.valuestackdepth = finaldepth
 
-    def savevaluestack(self):
-        return self.valuestack_w[:self.valuestackdepth]
+    def save_locals_stack(self):
+        return self.locals_stack_w[:self.valuestackdepth]
 
-    def restorevaluestack(self, items_w):
-        assert None not in items_w
-        self.valuestack_w[:len(items_w)] = items_w
+    def restore_locals_stack(self, items_w):
+        self.locals_stack_w[:len(items_w)] = items_w
+        self.init_cells()
         self.dropvaluesuntil(len(items_w))
 
     def make_arguments(self, nargs):
@@ -314,11 +316,12 @@
         else:
             f_lineno = self.f_lineno
 
-        values_w = self.valuestack_w[0:self.valuestackdepth]
+        values_w = self.locals_stack_w[self.nlocals:self.valuestackdepth]
         w_valuestack = maker.slp_into_tuple_with_nulls(space, values_w)
         
         w_blockstack = nt([block._get_state_(space) for block in self.get_blocklist()])
-        w_fastlocals = maker.slp_into_tuple_with_nulls(space, self.fastlocals_w)
+        w_fastlocals = maker.slp_into_tuple_with_nulls(
+            space, self.locals_stack_w[:self.nlocals])
         if self.last_exception is None:
             w_exc_value = space.w_None
             w_tb = space.w_None
@@ -399,7 +402,8 @@
         new_frame.last_instr = space.int_w(w_last_instr)
         new_frame.frame_finished_execution = space.is_true(w_finished)
         new_frame.f_lineno = space.int_w(w_f_lineno)
-        new_frame.fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals)
+        fastlocals_w = maker.slp_from_tuple_with_nulls(space, w_fastlocals)
+        new_frame.locals_stack_w[:len(fastlocals_w)] = fastlocals_w
 
         if space.is_w(w_f_trace, space.w_None):
             new_frame.w_f_trace = None
@@ -423,28 +427,28 @@
     @jit.dont_look_inside
     def getfastscope(self):
         "Get the fast locals as a list."
-        return self.fastlocals_w
+        return self.locals_stack_w
 
     @jit.dont_look_inside
     def setfastscope(self, scope_w):
         """Initialize the fast locals from a list of values,
         where the order is according to self.pycode.signature()."""
         scope_len = len(scope_w)
-        if scope_len > len(self.fastlocals_w):
+        if scope_len > self.nlocals:
             raise ValueError, "new fastscope is longer than the allocated area"
-        # don't assign directly to 'fastlocals_w[:scope_len]' to be
+        # don't assign directly to 'locals_stack_w[:scope_len]' to be
         # virtualizable-friendly
         for i in range(scope_len):
-            self.fastlocals_w[i] = scope_w[i]
+            self.locals_stack_w[i] = scope_w[i]
         self.init_cells()
 
     def init_cells(self):
-        """Initialize cellvars from self.fastlocals_w
+        """Initialize cellvars from self.locals_stack_w.
         This is overridden in nestedscope.py"""
         pass
 
     def getfastscopelength(self):
-        return self.pycode.co_nlocals
+        return self.nlocals
 
     def getclosure(self):
         return None
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -324,7 +324,7 @@
 
     def LOAD_FAST(self, varindex, next_instr):
         # access a local variable directly
-        w_value = self.fastlocals_w[varindex]
+        w_value = self.locals_stack_w[varindex]
         if w_value is None:
             self._load_fast_failed(varindex)
         self.pushvalue(w_value)
@@ -343,7 +343,7 @@
     def STORE_FAST(self, varindex, next_instr):
         w_newvalue = self.popvalue()
         assert w_newvalue is not None
-        self.fastlocals_w[varindex] = w_newvalue
+        self.locals_stack_w[varindex] = w_newvalue
 
     def POP_TOP(self, oparg, next_instr):
         self.popvalue()
@@ -696,12 +696,12 @@
     LOAD_GLOBAL._always_inline_ = True
 
     def DELETE_FAST(self, varindex, next_instr):
-        if self.fastlocals_w[varindex] is None:
+        if self.locals_stack_w[varindex] is None:
             varname = self.getlocalvarname(varindex)
             message = "local variable '%s' referenced before assignment"
             raise operationerrfmt(self.space.w_UnboundLocalError, message,
                                   varname)
-        self.fastlocals_w[varindex] = None
+        self.locals_stack_w[varindex] = None
 
     def BUILD_TUPLE(self, itemcount, next_instr):
         items = self.popvalues(itemcount)
@@ -1048,30 +1048,18 @@
 
     def SET_ADD(self, oparg, next_instr):
         w_value = self.popvalue()
-        w_set = self.peekvalue(oparg)
+        w_set = self.peekvalue(oparg - 1)
         self.space.call_method(w_set, 'add', w_value)
 
     def MAP_ADD(self, oparg, next_instr):
         w_key = self.popvalue()
         w_value = self.popvalue()
-        w_dict = self.peekvalue(oparg)
+        w_dict = self.peekvalue(oparg - 1)
         self.space.setitem(w_dict, w_key, w_value)
 
     def SET_LINENO(self, lineno, next_instr):
         pass
 
-    def CALL_LIKELY_BUILTIN(self, oparg, next_instr):
-        # overridden by faster version in the standard object space.
-        from pypy.module.__builtin__ import OPTIMIZED_BUILTINS
-        varname = OPTIMIZED_BUILTINS[oparg >> 8]
-        w_function = self._load_global(varname)
-        nargs = oparg&0xFF
-        try:
-            w_result = self.space.call_valuestack(w_function, nargs, self)
-        finally:
-            self.dropvalues(nargs)
-        self.pushvalue(w_result)
-
     # overridden by faster version in the standard object space.
     LOOKUP_METHOD = LOAD_ATTR
     CALL_METHOD = CALL_FUNCTION
@@ -1091,12 +1079,10 @@
 
     @jit.unroll_safe
     def BUILD_SET(self, itemcount, next_instr):
-        w_set = self.space.call_function(self.space.w_set)
-        if itemcount:
-            w_add = self.space.getattr(w_set, self.space.wrap("add"))
-            for i in range(itemcount):
-                w_item = self.popvalue()
-                self.space.call_function(w_add, w_item)
+        w_set = self.space.newset()
+        for i in range(itemcount):
+            w_item = self.popvalue()
+            self.space.call_method(w_set, 'add', w_item)
         self.pushvalue(w_set)
 
     def STORE_MAP(self, oparg, next_instr):
diff --git a/pypy/interpreter/test/test_eval.py b/pypy/interpreter/test/test_eval.py
--- a/pypy/interpreter/test/test_eval.py
+++ b/pypy/interpreter/test/test_eval.py
@@ -15,16 +15,16 @@
                 self.code = code
                 Frame.__init__(self, space)
                 self.numlocals = numlocals
-                self.fastlocals_w = [None] * self.numlocals
+                self._fastlocals_w = [None] * self.numlocals
 
             def getcode(self):
                 return self.code
 
             def setfastscope(self, scope_w):
-                self.fastlocals_w = scope_w
+                self._fastlocals_w = scope_w
 
             def getfastscope(self):
-                return self.fastlocals_w
+                return self._fastlocals_w
 
             def getfastscopelength(self):
                 return self.numlocals
@@ -38,11 +38,11 @@
         self.f.fast2locals()
         assert space.eq_w(self.f.w_locals, self.space.wrap({}))
         
-        self.f.fastlocals_w[0] = w(5)
+        self.f._fastlocals_w[0] = w(5)
         self.f.fast2locals()
         assert space.eq_w(self.f.w_locals, self.space.wrap({'x': 5}))
 
-        self.f.fastlocals_w[2] = w(7)
+        self.f._fastlocals_w[2] = w(7)
         self.f.fast2locals()
         assert space.eq_w(self.f.w_locals, self.space.wrap({'x': 5, 'args': 7}))
 
@@ -57,13 +57,13 @@
         w = self.space.wrap
         self.f.w_locals = self.space.wrap({})
         self.f.locals2fast()
-        self.sameList(self.f.fastlocals_w, [None]*5)
+        self.sameList(self.f._fastlocals_w, [None]*5)
 
         self.f.w_locals = self.space.wrap({'x': 5})
         self.f.locals2fast()
-        self.sameList(self.f.fastlocals_w, [w(5)] + [None]*4)
+        self.sameList(self.f._fastlocals_w, [w(5)] + [None]*4)
 
         self.f.w_locals = self.space.wrap({'x':5, 'args':7})
         self.f.locals2fast()
-        self.sameList(self.f.fastlocals_w, [w(5), None, w(7),
-                                            None, None])
+        self.sameList(self.f._fastlocals_w, [w(5), None, w(7),
+                                             None, None])
diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py
--- a/pypy/interpreter/test/test_executioncontext.py
+++ b/pypy/interpreter/test/test_executioncontext.py
@@ -106,7 +106,7 @@
             if isinstance(seen[0], Method):
                 found = 'method %s of %s' % (
                     seen[0].w_function.name,
-                    seen[0].w_class.getname(space, '?'))
+                    seen[0].w_class.getname(space))
             else:
                 assert isinstance(seen[0], Function)
                 found = 'builtin %s' % seen[0].name
@@ -232,31 +232,6 @@
         assert [i[0] for i in events] == ['c_call', 'c_return', 'return', 'c_call']
         assert events[0][1] == events[1][1]
 
-    def test_tracing_range_builtinshortcut(self):
-        opts = {"objspace.opcodes.CALL_LIKELY_BUILTIN": True}
-        space = gettestobjspace(**opts)
-        source = """def f(profile):
-        import sys
-        sys.setprofile(profile)
-        range(10)
-        sys.setprofile(None)
-        """
-        w_events = space.appexec([space.wrap(source)], """(source):
-        import sys
-        l = []
-        def profile(frame, event, arg):
-            l.append((event, arg))
-        d = {}
-        exec source in d
-        f = d['f']
-        f(profile)
-        import dis
-        print dis.dis(f)
-        return l
-        """)
-        events = space.unwrap(w_events)
-        assert [i[0] for i in events] == ['c_call', 'c_return', 'c_call']
-
     def test_profile_and_exception(self):
         space = self.space
         w_res = space.appexec([], """():
@@ -280,9 +255,6 @@
         """)
 
 
-class TestExecutionContextWithCallLikelyBuiltin(TestExecutionContext):
-    keywords = {'objspace.opcodes.CALL_LIKELY_BUILTIN': True}
-
 class TestExecutionContextWithCallMethod(TestExecutionContext):
     keywords = {'objspace.opcodes.CALL_METHOD': True}
 
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -16,7 +16,7 @@
 
         def g():
             f()
-        
+
         try:
             g()
         except:
@@ -203,3 +203,27 @@
         lst = seen[:]
         assert lst == [5, 10, 2]
         raises(OSError, os.lseek, fd, 7, 0)
+
+    def test_method_attrs(self):
+        import sys
+        class A(object):
+            def m(self):
+                "aaa"
+            m.x = 3
+        class B(A):
+            pass
+
+        bm = B().m
+        assert bm.__func__ is bm.im_func
+        assert bm.__self__ is bm.im_self
+        assert bm.im_class is B
+        assert bm.__doc__ == "aaa"
+        assert bm.x == 3
+        raises(AttributeError, setattr, bm, 'x', 15)
+        l = []
+        assert l.append.__self__ is l
+        assert l.__add__.__self__ is l
+        # note: 'l.__add__.__objclass__' is not defined in pypy
+        # because it's a regular method, and .__objclass__
+        # differs from .im_class in case the method is
+        # defined in some parent class of l's actual class
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -9,7 +9,7 @@
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.tool.sourcetools import compile2, func_with_new_name
 from pypy.rlib.objectmodel import instantiate, compute_identity_hash, specialize
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import promote
 
 class TypeDef:
     def __init__(self, __name, __base=None, **rawdict):
@@ -206,7 +206,7 @@
             user_overridden_class = True
 
             def getclass(self, space):
-                return hint(self.w__class__, promote=True)
+                return promote(self.w__class__)
 
             def setclass(self, space, w_subtype):
                 # only used by descr_set___class__
@@ -761,12 +761,15 @@
     )
 Function.typedef.acceptable_as_base_class = False
 
-Method.typedef = TypeDef("method",
+Method.typedef = TypeDef(
+    "method",
     __new__ = interp2app(Method.descr_method__new__.im_func),
     __call__ = interp2app(Method.descr_method_call),
     __get__ = interp2app(Method.descr_method_get),
     im_func  = interp_attrproperty_w('w_function', cls=Method),
+    __func__ = interp_attrproperty_w('w_function', cls=Method),
     im_self  = interp_attrproperty_w('w_instance', cls=Method),
+    __self__ = interp_attrproperty_w('w_instance', cls=Method),
     im_class = interp_attrproperty_w('w_class', cls=Method),
     __getattribute__ = interp2app(Method.descr_method_getattribute),
     __eq__ = interp2app(Method.descr_method_eq),
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -703,22 +703,28 @@
         # we need to put two words into the shadowstack: the MARKER
         # and the address of the frame (ebp, actually)
         rst = gcrootmap.get_root_stack_top_addr()
-        assert rx86.fits_in_32bits(rst)
-        if IS_X86_64:
-            # cannot use rdx here, it's used to pass arguments!
-            tmp = X86_64_SCRATCH_REG
+        if rx86.fits_in_32bits(rst):
+            self.mc.MOV_rj(eax.value, rst)            # MOV eax, [rootstacktop]
         else:
-            tmp = edx
-        self.mc.MOV_rj(eax.value, rst)                # MOV eax, [rootstacktop]
-        self.mc.LEA_rm(tmp.value, (eax.value, 2*WORD))  # LEA edx, [eax+2*WORD]
+            self.mc.MOV_ri(r13.value, rst)            # MOV r13, rootstacktop
+            self.mc.MOV_rm(eax.value, (r13.value, 0)) # MOV eax, [r13]
+        #
+        self.mc.LEA_rm(ebx.value, (eax.value, 2*WORD))  # LEA ebx, [eax+2*WORD]
         self.mc.MOV_mi((eax.value, 0), gcrootmap.MARKER)    # MOV [eax], MARKER
         self.mc.MOV_mr((eax.value, WORD), ebp.value)      # MOV [eax+WORD], ebp
-        self.mc.MOV_jr(rst, tmp.value)                # MOV [rootstacktop], edx
+        #
+        if rx86.fits_in_32bits(rst):
+            self.mc.MOV_jr(rst, ebx.value)            # MOV [rootstacktop], ebx
+        else:
+            self.mc.MOV_mr((r13.value, 0), ebx.value) # MOV [r13], ebx
 
     def _call_footer_shadowstack(self, gcrootmap):
         rst = gcrootmap.get_root_stack_top_addr()
-        assert rx86.fits_in_32bits(rst)
-        self.mc.SUB_ji8(rst, 2*WORD)       # SUB [rootstacktop], 2*WORD
+        if rx86.fits_in_32bits(rst):
+            self.mc.SUB_ji8(rst, 2*WORD)       # SUB [rootstacktop], 2*WORD
+        else:
+            self.mc.MOV_ri(ebx.value, rst)           # MOV ebx, rootstacktop
+            self.mc.SUB_mi8((ebx.value, 0), 2*WORD)  # SUB [ebx], 2*WORD
 
     def _assemble_bootstrap_direct_call(self, arglocs, jmppos, stackdepth):
         if IS_X86_64:
@@ -889,7 +895,7 @@
 
     def regalloc_push(self, loc):
         if isinstance(loc, RegLoc) and loc.is_xmm:
-            self.mc.SUB_ri(esp.value, 2*WORD)
+            self.mc.SUB_ri(esp.value, 8)   # = size of doubles
             self.mc.MOVSD_sx(0, loc.value)
         elif WORD == 4 and isinstance(loc, StackLoc) and loc.width == 8:
             # XXX evil trick
@@ -901,7 +907,7 @@
     def regalloc_pop(self, loc):
         if isinstance(loc, RegLoc) and loc.is_xmm:
             self.mc.MOVSD_xs(loc.value, 0)
-            self.mc.ADD_ri(esp.value, 2*WORD)
+            self.mc.ADD_ri(esp.value, 8)   # = size of doubles
         elif WORD == 4 and isinstance(loc, StackLoc) and loc.width == 8:
             # XXX evil trick
             self.mc.POP_b(get_ebp_ofs(loc.position + 1))
diff --git a/pypy/jit/backend/x86/regloc.py b/pypy/jit/backend/x86/regloc.py
--- a/pypy/jit/backend/x86/regloc.py
+++ b/pypy/jit/backend/x86/regloc.py
@@ -318,7 +318,9 @@
             # must be careful not to combine it with location types that
             # might need to use the scratch register themselves.
             if loc2 is X86_64_SCRATCH_REG:
-                assert code1 != 'j'
+                if code1 == 'j':
+                    assert (name.startswith("MOV") and
+                            rx86.fits_in_32bits(loc1.value_j()))
             if loc1 is X86_64_SCRATCH_REG and not name.startswith("MOV"):
                 assert code2 not in ('j', 'i')
 
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -283,7 +283,7 @@
 # with immediate(argnum)).
 
 def encode_abs(mc, _1, _2, orbyte):
-    # expands to either '\x05' on 32-bit, or '\x04\x25' or 64-bit
+    # expands to either '\x05' on 32-bit, or '\x04\x25' on 64-bit
     if mc.WORD == 8:
         mc.writechar(chr(0x04 | orbyte))
         mc.writechar(chr(0x25))
@@ -370,6 +370,8 @@
     INSN_rj = insn(rex_w, chr(base+3), register(1,8), abs_, immediate(2))
     INSN_ji8 = insn(rex_w, '\x83', orbyte(base), abs_, immediate(1),
                     immediate(2,'b'))
+    INSN_mi8 = insn(rex_w, '\x83', orbyte(base), mem_reg_plus_const(1),
+                    immediate(2,'b'))
     INSN_bi8 = insn(rex_w, '\x83', orbyte(base), stack_bp(1), immediate(2,'b'))
     INSN_bi32= insn(rex_w, '\x81', orbyte(base), stack_bp(1), immediate(2))
 
@@ -388,7 +390,7 @@
     INSN_bi._always_inline_ = True      # try to constant-fold single_byte()
 
     return (INSN_ri, INSN_rr, INSN_rb, INSN_bi, INSN_br, INSN_rm, INSN_rj,
-            INSN_ji8)
+            INSN_ji8, INSN_mi8)
 
 def select_8_or_32_bit_immed(insn_8, insn_32):
     def INSN(*args):
@@ -467,13 +469,13 @@
 
     # ------------------------------ Arithmetic ------------------------------
 
-    ADD_ri, ADD_rr, ADD_rb, _, _, ADD_rm, ADD_rj, _ = common_modes(0)
-    OR_ri,  OR_rr,  OR_rb,  _, _, OR_rm,  OR_rj,  _ = common_modes(1)
-    AND_ri, AND_rr, AND_rb, _, _, AND_rm, AND_rj, _ = common_modes(4)
-    SUB_ri, SUB_rr, SUB_rb, _, _, SUB_rm, SUB_rj, SUB_ji8 = common_modes(5)
-    SBB_ri, SBB_rr, SBB_rb, _, _, SBB_rm, SBB_rj, _ = common_modes(3)
-    XOR_ri, XOR_rr, XOR_rb, _, _, XOR_rm, XOR_rj, _ = common_modes(6)
-    CMP_ri, CMP_rr, CMP_rb, CMP_bi, CMP_br, CMP_rm, CMP_rj, _ = common_modes(7)
+    ADD_ri,ADD_rr,ADD_rb,_,_,ADD_rm,ADD_rj,_,_ = common_modes(0)
+    OR_ri, OR_rr, OR_rb, _,_,OR_rm, OR_rj, _,_ = common_modes(1)
+    AND_ri,AND_rr,AND_rb,_,_,AND_rm,AND_rj,_,_ = common_modes(4)
+    SUB_ri,SUB_rr,SUB_rb,_,_,SUB_rm,SUB_rj,SUB_ji8,SUB_mi8 = common_modes(5)
+    SBB_ri,SBB_rr,SBB_rb,_,_,SBB_rm,SBB_rj,_,_ = common_modes(3)
+    XOR_ri,XOR_rr,XOR_rb,_,_,XOR_rm,XOR_rj,_,_ = common_modes(6)
+    CMP_ri,CMP_rr,CMP_rb,CMP_bi,CMP_br,CMP_rm,CMP_rj,_,_ = common_modes(7)
 
     CMP_mi8 = insn(rex_w, '\x83', orbyte(7<<3), mem_reg_plus_const(1), immediate(2, 'b'))
     CMP_mi32 = insn(rex_w, '\x81', orbyte(7<<3), mem_reg_plus_const(1), immediate(2))
diff --git a/pypy/jit/backend/x86/test/test_assembler.py b/pypy/jit/backend/x86/test/test_assembler.py
--- a/pypy/jit/backend/x86/test/test_assembler.py
+++ b/pypy/jit/backend/x86/test/test_assembler.py
@@ -1,13 +1,15 @@
 from pypy.jit.backend.x86.regloc import *
 from pypy.jit.backend.x86.assembler import Assembler386
 from pypy.jit.backend.x86.regalloc import X86FrameManager, get_ebp_ofs
-from pypy.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat, INT, REF, FLOAT
+from pypy.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat, ConstFloat
+from pypy.jit.metainterp.history import INT, REF, FLOAT
 from pypy.rlib.rarithmetic import intmask
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.jit.backend.x86.arch import WORD, IS_X86_32, IS_X86_64
 from pypy.jit.backend.detect_cpu import getcpuclass 
 from pypy.jit.backend.x86.regalloc import X86RegisterManager, X86_64_RegisterManager, X86XMMRegisterManager, X86_64_XMMRegisterManager
 from pypy.jit.codewriter import longlong
+import ctypes
 
 ACTUAL_CPU = getcpuclass()
 
@@ -238,3 +240,103 @@
         assert assembler.fail_boxes_int.getitem(i) == expected_ints[i]
         assert assembler.fail_boxes_ptr.getitem(i) == expected_ptrs[i]
         assert assembler.fail_boxes_float.getitem(i) == expected_floats[i]
+
+# ____________________________________________________________
+
+class TestRegallocPushPop(object):
+
+    def do_test(self, callback):
+        from pypy.jit.backend.x86.regalloc import X86FrameManager
+        from pypy.jit.backend.x86.regalloc import X86XMMRegisterManager
+        class FakeToken:
+            class compiled_loop_token:
+                asmmemmgr_blocks = None
+        cpu = ACTUAL_CPU(None, None)
+        cpu.setup()
+        looptoken = FakeToken()
+        asm = cpu.assembler
+        asm.setup_once()
+        asm.setup(looptoken)
+        self.fm = X86FrameManager()
+        self.xrm = X86XMMRegisterManager(None, frame_manager=self.fm,
+                                         assembler=asm)
+        callback(asm)
+        asm.mc.RET()
+        rawstart = asm.materialize_loop(looptoken)
+        #
+        F = ctypes.CFUNCTYPE(ctypes.c_long)
+        fn = ctypes.cast(rawstart, F)
+        res = fn()
+        return res
+
+    def test_simple(self):
+        def callback(asm):
+            asm.mov(imm(42), edx)
+            asm.regalloc_push(edx)
+            asm.regalloc_pop(eax)
+        res = self.do_test(callback)
+        assert res == 42
+
+    def test_push_stack(self):
+        def callback(asm):
+            loc = self.fm.frame_pos(5, INT)
+            asm.mc.SUB_ri(esp.value, 64)
+            asm.mov(imm(42), loc)
+            asm.regalloc_push(loc)
+            asm.regalloc_pop(eax)
+            asm.mc.ADD_ri(esp.value, 64)
+        res = self.do_test(callback)
+        assert res == 42
+
+    def test_pop_stack(self):
+        def callback(asm):
+            loc = self.fm.frame_pos(5, INT)
+            asm.mc.SUB_ri(esp.value, 64)
+            asm.mov(imm(42), edx)
+            asm.regalloc_push(edx)
+            asm.regalloc_pop(loc)
+            asm.mov(loc, eax)
+            asm.mc.ADD_ri(esp.value, 64)
+        res = self.do_test(callback)
+        assert res == 42
+
+    def test_simple_xmm(self):
+        def callback(asm):
+            c = ConstFloat(longlong.getfloatstorage(-42.5))
+            loc = self.xrm.convert_to_imm(c)
+            asm.mov(loc, xmm5)
+            asm.regalloc_push(xmm5)
+            asm.regalloc_pop(xmm0)
+            asm.mc.CVTTSD2SI(eax, xmm0)
+        res = self.do_test(callback)
+        assert res == -42
+
+    def test_push_stack_xmm(self):
+        def callback(asm):
+            c = ConstFloat(longlong.getfloatstorage(-42.5))
+            loc = self.xrm.convert_to_imm(c)
+            loc2 = self.fm.frame_pos(4, FLOAT)
+            asm.mc.SUB_ri(esp.value, 64)
+            asm.mov(loc, xmm5)
+            asm.mov(xmm5, loc2)
+            asm.regalloc_push(loc2)
+            asm.regalloc_pop(xmm0)
+            asm.mc.ADD_ri(esp.value, 64)
+            asm.mc.CVTTSD2SI(eax, xmm0)
+        res = self.do_test(callback)
+        assert res == -42
+
+    def test_pop_stack_xmm(self):
+        def callback(asm):
+            c = ConstFloat(longlong.getfloatstorage(-42.5))
+            loc = self.xrm.convert_to_imm(c)
+            loc2 = self.fm.frame_pos(4, FLOAT)
+            asm.mc.SUB_ri(esp.value, 64)
+            asm.mov(loc, xmm5)
+            asm.regalloc_push(xmm5)
+            asm.regalloc_pop(loc2)
+            asm.mov(loc2, xmm0)
+            asm.mc.ADD_ri(esp.value, 64)
+            asm.mc.CVTTSD2SI(eax, xmm0)
+        res = self.do_test(callback)
+        assert res == -42
diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py
--- a/pypy/jit/backend/x86/test/test_runner.py
+++ b/pypy/jit/backend/x86/test/test_runner.py
@@ -6,6 +6,7 @@
                                          ConstPtr, Box, BoxFloat, BasicFailDescr)
 from pypy.jit.backend.detect_cpu import getcpuclass
 from pypy.jit.backend.x86.arch import WORD
+from pypy.jit.backend.x86.rx86 import fits_in_32bits
 from pypy.jit.backend.llsupport import symbolic
 from pypy.jit.metainterp.resoperation import rop
 from pypy.jit.metainterp.executor import execute
@@ -241,6 +242,23 @@
         c = self.execute_operation(rop.GETFIELD_GC, [res], 'int', ofsc3)
         assert c.value == 3
 
+    def test_bug_setfield_64bit(self):
+        if WORD == 4:
+            py.test.skip("only for 64 bits")
+        TP = lltype.GcStruct('S', ('i', lltype.Signed))
+        ofsi = self.cpu.fielddescrof(TP, 'i')
+        for i in range(500):
+            p = lltype.malloc(TP)
+            addr = rffi.cast(lltype.Signed, p)
+            if fits_in_32bits(addr):
+                break    # fitting in 32 bits, good
+        else:
+            py.test.skip("cannot get a 32-bit pointer")
+        res = ConstPtr(rffi.cast(llmemory.GCREF, addr))
+        self.execute_operation(rop.SETFIELD_RAW, [res, ConstInt(3**33)],
+                               'void', ofsi)
+        assert p.i == 3**33
+
     def test_nullity_with_guard(self):
         allops = [rop.INT_IS_TRUE]
         guards = [rop.GUARD_TRUE, rop.GUARD_FALSE]
diff --git a/pypy/jit/backend/x86/test/test_rx86.py b/pypy/jit/backend/x86/test/test_rx86.py
--- a/pypy/jit/backend/x86/test/test_rx86.py
+++ b/pypy/jit/backend/x86/test/test_rx86.py
@@ -185,6 +185,13 @@
     cb = CodeBuilder32
     assert_encodes_as(cb, 'PUSH_i32', (9,), '\x68\x09\x00\x00\x00')
 
+def test_sub_ji8():
+    cb = CodeBuilder32
+    assert_encodes_as(cb, 'SUB_ji8', (11223344, 55),
+                      '\x83\x2D\x30\x41\xAB\x00\x37')
+    assert_encodes_as(cb, 'SUB_mi8', ((edx, 16), 55),
+                      '\x83\x6A\x10\x37')
+
 class CodeBuilder64(CodeBuilderMixin, X86_64_CodeBuilder):
     pass
 
diff --git a/pypy/jit/backend/x86/test/test_zrpy_gc.py b/pypy/jit/backend/x86/test/test_zrpy_gc.py
--- a/pypy/jit/backend/x86/test/test_zrpy_gc.py
+++ b/pypy/jit/backend/x86/test/test_zrpy_gc.py
@@ -10,7 +10,7 @@
 from pypy.rlib import rgc
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rlib.jit import JitDriver, dont_look_inside
-from pypy.rlib.jit import purefunction, unroll_safe
+from pypy.rlib.jit import elidable, unroll_safe
 from pypy.jit.backend.llsupport.gc import GcLLDescr_framework
 from pypy.tool.udir import udir
 from pypy.config.translationoption import DEFL_GC
@@ -561,7 +561,7 @@
         self.run('compile_framework_external_exception_handling')
 
     def define_compile_framework_bug1(self):
-        @purefunction
+        @elidable
         def nonmoving():
             x = X(1)
             for i in range(7):
diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py
--- a/pypy/jit/backend/x86/test/test_ztranslation.py
+++ b/pypy/jit/backend/x86/test/test_ztranslation.py
@@ -2,7 +2,7 @@
 from pypy.tool.udir import udir
 from pypy.rlib.jit import JitDriver, unroll_parameters
 from pypy.rlib.jit import PARAMETERS, dont_look_inside
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import promote
 from pypy.jit.metainterp.jitprof import Profiler
 from pypy.jit.backend.detect_cpu import getcpuclass
 from pypy.jit.backend.test.support import CCompiledMixin
@@ -78,8 +78,7 @@
             x = float(j)
             while i > 0:
                 jitdriver2.jit_merge_point(i=i, res=res, func=func, x=x)
-                jitdriver2.can_enter_jit(i=i, res=res, func=func, x=x)
-                func = hint(func, promote=True)
+                promote(func)
                 argchain = ArgChain()
                 argchain.arg(x)
                 res = func.call(argchain, rffi.DOUBLE)
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -208,12 +208,12 @@
         assert NON_VOID_ARGS == [T for T in ARGS if T is not lltype.Void]
         assert RESULT == FUNC.RESULT
         # ok
-        # get the 'pure' and 'loopinvariant' flags from the function object
-        pure = False
+        # get the 'elidable' and 'loopinvariant' flags from the function object
+        elidable = False
         loopinvariant = False
         if op.opname == "direct_call":
             func = getattr(get_funcobj(op.args[0].value), '_callable', None)
-            pure = getattr(func, "_pure_function_", False)
+            elidable = getattr(func, "_elidable_function_", False)
             loopinvariant = getattr(func, "_jit_loop_invariant_", False)
             if loopinvariant:
                 assert not NON_VOID_ARGS, ("arguments not supported for "
@@ -225,9 +225,9 @@
                 extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
             elif loopinvariant:
                 extraeffect = EffectInfo.EF_LOOPINVARIANT
-            elif pure:
+            elif elidable:
                 # XXX check what to do about exceptions (also MemoryError?)
-                extraeffect = EffectInfo.EF_PURE
+                extraeffect = EffectInfo.EF_ELIDABLE
             elif self._canraise(op):
                 extraeffect = EffectInfo.EF_CAN_RAISE
             else:
@@ -239,7 +239,7 @@
         #
         if oopspecindex != EffectInfo.OS_NONE:
             assert effectinfo is not None
-        if pure or loopinvariant:
+        if elidable or loopinvariant:
             assert effectinfo is not None
             assert extraeffect != EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
             # XXX this should also say assert not can_invalidate, but
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -9,7 +9,7 @@
     _cache = {}
 
     # the 'extraeffect' field is one of the following values:
-    EF_PURE                            = 0 #pure function (and cannot raise)
+    EF_ELIDABLE                        = 0 #elidable function (and cannot raise)
     EF_LOOPINVARIANT                   = 1 #special: call it only once per loop
     EF_CANNOT_RAISE                    = 2 #a function which cannot raise
     EF_CAN_RAISE                       = 3 #normal function (can raise)
@@ -75,12 +75,13 @@
     #
     OS_MATH_SQRT                = 100
 
-    def __new__(cls, readonly_descrs_fields,
+    def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
                 write_descrs_fields, write_descrs_arrays,
                 extraeffect=EF_CAN_RAISE,
                 oopspecindex=OS_NONE,
                 can_invalidate=False):
         key = (frozenset(readonly_descrs_fields),
+               frozenset(readonly_descrs_arrays),
                frozenset(write_descrs_fields),
                frozenset(write_descrs_arrays),
                extraeffect,
@@ -89,8 +90,9 @@
             return cls._cache[key]
         result = object.__new__(cls)
         result.readonly_descrs_fields = readonly_descrs_fields
+        result.readonly_descrs_arrays = readonly_descrs_arrays
         if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
-           extraeffect == EffectInfo.EF_PURE:            
+           extraeffect == EffectInfo.EF_ELIDABLE:
             result.write_descrs_fields = []
             result.write_descrs_arrays = []
         else:
@@ -119,7 +121,7 @@
     if effects is top_set:
         return None
     readonly_descrs_fields = []
-    # readonly_descrs_arrays = [] --- not enabled for now
+    readonly_descrs_arrays = []
     write_descrs_fields = []
     write_descrs_arrays = []
 
@@ -145,10 +147,13 @@
         elif tup[0] == "array":
             add_array(write_descrs_arrays, tup)
         elif tup[0] == "readarray":
-            pass
+            tupw = ("array",) + tup[1:]
+            if tupw not in effects:
+                add_array(readonly_descrs_arrays, tup)
         else:
             assert 0
     return EffectInfo(readonly_descrs_fields,
+                      readonly_descrs_arrays,
                       write_descrs_fields,
                       write_descrs_arrays,
                       extraeffect,
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -847,7 +847,7 @@
                 op1 = self.prepare_builtin_call(op, "llong_%s", args)
                 op2 = self._handle_oopspec_call(op1, args,
                                                 EffectInfo.OS_LLONG_%s,
-                                                EffectInfo.EF_PURE)
+                                                EffectInfo.EF_ELIDABLE)
                 if %r == "TO_INT":
                     assert op2.result.concretetype == lltype.Signed
                 return op2
@@ -1328,13 +1328,13 @@
                     otherindex += EffectInfo._OS_offset_uni
                 self._register_extra_helper(otherindex, othername,
                                             argtypes, resulttype,
-                                            EffectInfo.EF_PURE)
+                                            EffectInfo.EF_ELIDABLE)
         #
         return self._handle_oopspec_call(op, args, dict[oopspec_name],
-                                         EffectInfo.EF_PURE)
+                                         EffectInfo.EF_ELIDABLE)
 
     def _handle_str2unicode_call(self, op, oopspec_name, args):
-        # ll_str2unicode is not EF_PURE, because it can raise
+        # ll_str2unicode is not EF_ELIDABLE, because it can raise
         # UnicodeDecodeError...
         return self._handle_oopspec_call(op, args, EffectInfo.OS_STR2UNICODE)
 
@@ -1380,7 +1380,7 @@
     
     def _handle_math_sqrt_call(self, op, oopspec_name, args):
         return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT,
-                                         EffectInfo.EF_PURE)
+                                         EffectInfo.EF_ELIDABLE)
 
     def rewrite_op_jit_force_quasi_immutable(self, op):
         v_inst, c_fieldname = op.args
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -35,8 +35,8 @@
     def _reject_function(self, func):
         if hasattr(func, '_jit_look_inside_'):
             return not func._jit_look_inside_
-        # explicitly pure functions are always opaque
-        if getattr(func, '_pure_function_', False):
+        # explicitly elidable functions are always opaque
+        if getattr(func, '_elidable_function_', False):
             return True
         # pypy.rpython.module.* are opaque helpers
         mod = func.__module__ or '?'
diff --git a/pypy/jit/codewriter/test/test_effectinfo.py b/pypy/jit/codewriter/test/test_effectinfo.py
--- a/pypy/jit/codewriter/test/test_effectinfo.py
+++ b/pypy/jit/codewriter/test/test_effectinfo.py
@@ -34,6 +34,15 @@
     assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_arrays
 
+def test_include_read_array():
+    A = lltype.GcArray(lltype.Signed)
+    effects = frozenset([("readarray", lltype.Ptr(A))])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert not effectinfo.readonly_descrs_fields
+    assert list(effectinfo.readonly_descrs_arrays) == [('arraydescr', A)]
+    assert not effectinfo.write_descrs_fields
+    assert not effectinfo.write_descrs_arrays
+
 def test_include_write_array():
     A = lltype.GcArray(lltype.Signed)
     effects = frozenset([("array", lltype.Ptr(A))])
@@ -51,6 +60,16 @@
     assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
     assert not effectinfo.write_descrs_arrays
 
+def test_dont_include_read_and_write_array():
+    A = lltype.GcArray(lltype.Signed)
+    effects = frozenset([("readarray", lltype.Ptr(A)),
+                         ("array", lltype.Ptr(A))])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert not effectinfo.readonly_descrs_fields
+    assert not effectinfo.readonly_descrs_arrays
+    assert not effectinfo.write_descrs_fields
+    assert list(effectinfo.write_descrs_arrays) == [('arraydescr', A)]
+
 
 def test_filter_out_typeptr():
     effects = frozenset([("struct", lltype.Ptr(OBJECT), "typeptr")])
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -122,7 +122,7 @@
             if oopspecindex == EI.OS_STR2UNICODE:
                 assert extraeffect == None    # not pure, can raise!
             else:
-                assert extraeffect == EI.EF_PURE
+                assert extraeffect == EI.EF_ELIDABLE
         return 'calldescr-%d' % oopspecindex
     def calldescr_canraise(self, calldescr):
         return False
diff --git a/pypy/jit/codewriter/test/test_policy.py b/pypy/jit/codewriter/test/test_policy.py
--- a/pypy/jit/codewriter/test/test_policy.py
+++ b/pypy/jit/codewriter/test/test_policy.py
@@ -45,8 +45,8 @@
     policy.set_supports_floats(False)
     assert not policy.look_inside_graph(graph)
 
-def test_purefunction():
-    @jit.purefunction
+def test_elidable():
+    @jit.elidable
     def g(x):
         return x + 2
     graph = support.getgraph(g, [5])
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -3,7 +3,7 @@
 from pypy.rlib.rarithmetic import intmask, LONG_BIT, r_uint, ovfcheck
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.debug import debug_start, debug_stop
-from pypy.rlib.debug import make_sure_not_resized, fatalerror
+from pypy.rlib.debug import make_sure_not_resized
 from pypy.rpython.lltypesystem import lltype, llmemory, rclass
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.llinterp import LLException
diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -3,7 +3,7 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.objspace.flow.model import Constant, Variable
 from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.debug import debug_start, debug_stop
+from pypy.rlib.debug import debug_start, debug_stop, debug_print
 from pypy.rlib import rstack
 from pypy.conftest import option
 from pypy.tool.sourcetools import func_with_new_name
@@ -15,7 +15,7 @@
 from pypy.jit.metainterp import history
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
 from pypy.jit.metainterp.optimize import InvalidLoop
-from pypy.jit.metainterp.resume import NUMBERING
+from pypy.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP
 from pypy.jit.codewriter import heaptracker, longlong
 
 def giveup():
@@ -119,6 +119,7 @@
         old_loop_token = optimize_loop(metainterp_sd, old_loop_tokens, loop,
                                        jitdriver_sd.warmstate.enable_opts)
     except InvalidLoop:
+        debug_print("compile_new_loop: got an InvalidLoop")
         return None
     if old_loop_token is not None:
         metainterp.staticdata.log("reusing old loop")
@@ -302,7 +303,7 @@
     rd_numb = lltype.nullptr(NUMBERING)
     rd_consts = None
     rd_virtuals = None
-    rd_pendingfields = None
+    rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
 
     CNT_INT   = -0x20000000
     CNT_REF   = -0x40000000
@@ -633,6 +634,7 @@
                                             new_loop, state.enable_opts,
                                             inline_short_preamble, retraced)
     except InvalidLoop:
+        debug_print("compile_new_bridge: got an InvalidLoop")
         # XXX I am fairly convinced that optimize_bridge cannot actually raise
         # InvalidLoop
         return None
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -765,6 +765,7 @@
     """
     short_preamble = None
     failed_states = None
+    retraced_count = 0
     terminating = False # see TerminatingLoopToken in compile.py
     outermost_jitdriver_sd = None
     # and more data specified by the backend when the loop is compiled
@@ -791,6 +792,7 @@
 
     def dump(self):
         self.compiled_loop_token.cpu.dump_loop_token(self)
+
 class TreeLoop(object):
     inputargs = None
     operations = None
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -3,7 +3,7 @@
 from pypy.jit.metainterp.optimizeopt.intbounds import OptIntBounds
 from pypy.jit.metainterp.optimizeopt.virtualize import OptVirtualize
 from pypy.jit.metainterp.optimizeopt.heap import OptHeap
-from pypy.jit.metainterp.optimizeopt.string import OptString
+from pypy.jit.metainterp.optimizeopt.vstring import OptString
 from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll, OptInlineShortPreamble
 from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
 from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
@@ -21,15 +21,14 @@
 unroll_all_opts = unrolling_iterable(ALL_OPTS)
 
 ALL_OPTS_DICT = dict.fromkeys([name for name, _ in ALL_OPTS])
-
+ALL_OPTS_LIST = [name for name, _ in ALL_OPTS]
 ALL_OPTS_NAMES = ':'.join([name for name, _ in ALL_OPTS])
-PARAMETERS['enable_opts'] = ALL_OPTS_NAMES
 
 def build_opt_chain(metainterp_sd, enable_opts,
                     inline_short_preamble=True, retraced=False):
     config = metainterp_sd.config
     optimizations = []
-    unroll = 'unroll' in enable_opts
+    unroll = 'unroll' in enable_opts    # 'enable_opts' is normally a dict
     for name, opt in unroll_all_opts:
         if name in enable_opts:
             if opt is not None:
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -4,7 +4,7 @@
 from pypy.rlib.debug import debug_start, debug_stop, debug_print, have_debug_prints
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
 from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
 
@@ -203,13 +203,7 @@
     def propagate_forward(self, op):
         if self.logops is not None:
             debug_print(self.logops.repr_of_resop(op))
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
+        dispatch_opt(self, op)
 
     def _get_oopspec(self, op):
         effectinfo = op.getdescr().get_extra_info()
@@ -220,4 +214,5 @@
     def _get_funcval(self, op):
         return self.getvalue(op.getarg(1))
 
-optimize_ops = _findall(OptFfiCall, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptFfiCall, 'optimize_',
+        default=OptFfiCall.emit_operation)
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -1,5 +1,5 @@
 import os
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.jit.metainterp.jitexc import JitException
@@ -8,8 +8,8 @@
 
 class CachedField(object):
     def __init__(self):
-        # Cache information for a field descr.  It can be in one
-        # of two states:
+        # Cache information for a field descr, or for an (array descr, index)
+        # pair.  It can be in one of two states:
         #
         #   1. 'cached_fields' is a dict mapping OptValues of structs
         #      to OptValues of fields.  All fields on-heap are
@@ -27,19 +27,19 @@
         self._lazy_setfield_registered = False
 
     def do_setfield(self, optheap, op):
-        # Update the state with the SETFIELD_GC operation 'op'.
+        # Update the state with the SETFIELD_GC/SETARRAYITEM_GC operation 'op'.
         structvalue = optheap.getvalue(op.getarg(0))
-        fieldvalue  = optheap.getvalue(op.getarg(1))
+        fieldvalue  = optheap.getvalue(op.getarglist()[-1])
         if self.possible_aliasing(optheap, structvalue):
             self.force_lazy_setfield(optheap)
             assert not self.possible_aliasing(optheap, structvalue)
         cached_fieldvalue = self._cached_fields.get(structvalue, None)
         if cached_fieldvalue is not fieldvalue:
             # common case: store the 'op' as lazy_setfield, and register
-            # myself in the optheap's _lazy_setfields list
+            # myself in the optheap's _lazy_setfields_and_arrayitems list
             self._lazy_setfield = op
             if not self._lazy_setfield_registered:
-                optheap._lazy_setfields.append(self)
+                optheap._lazy_setfields_and_arrayitems.append(self)
                 self._lazy_setfield_registered = True
         else:
             # this is the case where the pending setfield ends up
@@ -65,7 +65,7 @@
         if self._lazy_setfield is not None:
             op = self._lazy_setfield
             assert optheap.getvalue(op.getarg(0)) is structvalue
-            return optheap.getvalue(op.getarg(1))
+            return optheap.getvalue(op.getarglist()[-1])
         else:
             return self._cached_fields.get(structvalue, None)
 
@@ -87,7 +87,7 @@
             # back in the cache: the value of this particular structure's
             # field.
             structvalue = optheap.getvalue(op.getarg(0))
-            fieldvalue  = optheap.getvalue(op.getarg(1))
+            fieldvalue  = optheap.getvalue(op.getarglist()[-1])
             self.remember_field_value(structvalue, fieldvalue)
 
     def get_reconstructed(self, optimizer, valuemap):
@@ -100,12 +100,6 @@
         return cf
 
 
-class CachedArrayItems(object):
-    def __init__(self):
-        self.fixed_index_items = {}
-        self.var_index_item = None
-        self.var_index_indexvalue = None
-
 class BogusPureField(JitException):
     pass
 
@@ -116,9 +110,10 @@
     def __init__(self):
         # cached fields:  {descr: CachedField}
         self.cached_fields = {}
-        self._lazy_setfields = []
-        # cached array items:  {descr: CachedArrayItems}
+        # cached array items:  {array descr: {index: CachedField}}
         self.cached_arrayitems = {}
+        #
+        self._lazy_setfields_and_arrayitems = []
         self._remove_guard_not_invalidated = False
         self._seen_guard_not_invalidated = False
 
@@ -126,34 +121,23 @@
         new = OptHeap()
 
         if True:
-            self.force_all_lazy_setfields()
+            self.force_all_lazy_setfields_and_arrayitems()
         else:
             assert 0   # was: new.lazy_setfields = self.lazy_setfields
 
         for descr, d in self.cached_fields.items():
             new.cached_fields[descr] = d.get_reconstructed(optimizer, valuemap)
 
-        new.cached_arrayitems = {}
-        for descr, d in self.cached_arrayitems.items():
-            newd = {}
-            new.cached_arrayitems[descr] = newd
-            for value, cache in d.items():
-                newcache = CachedArrayItems()
-                newd[value.get_reconstructed(optimizer, valuemap)] = newcache
-                if cache.var_index_item:
-                    newcache.var_index_item = \
-                          cache.var_index_item.get_reconstructed(optimizer, valuemap)
-                if cache.var_index_indexvalue:
-                    newcache.var_index_indexvalue = \
-                          cache.var_index_indexvalue.get_reconstructed(optimizer, valuemap)
-                for index, fieldvalue in cache.fixed_index_items.items():
-                    newcache.fixed_index_items[index] = \
-                           fieldvalue.get_reconstructed(optimizer, valuemap)
+        for descr, submap in self.cached_arrayitems.items():
+            newdict = {}
+            for index, d in submap.items():
+                newdict[index] = d.get_reconstructed(optimizer, valuemap)
+            new.cached_arrayitems[descr] = newdict
 
         return new
 
     def clean_caches(self):
-        del self._lazy_setfields[:]
+        del self._lazy_setfields_and_arrayitems[:]
         self.cached_fields.clear()
         self.cached_arrayitems.clear()
 
@@ -164,50 +148,16 @@
             cf = self.cached_fields[descr] = CachedField()
         return cf
 
-    def cache_arrayitem_value(self, descr, value, indexvalue, fieldvalue, write=False):
-        d = self.cached_arrayitems.get(descr, None)
-        if d is None:
-            d = self.cached_arrayitems[descr] = {}
-        cache = d.get(value, None)
-        if cache is None:
-            cache = d[value] = CachedArrayItems()
-        indexbox = self.get_constant_box(indexvalue.box)
-        if indexbox is not None:
-            index = indexbox.getint()
-            if write:
-                for value, othercache in d.iteritems():
-                    # fixed index, clean the variable index cache, in case the
-                    # index is the same
-                    othercache.var_index_indexvalue = None
-                    othercache.var_index_item = None
-                    try:
-                        del othercache.fixed_index_items[index]
-                    except KeyError:
-                        pass
-            cache.fixed_index_items[index] = fieldvalue
-        else:
-            if write:
-                for value, othercache in d.iteritems():
-                    # variable index, clear all caches for this descr
-                    othercache.var_index_indexvalue = None
-                    othercache.var_index_item = None
-                    othercache.fixed_index_items.clear()
-            cache.var_index_indexvalue = indexvalue
-            cache.var_index_item = fieldvalue
-
-    def read_cached_arrayitem(self, descr, value, indexvalue):
-        d = self.cached_arrayitems.get(descr, None)
-        if d is None:
-            return None
-        cache = d.get(value, None)
-        if cache is None:
-            return None
-        indexbox = self.get_constant_box(indexvalue.box)
-        if indexbox is not None:
-            return cache.fixed_index_items.get(indexbox.getint(), None)
-        elif cache.var_index_indexvalue is indexvalue:
-            return cache.var_index_item
-        return None
+    def arrayitem_cache(self, descr, index):
+        try:
+            submap = self.cached_arrayitems[descr]
+        except KeyError:
+            submap = self.cached_arrayitems[descr] = {}
+        try:
+            cf = submap[index]
+        except KeyError:
+            cf = submap[index] = CachedField()
+        return cf
 
     def emit_operation(self, op):
         self.emitting_operation(op)
@@ -219,7 +169,8 @@
         if op.is_ovf():
             return
         if op.is_guard():
-            self.optimizer.pendingfields = self.force_lazy_setfields_for_guard()
+            self.optimizer.pendingfields = (
+                self.force_lazy_setfields_and_arrayitems_for_guard())
             return
         opnum = op.getopnum()
         if (opnum == rop.SETFIELD_GC or        # handled specially
@@ -248,6 +199,8 @@
                 # XXX stored on effectinfo are large
                 for fielddescr in effectinfo.readonly_descrs_fields:
                     self.force_lazy_setfield(fielddescr)
+                for arraydescr in effectinfo.readonly_descrs_arrays:
+                    self.force_lazy_setarrayitem(arraydescr)
                 for fielddescr in effectinfo.write_descrs_fields:
                     self.force_lazy_setfield(fielddescr)
                     try:
@@ -256,8 +209,11 @@
                     except KeyError:
                         pass
                 for arraydescr in effectinfo.write_descrs_arrays:
+                    self.force_lazy_setarrayitem(arraydescr)
                     try:
-                        del self.cached_arrayitems[arraydescr]
+                        submap = self.cached_arrayitems[arraydescr]
+                        for cf in submap.itervalues():
+                            cf._cached_fields.clear()
                     except KeyError:
                         pass
                 if effectinfo.check_forces_virtual_or_virtualizable():
@@ -266,7 +222,7 @@
                     # ^^^ we only need to force this field; the other fields
                     # of virtualref_info and virtualizable_info are not gcptrs.
                 return
-        self.force_all_lazy_setfields()
+        self.force_all_lazy_setfields_and_arrayitems()
         self.clean_caches()
 
 
@@ -277,6 +233,10 @@
             for cf in self.cached_fields.itervalues():
                 if value in cf._cached_fields:
                     cf._cached_fields[newvalue] = cf._cached_fields[value]
+            for submap in self.cached_arrayitems.itervalues():
+                for cf in submap.itervalues():
+                    if value in cf._cached_fields:
+                        cf._cached_fields[newvalue] = cf._cached_fields[value]
 
     def force_lazy_setfield(self, descr):
         try:
@@ -285,6 +245,14 @@
             return
         cf.force_lazy_setfield(self)
 
+    def force_lazy_setarrayitem(self, arraydescr):
+        try:
+            submap = self.cached_arrayitems[arraydescr]
+        except KeyError:
+            return
+        for cf in submap.values():
+            cf.force_lazy_setfield(self)
+
     def fixup_guard_situation(self):
         # hackish: reverse the order of the last two operations if it makes
         # sense to avoid a situation like "int_eq/setfield_gc/guard_true",
@@ -309,30 +277,49 @@
         newoperations[-2] = lastop
         newoperations[-1] = prevop
 
-    def force_all_lazy_setfields(self):
-        for cf in self._lazy_setfields:
-            if not we_are_translated():
-                assert cf in self.cached_fields.values()
+    def _assert_valid_cf(self, cf):
+        # check that 'cf' is in cached_fields or cached_arrayitems
+        if not we_are_translated():
+            if cf not in self.cached_fields.values():
+                for submap in self.cached_arrayitems.values():
+                    if cf in submap.values():
+                        break
+                else:
+                    assert 0, "'cf' not in cached_fields/cached_arrayitems"
+
+    def force_all_lazy_setfields_and_arrayitems(self):
+        for cf in self._lazy_setfields_and_arrayitems:
+            self._assert_valid_cf(cf)
             cf.force_lazy_setfield(self)
 
-    def force_lazy_setfields_for_guard(self):
+    def force_lazy_setfields_and_arrayitems_for_guard(self):
         pendingfields = []
-        for cf in self._lazy_setfields:
-            if not we_are_translated():
-                assert cf in self.cached_fields.values()
+        for cf in self._lazy_setfields_and_arrayitems:
+            self._assert_valid_cf(cf)
             op = cf._lazy_setfield
             if op is None:
                 continue
             # the only really interesting case that we need to handle in the
             # guards' resume data is that of a virtual object that is stored
-            # into a field of a non-virtual object.
+            # into a field of a non-virtual object.  Here, 'op' in either
+            # SETFIELD_GC or SETARRAYITEM_GC.
             value = self.getvalue(op.getarg(0))
             assert not value.is_virtual()      # it must be a non-virtual
-            fieldvalue = self.getvalue(op.getarg(1))
+            fieldvalue = self.getvalue(op.getarglist()[-1])
             if fieldvalue.is_virtual():
                 # this is the case that we leave to resume.py
+                opnum = op.getopnum()
+                if opnum == rop.SETFIELD_GC:
+                    itemindex = -1
+                elif opnum == rop.SETARRAYITEM_GC:
+                    indexvalue = self.getvalue(op.getarg(1))
+                    assert indexvalue.is_constant()
+                    itemindex = indexvalue.box.getint()
+                    assert itemindex >= 0
+                else:
+                    assert 0
                 pendingfields.append((op.getdescr(), value.box,
-                                      fieldvalue.get_key_box()))
+                                      fieldvalue.get_key_box(), itemindex))
             else:
                 cf.force_lazy_setfield(self)
                 self.fixup_guard_situation()
@@ -364,24 +351,45 @@
         cf.do_setfield(self, op)
 
     def optimize_GETARRAYITEM_GC(self, op):
-        value = self.getvalue(op.getarg(0))
+        arrayvalue = self.getvalue(op.getarg(0))
         indexvalue = self.getvalue(op.getarg(1))
-        fieldvalue = self.read_cached_arrayitem(op.getdescr(), value, indexvalue)
-        if fieldvalue is not None:
-            self.make_equal_to(op.result, fieldvalue)
-            return
-        ###self.optimizer.optimize_default(op)
+        cf = None
+        if indexvalue.is_constant():
+            # use the cache on (arraydescr, index), which is a constant
+            cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
+            fieldvalue = cf.getfield_from_cache(self, arrayvalue)
+            if fieldvalue is not None:
+                self.make_equal_to(op.result, fieldvalue)
+                return
+        else:
+            # variable index, so make sure the lazy setarrayitems are done
+            self.force_lazy_setarrayitem(op.getdescr())
+        # default case: produce the operation
+        arrayvalue.ensure_nonnull()
         self.emit_operation(op)
-        fieldvalue = self.getvalue(op.result)
-        self.cache_arrayitem_value(op.getdescr(), value, indexvalue, fieldvalue)
+        # the remember the result of reading the array item
+        if cf is not None:
+            fieldvalue = self.getvalue(op.result)
+            cf.remember_field_value(arrayvalue, fieldvalue)
 
     def optimize_SETARRAYITEM_GC(self, op):
-        self.emit_operation(op)
-        value = self.getvalue(op.getarg(0))
-        fieldvalue = self.getvalue(op.getarg(2))
+        if self.has_pure_result(rop.GETARRAYITEM_GC_PURE, [op.getarg(0),
+                                                           op.getarg(1)],
+                                op.getdescr()):
+            os.write(2, '[bogus immutable array declaration: %s]\n' %
+                     (op.getdescr().repr_of_descr()))
+            raise BogusPureField
+        #
         indexvalue = self.getvalue(op.getarg(1))
-        self.cache_arrayitem_value(op.getdescr(), value, indexvalue, fieldvalue,
-                                   write=True)
+        if indexvalue.is_constant():
+            # use the cache on (arraydescr, index), which is a constant
+            cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
+            cf.do_setfield(self, op)
+        else:
+            # variable index, so make sure the lazy setarrayitems are done
+            self.force_lazy_setarrayitem(op.getdescr())
+            # and then emit the operation
+            self.emit_operation(op)
 
     def optimize_QUASIIMMUT_FIELD(self, op):
         # Pattern: QUASIIMMUT_FIELD(s, descr=QuasiImmutDescr)
@@ -423,13 +431,7 @@
         self._seen_guard_not_invalidated = True
         self.emit_operation(op)
 
-    def propagate_forward(self, op):
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
 
-optimize_ops = _findall(OptHeap, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_',
+        default=OptHeap.emit_operation)
+OptHeap.propagate_forward = dispatch_opt
diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py
--- a/pypy/jit/metainterp/optimizeopt/intbounds.py
+++ b/pypy/jit/metainterp/optimizeopt/intbounds.py
@@ -1,7 +1,7 @@
 from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, CONST_1, CONST_0
-from pypy.jit.metainterp.optimizeopt.util import _findall
-from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded, \
-    IntLowerBound, IntUpperBound
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.optimizeopt.intutils import (IntBound, IntUnbounded,
+    IntLowerBound, IntUpperBound)
 from pypy.jit.metainterp.history import Const, ConstInt
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 
@@ -34,14 +34,11 @@
             op = self.posponedop
             self.posponedop = None
 
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            assert not op.is_ovf()
-            self.emit_operation(op)
+        dispatch_opt(self, op)
+
+    def opt_default(self, op):
+        assert not op.is_ovf()
+        self.emit_operation(op)
 
 
     def propagate_bounds_backward(self, box):
@@ -57,11 +54,7 @@
             op = self.optimizer.producer[box]
         except KeyError:
             return
-        opnum = op.getopnum()
-        for value, func in propagate_bounds_ops:
-            if opnum == value:
-                func(self, op)
-                break
+        dispatch_bounds_ops(self, op)
 
     def optimize_GUARD_TRUE(self, op):
         self.emit_operation(op)
@@ -373,6 +366,27 @@
                 if v2.intbound.intersect(v1.intbound):
                     self.propagate_bounds_backward(op.getarg(1))
 
+    def propagate_bounds_INT_IS_TRUE(self, op):
+        r = self.getvalue(op.result)
+        if r.is_constant():
+            if r.box.same_constant(CONST_1):
+                v1 = self.getvalue(op.getarg(0))
+                if v1.intbound.known_ge(IntBound(0, 0)):
+                    v1.intbound.make_gt(IntBound(0, 0))
+                    self.propagate_bounds_backward(op.getarg(0))
+
+    def propagate_bounds_INT_IS_ZERO(self, op):
+        r = self.getvalue(op.result)
+        if r.is_constant():
+            if r.box.same_constant(CONST_1):
+                v1 = self.getvalue(op.getarg(0))
+                # Clever hack, we can't use self.make_constant_int yet because
+                # the args aren't in the values dictionary yet so it runs into
+                # an assert, this is a clever way of expressing the same thing.
+                v1.intbound.make_ge(IntBound(0, 0))
+                v1.intbound.make_lt(IntBound(1, 1))
+                self.propagate_bounds_backward(op.getarg(0))
+
     def propagate_bounds_INT_ADD(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
@@ -418,5 +432,7 @@
     propagate_bounds_INT_SUB_OVF  = propagate_bounds_INT_SUB
     propagate_bounds_INT_MUL_OVF  = propagate_bounds_INT_MUL
 
-optimize_ops = _findall(OptIntBounds, 'optimize_')
-propagate_bounds_ops = _findall(OptIntBounds, 'propagate_bounds_')
+
+dispatch_opt = make_dispatcher_method(OptIntBounds, 'optimize_',
+        default=OptIntBounds.opt_default)
+dispatch_bounds_ops = make_dispatcher_method(OptIntBounds, 'propagate_bounds_')
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -4,7 +4,7 @@
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.metainterp import jitprof
 from pypy.jit.metainterp.executor import execute_nonspec
-from pypy.jit.metainterp.optimizeopt.util import _findall, sort_descrs
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method, sort_descrs
 from pypy.jit.metainterp.optimizeopt.util import descrlist_dict, args_dict
 from pypy.jit.metainterp.optimize import InvalidLoop
 from pypy.jit.metainterp import resume, compile
@@ -434,14 +434,7 @@
 
     def propagate_forward(self, op):
         self.producer[op.result] = op
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.optimize_default(op)
-        #print '\n'.join([str(o) for o in self.newoperations]) + '\n---\n'
+        dispatch_opt(self, op)
 
     def test_emittable(self, op):
         return True
@@ -569,7 +562,8 @@
     def optimize_DEBUG_MERGE_POINT(self, op):
         self.emit_operation(op)
 
-optimize_ops = _findall(Optimizer, 'optimize_')
+dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
+        default=Optimizer.optimize_default)
 
 
 
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -1,7 +1,7 @@
 from pypy.jit.metainterp.optimizeopt.optimizer import *
 from pypy.jit.metainterp.resoperation import opboolinvers, opboolreflex
 from pypy.jit.metainterp.history import ConstInt
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import _findall, make_dispatcher_method
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.metainterp.optimizeopt.intutils import IntBound
@@ -21,18 +21,13 @@
         if self.find_rewritable_bool(op, args):
             return
 
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
+        dispatch_opt(self, op)
 
     def test_emittable(self, op):
         opnum = op.getopnum()
-        for value, func in optimize_guards:
+        for value, cls, func in optimize_guards:
             if opnum == value:
+                assert isinstance(op, cls)
                 try:
                     func(self, op, dryrun=True)
                     return self.is_emittable(op)
@@ -477,5 +472,6 @@
         self.emit_operation(op)
 
 
-optimize_ops = _findall(OptRewrite, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptRewrite, 'optimize_',
+        default=OptRewrite.emit_operation)
 optimize_guards = _findall(OptRewrite, 'optimize_', 'GUARD')
diff --git a/pypy/jit/metainterp/optimizeopt/simplify.py b/pypy/jit/metainterp/optimizeopt/simplify.py
--- a/pypy/jit/metainterp/optimizeopt/simplify.py
+++ b/pypy/jit/metainterp/optimizeopt/simplify.py
@@ -1,7 +1,7 @@
 
 from pypy.jit.metainterp.resoperation import ResOperation, rop
 from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 
 class OptSimplify(Optimization):
     def optimize_CALL_PURE(self, op):
@@ -25,13 +25,7 @@
         #     but it's a bit hard to implement robustly if heap.py is also run
         pass
 
-    def propagate_forward(self, op):
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
 
-optimize_ops = _findall(OptSimplify, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
+        default=OptSimplify.emit_operation)
+OptSimplify.propagate_forward = dispatch_opt
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -121,6 +121,41 @@
         print '\n'.join([str(o) for o in loop.operations])
         self.assert_equal(loop, expected)
 
+    def setup_method(self, meth=None):
+        class FailDescr(compile.ResumeGuardDescr):
+            oparse = None
+            def _oparser_uses_descr_of_guard(self, oparse, fail_args):
+                # typically called 3 times: once when parsing 'ops',
+                # once when parsing 'preamble', once when parsing 'expected'.
+                self.oparse = oparse
+                self.rd_frame_info_list, self.rd_snapshot = snapshot(fail_args)
+            def _clone_if_mutable(self):
+                assert self is fdescr
+                return fdescr2
+            def __repr__(self):
+                if self is fdescr:
+                    return 'fdescr'
+                if self is fdescr2:
+                    return 'fdescr2'
+                return compile.ResumeGuardDescr.__repr__(self)
+        #
+        def snapshot(fail_args, got=[]):
+            if not got:    # only the first time, i.e. when parsing 'ops'
+                rd_frame_info_list = resume.FrameInfo(None, "code", 11)
+                rd_snapshot = resume.Snapshot(None, fail_args)
+                got.append(rd_frame_info_list)
+                got.append(rd_snapshot)
+            return got
+        #
+        fdescr = instantiate(FailDescr)
+        self.namespace['fdescr'] = fdescr
+        fdescr2 = instantiate(FailDescr)
+        self.namespace['fdescr2'] = fdescr2
+
+    def teardown_method(self, meth):
+        self.namespace.pop('fdescr', None)
+        self.namespace.pop('fdescr2', None)
+
 
 
 class BaseTestOptimizeBasic(BaseTestBasic):
@@ -404,6 +439,23 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_int_is_zero_int_is_true(self):
+        ops = """
+        [i0]
+        i1 = int_is_zero(i0)
+        guard_true(i1) []
+        i2 = int_is_true(i0)
+        guard_false(i2) []
+        jump(i0)
+        """
+        expected = """
+        [i0]
+        i1 = int_is_zero(i0)
+        guard_true(i1) []
+        jump(0)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_ooisnull_oononnull_2(self):
         ops = """
         [p0]
@@ -1070,8 +1122,8 @@
         """
         expected = """
         [i1, p0]
+        p1 = new_array(i1, descr=arraydescr)
         setarrayitem_gc(p0, 0, i1, descr=arraydescr)
-        p1 = new_array(i1, descr=arraydescr)
         jump(i1, p1)
         """
         self.optimize_loop(ops, expected)
@@ -1436,9 +1488,9 @@
         i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
         i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
         i5 = int_add(i3, i4)
-        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         #
         setfield_gc(p1, i2, descr=valuedescr)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         setfield_gc(p1, i4, descr=nextdescr)
         jump(p1, i1, i2, p3)
         """
@@ -1612,6 +1664,7 @@
         self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_2(self):
+        py.test.skip("setarrayitem with variable index")
         ops = """
         [p1, p2, p3, i1]
         setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
@@ -1874,7 +1927,6 @@
         self.optimize_loop(ops, expected)
 
     def test_merge_guard_nonnull_guard_class(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -1892,7 +1944,6 @@
         self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS)
 
     def test_merge_guard_nonnull_guard_value(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -1910,7 +1961,6 @@
         self.check_expanded_fail_descr("i0", rop.GUARD_VALUE)
 
     def test_merge_guard_nonnull_guard_class_guard_value(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -2203,23 +2253,6 @@
 
     # ----------
 
-    def make_fail_descr(self):
-        class FailDescr(compile.ResumeGuardDescr):
-            oparse = None
-            def _oparser_uses_descr_of_guard(self, oparse, fail_args):
-                # typically called twice, before and after optimization
-                if self.oparse is None:
-                    fdescr.rd_frame_info_list = resume.FrameInfo(None,
-                                                                 "code", 11)
-                    fdescr.rd_snapshot = resume.Snapshot(None, fail_args)
-                self.oparse = oparse
-        #
-        fdescr = instantiate(FailDescr)
-        self.namespace['fdescr'] = fdescr
-
-    def teardown_method(self, meth):
-        self.namespace.pop('fdescr', None)
-
     def _verify_fail_args(self, boxes, oparse, text):
         import re
         r = re.compile(r"\bwhere\s+(\w+)\s+is a\s+(\w+)")
@@ -2328,7 +2361,6 @@
         self._verify_fail_args(boxes, fdescr.oparse, expectedtext)
 
     def test_expand_fail_1(self):
-        self.make_fail_descr()
         ops = """
         [i1, i3]
         # first rename i3 into i4
@@ -2349,7 +2381,6 @@
         self.check_expanded_fail_descr('15, i3', rop.GUARD_TRUE)
 
     def test_expand_fail_2(self):
-        self.make_fail_descr()
         ops = """
         [i1, i2]
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -2369,7 +2400,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_3(self):
-        self.make_fail_descr()
         ops = """
         [i1, i2, i3, p3]
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -2395,7 +2425,7 @@
     def test_expand_fail_4(self):
         for arg in ['p1', 'i2,p1', 'p1,p2', 'p2,p1',
                     'i2,p1,p2', 'i2,p2,p1']:
-            self.make_fail_descr()
+            self.setup_method() # humpf
             ops = """
             [i1, i2, i3]
             p1 = new_with_vtable(ConstClass(node_vtable))
@@ -2420,7 +2450,6 @@
                                            rop.GUARD_TRUE)
 
     def test_expand_fail_5(self):
-        self.make_fail_descr()
         ops = """
         [i1, i2, i3, i4]
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -2444,7 +2473,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_6(self):
-        self.make_fail_descr()
         ops = """
         [p0, i0, i1]
         guard_true(i0, descr=fdescr) [p0]
@@ -2465,7 +2493,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_varray(self):
-        self.make_fail_descr()
         ops = """
         [i1]
         p1 = new_array(3, descr=arraydescr)
@@ -2486,7 +2513,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_vstruct(self):
-        self.make_fail_descr()
         ops = """
         [i1, p1]
         p2 = new(descr=ssize)
@@ -2508,7 +2534,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_v_all_1(self):
-        self.make_fail_descr()
         ops = """
         [i1, p1a, i2]
         p6s = getarrayitem_gc(p1a, 0, descr=arraydescr2)
@@ -2550,7 +2575,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_lazy_setfield_1(self):
-        self.make_fail_descr()
         ops = """
         [p1, i2, i3]
         p2 = new_with_vtable(ConstClass(node_vtable))
@@ -2576,7 +2600,6 @@
             ''', rop.GUARD_TRUE)
 
     def test_expand_fail_lazy_setfield_2(self):
-        self.make_fail_descr()
         ops = """
         [i2, i3]
         p2 = new_with_vtable(ConstClass(node_vtable))
@@ -2600,9 +2623,6 @@
             where p2 is a node_vtable, valuedescr=i2
             ''', rop.GUARD_TRUE)
 
-
-class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
-
     def test_residual_call_does_not_invalidate_caches(self):
         ops = """
         [p1, p2]
@@ -2894,7 +2914,6 @@
         self.optimize_loop(ops, expected)
 
     def test_vref_virtual_2(self):
-        self.make_fail_descr()
         ops = """
         [p0, i1]
         #
@@ -2940,7 +2959,6 @@
             ''', rop.GUARD_NOT_FORCED)
 
     def test_vref_virtual_and_lazy_setfield(self):
-        self.make_fail_descr()
         ops = """
         [p0, i1]
         #
@@ -2979,7 +2997,6 @@
             ''', rop.GUARD_NO_EXCEPTION)
 
     def test_vref_virtual_after_finish(self):
-        self.make_fail_descr()
         ops = """
         [i1]
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -3006,7 +3023,6 @@
         self.optimize_loop(ops, expected)
 
     def test_vref_nonvirtual_and_lazy_setfield(self):
-        self.make_fail_descr()
         ops = """
         [i1, p1]
         p2 = virtual_ref(p1, 23)
@@ -4124,7 +4140,6 @@
 
     # ----------
     def optimize_strunicode_loop_extradescrs(self, ops, optops):
-        from pypy.jit.metainterp.optimizeopt import string
         class FakeCallInfoCollection:
             def callinfo_for_oopspec(self, oopspecindex):
                 calldescrtype = type(LLtypeMixin.strequaldescr)
@@ -4498,6 +4513,29 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_int_is_true_bounds(self):
+        ops = """
+        [p0]
+        i0 = strlen(p0)
+        i1 = int_is_true(i0)
+        guard_true(i1) []
+        i2 = int_ge(0, i0)
+        guard_false(i2) []
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        i0 = strlen(p0)
+        i1 = int_is_true(i0)
+        guard_true(i1) []
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+
+class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
+    pass
+
 
 ##class TestOOtype(BaseTestOptimizeBasic, OOtypeMixin):
 
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
@@ -51,7 +51,7 @@
                              restype=types.sint)
         #
         def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
-            einfo = EffectInfo([], [], [], oopspecindex=oopspecindex,
+            einfo = EffectInfo([], [], [], [], oopspecindex=oopspecindex,
                                extraeffect=extraeffect)
             return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
         #
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -1381,8 +1381,8 @@
         """
         expected = """
         [i1, p0]
+        p1 = new_array(i1, descr=arraydescr)
         setarrayitem_gc(p0, 0, i1, descr=arraydescr)
-        p1 = new_array(i1, descr=arraydescr)
         jump(i1, p1)
         """
         self.optimize_loop(ops, expected)
@@ -1806,9 +1806,9 @@
         i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
         i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
         i5 = int_add(i3, i4)
-        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         #
         setfield_gc(p1, i2, descr=valuedescr)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         setfield_gc(p1, i4, descr=nextdescr)
         escape()
         jump(p1, i1, i2, p3, i3)
@@ -1818,9 +1818,9 @@
         #
         i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
         i5 = int_add(i3, i4)
-        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         #
         setfield_gc(p1, i2, descr=valuedescr)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
         setfield_gc(p1, i4, descr=nextdescr)
         escape()
         jump(p1, i1, i2, p3, i3)
@@ -2055,6 +2055,7 @@
         self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_2(self):
+        py.test.skip("setarrayitem with variable index")
         ops = """
         [p1, p2, p3, i1]
         setarrayitem_gc(p1, 0, p2, descr=arraydescr2)
@@ -2741,8 +2742,6 @@
 
     # ----------
 
-class TestLLtype(OptimizeOptTest, LLtypeMixin):
-
     def test_residual_call_does_not_invalidate_caches(self):
         ops = """
         [p1, p2]
@@ -5374,7 +5373,6 @@
 
     # ----------
     def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None):
-        from pypy.jit.metainterp.optimizeopt import string
         class FakeCallInfoCollection:
             def callinfo_for_oopspec(self, oopspecindex):
                 calldescrtype = type(LLtypeMixin.strequaldescr)
@@ -5876,4 +5874,28 @@
         escape(p0)
         jump(p0)
         """
-        self.optimize_loop(ops, expected)
\ No newline at end of file
+        self.optimize_loop(ops, expected)
+
+    def test_setarrayitem_lazy(self):
+        ops = """
+        [i0, i1]
+        p0 = escape()
+        i2 = escape()
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setarrayitem_gc(p0, 2, p1, descr=arraydescr)
+        guard_true(i2) []
+        setarrayitem_gc(p0, 2, p0, descr=arraydescr)
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        p0 = escape()
+        i2 = escape()
+        guard_true(i2) [p0]
+        setarrayitem_gc(p0, 2, p0, descr=arraydescr)
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+class TestLLtype(OptimizeOptTest, LLtypeMixin):
+    pass
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -166,19 +166,19 @@
     FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
     plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
     nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                    EffectInfo([], [], []))
+                                    EffectInfo([], [], [], []))
     writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                  EffectInfo([], [adescr], []))
+                                  EffectInfo([], [], [adescr], []))
     writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                      EffectInfo([], [adescr], [arraydescr]))
+                                  EffectInfo([], [], [adescr], [arraydescr]))
     readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                 EffectInfo([adescr], [], []))
+                                 EffectInfo([adescr], [], [], []))
     mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([nextdescr], [], [],
+                 EffectInfo([nextdescr], [], [], [],
                             EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE,
                             can_invalidate=True))
     arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
+             EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
 
     for _name, _os in [
         ('strconcatdescr',               'OS_STR_CONCAT'),
@@ -195,15 +195,15 @@
         _oopspecindex = getattr(EffectInfo, _os)
         locals()[_name] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=_oopspecindex))
+                EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
         #
         _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
         locals()[_name.replace('str', 'unicode')] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=_oopspecindex))
+                EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
 
     s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
+            EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
     #
 
     class LoopToken(AbstractDescr):
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -546,7 +546,7 @@
             effectinfo = descr.get_extra_info()
             if effectinfo is not None:
                 if effectinfo.extraeffect == EffectInfo.EF_LOOPINVARIANT or \
-                   effectinfo.extraeffect == EffectInfo.EF_PURE:
+                   effectinfo.extraeffect == EffectInfo.EF_ELIDABLE:
                     return True
         return False
     
@@ -676,24 +676,28 @@
                             jumpop = self.optimizer.newoperations.pop()
                             assert jumpop.getopnum() == rop.JUMP
                             for guard in extra_guards:
-                                descr = sh.start_resumedescr.clone_if_mutable()
-                                self.inliner.inline_descr_inplace(descr)
-                                guard.setdescr(descr)
+                                d = sh.start_resumedescr.clone_if_mutable()
+                                self.inliner.inline_descr_inplace(d)
+                                guard.setdescr(d)
                                 self.emit_operation(guard)
                             self.optimizer.newoperations.append(jumpop)
                         return
-                retraced_count = len(short)
-                if descr.failed_states:
-                    retraced_count += len(descr.failed_states)
+                retraced_count = descr.retraced_count
+                descr.retraced_count += 1
                 limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit
                 if not self.retraced and retraced_count<limit:
                     if not descr.failed_states:
+                        debug_print("Retracing (%d of %d)" % (retraced_count,
+                                                              limit))
                         raise RetraceLoop
                     for failed in descr.failed_states:
                         if failed.generalization_of(virtual_state):
                             # Retracing once more will most likely fail again
                             break
                     else:
+                        debug_print("Retracing (%d of %d)" % (retraced_count,
+                                                              limit))
+                                                              
                         raise RetraceLoop
                 else:
                     if not descr.failed_states:
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -20,9 +20,25 @@
         if op_prefix and not name.startswith(op_prefix):
             continue
         if hasattr(Class, name_prefix + name):
-            result.append((value, getattr(Class, name_prefix + name)))
+            opclass = resoperation.opclasses[getattr(rop, name)]
+            print value, name, opclass
+            result.append((value, opclass, getattr(Class, name_prefix + name)))
     return unrolling_iterable(result)
 
+def make_dispatcher_method(Class, name_prefix, op_prefix=None, default=None):
+    ops = _findall(Class, name_prefix, op_prefix)
+    def dispatch(self, op, *args):
+        opnum = op.getopnum()
+        for value, cls, func in ops:
+            if opnum == value:
+                assert isinstance(op, cls)
+                return func(self, op, *args)
+        if default:
+            return default(self, op, *args)
+    dispatch.func_name = "dispatch_" + name_prefix
+    return dispatch
+
+
 def partition(array, left, right):
     last_item = array[right]
     pivot = last_item.sort_key()
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -1,7 +1,7 @@
 from pypy.jit.metainterp.history import Const, ConstInt, BoxInt
 from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt.util import _findall, sort_descrs
-from pypy.jit.metainterp.optimizeopt.util import descrlist_dict
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.optimizeopt.util import descrlist_dict, sort_descrs
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.jit.metainterp.optimizeopt import optimizer
 from pypy.jit.metainterp.executor import execute
@@ -456,13 +456,8 @@
         ###self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
         self.emit_operation(op)
 
-    def propagate_forward(self, op):
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
 
-optimize_ops = _findall(OptVirtualize, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptVirtualize, 'optimize_',
+        default=OptVirtualize.emit_operation)
+
+OptVirtualize.propagate_forward = dispatch_opt
diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/vstring.py
rename from pypy/jit/metainterp/optimizeopt/string.py
rename to pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/string.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -8,7 +8,7 @@
 from pypy.jit.metainterp.optimizeopt import optimizer, virtualize
 from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
 from pypy.jit.metainterp.optimizeopt.optimizer import llhelper
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.codewriter import heaptracker
 from pypy.rlib.unroll import unrolling_iterable
@@ -649,16 +649,11 @@
             self.emit_operation(op)
             return
 
-        opnum = op.getopnum()
-        for value, func in optimize_ops:
-            if opnum == value:
-                func(self, op)
-                break
-        else:
-            self.emit_operation(op)
+        dispatch_opt(self, op)
 
 
-optimize_ops = _findall(OptString, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptString, 'optimize_',
+        default=OptString.emit_operation)
 
 def _findall_call_oopspec():
     prefix = 'opt_call_stroruni_'
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -867,7 +867,7 @@
         any_operation = len(self.metainterp.history.operations) > 0
         jitdriver_sd = self.metainterp.staticdata.jitdrivers_sd[jdindex]
         self.verify_green_args(jitdriver_sd, greenboxes)
-        self.debug_merge_point(jdindex, self.metainterp.in_recursion,
+        self.debug_merge_point(jitdriver_sd, jdindex, self.metainterp.in_recursion,
                                greenboxes)
 
         if self.metainterp.seen_loop_header_for_jdindex < 0:
@@ -914,8 +914,10 @@
                                     assembler_call=True)
             raise ChangeFrame
 
-    def debug_merge_point(self, jd_index, in_recursion, greenkey):
+    def debug_merge_point(self, jitdriver_sd, jd_index, in_recursion, greenkey):
         # debugging: produce a DEBUG_MERGE_POINT operation
+        loc = jitdriver_sd.warmstate.get_location_str(greenkey)
+        debug_print(loc)
         args = [ConstInt(jd_index), ConstInt(in_recursion)] + greenkey
         self.metainterp.history.record(rop.DEBUG_MERGE_POINT, args, None)
 
@@ -1231,7 +1233,7 @@
             effect = effectinfo.extraeffect
             if effect == effectinfo.EF_CANNOT_RAISE:
                 return self.execute_varargs(rop.CALL, allboxes, descr, False)
-            elif effect == effectinfo.EF_PURE:
+            elif effect == effectinfo.EF_ELIDABLE:
                 return self.metainterp.record_result_of_call_pure(
                     self.execute_varargs(rop.CALL, allboxes, descr, False))
             elif effect == effectinfo.EF_LOOPINVARIANT:
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -2,10 +2,12 @@
 from pypy.jit.metainterp.history import Box, Const, ConstInt, getkind
 from pypy.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat
 from pypy.jit.metainterp.history import INT, REF, FLOAT, HOLE
+from pypy.jit.metainterp.history import AbstractDescr
 from pypy.jit.metainterp.resoperation import rop
 from pypy.jit.metainterp import jitprof
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
+from pypy.rpython import annlowlevel
 from pypy.rlib import rarithmetic, rstack
 from pypy.rlib.objectmodel import we_are_translated, specialize
 from pypy.rlib.debug import have_debug_prints, ll_assert
@@ -82,6 +84,13 @@
                             ('nums', lltype.Array(rffi.SHORT)))
 NUMBERINGP.TO.become(NUMBERING)
 
+PENDINGFIELDSTRUCT = lltype.Struct('PendingField',
+                                   ('lldescr', annlowlevel.base_ptr_lltype()),
+                                   ('num', rffi.SHORT),
+                                   ('fieldnum', rffi.SHORT),
+                                   ('itemindex', rffi.INT))
+PENDINGFIELDSP = lltype.Ptr(lltype.GcArray(PENDINGFIELDSTRUCT))
+
 TAGMASK = 3
 
 def tag(value, tagbits):
@@ -329,7 +338,7 @@
                 value = values[box]
                 value.get_args_for_fail(self)
 
-        for _, box, fieldbox in pending_setfields:
+        for _, box, fieldbox, _ in pending_setfields:
             self.register_box(box)
             self.register_box(fieldbox)
             value = values[fieldbox]
@@ -405,13 +414,25 @@
         return False
 
     def _add_pending_fields(self, pending_setfields):
-        rd_pendingfields = None
+        rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
         if pending_setfields:
-            rd_pendingfields = []
-            for descr, box, fieldbox in pending_setfields:
+            n = len(pending_setfields)
+            rd_pendingfields = lltype.malloc(PENDINGFIELDSP.TO, n)
+            for i in range(n):
+                descr, box, fieldbox, itemindex = pending_setfields[i]
+                lldescr = annlowlevel.cast_instance_to_base_ptr(descr)
                 num = self._gettagged(box)
                 fieldnum = self._gettagged(fieldbox)
-                rd_pendingfields.append((descr, num, fieldnum))
+                # the index is limited to 2147483647 (64-bit machines only)
+                if itemindex > 2147483647:
+                    from pypy.jit.metainterp import compile
+                    compile.giveup()
+                itemindex = rffi.cast(rffi.INT, itemindex)
+                #
+                rd_pendingfields[i].lldescr  = lldescr
+                rd_pendingfields[i].num      = num
+                rd_pendingfields[i].fieldnum = fieldnum
+                rd_pendingfields[i].itemindex= itemindex
         self.storage.rd_pendingfields = rd_pendingfields
 
     def _gettagged(self, box):
@@ -727,10 +748,28 @@
             self.virtuals_cache = [self.virtual_default] * len(virtuals)
 
     def _prepare_pendingfields(self, pendingfields):
-        if pendingfields is not None:
-            for descr, num, fieldnum in pendingfields:
+        if pendingfields:
+            for i in range(len(pendingfields)):
+                lldescr  = pendingfields[i].lldescr
+                num      = pendingfields[i].num
+                fieldnum = pendingfields[i].fieldnum
+                itemindex= pendingfields[i].itemindex
+                descr = annlowlevel.cast_base_ptr_to_instance(AbstractDescr,
+                                                              lldescr)
                 struct = self.decode_ref(num)
-                self.setfield(descr, struct, fieldnum)
+                itemindex = rffi.cast(lltype.Signed, itemindex)
+                if itemindex < 0:
+                    self.setfield(descr, struct, fieldnum)
+                else:
+                    self.setarrayitem(descr, struct, itemindex, fieldnum)
+
+    def setarrayitem(self, arraydescr, array, index, fieldnum):
+        if arraydescr.is_array_of_pointers():
+            self.setarrayitem_ref(arraydescr, array, index, fieldnum)
+        elif arraydescr.is_array_of_floats():
+            self.setarrayitem_float(arraydescr, array, index, fieldnum)
+        else:
+            self.setarrayitem_int(arraydescr, array, index, fieldnum)
 
     def _prepare_next_section(self, info):
         # Use info.enumerate_vars(), normally dispatching to
@@ -903,15 +942,15 @@
                                            structbox, fieldbox)
 
     def setarrayitem_int(self, arraydescr, arraybox, index, fieldnum):
-        self.setarrayitem(arraydescr, arraybox, index, fieldnum, INT)
+        self._setarrayitem(arraydescr, arraybox, index, fieldnum, INT)
 
     def setarrayitem_ref(self, arraydescr, arraybox, index, fieldnum):
-        self.setarrayitem(arraydescr, arraybox, index, fieldnum, REF)
+        self._setarrayitem(arraydescr, arraybox, index, fieldnum, REF)
 
     def setarrayitem_float(self, arraydescr, arraybox, index, fieldnum):
-        self.setarrayitem(arraydescr, arraybox, index, fieldnum, FLOAT)
+        self._setarrayitem(arraydescr, arraybox, index, fieldnum, FLOAT)
 
-    def setarrayitem(self, arraydescr, arraybox, index, fieldnum, kind):
+    def _setarrayitem(self, arraydescr, arraybox, index, fieldnum, kind):
         itembox = self.decode_box(fieldnum, kind)
         self.metainterp.execute_and_record(rop.SETARRAYITEM_GC,
                                            arraydescr, arraybox,
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -1,7 +1,7 @@
 import py
 import sys
 from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside
-from pypy.rlib.jit import loop_invariant
+from pypy.rlib.jit import loop_invariant, elidable, promote
 from pypy.rlib.jit import jit_debug, assert_green, AssertGreenFailed
 from pypy.rlib.jit import unroll_safe, current_trace_length
 from pypy.jit.metainterp import pyjitpl, history
@@ -304,12 +304,12 @@
         assert res == 42
         self.check_operations_history(int_add=1, int_mul=0, call=1, guard_no_exception=0)
 
-    def test_residual_call_pure(self):
+    def test_residual_call_elidable(self):
         def externfn(x, y):
             return x * y
-        externfn._pure_function_ = True
+        externfn._elidable_function_ = True
         def f(n):
-            n = hint(n, promote=True)
+            promote(n)
             return externfn(n, n+1)
         res = self.interp_operations(f, [6])
         assert res == 42
@@ -317,10 +317,10 @@
         self.check_operations_history(int_add=0, int_mul=0,
                                       call=0, call_pure=0)
 
-    def test_residual_call_pure_1(self):
+    def test_residual_call_elidable_1(self):
+        @elidable
         def externfn(x, y):
             return x * y
-        externfn._pure_function_ = True
         def f(n):
             return externfn(n, n+1)
         res = self.interp_operations(f, [6])
@@ -329,11 +329,11 @@
         self.check_operations_history(int_add=1, int_mul=0,
                                       call=0, call_pure=1)
 
-    def test_residual_call_pure_2(self):
+    def test_residual_call_elidable_2(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
+        @elidable
         def externfn(x):
             return x - 1
-        externfn._pure_function_ = True
         def f(n):
             while n > 0:
                 myjitdriver.can_enter_jit(n=n)
@@ -346,11 +346,11 @@
         # by optimizeopt.py
         self.check_loops(int_sub=0, call=1, call_pure=0)
 
-    def test_constfold_call_pure(self):
+    def test_constfold_call_elidable(self):
         myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
+        @elidable
         def externfn(x):
             return x - 3
-        externfn._pure_function_ = True
         def f(n, m):
             while n > 0:
                 myjitdriver.can_enter_jit(n=n, m=m)
@@ -362,11 +362,11 @@
         # the CALL_PURE is constant-folded away by optimizeopt.py
         self.check_loops(int_sub=1, call=0, call_pure=0)
 
-    def test_constfold_call_pure_2(self):
+    def test_constfold_call_elidable_2(self):
         myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
+        @elidable
         def externfn(x):
             return x - 3
-        externfn._pure_function_ = True
         class V:
             def __init__(self, value):
                 self.value = value
@@ -382,19 +382,19 @@
         # the CALL_PURE is constant-folded away by optimizeopt.py
         self.check_loops(int_sub=1, call=0, call_pure=0)
 
-    def test_pure_function_returning_object(self):
+    def test_elidable_function_returning_object(self):
         myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
         class V:
             def __init__(self, x):
                 self.x = x
         v1 = V(1)
         v2 = V(2)
+        @elidable
         def externfn(x):
             if x:
                 return v1
             else:
                 return v2
-        externfn._pure_function_ = True
         def f(n, m):
             while n > 0:
                 myjitdriver.can_enter_jit(n=n, m=m)
@@ -1252,7 +1252,7 @@
                 myjitdriver.jit_merge_point(x=x, l=l)
                 a = l[x]
                 x = a.g(x)
-                hint(a, promote=True)
+                promote(a)
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
@@ -1312,7 +1312,7 @@
                     x -= 5
                 else:
                     x -= 7
-                hint(a, promote=True)
+                promote(a)
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
@@ -1343,7 +1343,7 @@
                     x -= 5
                 else:
                     x -= 7
-                hint(a, promote=True)
+                promote(a)
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
@@ -1377,7 +1377,7 @@
                     x = a.g(x)
                 else:
                     x -= 7
-                hint(a, promote=True)
+                promote(a)
             return x
         res = self.meta_interp(f, [399], listops=True)
         assert res == f(399)
@@ -1496,7 +1496,7 @@
                     glob.a = B()
                     const = 2
                 else:
-                    const = hint(const, promote=True)
+                    promote(const)
                     x -= const
                     res += a.x
                     a = None
@@ -1531,7 +1531,7 @@
                 myjitdriver.can_enter_jit(x=x)
                 myjitdriver.jit_merge_point(x=x)
                 a = A()
-                hint(a, promote=True)
+                promote(a)
                 x -= 1
         self.meta_interp(f, [50])
         self.check_loop_count(1)
@@ -1595,9 +1595,9 @@
         self.check_loops(jit_debug=2)
 
     def test_assert_green(self):
-        def f(x, promote):
-            if promote:
-                x = hint(x, promote=True)
+        def f(x, promote_flag):
+            if promote_flag:
+                promote(x)
             assert_green(x)
             return x
         res = self.interp_operations(f, [8, 1])
@@ -1676,7 +1676,9 @@
             return a1.val + b1.val
         res = self.meta_interp(g, [6, 14])
         assert res == g(6, 14)
-        self.check_loop_count(9)
+        self.check_loop_count(8)
+        self.check_loops(getarrayitem_gc=7, everywhere=True)
+        py.test.skip("for the following, we need setarrayitem(varindex)")
         self.check_loops(getarrayitem_gc=6, everywhere=True)
 
     def test_multiple_specialied_versions_bridge(self):
@@ -1815,7 +1817,7 @@
             while y > 0:
                 myjitdriver.can_enter_jit(y=y, x=x, res=res, const=const)
                 myjitdriver.jit_merge_point(y=y, x=x, res=res, const=const)
-                const = hint(const, promote=True)
+                const = promote(const)
                 res = res.binop(A(const))
                 if y<7:
                     res = x
@@ -2000,7 +2002,7 @@
             n = sa = 0
             while n < 10:
                 myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
-                if 0 < a < hint(sys.maxint/2, promote=True): pass
+                if 0 < a < promote(sys.maxint/2): pass
                 if 0 < b < 100: pass
                 sa += (((((a << b) << b) << b) >> b) >> b) >> b                
                 n += 1
@@ -2045,7 +2047,7 @@
             n = sa = 0
             while n < 10:
                 myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
-                if -hint(sys.maxint/2, promote=True) < a < 0: pass
+                if -promote(sys.maxint/2) < a < 0: pass
                 if 0 < b < 100: pass
                 sa += (((((a << b) << b) << b) >> b) >> b) >> b                
                 n += 1
@@ -2080,7 +2082,7 @@
             n = sa = 0
             while n < 10:
                 myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
-                if 0 < a < hint(sys.maxint/2, promote=True): pass
+                if 0 < a < promote(sys.maxint/2): pass
                 if 0 < b < 100: pass
                 sa += (a << b) >> b
                 n += 1
@@ -2137,7 +2139,7 @@
                 if op == 'j':
                     j += 1
                 elif op == 'c':
-                    c = hint(c, promote=True)
+                    promote(c)
                     c = 1 - c
                 elif op == '2':
                     if j < 3:
@@ -2206,7 +2208,8 @@
                 self.local_names[0] = 1
 
             def retrieve(self):
-                variables = hint(self.variables, promote=True)
+                variables = self.variables
+                promote(variables)
                 result = self.local_names[0]
                 if result == 0:
                     return -1
@@ -2296,6 +2299,82 @@
         res = self.meta_interp(f, [1])
         assert res == f(1)
 
+    def test_remove_array_operations(self):
+        myjitdriver = JitDriver(greens = [], reds = ['a'])
+        class W_Int:
+            def __init__(self, intvalue):
+                self.intvalue = intvalue
+        def f(x):
+            a = [W_Int(x)]
+            while a[0].intvalue > 0:
+                myjitdriver.jit_merge_point(a=a)
+                a[0] = W_Int(a[0].intvalue - 3)
+            return a[0].intvalue
+        res = self.meta_interp(f, [100])
+        assert res == -2
+        #self.check_loops(getarrayitem_gc=0, setarrayitem_gc=0) -- xxx?
+
+    def test_retrace_ending_up_retrazing_another_loop(self):
+
+        myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'i', 'sa'])
+        bytecode = "0+sI0+SI"
+        def f(n):
+            myjitdriver.set_param('threshold', 3)
+            myjitdriver.set_param('trace_eagerness', 1)
+            myjitdriver.set_param('retrace_limit', 5)
+            myjitdriver.set_param('function_threshold', -1)
+            pc = sa = i = 0
+            while pc < len(bytecode):
+                myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i)
+                n = hint(n, promote=True)
+                op = bytecode[pc]
+                if op == '0':
+                    i = 0
+                elif op == '+':
+                    i += 1
+                elif op == 's':
+                    sa += i
+                elif op == 'S':
+                    sa += 2
+                elif op == 'I':
+                    if i < n:
+                        pc -= 2
+                        myjitdriver.can_enter_jit(pc=pc, n=n, sa=sa, i=i)
+                        continue
+                pc += 1
+            return sa
+
+        def g(n1, n2):
+            for i in range(10):
+                f(n1)
+            for i in range(10):                
+                f(n2)
+
+        nn = [10, 3]
+        assert self.meta_interp(g, nn) == g(*nn)
+        
+        # The attempts of retracing first loop will end up retracing the
+        # second and thus fail 5 times, saturating the retrace_count. Instead a
+        # bridge back to the preamble of the first loop is produced. A guard in
+        # this bridge is later traced resulting in a retrace of the second loop.
+        # Thus we end up with:
+        #   1 preamble and 1 specialized version of first loop
+        #   1 preamble and 2 specialized version of second loop
+        self.check_tree_loop_count(2 + 3)
+
+        # FIXME: Add a gloabl retrace counter and test that we are not trying more than 5 times.
+        
+        def g(n):
+            for i in range(n):
+                for j in range(10):
+                    f(n-i)
+
+        res = self.meta_interp(g, [10])
+        assert res == g(10)
+        # 1 preamble and 6 speciealized versions of each loop
+        self.check_tree_loop_count(2*(1 + 6))
+
+
 class TestOOtype(BasicTests, OOJitMixin):
 
     def test_oohash(self):
diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -1,7 +1,7 @@
 
 import py
 from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.libffi import ArgChain, longlong2float, float2longlong
 from pypy.rlib.libffi import IS_32_BIT
@@ -49,8 +49,7 @@
             res = init_result
             while n < 10:
                 driver.jit_merge_point(n=n, res=res, func=func)
-                driver.can_enter_jit(n=n, res=res, func=func)
-                func = hint(func, promote=True)
+                promote(func)
                 argchain = ArgChain()
                 # this loop is unrolled
                 for method_name, argval in method_and_args:
diff --git a/pypy/jit/metainterp/test/test_jitprof.py b/pypy/jit/metainterp/test/test_jitprof.py
--- a/pypy/jit/metainterp/test/test_jitprof.py
+++ b/pypy/jit/metainterp/test/test_jitprof.py
@@ -1,6 +1,6 @@
 
 from pypy.jit.metainterp.warmspot import ll_meta_interp
-from pypy.rlib.jit import JitDriver, dont_look_inside, purefunction
+from pypy.rlib.jit import JitDriver, dont_look_inside, elidable
 from pypy.jit.metainterp.test.support import LLJitMixin
 from pypy.jit.metainterp import pyjitpl
 from pypy.jit.metainterp.jitprof import *
@@ -89,7 +89,7 @@
         assert profiler.calls == 1
 
     def test_blackhole_pure(self):
-        @purefunction
+        @elidable
         def g(n):
             return n+1
         
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -49,7 +49,7 @@
                 x = l[n]
                 l = [3] * 100
                 l[3] = x
-                l[3] = x + 1
+                l[4] = x + 1
                 n -= 1
             return l[0]
 
diff --git a/pypy/jit/metainterp/test/test_recursive.py b/pypy/jit/metainterp/test/test_recursive.py
--- a/pypy/jit/metainterp/test/test_recursive.py
+++ b/pypy/jit/metainterp/test/test_recursive.py
@@ -1,6 +1,6 @@
 import py
 from pypy.rlib.jit import JitDriver, we_are_jitted, hint
-from pypy.rlib.jit import unroll_safe, dont_look_inside
+from pypy.rlib.jit import unroll_safe, dont_look_inside, promote
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.debug import fatalerror
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -926,7 +926,7 @@
                 myjitdriver.can_enter_jit(codeno=codeno, frame=frame, n=n, x=x)
                 myjitdriver.jit_merge_point(codeno=codeno, frame=frame, n=n,
                                             x=x)
-                frame.s = hint(frame.s, promote=True)
+                frame.s = promote(frame.s)
                 n -= 1
                 s = frame.s
                 assert s >= 0
diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -1238,7 +1238,7 @@
     liveboxes = []
     modifier._number_virtuals(liveboxes, values, 0)
     assert liveboxes == [b2s, b4s] or liveboxes == [b4s, b2s]
-    modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s)])
+    modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s, -1)])
     storage.rd_consts = memo.consts[:]
     storage.rd_numb = None
     # resume
@@ -1259,6 +1259,106 @@
     assert len(expected) == len(trace)
     assert demo55.next == demo66
 
+def test_virtual_adder_pending_fields_and_arrayitems():
+    class Storage(object):
+        pass
+    storage = Storage()
+    modifier = ResumeDataVirtualAdder(storage, None)
+    modifier._add_pending_fields([])
+    assert not storage.rd_pendingfields
+    #
+    class FieldDescr(object):
+        pass
+    field_a = FieldDescr()
+    storage = Storage()
+    modifier = ResumeDataVirtualAdder(storage, None)
+    modifier.liveboxes_from_env = {42: rffi.cast(rffi.SHORT, 1042),
+                                   61: rffi.cast(rffi.SHORT, 1061)}
+    modifier._add_pending_fields([(field_a, 42, 61, -1)])
+    pf = storage.rd_pendingfields
+    assert len(pf) == 1
+    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
+            is field_a)
+    assert rffi.cast(lltype.Signed, pf[0].num) == 1042
+    assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
+    assert rffi.cast(lltype.Signed, pf[0].itemindex) == -1
+    #
+    array_a = FieldDescr()
+    storage = Storage()
+    modifier = ResumeDataVirtualAdder(storage, None)
+    modifier.liveboxes_from_env = {42: rffi.cast(rffi.SHORT, 1042),
+                                   61: rffi.cast(rffi.SHORT, 1061),
+                                   62: rffi.cast(rffi.SHORT, 1062),
+                                   63: rffi.cast(rffi.SHORT, 1063)}
+    modifier._add_pending_fields([(array_a, 42, 61, 0),
+                                  (array_a, 42, 62, 2147483647)])
+    pf = storage.rd_pendingfields
+    assert len(pf) == 2
+    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr)
+            is array_a)
+    assert rffi.cast(lltype.Signed, pf[0].num) == 1042
+    assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061
+    assert rffi.cast(lltype.Signed, pf[0].itemindex) == 0
+    assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[1].lldescr)
+            is array_a)
+    assert rffi.cast(lltype.Signed, pf[1].num) == 1042
+    assert rffi.cast(lltype.Signed, pf[1].fieldnum) == 1062
+    assert rffi.cast(lltype.Signed, pf[1].itemindex) == 2147483647
+    #
+    from pypy.jit.metainterp.pyjitpl import SwitchToBlackhole
+    py.test.raises(SwitchToBlackhole, modifier._add_pending_fields,
+                   [(array_a, 42, 63, 2147483648)])
+
+def test_resume_reader_fields_and_arrayitems():
+    class ResumeReader(AbstractResumeDataReader):
+        def __init__(self, got=None, got_array=None):
+            self.got = got
+            self.got_array = got_array
+        def setfield(self, descr, struct, fieldnum):
+            assert lltype.typeOf(struct) is lltype.Signed
+            assert lltype.typeOf(fieldnum) is rffi.SHORT
+            fieldnum = rffi.cast(lltype.Signed, fieldnum)
+            self.got.append((descr, struct, fieldnum))
+        def setarrayitem(self, arraydescr, array, index, fieldnum):
+            assert lltype.typeOf(array) is lltype.Signed
+            assert lltype.typeOf(index) is lltype.Signed
+            assert lltype.typeOf(fieldnum) is rffi.SHORT
+            fieldnum = rffi.cast(lltype.Signed, fieldnum)
+            self.got_array.append((arraydescr, array, index, fieldnum))
+        def decode_ref(self, num):
+            return rffi.cast(lltype.Signed, num) * 100
+    got = []
+    pf = lltype.nullptr(PENDINGFIELDSP.TO)
+    ResumeReader(got)._prepare_pendingfields(pf)
+    assert got == []
+    #
+    class FieldDescr(AbstractDescr):
+        pass
+    field_a = FieldDescr()
+    field_b = FieldDescr()
+    pf = lltype.malloc(PENDINGFIELDSP.TO, 2)
+    pf[0].lldescr = annlowlevel.cast_instance_to_base_ptr(field_a)
+    pf[0].num = rffi.cast(rffi.SHORT, 1042)
+    pf[0].fieldnum = rffi.cast(rffi.SHORT, 1061)
+    pf[0].itemindex = rffi.cast(rffi.INT, -1)
+    pf[1].lldescr = annlowlevel.cast_instance_to_base_ptr(field_b)
+    pf[1].num = rffi.cast(rffi.SHORT, 2042)
+    pf[1].fieldnum = rffi.cast(rffi.SHORT, 2061)
+    pf[1].itemindex = rffi.cast(rffi.INT, -1)
+    got = []
+    ResumeReader(got)._prepare_pendingfields(pf)
+    assert got == [(field_a, 104200, 1061), (field_b, 204200, 2061)]
+    #
+    array_a = FieldDescr()
+    pf = lltype.malloc(PENDINGFIELDSP.TO, 1)
+    pf[0].lldescr = annlowlevel.cast_instance_to_base_ptr(array_a)
+    pf[0].num = rffi.cast(rffi.SHORT, 1042)
+    pf[0].fieldnum = rffi.cast(rffi.SHORT, 1063)
+    pf[0].itemindex = rffi.cast(rffi.INT, 123)
+    got_array = []
+    ResumeReader(got_array=got_array)._prepare_pendingfields(pf)
+    assert got_array == [(array_a, 104200, 123, 1063)]
+
 
 def test_invalidation_needed():
     class options:
diff --git a/pypy/jit/metainterp/test/test_send.py b/pypy/jit/metainterp/test/test_send.py
--- a/pypy/jit/metainterp/test/test_send.py
+++ b/pypy/jit/metainterp/test/test_send.py
@@ -1,5 +1,5 @@
 import py
-from pypy.rlib.jit import JitDriver, hint, purefunction
+from pypy.rlib.jit import JitDriver, promote, elidable
 from pypy.jit.codewriter.policy import StopAtXPolicy
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
 
@@ -604,7 +604,7 @@
     def test_constfold_pure_oosend(self):
         myjitdriver = JitDriver(greens=[], reds = ['i', 'obj'])
         class A:
-            @purefunction
+            @elidable
             def foo(self):
                 return 42
         def fn(n, i):
@@ -613,7 +613,7 @@
             while i > 0:
                 myjitdriver.can_enter_jit(i=i, obj=obj)
                 myjitdriver.jit_merge_point(i=i, obj=obj)
-                obj = hint(obj, promote=True)
+                promote(obj)
                 res = obj.foo()
                 i-=1
             return res
diff --git a/pypy/jit/metainterp/test/test_virtual.py b/pypy/jit/metainterp/test/test_virtual.py
--- a/pypy/jit/metainterp/test/test_virtual.py
+++ b/pypy/jit/metainterp/test/test_virtual.py
@@ -1,5 +1,5 @@
 import py
-from pypy.rlib.jit import JitDriver, hint
+from pypy.rlib.jit import JitDriver, promote
 from pypy.rlib.objectmodel import compute_unique_id
 from pypy.jit.codewriter.policy import StopAtXPolicy
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -300,7 +300,7 @@
             while n > 0:
                 myjitdriver.can_enter_jit(n=n, i=i, stufflist=stufflist)
                 myjitdriver.jit_merge_point(n=n, i=i, stufflist=stufflist)
-                i = hint(i, promote=True)
+                promote(i)
                 v = Stuff(i)
                 n -= stufflist.lst[v.x].x
             return n
diff --git a/pypy/jit/metainterp/test/test_virtualizable.py b/pypy/jit/metainterp/test/test_virtualizable.py
--- a/pypy/jit/metainterp/test/test_virtualizable.py
+++ b/pypy/jit/metainterp/test/test_virtualizable.py
@@ -5,7 +5,7 @@
 from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
 from pypy.jit.codewriter.policy import StopAtXPolicy
 from pypy.jit.codewriter import heaptracker
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, hint, dont_look_inside, promote
 from pypy.rlib.rarithmetic import intmask
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
 from pypy.rpython.rclass import FieldListAccessor
@@ -480,7 +480,7 @@
             while n > 0:
                 myjitdriver.can_enter_jit(frame=frame, n=n, x=x)
                 myjitdriver.jit_merge_point(frame=frame, n=n, x=x)
-                frame.s = hint(frame.s, promote=True)
+                frame.s = promote(frame.s)
                 n -= 1
                 s = frame.s
                 assert s >= 0
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -237,7 +237,7 @@
         d = {}
         if NonConstant(False):
             value = 'blah' # not a constant ''
-        if value is None:
+        if value is None or value == 'all':
             value = ALL_OPTS_NAMES
         for name in value.split(":"):
             if name:
diff --git a/pypy/jit/tl/spli/interpreter.py b/pypy/jit/tl/spli/interpreter.py
--- a/pypy/jit/tl/spli/interpreter.py
+++ b/pypy/jit/tl/spli/interpreter.py
@@ -2,7 +2,7 @@
 from pypy.tool import stdlib_opcode
 from pypy.jit.tl.spli import objects, pycode
 from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
 from pypy.rlib.objectmodel import we_are_translated
 
 opcode_method_names = stdlib_opcode.host_bytecode_spec.method_names
@@ -78,7 +78,7 @@
         while True:
             jitdriver.jit_merge_point(code=code, instr_index=instr_index,
                                       frame=self)
-            self.stack_depth = hint(self.stack_depth, promote=True)
+            self.stack_depth = promote(self.stack_depth)
             op = ord(code[instr_index])
             instr_index += 1
             if op >= HAVE_ARGUMENT:
diff --git a/pypy/jit/tl/tiny2.py b/pypy/jit/tl/tiny2.py
--- a/pypy/jit/tl/tiny2.py
+++ b/pypy/jit/tl/tiny2.py
@@ -27,7 +27,7 @@
     { #1 #1 1 SUB ->#1 #1 }    => when called with 5, gives '5 4 3 2 1'
 
 """
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import hint, promote
 
 #
 # See pypy/doc/jit.txt for a higher-level overview of the JIT techniques
@@ -75,9 +75,9 @@
     # ones.  The JIT compiler cannot look into indirect calls, but it
     # can analyze and inline the code in directly-called functions.
     y = stack.pop()
-    hint(y.__class__, promote=True)
+    promote(y.__class__)
     x = stack.pop()
-    hint(x.__class__, promote=True)
+    promote(x.__class__)
     try:
         z = IntBox(func_int(x.as_int(), y.as_int()))
     except ValueError:
@@ -108,7 +108,7 @@
     # doesn't have to worry about the 'args' list being unpredictably
     # modified.
     oldargs = args
-    argcount = hint(len(oldargs), promote=True)
+    argcount = promote(len(oldargs))
     args = []
     n = 0
     while n < argcount:
@@ -160,8 +160,7 @@
                 # read out of the 'loops' list will be a compile-time constant
                 # because it was pushed as a compile-time constant by the '{'
                 # case above into 'loops', which is a virtual list, so the
-                # promotion below is just a way to make the colors match.
-                pos = hint(pos, promote=True)
+                promote(pos)
         else:
             stack.append(StrBox(opcode))
     return stack
diff --git a/pypy/jit/tl/tiny2_hotpath.py b/pypy/jit/tl/tiny2_hotpath.py
--- a/pypy/jit/tl/tiny2_hotpath.py
+++ b/pypy/jit/tl/tiny2_hotpath.py
@@ -27,7 +27,7 @@
     { #1 #1 1 SUB ->#1 #1 }    => when called with 5, gives '5 4 3 2 1'
 
 """
-from pypy.rlib.jit import hint, JitDriver
+from pypy.rlib.jit import hint, promote, JitDriver
 
 #
 # See pypy/doc/jit.txt for a higher-level overview of the JIT techniques
@@ -77,9 +77,9 @@
     # ones.  The JIT compiler cannot look into indirect calls, but it
     # can analyze and inline the code in directly-called functions.
     stack, y = stack.pop()
-    hint(y.__class__, promote=True)
+    promote(y.__class__)
     stack, x = stack.pop()
-    hint(x.__class__, promote=True)
+    promote(x.__class__)
     try:
         z = IntBox(func_int(x.as_int(), y.as_int()))
     except ValueError:
@@ -120,7 +120,7 @@
         # modified.
         oldloops = invariants
         oldargs = reds.args
-        argcount = hint(len(oldargs), promote=True)
+        argcount = promote(len(oldargs))
         args = []
         n = 0
         while n < argcount:
@@ -189,7 +189,7 @@
                 # because it was pushed as a compile-time constant by the '{'
                 # case above into 'loops', which is a virtual list, so the
                 # promotion below is just a way to make the colors match.
-                pos = hint(pos, promote=True)
+                pos = promote(pos)
                 tinyjitdriver.can_enter_jit(args=args, loops=loops, stack=stack,
                                             bytecode=bytecode, pos=pos)
         else:
diff --git a/pypy/jit/tl/tiny3_hotpath.py b/pypy/jit/tl/tiny3_hotpath.py
--- a/pypy/jit/tl/tiny3_hotpath.py
+++ b/pypy/jit/tl/tiny3_hotpath.py
@@ -28,7 +28,7 @@
     { #1 #1 1 SUB ->#1 #1 }    => when called with 5, gives '5 4 3 2 1'
 
 """
-from pypy.rlib.jit import hint, JitDriver
+from pypy.rlib.jit import promote, hint, JitDriver
 from pypy.rlib.objectmodel import specialize
 
 #
@@ -83,9 +83,9 @@
     # ones.  The JIT compiler cannot look into indirect calls, but it
     # can analyze and inline the code in directly-called functions.
     stack, y = stack.pop()
-    hint(y.__class__, promote=True)
+    promote(y.__class__)
     stack, x = stack.pop()
-    hint(x.__class__, promote=True)
+    promote(x.__class__)
     if isinstance(x, IntBox) and isinstance(y, IntBox):
         z = IntBox(func_int(x.as_int(), y.as_int()))
     else:
@@ -125,7 +125,7 @@
         # modified.
         oldloops = invariants
         oldargs = reds.args
-        argcount = hint(len(oldargs), promote=True)
+        argcount = promote(len(oldargs))
         args = []
         n = 0
         while n < argcount:
@@ -194,7 +194,7 @@
                 # because it was pushed as a compile-time constant by the '{'
                 # case above into 'loops', which is a virtual list, so the
                 # promotion below is just a way to make the colors match.
-                pos = hint(pos, promote=True)
+                pos = promote(pos)
                 tinyjitdriver.can_enter_jit(args=args, loops=loops, stack=stack,
                                             bytecode=bytecode, pos=pos)
         else:
diff --git a/pypy/jit/tl/tl.py b/pypy/jit/tl/tl.py
--- a/pypy/jit/tl/tl.py
+++ b/pypy/jit/tl/tl.py
@@ -2,7 +2,7 @@
 
 import py
 from pypy.jit.tl.tlopcode import *
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, hint, dont_look_inside, promote
 
 def char2int(c):
     t = ord(c)
@@ -81,7 +81,7 @@
             myjitdriver.jit_merge_point(pc=pc, code=code,
                                         stack=stack, inputarg=inputarg)
             opcode = ord(code[pc])
-            stack.stackpos = hint(stack.stackpos, promote=True)
+            stack.stackpos = promote(stack.stackpos)
             pc += 1
 
             if opcode == NOP:
diff --git a/pypy/jit/tl/tlc.py b/pypy/jit/tl/tlc.py
--- a/pypy/jit/tl/tlc.py
+++ b/pypy/jit/tl/tlc.py
@@ -5,7 +5,7 @@
 from pypy.rlib.objectmodel import specialize, we_are_translated
 from pypy.jit.tl.tlopcode import *
 from pypy.jit.tl import tlopcode
-from pypy.rlib.jit import JitDriver
+from pypy.rlib.jit import JitDriver, elidable
 
 class Obj(object):
 
@@ -71,6 +71,7 @@
 
     classes = [] # [(descr, cls), ...]
 
+    @elidable
     def get(key):
         for descr, cls in Class.classes:
             if key.attributes == descr.attributes and\
@@ -79,7 +80,6 @@
         result = Class(key)
         Class.classes.append((key, result))
         return result
-    get._pure_function_ = True
     get = staticmethod(get)
 
     def __init__(self, descr):
diff --git a/pypy/jit/tool/pypytrace-mode.el b/pypy/jit/tool/pypytrace-mode.el
--- a/pypy/jit/tool/pypytrace-mode.el
+++ b/pypy/jit/tool/pypytrace-mode.el
@@ -32,7 +32,7 @@
     ("<.*FieldDescr \\([^ ]*\\)" (1 'font-lock-variable-name-face))
     ;; comment out debug_merge_point, but then highlight specific part of it
     ("^debug_merge_point.*" . font-lock-comment-face)
-    ("^\\(debug_merge_point\\).*code object\\(.*\\), file \\('.*'\\), \\(line .*\\)> \\(.*\\)"
+    ("^\\(debug_merge_point\\).*code object\\(.*\\). file \\('.*'\\). \\(line .*\\)> \\(.*\\)"
      (1 'compilation-warning t)
      (2 'escape-glyph t)
      (3 'font-lock-string-face t)
diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -5,20 +5,6 @@
 
 # put builtins here that should be optimized somehow
 
-OPTIMIZED_BUILTINS = ["len", "range", "xrange", "min", "max", "enumerate",
-        "isinstance", "type", "zip", "file", "format", "open", "abs", "chr",
-        "unichr", "ord", "pow", "repr", "hash", "oct", "hex", "round", "cmp",
-        "getattr", "setattr", "delattr", "callable", "int", "str", "float"]
-
-assert len(OPTIMIZED_BUILTINS) <= 256
-
-BUILTIN_TO_INDEX = {}
-
-for i, name in enumerate(OPTIMIZED_BUILTINS):
-    BUILTIN_TO_INDEX[name] = i
-
-assert len(OPTIMIZED_BUILTINS) == len(BUILTIN_TO_INDEX)
-
 class Module(MixedModule):
     """Built-in functions, exceptions, and other objects."""
     expose__file__attribute = False
@@ -141,9 +127,6 @@
     def setup_after_space_initialization(self):
         """NOT_RPYTHON"""
         space = self.space
-        self.builtins_by_index = [None] * len(OPTIMIZED_BUILTINS)
-        for i, name in enumerate(OPTIMIZED_BUILTINS):
-            self.builtins_by_index[i] = space.getattr(self, space.wrap(name))
         # install the more general version of isinstance() & co. in the space
         from pypy.module.__builtin__ import abstractinst as ab
         space.abstract_isinstance_w = ab.abstract_isinstance_w.__get__(space)
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -294,7 +294,7 @@
             break
         new_frame = space.createframe(code, w_func.w_func_globals,
                                       w_func.closure)
-        new_frame.fastlocals_w[0] = w_item
+        new_frame.locals_stack_w[0] = w_item
         w_res = new_frame.run()
         result_w.append(w_res)
     return result_w
diff --git a/pypy/module/__builtin__/interp_classobj.py b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -12,7 +12,7 @@
 
 
 def raise_type_err(space, argument, expected, w_obj):
-    type_name = space.type(w_obj).getname(space, '?')
+    type_name = space.type(w_obj).getname(space)
     raise operationerrfmt(space.w_TypeError,
                           "argument %s must be %s, not %s",
                           argument, expected, type_name)
diff --git a/pypy/module/__builtin__/test/test_builtin.py b/pypy/module/__builtin__/test/test_builtin.py
--- a/pypy/module/__builtin__/test/test_builtin.py
+++ b/pypy/module/__builtin__/test/test_builtin.py
@@ -631,62 +631,6 @@
         raises(TypeError, pr, end=3)
         raises(TypeError, pr, sep=42)
 
-class AppTestBuiltinOptimized(object):
-    def setup_class(cls):
-        from pypy.conftest import gettestobjspace
-        cls.space = gettestobjspace(**{"objspace.opcodes.CALL_LIKELY_BUILTIN": True})
-
-    # hum, we need to invoke the compiler explicitely
-    def test_xrange_len(self):
-        s = """def test():
-        x = xrange(33)
-        assert len(x) == 33
-        x = xrange(33.2)
-        assert len(x) == 33
-        x = xrange(33,0,-1)
-        assert len(x) == 33
-        x = xrange(33,0)
-        assert len(x) == 0
-        x = xrange(33,0.2)
-        assert len(x) == 0
-        x = xrange(0,33)
-        assert len(x) == 33
-        x = xrange(0,33,-1)
-        assert len(x) == 0
-        x = xrange(0,33,2)
-        assert len(x) == 17
-        x = xrange(0,32,2)
-        assert len(x) == 16
-        """
-        ns = {}
-        exec s in ns
-        ns["test"]()
-
-    def test_delete_from_builtins(self):
-        s = """ """
-        # XXX write this test!
-
-    def test_shadow_case_bound_method(self):
-        s = """def test(l):
-        n = len(l)
-        old_len = len
-        class A(object):
-            x = 5
-            def length(self, o):
-                return self.x*old_len(o)
-        import __builtin__
-        __builtin__.len = A().length
-        try:
-            m = len(l)
-        finally:
-            __builtin__.len = old_len
-        return n+m
-        """
-        ns = {}
-        exec s in ns
-        res = ns["test"]([2,3,4])
-        assert res == 18
-
     def test_round(self):
         assert round(11.234) == 11.0
         assert round(11.234, -1) == 10.0
diff --git a/pypy/module/__builtin__/test/test_classobj.py b/pypy/module/__builtin__/test/test_classobj.py
--- a/pypy/module/__builtin__/test/test_classobj.py
+++ b/pypy/module/__builtin__/test/test_classobj.py
@@ -987,9 +987,9 @@
         if option.runappdirect:
             py.test.skip("can only be run on py.py")
         def is_strdict(space, w_class):
-            from pypy.objspace.std.dictmultiobject import StrDictImplementation
+            from pypy.objspace.std.dictmultiobject import StringDictStrategy
             w_d = w_class.getdict(space)
-            return space.wrap(isinstance(w_d, StrDictImplementation) and w_d.r_dict_content is None)
+            return space.wrap(isinstance(w_d.strategy, StringDictStrategy))
 
         cls.w_is_strdict = cls.space.wrap(gateway.interp2app(is_strdict))
 
diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py
--- a/pypy/module/_codecs/interp_codecs.py
+++ b/pypy/module/_codecs/interp_codecs.py
@@ -186,7 +186,7 @@
         text = u'\ufffd' * size
         return space.newtuple([space.wrap(text), w_end])
     else:
-        typename = space.type(w_exc).getname(space, '?')
+        typename = space.type(w_exc).getname(space)
         raise operationerrfmt(space.w_TypeError,
             "don't know how to handle %s in error callback", typename)
 
@@ -207,7 +207,7 @@
             pos += 1
         return space.newtuple([space.wrap(builder.build()), w_end])
     else:
-        typename = space.type(w_exc).getname(space, '?')
+        typename = space.type(w_exc).getname(space)
         raise operationerrfmt(space.w_TypeError,
             "don't know how to handle %s in error callback", typename)
 
@@ -240,7 +240,7 @@
             pos += 1
         return space.newtuple([space.wrap(builder.build()), w_end])
     else:
-        typename = space.type(w_exc).getname(space, '?')
+        typename = space.type(w_exc).getname(space)
         raise operationerrfmt(space.w_TypeError,
             "don't know how to handle %s in error callback", typename)
 
diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_ffi.py
--- a/pypy/module/_ffi/interp_ffi.py
+++ b/pypy/module/_ffi/interp_ffi.py
@@ -235,7 +235,7 @@
         argchain.arg_longlong(floatval)
 
     def call(self, space, args_w):
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         argchain = self.build_argchain(space, args_w)
         w_restype = self.w_restype
         if w_restype.is_longlong():
diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -175,7 +175,7 @@
         return space.call_method(self.w_raw, "isatty")
 
     def repr_w(self, space):
-        typename = space.type(self).getname(space, '?')
+        typename = space.type(self).getname(space)
         module = space.str_w(space.type(self).get_module())
         try:
             w_name = space.getattr(self, space.wrap("name"))
diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py
--- a/pypy/module/_io/interp_io.py
+++ b/pypy/module/_io/interp_io.py
@@ -119,7 +119,7 @@
     if buffering < 0:
         buffering = DEFAULT_BUFFER_SIZE
 
-        if "st_blksize" in STAT_FIELD_TYPES:
+        if space.config.translation.type_system == 'lltype' and 'st_blksize' in STAT_FIELD_TYPES:
             fileno = space.int_w(space.call_method(w_raw, "fileno"))
             try:
                 st = os.fstat(fileno)
diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py
--- a/pypy/module/_io/interp_iobase.py
+++ b/pypy/module/_io/interp_iobase.py
@@ -155,7 +155,7 @@
                     raise operationerrfmt(
                         space.w_IOError,
                         "peek() should have returned a bytes object, "
-                        "not '%s'", space.type(w_readahead).getname(space, '?'))
+                        "not '%s'", space.type(w_readahead).getname(space))
                 length = space.len_w(w_readahead)
                 if length > 0:
                     n = 0
@@ -181,7 +181,7 @@
                 raise operationerrfmt(
                     space.w_IOError,
                     "peek() should have returned a bytes object, "
-                    "not '%s'", space.type(w_read).getname(space, '?'))
+                    "not '%s'", space.type(w_read).getname(space))
             read = space.str_w(w_read)
             if not read:
                 break
diff --git a/pypy/module/_io/interp_stringio.py b/pypy/module/_io/interp_stringio.py
--- a/pypy/module/_io/interp_stringio.py
+++ b/pypy/module/_io/interp_stringio.py
@@ -129,7 +129,7 @@
         if not space.isinstance_w(w_obj, space.w_unicode):
             raise operationerrfmt(space.w_TypeError,
                                   "string argument expected, got '%s'",
-                                  space.type(w_obj).getname(space, '?'))
+                                  space.type(w_obj).getname(space))
         self._check_closed(space)
 
         orig_size = space.len_w(w_obj)
diff --git a/pypy/module/_lsprof/interp_lsprof.py b/pypy/module/_lsprof/interp_lsprof.py
--- a/pypy/module/_lsprof/interp_lsprof.py
+++ b/pypy/module/_lsprof/interp_lsprof.py
@@ -149,7 +149,7 @@
                             factor * float(self.ll_it), w_sublist)
         return space.wrap(w_se)
 
-    @jit.purefunction
+    @jit.elidable
     def _get_or_make_subentry(self, entry, make=True):
         try:
             return self.calls[entry]
@@ -167,7 +167,7 @@
         self.previous = profobj.current_context
         entry.recursionLevel += 1
         if profobj.subcalls and self.previous:
-            caller = jit.hint(self.previous.entry, promote=True)
+            caller = jit.promote(self.previous.entry)
             subentry = caller._get_or_make_subentry(entry)
             subentry.recursionLevel += 1
         self.ll_t0 = profobj.ll_timer()
@@ -179,7 +179,7 @@
             self.previous.ll_subt += tt
         entry._stop(tt, it)
         if profobj.subcalls and self.previous:
-            caller = jit.hint(self.previous.entry, promote=True)
+            caller = jit.promote(self.previous.entry)
             subentry = caller._get_or_make_subentry(entry, False)
             if subentry is not None:
                 subentry._stop(tt, it)
@@ -212,7 +212,7 @@
                 module += '.'
         return '{%s%s}' % (module, w_arg.name)
     else:
-        class_name = space.type(w_arg).getname(space, '?')
+        class_name = space.type(w_arg).getname(space)
         return "{'%s' object}" % (class_name,)
 
 def lsprof_call(space, w_self, frame, event, w_arg):
@@ -282,7 +282,7 @@
         c_setup_profiling()
         space.getexecutioncontext().setllprofile(lsprof_call, space.wrap(self))
 
-    @jit.purefunction
+    @jit.elidable
     def _get_or_make_entry(self, f_code, make=True):
         try:
             return self.data[f_code]
@@ -293,7 +293,7 @@
                 return entry
             return None
 
-    @jit.purefunction
+    @jit.elidable
     def _get_or_make_builtin_entry(self, key, make=True):
         try:
             return self.builtin_data[key]
@@ -306,7 +306,7 @@
 
     def _enter_call(self, f_code):
         # we have a superb gc, no point in freelist :)
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         entry = self._get_or_make_entry(f_code)
         self.current_context = ProfilerContext(self, entry)
 
@@ -314,14 +314,14 @@
         context = self.current_context
         if context is None:
             return
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         entry = self._get_or_make_entry(f_code, False)
         if entry is not None:
             context._stop(self, entry)
         self.current_context = context.previous
 
     def _enter_builtin_call(self, key):
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         entry = self._get_or_make_builtin_entry(key)
         self.current_context = ProfilerContext(self, entry)
 
@@ -329,7 +329,7 @@
         context = self.current_context
         if context is None:
             return
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         entry = self._get_or_make_builtin_entry(key, False)
         if entry is not None:
             context._stop(self, entry)
diff --git a/pypy/module/_lsprof/test/test_cprofile.py b/pypy/module/_lsprof/test/test_cprofile.py
--- a/pypy/module/_lsprof/test/test_cprofile.py
+++ b/pypy/module/_lsprof/test/test_cprofile.py
@@ -181,8 +181,7 @@
 
 
 class AppTestWithDifferentBytecodes(AppTestCProfile):
-    keywords = {'objspace.opcodes.CALL_LIKELY_BUILTIN': True,
-                'objspace.opcodes.CALL_METHOD': True}
+    keywords = {'objspace.opcodes.CALL_METHOD': True}
 
 
 expected_output = {}
diff --git a/pypy/module/_multiprocessing/interp_connection.py b/pypy/module/_multiprocessing/interp_connection.py
--- a/pypy/module/_multiprocessing/interp_connection.py
+++ b/pypy/module/_multiprocessing/interp_connection.py
@@ -360,7 +360,7 @@
         conn_type = ["read-only", "write-only", "read-write"][self.flags]
 
         return space.wrap("<%s %s, handle %zd>" % (
-            conn_type, space.type(self).getname(space, '?'), self.do_fileno()))
+            conn_type, space.type(self).getname(space), self.do_fileno()))
 
     def is_valid(self):
         return self.handle != self.INVALID_HANDLE_VALUE
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -900,7 +900,7 @@
 
 def _ssl_thread_id_function():
     from pypy.module.thread import ll_thread
-    return rffi.cast(rffi.INT, ll_thread.get_ident())
+    return rffi.cast(rffi.LONG, ll_thread.get_ident())
 
 def setup_ssl_threads():
     from pypy.module.thread import ll_thread
diff --git a/pypy/module/_stackless/interp_coroutine.py b/pypy/module/_stackless/interp_coroutine.py
--- a/pypy/module/_stackless/interp_coroutine.py
+++ b/pypy/module/_stackless/interp_coroutine.py
@@ -40,7 +40,7 @@
             raise operationerrfmt(
                 space.w_TypeError, 
                 "'%s' object is not callable",
-                space.type(w_obj).getname(space, '?'))
+                space.type(w_obj).getname(space))
         self.w_func = w_obj
         self.args = args
 
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
@@ -129,7 +129,7 @@
         if w_obj is None:
             state = '; dead'
         else:
-            typename = space.type(w_obj).getname(space, '?')
+            typename = space.type(w_obj).getname(space)
             objname = w_obj.getname(space, '')
             if objname:
                 state = "; to '%s' (%s)" % (typename, objname)
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -1,18 +1,21 @@
 from __future__ import with_statement
 
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.buffer import RWBuffer
 from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef, GetSetProperty, make_weakref_descr
-from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.rarithmetic import ovfcheck
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.module._file.interp_file import W_File
+from pypy.objspace.std.model import W_Object
+from pypy.objspace.std.multimethod import FailedToImplement
 from pypy.objspace.std.stdtypedef import SMM, StdTypeDef
 from pypy.objspace.std.register_all import register_all
-from pypy.objspace.std.model import W_Object
-from pypy.module._file.interp_file import W_File
-from pypy.interpreter.buffer import RWBuffer
-from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rpython.lltypesystem import lltype, rffi
+
+
+memcpy = rffi.llexternal("memcpy", [rffi.VOIDP, rffi.VOIDP, rffi.SIZE_T], lltype.Void)
 
 @unwrap_spec(typecode=str)
 def w_array(space, w_cls, typecode, __args__):
@@ -37,7 +40,7 @@
             if len(__args__.arguments_w) > 0:
                 w_initializer = __args__.arguments_w[0]
                 if space.type(w_initializer) is space.w_str:
-                    a.fromstring(w_initializer)
+                    a.fromstring(space.str_w(w_initializer))
                 elif space.type(w_initializer) is space.w_unicode:
                     a.fromsequence(w_initializer)
                 elif space.type(w_initializer) is space.w_list:
@@ -73,6 +76,7 @@
 
 array_buffer_info = SMM('buffer_info', 1)
 array_reduce = SMM('__reduce__', 1)
+array_copy = SMM('__copy__', 1)
 array_byteswap = SMM('byteswap', 1)
 
 
@@ -96,7 +100,7 @@
     itemsize = GetSetProperty(descr_itemsize),
     typecode = GetSetProperty(descr_typecode),
     __weakref__ = make_weakref_descr(W_ArrayBase),
-    )
+)
 W_ArrayBase.typedef.registermethods(globals())
 
 
@@ -159,8 +163,6 @@
         self.data[index] = char
 
 
-
-
 def make_array(mytype):
     class W_Array(W_ArrayBase):
         itemsize = mytype.bytes
@@ -268,12 +270,10 @@
                 raise
             self.setlen(oldlen + i)
 
-        def fromstring(self, w_s):
-            space = self.space
-            s = space.str_w(w_s)
+        def fromstring(self, s):
             if len(s) % self.itemsize != 0:
                 msg = 'string length not a multiple of item size'
-                raise OperationError(space.w_ValueError, space.wrap(msg))
+                raise OperationError(self.space.w_ValueError, self.space.wrap(msg))
             oldlen = self.len
             new = len(s) / mytype.bytes
             self.setlen(oldlen + new)
@@ -311,6 +311,14 @@
         def charbuf(self):
             return  rffi.cast(rffi.CCHARP, self.buffer)
 
+        def w_getitem(self, space, idx):
+            item = self.buffer[idx]
+            if mytype.typecode in 'bBhHil':
+                item = rffi.cast(lltype.Signed, item)
+            elif mytype.typecode == 'f':
+                item = float(item)
+            return space.wrap(item)
+
     # Basic get/set/append/extend methods
 
     def len__Array(space, self):
@@ -319,12 +327,7 @@
     def getitem__Array_ANY(space, self, w_idx):
         idx, stop, step = space.decode_index(w_idx, self.len)
         assert step == 0
-        item = self.buffer[idx]
-        if mytype.typecode in 'bBhHil':
-            item = rffi.cast(lltype.Signed, item)
-        elif mytype.typecode == 'f':
-            item = float(item)
-        return self.space.wrap(item)
+        return self.w_getitem(space, idx)
 
     def getitem__Array_Slice(space, self, w_slice):
         start, stop, step, size = space.decode_index4(w_slice, self.len)
@@ -387,7 +390,7 @@
     def array_count__Array_ANY(space, self, w_val):
         cnt = 0
         for i in range(self.len):
-            w_item = getitem__Array_ANY(space, self, space.wrap(i))
+            w_item = self.w_getitem(space, i)
             if space.is_true(space.eq(w_item, w_val)):
                 cnt += 1
         return space.wrap(cnt)
@@ -395,7 +398,7 @@
     def array_index__Array_ANY(space, self, w_val):
         cnt = 0
         for i in range(self.len):
-            w_item = getitem__Array_ANY(space, self, space.wrap(i))
+            w_item = self.w_getitem(space, i)
             if space.is_true(space.eq(w_item, w_val)):
                 return space.wrap(i)
         msg = 'array.index(x): x not in list'
@@ -413,7 +416,7 @@
         if i < 0 or i >= self.len:
             msg = 'pop index out of range'
             raise OperationError(space.w_IndexError, space.wrap(msg))
-        w_val = getitem__Array_ANY(space, self, space.wrap(i))
+        w_val = self.w_getitem(space, i)
         while i < self.len - 1:
             self.buffer[i] = self.buffer[i + 1]
             i += 1
@@ -515,14 +518,14 @@
     def array_tolist__Array(space, self):
         w_l = space.newlist([])
         for i in range(self.len):
-            w_l.append(getitem__Array_ANY(space, self, space.wrap(i)))
+            w_l.append(self.w_getitem(space, i))
         return w_l
 
     def array_fromlist__Array_List(space, self, w_lst):
         self.fromlist(w_lst)
 
     def array_fromstring__Array_ANY(space, self, w_s):
-        self.fromstring(w_s)
+        self.fromstring(space.str_w(w_s))
 
     def array_tostring__Array(space, self):
         cbuf = self.charbuf()
@@ -615,6 +618,16 @@
             dct = space.w_None
         return space.newtuple([space.type(self), space.newtuple(args), dct])
 
+    def array_copy__Array(space, self):
+        w_a = mytype.w_class(self.space)
+        w_a.setlen(self.len)
+        memcpy(
+            rffi.cast(rffi.VOIDP, w_a.buffer),
+            rffi.cast(rffi.VOIDP, self.buffer),
+            self.len * mytype.bytes
+        )
+        return w_a
+
     def array_byteswap__Array(space, self):
         if mytype.bytes not in [1, 2, 4, 8]:
             msg = "byteswap not supported for this array"
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -39,6 +39,7 @@
 import pypy.module.cpyext.object
 import pypy.module.cpyext.stringobject
 import pypy.module.cpyext.tupleobject
+import pypy.module.cpyext.setobject
 import pypy.module.cpyext.dictobject
 import pypy.module.cpyext.intobject
 import pypy.module.cpyext.longobject
@@ -64,6 +65,7 @@
 import pypy.module.cpyext.memoryobject
 import pypy.module.cpyext.codecs
 import pypy.module.cpyext.pyfile
+import pypy.module.cpyext.pystrtod
 
 # now that all rffi_platform.Struct types are registered, configure them
 api.configure_types()
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
@@ -562,7 +562,8 @@
             elif callable.api_func.restype is not lltype.Void:
                 retval = rffi.cast(callable.api_func.restype, result)
         except Exception, e:
-            print 'Fatal error in cpyext, calling', callable.__name__
+            print 'Fatal error in cpyext, CPython compatibility layer, calling', callable.__name__
+            print 'Either report a bug or consider not using this particular extension'
             if not we_are_translated():
                 import traceback
                 traceback.print_exc()
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
@@ -122,7 +122,7 @@
         return self.space.unwrap(self.descr_method_repr())
 
     def descr_method_repr(self):
-        return self.getrepr(self.space, "built-in method '%s' of '%s' object" % (self.name, self.w_objclass.getname(self.space, '?')))
+        return self.getrepr(self.space, "built-in method '%s' of '%s' object" % (self.name, self.w_objclass.getname(self.space)))
 
 PyCFunction_Check, PyCFunction_CheckExact = build_type_checkers("CFunction", W_PyCFunctionObject)
 
@@ -151,7 +151,7 @@
 
     def descr_method_repr(self):
         return self.space.wrap("<slot wrapper '%s' of '%s' objects>" % (self.method_name,
-            self.w_objclass.getname(self.space, '?')))
+            self.w_objclass.getname(self.space)))
 
 def cwrapper_descr_call(space, w_self, __args__):
     self = space.interp_w(W_PyCWrapperObject, w_self)
diff --git a/pypy/module/cpyext/pystrtod.py b/pypy/module/cpyext/pystrtod.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/pystrtod.py
@@ -0,0 +1,68 @@
+import errno
+from pypy.interpreter.error import OperationError
+from pypy.module.cpyext.api import cpython_api
+from pypy.module.cpyext.pyobject import PyObject
+from pypy.rlib import rdtoa
+from pypy.rlib import rfloat
+from pypy.rlib import rposix
+from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import rffi
+
+
+ at cpython_api([rffi.CCHARP, rffi.CCHARPP, PyObject], rffi.DOUBLE, error=-1.0)
+def PyOS_string_to_double(space, s, endptr, w_overflow_exception):
+    """Convert a string s to a double, raising a Python
+    exception on failure.  The set of accepted strings corresponds to
+    the set of strings accepted by Python's float() constructor,
+    except that s must not have leading or trailing whitespace.
+    The conversion is independent of the current locale.
+
+    If endptr is NULL, convert the whole string.  Raise
+    ValueError and return -1.0 if the string is not a valid
+    representation of a floating-point number.
+
+    If endptr is not NULL, convert as much of the string as
+    possible and set *endptr to point to the first unconverted
+    character.  If no initial segment of the string is the valid
+    representation of a floating-point number, set *endptr to point
+    to the beginning of the string, raise ValueError, and return
+    -1.0.
+
+    If s represents a value that is too large to store in a float
+    (for example, "1e500" is such a string on many platforms) then
+    if overflow_exception is NULL return Py_HUGE_VAL (with
+    an appropriate sign) and don't set any exception.  Otherwise,
+    overflow_exception must point to a Python exception object;
+    raise that exception and return -1.0.  In both cases, set
+    *endptr to point to the first character after the converted value.
+
+    If any other error occurs during the conversion (for example an
+    out-of-memory error), set the appropriate Python exception and
+    return -1.0.
+    """
+    user_endptr = True
+    try:
+        if not endptr:
+            endptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+            user_endptr = False
+        result = rdtoa.dg_strtod(s, endptr)
+        endpos = (rffi.cast(rffi.LONG, endptr[0]) -
+                  rffi.cast(rffi.LONG, s))
+        if endpos == 0 or (not user_endptr and not endptr[0][0] == '\0'):
+            raise OperationError(
+                space.w_ValueError,
+                space.wrap('invalid input at position %s' % endpos))
+        if rposix.get_errno() == errno.ERANGE:
+            rposix.set_errno(0)
+            if w_overflow_exception is None:
+                if result > 0:
+                    return rfloat.INFINITY
+                else:
+                    return -rfloat.INFINITY
+            else:
+                raise OperationError(w_overflow_exception,
+                                     space.wrap('value too large'))
+        return result
+    finally:
+        if not user_endptr:
+            lltype.free(endptr, flavor='raw')
diff --git a/pypy/module/cpyext/setobject.py b/pypy/module/cpyext/setobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/setobject.py
@@ -0,0 +1,46 @@
+from pypy.interpreter.error import OperationError
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
+                                    build_type_checkers)
+from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
+    borrow_from, make_ref, from_ref)
+from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
+from pypy.objspace.std.setobject import W_SetObject, newset
+from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
+
+
+PySet_Check, PySet_CheckExact = build_type_checkers("Set")
+
+
+ at cpython_api([PyObject], PyObject)
+def PySet_New(space, w_iterable):
+    if w_iterable is None:
+        return space.call_function(space.w_set)
+    else:
+        return space.call_function(space.w_set, w_iterable)
+
+ at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
+def PySet_Add(space, w_s, w_obj):
+    if not PySet_Check(space, w_s):
+        PyErr_BadInternalCall(space)
+    space.call_method(w_s, 'add', w_obj)
+    return 0
+
+ at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
+def PySet_Discard(space, w_s, w_obj):
+    if not PySet_Check(space, w_s):
+        PyErr_BadInternalCall(space)
+    space.call_method(w_s, 'discard', w_obj)
+    return 0
+
+
+ at cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
+def PySet_GET_SIZE(space, w_s):
+    return space.int_w(space.len(w_s))
+
+ at cpython_api([PyObject], Py_ssize_t, error=-1)
+def PySet_Size(space, ref):
+    if not PySet_Check(space, ref):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("expected set object"))
+    return PySet_GET_SIZE(space, ref)
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -480,39 +480,6 @@
     """Create a new Python complex number object from a C Py_complex value."""
     raise NotImplementedError
 
- at cpython_api([rffi.CCHARP, rffi.CCHARPP, PyObject], rffi.DOUBLE, error=-1.0)
-def PyOS_string_to_double(space, s, endptr, overflow_exception):
-    """Convert a string s to a double, raising a Python
-    exception on failure.  The set of accepted strings corresponds to
-    the set of strings accepted by Python's float() constructor,
-    except that s must not have leading or trailing whitespace.
-    The conversion is independent of the current locale.
-
-    If endptr is NULL, convert the whole string.  Raise
-    ValueError and return -1.0 if the string is not a valid
-    representation of a floating-point number.
-
-    If endptr is not NULL, convert as much of the string as
-    possible and set *endptr to point to the first unconverted
-    character.  If no initial segment of the string is the valid
-    representation of a floating-point number, set *endptr to point
-    to the beginning of the string, raise ValueError, and return
-    -1.0.
-
-    If s represents a value that is too large to store in a float
-    (for example, "1e500" is such a string on many platforms) then
-    if overflow_exception is NULL return Py_HUGE_VAL (with
-    an appropriate sign) and don't set any exception.  Otherwise,
-    overflow_exception must point to a Python exception object;
-    raise that exception and return -1.0.  In both cases, set
-    *endptr to point to the first character after the converted value.
-
-    If any other error occurs during the conversion (for example an
-    out-of-memory error), set the appropriate Python exception and
-    return -1.0.
-    """
-    raise NotImplementedError
-
 @cpython_api([rffi.CCHARP, rffi.CCHARPP], rffi.DOUBLE, error=CANNOT_FAIL)
 def PyOS_ascii_strtod(space, nptr, endptr):
     """Convert a string to a double. This function behaves like the Standard C
diff --git a/pypy/module/cpyext/test/test_pystrtod.py b/pypy/module/cpyext/test/test_pystrtod.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_pystrtod.py
@@ -0,0 +1,93 @@
+import math
+
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.rpython.lltypesystem import rffi
+from pypy.rpython.lltypesystem import lltype
+
+
+class TestPyOS_string_to_double(BaseApiTest):
+
+    def test_simple_float(self, api):
+        s = rffi.str2charp('0.4')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, None)
+        assert r == 0.4
+        rffi.free_charp(s)
+
+    def test_empty_string(self, api):
+        s = rffi.str2charp('')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, None)
+        assert r == -1.0
+        raises(ValueError)
+        api.PyErr_Clear()
+        rffi.free_charp(s)
+
+    def test_bad_string(self, api):
+        s = rffi.str2charp(' 0.4')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, None)
+        assert r == -1.0
+        raises(ValueError)
+        api.PyErr_Clear()
+        rffi.free_charp(s)
+
+    def test_overflow_pos(self, api):
+        s = rffi.str2charp('1e500')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, None)
+        assert math.isinf(r)
+        assert r > 0
+        rffi.free_charp(s)
+
+    def test_overflow_neg(self, api):
+        s = rffi.str2charp('-1e500')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, None)
+        assert math.isinf(r)
+        assert r < 0
+        rffi.free_charp(s)
+
+    def test_overflow_exc(self, space, api):
+        s = rffi.str2charp('1e500')
+        null = lltype.nullptr(rffi.CCHARPP.TO)
+        r = api.PyOS_string_to_double(s, null, space.w_ValueError)
+        assert r == -1.0
+        raises(ValueError)
+        api.PyErr_Clear()
+        rffi.free_charp(s)
+
+    def test_endptr_number(self, api):
+        s = rffi.str2charp('0.4')
+        endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+        r = api.PyOS_string_to_double(s, endp, None)
+        assert r == 0.4
+        endp_addr = rffi.cast(rffi.LONG, endp[0])
+        s_addr = rffi.cast(rffi.LONG, s)
+        assert endp_addr == s_addr + 3
+        rffi.free_charp(s)
+        lltype.free(endp, flavor='raw')
+
+    def test_endptr_tail(self, api):
+        s = rffi.str2charp('0.4 foo')
+        endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+        r = api.PyOS_string_to_double(s, endp, None)
+        assert r == 0.4
+        endp_addr = rffi.cast(rffi.LONG, endp[0])
+        s_addr = rffi.cast(rffi.LONG, s)
+        assert endp_addr == s_addr + 3
+        rffi.free_charp(s)
+        lltype.free(endp, flavor='raw')
+
+    def test_endptr_no_conversion(self, api):
+        s = rffi.str2charp('foo')
+        endp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+        r = api.PyOS_string_to_double(s, endp, None)
+        assert r == -1.0
+        raises(ValueError)
+        endp_addr = rffi.cast(rffi.LONG, endp[0])
+        s_addr = rffi.cast(rffi.LONG, s)
+        assert endp_addr == s_addr
+        api.PyErr_Clear()
+        rffi.free_charp(s)
+        lltype.free(endp, flavor='raw')
diff --git a/pypy/module/cpyext/test/test_setobject.py b/pypy/module/cpyext/test/test_setobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_setobject.py
@@ -0,0 +1,29 @@
+import py
+
+from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.conftest import gettestobjspace
+
+
+class TestTupleObject(BaseApiTest):
+    def test_setobj(self, space, api):
+        assert not api.PySet_Check(space.w_None)
+        assert api.PySet_Add(space.w_None, space.w_None) == -1
+        api.PyErr_Clear()
+        w_set = space.call_function(space.w_set)
+        space.call_method(w_set, 'update', space.wrap([1,2,3,4]))
+        assert api.PySet_Size(w_set) == 4
+        assert api.PySet_GET_SIZE(w_set) == 4
+        raises(TypeError, api.PySet_Size(space.newlist([])))
+        api.PyErr_Clear()
+
+    def test_set_add_discard(self, space, api):
+        w_set = api.PySet_New(None)
+        assert api.PySet_Size(w_set) == 0
+        w_set = api.PySet_New(space.wrap([1,2,3,4]))
+        assert api.PySet_Size(w_set) == 4
+        api.PySet_Add(w_set, space.wrap(6))
+        assert api.PySet_Size(w_set) == 5
+        api.PySet_Discard(w_set, space.wrap(6))
+        assert api.PySet_Size(w_set) == 4
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -367,3 +367,14 @@
                     data, len(u), lltype.nullptr(rffi.CCHARP.TO))
         rffi.free_wcharp(data)
 
+    def test_format(self, space, api):
+        w_format = space.wrap(u'hi %s')
+        w_args = space.wrap((u'test',))
+        w_formated = api.PyUnicode_Format(w_format, w_args)
+        assert space.unwrap(w_formated) == space.unwrap(space.mod(w_format, w_args))
+
+    def test_join(self, space, api):
+        w_sep = space.wrap(u'<sep>')
+        w_seq = space.wrap([u'a', u'b'])
+        w_joined = api.PyUnicode_Join(w_sep, w_seq)
+        assert space.unwrap(w_joined) == u'a<sep>b'
diff --git a/pypy/module/cpyext/test/test_weakref.py b/pypy/module/cpyext/test/test_weakref.py
--- a/pypy/module/cpyext/test/test_weakref.py
+++ b/pypy/module/cpyext/test/test_weakref.py
@@ -7,6 +7,7 @@
         w_ref = api.PyWeakref_NewRef(w_obj, space.w_None)
         assert w_ref is not None
         assert space.is_w(api.PyWeakref_GetObject(w_ref), w_obj)
+        assert space.is_w(api.PyWeakref_GET_OBJECT(w_ref), w_obj)
         assert space.is_w(api.PyWeakref_LockObject(w_ref), w_obj)
 
         w_obj = space.newtuple([])
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
@@ -450,7 +450,7 @@
             PyObject_Del.api_func.get_wrapper(space))
     pto.c_tp_alloc = llhelper(PyType_GenericAlloc.api_func.functype,
             PyType_GenericAlloc.api_func.get_wrapper(space))
-    pto.c_tp_name = rffi.str2charp(w_type.getname(space, "?"))
+    pto.c_tp_name = rffi.str2charp(w_type.getname(space))
     pto.c_tp_basicsize = -1 # hopefully this makes malloc bail out
     pto.c_tp_itemsize = 0
     # uninitialized fields:
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
@@ -523,3 +523,11 @@
     copies sizeof(Py_UNICODE) * length bytes from source to target"""
     for i in range(0, length):
         target[i] = source[i]
+
+ at cpython_api([PyObject, PyObject], PyObject)
+def PyUnicode_Format(space, w_format, w_args):
+    return space.mod(w_format, w_args)
+
+ at cpython_api([PyObject, PyObject], PyObject)
+def PyUnicode_Join(space, w_sep, w_seq):
+    return space.call_method(w_sep, 'join', w_seq)
diff --git a/pypy/module/cpyext/weakrefobject.py b/pypy/module/cpyext/weakrefobject.py
--- a/pypy/module/cpyext/weakrefobject.py
+++ b/pypy/module/cpyext/weakrefobject.py
@@ -21,6 +21,10 @@
     """Return the referenced object from a weak reference.  If the referent is
     no longer live, returns None. This function returns a borrowed reference.
     """
+    return PyWeakref_GET_OBJECT(space, w_ref)
+
+ at cpython_api([PyObject], PyObject)
+def PyWeakref_GET_OBJECT(space, w_ref):
     return borrow_from(w_ref, space.call_function(w_ref))
 
 @cpython_api([PyObject], PyObject)
diff --git a/pypy/module/exceptions/interp_exceptions.py b/pypy/module/exceptions/interp_exceptions.py
--- a/pypy/module/exceptions/interp_exceptions.py
+++ b/pypy/module/exceptions/interp_exceptions.py
@@ -136,7 +136,7 @@
             args_repr = space.str_w(space.repr(space.newtuple(self.args_w)))
         else:
             args_repr = "()"
-        clsname = self.getclass(space).getname(space, '?')
+        clsname = self.getclass(space).getname(space)
         return space.wrap(clsname + args_repr)
 
     def descr_getargs(self, space):
@@ -546,7 +546,7 @@
             w_tuple = space.newtuple(values_w + [self.w_lastlineno])
             args_w = [self.args_w[0], w_tuple]
             args_repr = space.str_w(space.repr(space.newtuple(args_w)))
-            clsname = self.getclass(space).getname(space, '?')
+            clsname = self.getclass(space).getname(space)
             return space.wrap(clsname + args_repr)
         else:
             return W_StandardError.descr_repr(self, space)
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -120,7 +120,7 @@
 def check_sys_modules_w(space, modulename):
     return space.finditem_str(space.sys.get('modules'), modulename)
 
- at jit.purefunction
+ at jit.elidable
 def _get_dot_position(str, n):
     # return the index in str of the '.' such that there are n '.'-separated
     # strings after it
@@ -133,8 +133,8 @@
 def _get_relative_name(space, modulename, level, w_globals):
     w = space.wrap
     ctxt_w_package = space.finditem_str(w_globals, '__package__')
-    ctxt_w_package = jit.hint(ctxt_w_package, promote=True)
-    level = jit.hint(level, promote=True)
+    ctxt_w_package = jit.promote(ctxt_w_package)
+    level = jit.promote(level)
 
     ctxt_package = None
     if ctxt_w_package is not None and ctxt_w_package is not space.w_None:
@@ -184,7 +184,7 @@
         ctxt_w_name = space.finditem_str(w_globals, '__name__')
         ctxt_w_path = space.finditem_str(w_globals, '__path__')
 
-        ctxt_w_name = jit.hint(ctxt_w_name, promote=True)
+        ctxt_w_name = jit.promote(ctxt_w_name)
         ctxt_name = None
         if ctxt_w_name is not None:
             try:
@@ -799,14 +799,13 @@
 
 """
 
-# XXX picking a magic number is a mess.  So far it works because we
-# have only two extra opcodes, which bump the magic number by +1 and
-# +2 respectively, and CPython leaves a gap of 10 when it increases
+# picking a magic number is a mess.  So far it works because we
+# have only one extra opcode, which bumps the magic number by +2, and CPython
+# leaves a gap of 10 when it increases
 # its own magic number.  To avoid assigning exactly the same numbers
 # as CPython we always add a +2.  We'll have to think again when we
-# get at the fourth new opcode :-(
+# get three more new opcodes
 #
-#  * CALL_LIKELY_BUILTIN    +1
 #  * CALL_METHOD            +2
 #
 # In other words:
@@ -829,8 +828,6 @@
             return struct.unpack('<i', magic)[0]
 
     result = default_magic
-    if space.config.objspace.opcodes.CALL_LIKELY_BUILTIN:
-        result += 1
     if space.config.objspace.opcodes.CALL_METHOD:
         result += 2
     return result
diff --git a/pypy/module/math/__init__.py b/pypy/module/math/__init__.py
--- a/pypy/module/math/__init__.py
+++ b/pypy/module/math/__init__.py
@@ -4,6 +4,7 @@
 
 class Module(MixedModule):
     appleveldefs = {
+       'factorial' : 'app_math.factorial'
     }
 
     interpleveldefs = {
@@ -40,7 +41,6 @@
        'isnan'          : 'interp_math.isnan',
        'trunc'          : 'interp_math.trunc',
        'fsum'           : 'interp_math.fsum',
-       'factorial'      : 'interp_math.factorial',
        'asinh'          : 'interp_math.asinh',
        'acosh'          : 'interp_math.acosh',
        'atanh'          : 'interp_math.atanh',
diff --git a/pypy/module/math/app_math.py b/pypy/module/math/app_math.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/math/app_math.py
@@ -0,0 +1,13 @@
+def factorial(x):
+    """Find x!."""
+    if isinstance(x, float):
+        fl = int(x)
+        if fl != x:
+            raise ValueError("float arguments must be integral")
+        x = fl
+    if x < 0:
+        raise ValueError("x must be >= 0")
+    res = 1
+    for i in range(1, x + 1):
+        res *= i
+    return res
diff --git a/pypy/module/math/interp_math.py b/pypy/module/math/interp_math.py
--- a/pypy/module/math/interp_math.py
+++ b/pypy/module/math/interp_math.py
@@ -373,22 +373,6 @@
                 hi = v
     return space.wrap(hi)
 
-def factorial(space, w_x):
-    """Find x!."""
-    if space.isinstance_w(w_x, space.w_float):
-        fl = space.float_w(w_x)
-        if math.floor(fl) != fl:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("float arguments must be integral"))
-        w_x = space.long(w_x)
-    x = space.int_w(w_x)
-    if x < 0:
-        raise OperationError(space.w_ValueError, space.wrap("x must be >= 0"))
-    w_res = space.wrap(1)
-    for i in range(1, x + 1):
-        w_res = space.mul(w_res, space.wrap(i))
-    return w_res
-
 def log1p(space, w_x):
     """Find log(x + 1)."""
     return math1(space, rfloat.log1p, w_x)
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
@@ -40,6 +40,11 @@
         elif b == '/':
             right = stack.pop()
             stack.append(stack.pop().descr_div(space, right))
+        elif b == '%':
+            right = stack.pop()
+            stack.append(stack.pop().descr_mod(space, right))
+        elif b == '|':
+            stack.append(stack.pop().descr_abs(space))
         else:
             print "Unknown opcode: %s" % b
             raise BogusBytecode()
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -5,6 +5,7 @@
 from pypy.rlib import jit
 from pypy.rpython.lltypesystem import lltype
 from pypy.tool.sourcetools import func_with_new_name
+import math
 
 
 def dummy1(v):
@@ -30,6 +31,12 @@
         self.transitions[target] = new = Signature()
         return new
 
+def pos(v):
+    return v
+def neg(v):
+    return -v
+def absolute(v):
+    return abs(v)
 def add(v1, v2):
     return v1 + v2
 def sub(v1, v2):
@@ -38,6 +45,10 @@
     return v1 * v2
 def div(v1, v2):
     return v1 / v2
+def pow(v1, v2):
+    return math.pow(v1, v2)
+def mod(v1, v2):
+    return math.fmod(v1, v2)
 
 class BaseArray(Wrappable):
     def __init__(self):
@@ -52,6 +63,22 @@
             arr.force_if_needed()
         del self.invalidates[:]
 
+    def _unop_impl(function):
+        signature = Signature()
+        def impl(self, space):
+            new_sig = self.signature.transition(signature)
+            res = Call1(
+                function,
+                self,
+                new_sig)
+            self.invalidates.append(res)
+            return space.wrap(res)
+        return func_with_new_name(impl, "uniop_%s_impl" % function.__name__)
+
+    descr_pos = _unop_impl(pos)
+    descr_neg = _unop_impl(neg)
+    descr_abs = _unop_impl(absolute)
+
     def _binop_impl(function):
         signature = Signature()
         def impl(self, space, w_other):
@@ -80,6 +107,8 @@
     descr_sub = _binop_impl(sub)
     descr_mul = _binop_impl(mul)
     descr_div = _binop_impl(div)
+    descr_pow = _binop_impl(pow)
+    descr_mod = _binop_impl(mod)
 
     def get_concrete(self):
         raise NotImplementedError
@@ -362,10 +391,15 @@
     __getitem__ = interp2app(BaseArray.descr_getitem),
     __setitem__ = interp2app(BaseArray.descr_setitem),
 
+    __pos__ = interp2app(BaseArray.descr_pos),
+    __neg__ = interp2app(BaseArray.descr_neg),
+    __abs__ = interp2app(BaseArray.descr_abs),
     __add__ = interp2app(BaseArray.descr_add),
     __sub__ = interp2app(BaseArray.descr_sub),
     __mul__ = interp2app(BaseArray.descr_mul),
     __div__ = interp2app(BaseArray.descr_div),
+    __pow__ = interp2app(BaseArray.descr_pow),
+    __mod__ = interp2app(BaseArray.descr_mod),
 
     mean = interp2app(BaseArray.descr_mean),
 )
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -163,6 +163,72 @@
         for i in range(5):
             assert b[i] == i / 5.0
 
+    def test_pow(self):
+        from numpy import array
+        a = array(range(5))
+        b = a ** a
+        for i in range(5):
+            print b[i], i**i
+            assert b[i] == i**i
+
+    def test_pow_other(self):
+        from numpy import array
+        a = array(range(5))
+        b = array([2, 2, 2, 2, 2])
+        c = a ** b
+        for i in range(5):
+            assert c[i] == i ** 2
+
+    def test_pow_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a ** 2
+        for i in range(5):
+            assert b[i] == i ** 2
+
+    def test_mod(self):
+        from numpy import array
+        a = array(range(1,6))
+        b = a % a
+        for i in range(5):
+            assert b[i] == 0
+
+    def test_mod_other(self):
+        from numpy import array
+        a = array(range(5))
+        b = array([2, 2, 2, 2, 2])
+        c = a % b
+        for i in range(5):
+            assert c[i] == i % 2
+
+    def test_mod_constant(self):
+        from numpy import array
+        a = array(range(5))
+        b = a % 2
+        for i in range(5):
+            assert b[i] == i % 2
+
+    def test_pos(self):
+        from numpy import array
+        a = array([1.,-2.,3.,-4.,-5.])
+        b = +a
+        for i in range(5):
+            assert b[i] == a[i]
+
+    def test_neg(self):
+        from numpy import array
+        a = array([1.,-2.,3.,-4.,-5.])
+        b = -a
+        for i in range(5):
+            assert b[i] == -a[i]
+
+    def test_abs(self):
+        from numpy import array
+        a = array([1.,-2.,3.,-4.,-5.])
+        b = abs(a)
+        for i in range(5):
+            assert b[i] == abs(a[i])
+
     def test_auto_force(self):
         from numpy import array
         a = array(range(5))
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
@@ -1,7 +1,7 @@
 from pypy.jit.metainterp.test.support import LLJitMixin
 from pypy.rpython.test.test_llinterp import interpret
 from pypy.module.micronumpy.interp_numarray import (SingleDimArray, Signature,
-    FloatWrapper, Call1, Call2, SingleDimSlice, add, mul)
+    FloatWrapper, Call2, SingleDimSlice, add, mul, neg, Call1)
 from pypy.module.micronumpy.interp_ufuncs import negative
 from pypy.module.micronumpy.compile import numpy_compile
 
@@ -13,8 +13,6 @@
         cls.space = FakeSpace()
 
     def test_add(self):
-        space = self.space
-
         def f(i):
             ar = SingleDimArray(i)
             v = Call2(add, ar, ar, Signature())
@@ -27,8 +25,6 @@
         assert result == f(5)
 
     def test_floatadd(self):
-        space = self.space
-
         def f(i):
             ar = SingleDimArray(i)
             v = Call2(add, ar, FloatWrapper(4.5), Signature())
@@ -40,11 +36,24 @@
                           "int_lt": 1, "guard_true": 1, "jump": 1})
         assert result == f(5)
 
-    def test_already_forecd(self):
+    def test_neg(self):
         space = self.space
 
         def f(i):
             ar = SingleDimArray(i)
+            v = Call1(neg, ar, Signature())
+            return v.get_concrete().storage[3]
+
+        result = self.meta_interp(f, [5], listops=True, backendopt=True)
+        self.check_loops({"getarrayitem_raw": 1, "float_neg": 1,
+                          "setarrayitem_raw": 1, "int_add": 1,
+                          "int_lt": 1, "guard_true": 1, "jump": 1})
+
+        assert result == f(5)
+
+    def test_already_forecd(self):
+        def f(i):
+            ar = SingleDimArray(i)
             v1 = Call2(add, ar, FloatWrapper(4.5), Signature())
             v2 = Call2(mul, v1, FloatWrapper(4.5), Signature())
             v1.force_if_needed()
@@ -95,8 +104,6 @@
         self.check_loop_count(3)
 
     def test_slice(self):
-        space = self.space
-
         def f(i):
             step = 3
             ar = SingleDimArray(step*i)
@@ -111,8 +118,6 @@
         assert result == f(5)
 
     def test_slice2(self):
-        space = self.space
-
         def f(i):
             step1 = 2
             step2 = 3
diff --git a/pypy/module/oracle/interp_variable.py b/pypy/module/oracle/interp_variable.py
--- a/pypy/module/oracle/interp_variable.py
+++ b/pypy/module/oracle/interp_variable.py
@@ -1484,7 +1484,7 @@
     raise OperationError(
         moduledict.w_NotSupportedError,
         space.wrap("Variable_TypeByValue(): unhandled data type %s" %
-                   (space.type(w_value).getname(space, '?'),)))
+                   (space.type(w_value).getname(space),)))
 
 def newByInputTypeHandler(space, cursor, w_inputTypeHandler, w_value, numElements):
     w_var = space.call(w_inputTypeHandler,
diff --git a/pypy/module/pyexpat/__init__.py b/pypy/module/pyexpat/__init__.py
--- a/pypy/module/pyexpat/__init__.py
+++ b/pypy/module/pyexpat/__init__.py
@@ -2,6 +2,22 @@
 
 from pypy.interpreter.mixedmodule import MixedModule
 
+class ErrorsModule(MixedModule):
+    "Definition of pyexpat.errors module."
+
+    appleveldefs = {
+        }
+
+    interpleveldefs = {
+        }
+
+    def setup_after_space_initialization(self):
+        from pypy.module.pyexpat import interp_pyexpat
+        for name in interp_pyexpat.xml_error_list:
+            self.space.setattr(self, self.space.wrap(name),
+                    interp_pyexpat.ErrorString(self.space,
+                    getattr(interp_pyexpat, name)))
+
 class Module(MixedModule):
     "Python wrapper for Expat parser."
 
@@ -21,6 +37,10 @@
         'version_info':  'interp_pyexpat.get_expat_version_info(space)',
         }
 
+    submodules = {
+        'errors': ErrorsModule,
+    }
+
     for name in ['XML_PARAM_ENTITY_PARSING_NEVER',
                  'XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE',
                  'XML_PARAM_ENTITY_PARSING_ALWAYS']:
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
@@ -31,6 +31,48 @@
 XML_Content_Ptr = lltype.Ptr(lltype.ForwardReference())
 XML_Parser = rffi.COpaquePtr(typedef='XML_Parser')
 
+xml_error_list = [
+    "XML_ERROR_NO_MEMORY",
+    "XML_ERROR_SYNTAX",
+    "XML_ERROR_NO_ELEMENTS",
+    "XML_ERROR_INVALID_TOKEN",
+    "XML_ERROR_UNCLOSED_TOKEN",
+    "XML_ERROR_PARTIAL_CHAR",
+    "XML_ERROR_TAG_MISMATCH",
+    "XML_ERROR_DUPLICATE_ATTRIBUTE",
+    "XML_ERROR_JUNK_AFTER_DOC_ELEMENT",
+    "XML_ERROR_PARAM_ENTITY_REF",
+    "XML_ERROR_UNDEFINED_ENTITY",
+    "XML_ERROR_RECURSIVE_ENTITY_REF",
+    "XML_ERROR_ASYNC_ENTITY",
+    "XML_ERROR_BAD_CHAR_REF",
+    "XML_ERROR_BINARY_ENTITY_REF",
+    "XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF",
+    "XML_ERROR_MISPLACED_XML_PI",
+    "XML_ERROR_UNKNOWN_ENCODING",
+    "XML_ERROR_INCORRECT_ENCODING",
+    "XML_ERROR_UNCLOSED_CDATA_SECTION",
+    "XML_ERROR_EXTERNAL_ENTITY_HANDLING",
+    "XML_ERROR_NOT_STANDALONE",
+    "XML_ERROR_UNEXPECTED_STATE",
+    "XML_ERROR_ENTITY_DECLARED_IN_PE",
+    "XML_ERROR_FEATURE_REQUIRES_XML_DTD",
+    "XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING",
+    # Added in Expat 1.95.7.
+    "XML_ERROR_UNBOUND_PREFIX",
+    # Added in Expat 1.95.8.
+    "XML_ERROR_UNDECLARING_PREFIX",
+    "XML_ERROR_INCOMPLETE_PE",
+    "XML_ERROR_XML_DECL",
+    "XML_ERROR_TEXT_DECL",
+    "XML_ERROR_PUBLICID",
+    "XML_ERROR_SUSPENDED",
+    "XML_ERROR_NOT_SUSPENDED",
+    "XML_ERROR_ABORTED",
+    "XML_ERROR_FINISHED",
+    "XML_ERROR_SUSPEND_PE",
+    ]
+
 class CConfigure:
     _compilation_info_ = eci
     XML_Content = rffi_platform.Struct('XML_Content', [
@@ -56,6 +98,9 @@
     XML_FALSE = rffi_platform.ConstantInteger('XML_FALSE')
     XML_TRUE = rffi_platform.ConstantInteger('XML_TRUE')
 
+    for name in xml_error_list:
+        locals()[name] = rffi_platform.ConstantInteger(name)
+
 for k, v in rffi_platform.configure(CConfigure).items():
     globals()[k] = v
 
@@ -298,7 +343,8 @@
 XML_GetErrorCode = expat_external(
     'XML_GetErrorCode', [XML_Parser], rffi.INT)
 XML_ErrorString = expat_external(
-    'XML_ErrorString', [rffi.INT], rffi.CCHARP)
+    'XML_ErrorString', [rffi.INT],
+    rffi.CCHARP)
 XML_GetCurrentLineNumber = expat_external(
     'XML_GetCurrentLineNumber', [XML_Parser], rffi.INT)
 XML_GetErrorLineNumber = XML_GetCurrentLineNumber
@@ -691,7 +737,7 @@
     elif space.is_true(space.isinstance(w_encoding, space.w_str)):
         encoding = space.str_w(w_encoding)
     else:
-        type_name = space.type(w_encoding).getname(space, '?')
+        type_name = space.type(w_encoding).getname(space)
         raise OperationError(
             space.w_TypeError,
             space.wrap('ParserCreate() argument 1 must be string or None,'
@@ -711,7 +757,7 @@
                 space.wrap('namespace_separator must be at most one character,'
                            ' omitted, or None'))
     else:
-        type_name = space.type(w_namespace_separator).getname(space, '?')
+        type_name = space.type(w_namespace_separator).getname(space)
         raise OperationError(
             space.w_TypeError,
             space.wrap('ParserCreate() argument 2 must be string or None,'
diff --git a/pypy/module/pyexpat/test/test_parser.py b/pypy/module/pyexpat/test/test_parser.py
--- a/pypy/module/pyexpat/test/test_parser.py
+++ b/pypy/module/pyexpat/test/test_parser.py
@@ -38,7 +38,7 @@
         parser = pyexpat.ParserCreate()
         raises(pyexpat.ExpatError, "parser.Parse(xml, True)")
 
-    def test_encoding(self):
+    def test_encoding_argument(self):
         import pyexpat
         for encoding_arg in (None, 'utf-8', 'iso-8859-1'):
             for namespace_arg in (None, '{'):
@@ -68,7 +68,7 @@
         assert p.buffer_size == 150
         raises(TypeError, setattr, p, 'buffer_size', sys.maxint + 1)
 
-    def test_encoding(self):
+    def test_encoding_xml(self):
         # use one of the few encodings built-in in expat
         xml = "<?xml version='1.0' encoding='iso-8859-1'?><s>caf\xe9</s>"
         import pyexpat
@@ -120,3 +120,14 @@
             return True
         p.ExternalEntityRefHandler = handler
         p.Parse(xml)
+
+    def test_errors(self):
+        import types
+        import pyexpat
+        assert isinstance(pyexpat.errors, types.ModuleType)
+        # check a few random errors
+        assert pyexpat.errors.XML_ERROR_SYNTAX == 'syntax error'
+        assert (pyexpat.errors.XML_ERROR_INCORRECT_ENCODING ==
+               'encoding specified in XML declaration is incorrect')
+        assert (pyexpat.errors.XML_ERROR_XML_DECL ==
+                'XML declaration not well-formed')
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -21,8 +21,7 @@
 from pypy.module.pypyjit.interp_resop import debug_merge_point_from_boxes
 
 PyFrame._virtualizable2_ = ['last_instr', 'pycode',
-                            'valuestackdepth', 'valuestack_w[*]',
-                            'fastlocals_w[*]',
+                            'valuestackdepth', 'locals_stack_w[*]',
                             'last_exception',
                             'lastblock',
                             'is_being_profiled',
@@ -177,6 +176,8 @@
     '''Configure the tunable JIT parameters.
         * set_param(name=value, ...)            # as keyword arguments
         * set_param("name=value,name=value")    # as a user-supplied string
+        * set_param("off")                      # disable the jit
+        * set_param("default")                  # restore all defaults
     '''
     # XXXXXXXXX
     args_w, kwds_w = __args__.unpack()
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -15,7 +15,7 @@
         if modname in ['pypyjit', 'signal', 'micronumpy', 'math', 'exceptions',
                        'imp', 'sys', 'array', '_ffi', 'itertools', 'operator',
                        'posix', '_socket', '_sre', '_lsprof', '_weakref',
-                       '__pypy__']:
+                       '__pypy__', 'cStringIO']:
             return True
         return False
 
diff --git a/pypy/module/pypyjit/test/test_jit_setup.py b/pypy/module/pypyjit/test/test_jit_setup.py
--- a/pypy/module/pypyjit/test/test_jit_setup.py
+++ b/pypy/module/pypyjit/test/test_jit_setup.py
@@ -9,21 +9,42 @@
         # this just checks that the module is setting up things correctly, and
         # the resulting code makes sense on top of CPython.
         import pypyjit
-        pypyjit.set_param(threshold=5, inlining=1)
-        pypyjit.set_param("trace_eagerness=3,inlining=0")
+        try:
+            pypyjit.set_param(threshold=5, inlining=1)
+            pypyjit.set_param("trace_eagerness=3,inlining=0")
 
-        def f(x, y):
-            return x*y+1
+            def f(x, y):
+                return x*y+1
 
-        assert f(6, 7) == 43
+            assert f(6, 7) == 43
 
-        def gen(x):
-            i = 0
-            while i < x:
-                yield i*i
-                i += 1
+            def gen(x):
+                i = 0
+                while i < x:
+                    yield i*i
+                    i += 1
 
-        assert list(gen(3)) == [0, 1, 4]
+            assert list(gen(3)) == [0, 1, 4]
+        finally:
+            pypyjit.set_param('default')
+
+    def test_no_jit(self):
+        import pypyjit
+        was_called = []
+        def should_not_be_called(*args, **kwds):
+            was_called.append((args, kwds))
+        try:
+            pypyjit.set_param('off')
+            pypyjit.set_compile_hook(should_not_be_called)
+            def f():
+                pass
+            for i in range(2500):
+                f()
+            assert not was_called
+        finally:
+            pypyjit.set_compile_hook(None)
+            pypyjit.set_param('default')
+
 
 def test_interface_residual_call():
     space = gettestobjspace(usemodules=['pypyjit'])
diff --git a/pypy/module/pypyjit/test_pypy_c/test_00_model.py b/pypy/module/pypyjit/test_pypy_c/test_00_model.py
--- a/pypy/module/pypyjit/test_pypy_c/test_00_model.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_00_model.py
@@ -58,6 +58,8 @@
         stdout, stderr = pipe.communicate()
         if stderr.startswith('SKIP:'):
             py.test.skip(stderr)
+        if stderr.startswith('debug_alloc.h:'):   # lldebug builds
+            stderr = ''
         assert not stderr
         #
         # parse the JIT log
diff --git a/pypy/module/pypyjit/test_pypy_c/test_array.py b/pypy/module/pypyjit/test_pypy_c/test_array.py
--- a/pypy/module/pypyjit/test_pypy_c/test_array.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_array.py
@@ -46,7 +46,7 @@
             guard_no_overflow(descr=<Guard4>)
             i18 = int_add(i7, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, p6, i18, i16, i9, i10, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, p5, i18, i16, p8, i9, i10, descr=<Loop0>)
         """)
 
     def test_array_intimg(self):
@@ -83,7 +83,7 @@
             setarrayitem_raw(i11, i8, _, descr=<.*ArrayNoLengthDescr>)
             i28 = int_add(i8, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, p6, p7, i28, i15, i10, i11, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, p5, p6, i28, i15, p9, i10, i11, descr=<Loop0>)
         """)
 
 
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -66,13 +66,13 @@
         ops = entry_bridge.ops_by_id('cond', opcode='LOAD_GLOBAL')
         assert log.opnames(ops) == ["guard_value",
                                     "getfield_gc", "guard_value",
-                                    "getfield_gc", "guard_isnull",
+                                    "getfield_gc", "guard_value",
                                     "getfield_gc", "guard_nonnull_class"]
         # LOAD_GLOBAL of OFFSET but in different function partially folded
         # away
         # XXX could be improved
         ops = entry_bridge.ops_by_id('add', opcode='LOAD_GLOBAL')
-        assert log.opnames(ops) == ["guard_value", "getfield_gc", "guard_isnull"]
+        assert log.opnames(ops) == ["guard_value", "getfield_gc", "guard_value"]
         #
         # two LOAD_GLOBAL of f, the second is folded away
         ops = entry_bridge.ops_by_id('call', opcode='LOAD_GLOBAL')
@@ -187,7 +187,7 @@
             guard_no_overflow(descr=<Guard5>)
             i18 = force_token()
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, i8, p7, i17, i9, p10, p11, p12, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, i8, p7, i17, p8, i9, p10, p11, p12, descr=<Loop0>)
         """)
 
     def test_default_and_kw(self):
@@ -209,6 +209,26 @@
             i16 = force_token()
         """)
 
+    def test_kwargs_empty(self):
+        def main(x):
+            def g(**args):
+                return len(args) + 1
+            #
+            s = 0
+            d = {}
+            i = 0
+            while i < x:
+                s += g(**d)       # ID: call
+                i += 1
+            return s
+        #
+        log = self.run(main, [1000])
+        assert log.result == 1000
+        loop, = log.loops_by_id('call')
+        ops = log.opnames(loop.ops_by_id('call'))
+        guards = [ops for ops in ops if ops.startswith('guard')]
+        assert guards == ["guard_no_overflow"]
+
     def test_kwargs(self):
         # this is not a very precise test, could be improved
         def main(x):
@@ -216,20 +236,24 @@
                 return len(args)
             #
             s = 0
-            d = {}
-            for i in range(x):
+            d = {"a": 1}
+            i = 0
+            while i < x:
                 s += g(**d)       # ID: call
                 d[str(i)] = i
                 if i % 100 == 99:
-                    d = {}
+                    d = {"a": 1}
+                i += 1
             return s
         #
         log = self.run(main, [1000])
-        assert log.result == 49500
+        assert log.result == 50500
         loop, = log.loops_by_id('call')
+        print loop.ops_by_id('call')
         ops = log.opnames(loop.ops_by_id('call'))
         guards = [ops for ops in ops if ops.startswith('guard')]
-        assert len(guards) <= 5
+        print guards
+        assert len(guards) <= 20
 
     def test_stararg_virtual(self):
         def main(x):
@@ -277,12 +301,12 @@
             i21 = force_token()
             setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
             i23 = int_add_ovf(i9, 3)
-            guard_no_overflow(descr=<Guard37>)
+            guard_no_overflow(descr=...)
         """)
         assert loop1.match_by_id('h2', """
             i25 = force_token()
             i27 = int_add_ovf(i23, 2)
-            guard_no_overflow(descr=<Guard38>)
+            guard_no_overflow(descr=...)
         """)
 
     def test_stararg(self):
diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py
@@ -0,0 +1,25 @@
+
+import py, sys
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+
+class TestDicts(BaseTestPyPyC):
+    def test_strdict(self):
+        def fn(n):
+            import sys
+            d = {}
+            class A(object):
+                pass
+            a = A()
+            a.x = 1
+            for s in sys.modules.keys() * 1000:
+                inc = a.x # ID: look
+                d[s] = d.get(s, 0) + inc
+            return sum(d.values())
+        #
+        log = self.run(fn, [1000])
+        assert log.result % 1000 == 0
+        loop, = log.loops_by_filename(self.filepath)
+        ops = loop.ops_by_id('look')
+        assert log.opnames(ops) == ['setfield_gc',
+                                    'guard_not_invalidated']
diff --git a/pypy/module/pypyjit/test_pypy_c/test_intbound.py b/pypy/module/pypyjit/test_pypy_c/test_intbound.py
--- a/pypy/module/pypyjit/test_pypy_c/test_intbound.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_intbound.py
@@ -97,7 +97,7 @@
             guard_no_overflow(descr=...)
             i17 = int_add(i8, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, i14, i12, i17, i9, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, i14, i12, i17, p8, i9, descr=<Loop0>)
         """)
 
     def test_intbound_sub_lt(self):
@@ -149,7 +149,7 @@
             guard_no_overflow(descr=...)
             i19 = int_add(i8, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, i16, i14, i19, i9, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, i16, i14, i19, p8, i9, descr=<Loop0>)
         """)
 
     def test_intbound_addmul_ge(self):
@@ -177,7 +177,7 @@
             guard_no_overflow(descr=...)
             i21 = int_add(i8, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, i18, i14, i21, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, i18, i14, i21, p8, descr=<Loop0>)
         """)
 
     def test_intbound_eq(self):
@@ -209,7 +209,7 @@
             guard_no_overflow(descr=...)
             i16 = int_add(i8, 1)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, p6, i14, i16, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, p6, i14, i16, p8, descr=<Loop0>)
         """)
 
     def test_intbound_mul(self):
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
@@ -167,7 +167,7 @@
             guard_false(i16, descr=<Guard3>)
             p17 = getarrayitem_gc(p15, i12, descr=<GcPtrArrayDescr>)
             i19 = int_add(i12, 1)
-            setfield_gc(p4, i19, descr=<SignedFieldDescr .*W_AbstractSeqIterObject.inst_index .*>)
+            setfield_gc(p9, i19, descr=<SignedFieldDescr .*W_AbstractSeqIterObject.inst_index .*>)
             guard_nonnull_class(p17, 146982464, descr=<Guard4>)
             i21 = getfield_gc(p17, descr=<SignedFieldDescr .*W_ArrayTypei.inst_len .*>)
             i23 = int_lt(0, i21)
@@ -179,7 +179,7 @@
             i28 = int_add_ovf(i10, i25)
             guard_no_overflow(descr=<Guard7>)
             --TICK--
-            jump(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, i28, i25, i19, i13, p14, p15, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, p5, p6, i28, i25, p9, p10, p11, i19, i13, p14, p15, descr=<Loop0>)
         """)
 
 
diff --git a/pypy/module/signal/interp_signal.py b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -80,7 +80,7 @@
 
 pypysig_getaddr_occurred = external('pypysig_getaddr_occurred', [],
                                     lltype.Ptr(LONG_STRUCT), _nowrapper=True,
-                                    pure_function=True)
+                                    elidable_function=True)
 c_alarm = external('alarm', [rffi.INT], rffi.INT)
 c_pause = external('pause', [], rffi.INT)
 c_siginterrupt = external('siginterrupt', [rffi.INT, rffi.INT], rffi.INT)
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -57,7 +57,8 @@
         raise OperationError(space.w_ValueError,
                              space.wrap("recursion limit must be positive"))
     space.sys.recursionlimit = new_limit
-    _stack_set_length_fraction(new_limit * 0.001)
+    if space.config.translation.type_system == 'lltype':
+        _stack_set_length_fraction(new_limit * 0.001)
 
 def getrecursionlimit(space):
     """Return the last value set by setrecursionlimit().
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_commethods.py
@@ -0,0 +1,82 @@
+# unittest for SOME ctypes com function calls.
+# Can't resist from implementing some kind of mini-comtypes
+# theller ;-)
+
+import py
+import sys
+if sys.platform != "win32":
+    py.test.skip('windows only test')
+
+import ctypes, new, unittest
+from ctypes.wintypes import HRESULT
+from _ctypes import COMError
+
+oleaut32 = ctypes.OleDLL("oleaut32")
+
+class UnboundMethod(object):
+    def __init__(self, func, index, name):
+        self.func = func
+        self.index = index
+        self.name = name
+        self.__doc__ = func.__doc__
+
+    def __repr__(self):
+        return "<Unbound COM method index %d: %s at %x>" % (self.index, self.name, id(self))
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        return new.instancemethod(self.func, instance, owner)
+    
+def commethod(index, restype, *argtypes):
+    """A decorator that generates COM methods.  The decorated function
+    itself is not used except for it's name."""
+    def make_commethod(func):
+        comfunc = ctypes.WINFUNCTYPE(restype, *argtypes)(index, func.__name__)
+        comfunc.__name__ = func.__name__
+        comfunc.__doc__ = func.__doc__
+        return UnboundMethod(comfunc, index, func.__name__)
+    return make_commethod
+
+class ICreateTypeLib2(ctypes.c_void_p):
+
+    @commethod(1, ctypes.c_long)
+    def AddRef(self):
+        pass
+
+    @commethod(2, ctypes.c_long)
+    def Release(self):
+        pass
+
+    @commethod(4, HRESULT, ctypes.c_wchar_p)
+    def SetName(self):
+        """Set the name of the library."""
+
+    @commethod(12, HRESULT)
+    def SaveAllChanges(self):
+        pass
+
+
+CreateTypeLib2 = oleaut32.CreateTypeLib2
+CreateTypeLib2.argtypes = (ctypes.c_int, ctypes.c_wchar_p, ctypes.POINTER(ICreateTypeLib2))
+
+################################################################
+
+def test_basic_comtypes():
+    punk = ICreateTypeLib2()
+    hr = CreateTypeLib2(0, "foobar.tlb", punk)
+    assert hr == 0
+
+    assert 2 == punk.AddRef()
+    assert 3 == punk.AddRef()
+    assert 4 == punk.AddRef()
+
+    punk.SetName("TypeLib_ByPYPY")
+    py.test.raises(COMError, lambda: punk.SetName(None))
+
+    # This would save the typelib to disk.
+    ## punk.SaveAllChanges()
+
+    assert 3 == punk.Release()
+    assert 2 == punk.Release()
+    assert 1 == punk.Release()
diff --git a/pypy/module/test_lib_pypy/test_pwd.py b/pypy/module/test_lib_pypy/test_pwd.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/test_pwd.py
@@ -0,0 +1,12 @@
+from pypy.conftest import gettestobjspace
+
+class AppTestPwd:
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=('_ffi', '_rawffi'))
+        cls.space.appexec((), "(): import pwd")
+
+    def test_getpwuid(self):
+        import os, pwd
+        passwd_info = pwd.getpwuid(os.getuid())
+        assert type(passwd_info).__name__ == 'struct_passwd'
+        assert repr(passwd_info).startswith("pwd.struct_passwd(pw_name=")
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -416,7 +416,7 @@
             # obscure circumstances.
             return default_identity_hash(space, w_obj)
         if space.is_w(w_hash, space.w_None):
-            typename = space.type(w_obj).getname(space, '?')
+            typename = space.type(w_obj).getname(space)
             raise operationerrfmt(space.w_TypeError,
                                   "'%s' objects are unhashable", typename)
         w_result = space.get_and_call_function(w_hash, w_obj)
diff --git a/pypy/objspace/flow/flowcontext.py b/pypy/objspace/flow/flowcontext.py
--- a/pypy/objspace/flow/flowcontext.py
+++ b/pypy/objspace/flow/flowcontext.py
@@ -384,8 +384,9 @@
     # hack for unrolling iterables, don't use this
     def replace_in_stack(self, oldvalue, newvalue):
         w_new = Constant(newvalue)
-        stack_items_w = self.crnt_frame.valuestack_w
-        for i in range(self.crnt_frame.valuestackdepth-1, -1, -1):
+        f = self.crnt_frame
+        stack_items_w = f.locals_stack_w
+        for i in range(f.valuestackdepth-1, f.nlocals-1, -1):
             w_v = stack_items_w[i]
             if isinstance(w_v, Constant):
                 if w_v.value is oldvalue:
diff --git a/pypy/objspace/flow/framestate.py b/pypy/objspace/flow/framestate.py
--- a/pypy/objspace/flow/framestate.py
+++ b/pypy/objspace/flow/framestate.py
@@ -10,7 +10,7 @@
     def __init__(self, state):
         if isinstance(state, PyFrame):
             # getfastscope() can return real None, for undefined locals
-            data = state.getfastscope() + state.savevaluestack()
+            data = state.save_locals_stack()
             if state.last_exception is None:
                 data.append(Constant(None))
                 data.append(Constant(None))
@@ -36,11 +36,9 @@
 
     def restoreframe(self, frame):
         if isinstance(frame, PyFrame):
-            fastlocals = len(frame.fastlocals_w)
             data = self.mergeable[:]
             recursively_unflatten(frame.space, data)
-            frame.setfastscope(data[:fastlocals])  # Nones == undefined locals
-            frame.restorevaluestack(data[fastlocals:-2])
+            frame.restore_locals_stack(data[:-2])  # Nones == undefined locals
             if data[-2] == Constant(None):
                 assert data[-1] == Constant(None)
                 frame.last_exception = None
diff --git a/pypy/objspace/flow/operation.py b/pypy/objspace/flow/operation.py
--- a/pypy/objspace/flow/operation.py
+++ b/pypy/objspace/flow/operation.py
@@ -143,9 +143,6 @@
 def mod_ovf(x, y):
     return ovfcheck(x % y)
 
-##def pow_ovf(*two_or_three_args):
-##    return ovfcheck(pow(*two_or_three_args))
-
 def lshift_ovf(x, y):
     return ovfcheck_lshift(x, y)
 
diff --git a/pypy/objspace/flow/test/test_framestate.py b/pypy/objspace/flow/test/test_framestate.py
--- a/pypy/objspace/flow/test/test_framestate.py
+++ b/pypy/objspace/flow/test/test_framestate.py
@@ -25,7 +25,7 @@
         dummy = Constant(None)
         #dummy.dummy = True
         arg_list = ([Variable() for i in range(formalargcount)] +
-                    [dummy] * (len(frame.fastlocals_w) - formalargcount))
+                    [dummy] * (frame.nlocals - formalargcount))
         frame.setfastscope(arg_list)
         return frame
 
@@ -42,7 +42,7 @@
     def test_neq_hacked_framestate(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Variable()
+        frame.locals_stack_w[frame.nlocals-1] = Variable()
         fs2 = FrameState(frame)
         assert fs1 != fs2
 
@@ -55,7 +55,7 @@
     def test_union_on_hacked_framestates(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Variable()
+        frame.locals_stack_w[frame.nlocals-1] = Variable()
         fs2 = FrameState(frame)
         assert fs1.union(fs2) == fs2  # fs2 is more general
         assert fs2.union(fs1) == fs2  # fs2 is more general
@@ -63,7 +63,7 @@
     def test_restore_frame(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Variable()
+        frame.locals_stack_w[frame.nlocals-1] = Variable()
         fs1.restoreframe(frame)
         assert fs1 == FrameState(frame)
 
@@ -82,25 +82,26 @@
     def test_getoutputargs(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Variable()
+        frame.locals_stack_w[frame.nlocals-1] = Variable()
         fs2 = FrameState(frame)
         outputargs = fs1.getoutputargs(fs2)
         # 'x' -> 'x' is a Variable
-        # fastlocals_w[-1] -> fastlocals_w[-1] is Constant(None)
-        assert outputargs == [frame.fastlocals_w[0], Constant(None)]
+        # locals_w[n-1] -> locals_w[n-1] is Constant(None)
+        assert outputargs == [frame.locals_stack_w[0], Constant(None)]
 
     def test_union_different_constants(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Constant(42)
+        frame.locals_stack_w[frame.nlocals-1] = Constant(42)
         fs2 = FrameState(frame)
         fs3 = fs1.union(fs2)
         fs3.restoreframe(frame)
-        assert isinstance(frame.fastlocals_w[-1], Variable) # generalized
+        assert isinstance(frame.locals_stack_w[frame.nlocals-1], Variable)
+                                 # ^^^ generalized
 
     def test_union_spectag(self):
         frame = self.getframe(self.func_simple)
         fs1 = FrameState(frame)
-        frame.fastlocals_w[-1] = Constant(SpecTag())
+        frame.locals_stack_w[frame.nlocals-1] = Constant(SpecTag())
         fs2 = FrameState(frame)
         assert fs1.union(fs2) is None   # UnionError
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -374,7 +374,7 @@
             raise operationerrfmt(
                 space.w_TypeError,
                 "sequence item %d: expected string, %s "
-                "found", i, space.type(w_s).getname(space, '?'))
+                "found", i, space.type(w_s).getname(space))
 
         if data and i != 0:
             newdata.extend(data)
diff --git a/pypy/objspace/std/celldict.py b/pypy/objspace/std/celldict.py
--- a/pypy/objspace/std/celldict.py
+++ b/pypy/objspace/std/celldict.py
@@ -4,8 +4,9 @@
 speed up global lookups a lot."""
 
 from pypy.objspace.std.dictmultiobject import IteratorImplementation
-from pypy.objspace.std.dictmultiobject import W_DictMultiObject, _is_sane_hash
-from pypy.rlib import jit
+from pypy.objspace.std.dictmultiobject import DictStrategy, _never_equal_to_string
+from pypy.objspace.std.dictmultiobject import ObjectDictStrategy
+from pypy.rlib import jit, rerased
 
 class ModuleCell(object):
     def __init__(self, w_value=None):
@@ -19,49 +20,59 @@
     def __repr__(self):
         return "<ModuleCell: %s>" % (self.w_value, )
 
-class ModuleDictImplementation(W_DictMultiObject):
+class ModuleDictStrategy(DictStrategy):
+
+    erase, unerase = rerased.new_erasing_pair("modulecell")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
     def __init__(self, space):
         self.space = space
-        self.content = {}
 
-    def getcell(self, key, makenew):
+    def get_empty_storage(self):
+       return self.erase({})
+
+    def getcell(self, w_dict, key, makenew):
         if makenew or jit.we_are_jitted():
             # when we are jitting, we always go through the pure function
             # below, to ensure that we have no residual dict lookup
-            self = jit.hint(self, promote=True)
-            return self._getcell_makenew(key)
-        return self.content.get(key, None)
+            w_dict = jit.promote(w_dict)
+            self = jit.promote(self)
+            return self._getcell_makenew(w_dict, key)
+        return self.unerase(w_dict.dstorage).get(key, None)
 
-    @jit.purefunction
-    def _getcell_makenew(self, key):
-        return self.content.setdefault(key, ModuleCell())
+    @jit.elidable
+    def _getcell_makenew(self, w_dict, key):
+        return self.unerase(w_dict.dstorage).setdefault(key, ModuleCell())
 
-    def impl_setitem(self, w_key, w_value):
+    def setitem(self, w_dict, w_key, w_value):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
-            self.impl_setitem_str(self.space.str_w(w_key), w_value)
+            self.setitem_str(w_dict, self.space.str_w(w_key), w_value)
         else:
-            self._as_rdict().impl_fallback_setitem(w_key, w_value)
+            self.switch_to_object_strategy(w_dict)
+            w_dict.setitem(w_key, w_value)
 
-    def impl_setitem_str(self, name, w_value):
-        self.getcell(name, True).w_value = w_value
+    def setitem_str(self, w_dict, key, w_value):
+        self.getcell(w_dict, key, True).w_value = w_value
 
-    def impl_setdefault(self, w_key, w_default):
+    def setdefault(self, w_dict, w_key, w_default):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
-            cell = self.getcell(space.str_w(w_key), True)
+            cell = self.getcell(w_dict, space.str_w(w_key), True)
             if cell.w_value is None:
                 cell.w_value = w_default
             return cell.w_value
         else:
-            return self._as_rdict().impl_fallback_setdefault(w_key, w_default)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.setdefault(w_key, w_default)
 
-    def impl_delitem(self, w_key):
+    def delitem(self, w_dict, w_key):
         space = self.space
         w_key_type = space.type(w_key)
         if space.is_w(w_key_type, space.w_str):
             key = space.str_w(w_key)
-            cell = self.getcell(key, False)
+            cell = self.getcell(w_dict, key, False)
             if cell is None or cell.w_value is None:
                 raise KeyError
             # note that we don't remove the cell from self.content, to make
@@ -69,75 +80,91 @@
             # maps to the same cell later (even if this cell no longer
             # represents a key)
             cell.invalidate()
-        elif _is_sane_hash(space, w_key_type):
+        elif _never_equal_to_string(space, w_key_type):
             raise KeyError
         else:
-            self._as_rdict().impl_fallback_delitem(w_key)
-        
-    def impl_length(self):
+            self.switch_to_object_strategy(w_dict)
+            w_dict.delitem(w_key)
+
+    def length(self, w_dict):
         # inefficient, but do we care?
         res = 0
-        for cell in self.content.itervalues():
+        for cell in self.unerase(w_dict.dstorage).itervalues():
             if cell.w_value is not None:
                 res += 1
         return res
 
-    def impl_getitem(self, w_lookup):
+    def getitem(self, w_dict, w_key):
         space = self.space
-        w_lookup_type = space.type(w_lookup)
+        w_lookup_type = space.type(w_key)
         if space.is_w(w_lookup_type, space.w_str):
-            return self.impl_getitem_str(space.str_w(w_lookup))
+            return self.getitem_str(w_dict, space.str_w(w_key))
 
-        elif _is_sane_hash(space, w_lookup_type):
+        elif _never_equal_to_string(space, w_lookup_type):
             return None
         else:
-            return self._as_rdict().impl_fallback_getitem(w_lookup)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.getitem(w_key)
 
-    def impl_getitem_str(self, lookup):
-        res = self.getcell(lookup, False)
+    def getitem_str(self, w_dict, key):
+        res = self.getcell(w_dict, key, False)
         if res is None:
             return None
         # note that even if the res.w_value is None, the next line is fine
         return res.w_value
 
-    def impl_iter(self):
-        return ModuleDictIteratorImplementation(self.space, self)
+    def iter(self, w_dict):
+        return ModuleDictIteratorImplementation(self.space, self, w_dict)
 
-    def impl_keys(self):
+    def keys(self, w_dict):
         space = self.space
-        return [space.wrap(key) for key, cell in self.content.iteritems()
+        iterator = self.unerase(w_dict.dstorage).iteritems
+        return [space.wrap(key) for key, cell in iterator()
                     if cell.w_value is not None]
 
-    def impl_values(self):
-        return [cell.w_value for cell in self.content.itervalues()
+    def values(self, w_dict):
+        iterator = self.unerase(w_dict.dstorage).itervalues
+        return [cell.w_value for cell in iterator()
                     if cell.w_value is not None]
 
-    def impl_items(self):
+    def items(self, w_dict):
         space = self.space
+        iterator = self.unerase(w_dict.dstorage).iteritems
         return [space.newtuple([space.wrap(key), cell.w_value])
-                    for (key, cell) in self.content.iteritems()
+                    for (key, cell) in iterator()
                         if cell.w_value is not None]
 
-    def impl_clear(self):
-        for k, cell in self.content.iteritems():
+    def clear(self, w_dict):
+        iterator = self.unerase(w_dict.dstorage).iteritems
+        for k, cell in iterator():
             cell.invalidate()
 
-    def _as_rdict(self):
-        r_dict_content = self.initialize_as_rdict()
-        for k, cell in self.content.iteritems():
+    def popitem(self, w_dict):
+        # This is O(n) if called repeatadly, you probably shouldn't be on a
+        # Module's dict though
+        for k, cell in self.unerase(w_dict.dstorage).iteritems():
             if cell.w_value is not None:
-                r_dict_content[self.space.wrap(k)] = cell.w_value
-            cell.invalidate()
-        self._clear_fields()
-        return self
+                w_value = cell.w_value
+                cell.invalidate()
+                return self.space.wrap(k), w_value
+        else:
+            raise KeyError
 
-    def _clear_fields(self):
-        self.content = None
+    def switch_to_object_strategy(self, w_dict):
+        d = self.unerase(w_dict.dstorage)
+        strategy = self.space.fromcache(ObjectDictStrategy)
+        d_new = strategy.unerase(strategy.get_empty_storage())
+        for key, cell in d.iteritems():
+            if cell.w_value is not None:
+                d_new[self.space.wrap(key)] = cell.w_value
+        w_dict.strategy = strategy
+        w_dict.dstorage = strategy.erase(d_new)
 
 class ModuleDictIteratorImplementation(IteratorImplementation):
-    def __init__(self, space, dictimplementation):
+    def __init__(self, space, strategy, dictimplementation):
         IteratorImplementation.__init__(self, space, dictimplementation)
-        self.iterator = dictimplementation.content.iteritems()
+        dict_w = strategy.unerase(dictimplementation.dstorage)
+        self.iterator = dict_w.iteritems()
 
     def next_entry(self):
         for key, cell in self.iterator:
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -1,18 +1,20 @@
 import py, sys
 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.settype import set_typedef as settypedef
 from pypy.interpreter import gateway
 from pypy.interpreter.argument import Signature
 from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.module.__builtin__.__init__ import BUILTIN_TO_INDEX, OPTIMIZED_BUILTINS
 
-from pypy.rlib.objectmodel import r_dict, we_are_translated
-from pypy.objspace.std.settype import set_typedef as settypedef
+from pypy.rlib.objectmodel import r_dict, we_are_translated, specialize
+from pypy.rlib.debug import mark_dict_non_null
+
+from pypy.rlib import rerased
 
 def _is_str(space, w_key):
     return space.is_w(space.type(w_key), space.w_str)
 
-def _is_sane_hash(space, w_lookup_type):
+def _never_equal_to_string(space, w_lookup_type):
     """ Handles the case of a non string key lookup.
     Types that have a sane hash/eq function should allow us to return True
     directly to signal that the key is not in the dict in any case.
@@ -28,48 +30,38 @@
 class W_DictMultiObject(W_Object):
     from pypy.objspace.std.dicttype import dict_typedef as typedef
 
-    r_dict_content = None
-
     @staticmethod
     def allocate_and_init_instance(space, w_type=None, module=False,
                                    instance=False, classofinstance=None,
                                    strdict=False):
+
         if space.config.objspace.std.withcelldict and module:
-            from pypy.objspace.std.celldict import ModuleDictImplementation
+            from pypy.objspace.std.celldict import ModuleDictStrategy
             assert w_type is None
-            return ModuleDictImplementation(space)
-        elif space.config.objspace.opcodes.CALL_LIKELY_BUILTIN and module:
-            assert w_type is None
-            return WaryDictImplementation(space)
-        elif space.config.objspace.std.withdictmeasurement:
-            assert w_type is None
-            return MeasuringDictImplementation(space)
+            strategy = space.fromcache(ModuleDictStrategy)
+
         elif instance or strdict or module:
             assert w_type is None
-            return StrDictImplementation(space)
+            strategy = space.fromcache(StringDictStrategy)
+
         else:
-            if w_type is None:
-                w_type = space.w_dict
-            w_self = space.allocate_instance(W_DictMultiObject, w_type)
-            W_DictMultiObject.__init__(w_self, space)
-            return w_self
+            strategy = space.fromcache(EmptyDictStrategy)
 
-    def __init__(self, space):
+        if w_type is None:
+            w_type = space.w_dict
+        storage = strategy.get_empty_storage()
+        w_self = space.allocate_instance(W_DictMultiObject, w_type)
+        W_DictMultiObject.__init__(w_self, space, strategy, storage)
+        return w_self
+
+    def __init__(self, space, strategy, storage):
         self.space = space
-
-    def initialize_as_rdict(self):
-        assert self.r_dict_content is None
-        self.r_dict_content = r_dict(self.space.eq_w, self.space.hash_w)
-        return self.r_dict_content
-
-
-    def initialize_content(w_self, list_pairs_w):
-        for w_k, w_v in list_pairs_w:
-            w_self.setitem(w_k, w_v)
+        self.strategy = strategy
+        self.dstorage = storage
 
     def __repr__(w_self):
         """ representation for debugging purposes """
-        return "%s()" % (w_self.__class__.__name__, )
+        return "%s(%s)" % (w_self.__class__.__name__, w_self.strategy)
 
     def unwrap(w_dict, space):
         result = {}
@@ -88,51 +80,38 @@
         else:
             return None
 
-    # _________________________________________________________________
-    # implementation methods
-    def impl_getitem(self, w_key):
-        #return w_value or None
-        # in case the key is unhashable, try to hash it
-        self.space.hash(w_key)
-        # return None anyway
-        return None
+    def initialize_content(w_self, list_pairs_w):
+        for w_k, w_v in list_pairs_w:
+            w_self.setitem(w_k, w_v)
 
-    def impl_getitem_str(self, key):
-        #return w_value or None
-        return None
+def _add_indirections():
+    dict_methods = "setitem setitem_str getitem \
+                    getitem_str delitem length \
+                    clear keys values \
+                    items iter setdefault \
+                    popitem".split()
 
-    def impl_setdefault(self, w_key, w_default):
-        # here the dict is always empty
-        self._as_rdict().impl_fallback_setitem(w_key, w_default)
-        return w_default
+    def make_method(method):
+        def f(self, *args):
+            return getattr(self.strategy, method)(self, *args)
+        f.func_name = method
+        return f
 
-    def impl_setitem(self, w_key, w_value):
-        self._as_rdict().impl_fallback_setitem(w_key, w_value)
+    for method in dict_methods:
+        setattr(W_DictMultiObject, method, make_method(method))
 
-    def impl_setitem_str(self, key, w_value):
-        self._as_rdict().impl_fallback_setitem_str(key, w_value)
+_add_indirections()
 
-    def impl_delitem(self, w_key):
-        # in case the key is unhashable, try to hash it
-        self.space.hash(w_key)
-        raise KeyError
+class DictStrategy(object):
 
-    def impl_length(self):
-        return 0
+    def __init__(self, space):
+        self.space = space
 
-    def impl_iter(self):
-        # XXX I guess it's not important to be fast in this case?
-        return self._as_rdict().impl_fallback_iter()
+    def get_empty_storage(self):
+        raise NotImplementedError
 
-    def impl_clear(self):
-        self.r_dict_content = None
-
-    def _as_rdict(self):
-        r_dict_content = self.initialize_as_rdict()
-        return self
-
-    def impl_keys(self):
-        iterator = self.impl_iter()
+    def keys(self, w_dict):
+        iterator = self.iter(w_dict)
         result = []
         while 1:
             w_key, w_value = iterator.next()
@@ -140,8 +119,9 @@
                 result.append(w_key)
             else:
                 return result
-    def impl_values(self):
-        iterator = self.impl_iter()
+
+    def values(self, w_dict):
+        iterator = self.iter(w_dict)
         result = []
         while 1:
             w_key, w_value = iterator.next()
@@ -149,8 +129,9 @@
                 result.append(w_value)
             else:
                 return result
-    def impl_items(self):
-        iterator = self.impl_iter()
+
+    def items(self, w_dict):
+        iterator = self.iter(w_dict)
         result = []
         while 1:
             w_key, w_value = iterator.next()
@@ -159,106 +140,90 @@
             else:
                 return result
 
-    # the following method only makes sense when the option to use the
-    # CALL_LIKELY_BUILTIN opcode is set. Otherwise it won't even be seen
-    # by the annotator
-    def impl_get_builtin_indexed(self, i):
-        key = OPTIMIZED_BUILTINS[i]
-        return self.impl_getitem_str(key)
+    def clear(self, w_dict):
+        strategy = self.space.fromcache(EmptyDictStrategy)
+        storage = strategy.get_empty_storage()
+        w_dict.strategy = strategy
+        w_dict.dstorage = storage
 
-    def impl_popitem(self):
-        # default implementation
-        space = self.space
-        iterator = self.impl_iter()
-        w_key, w_value = iterator.next()
-        if w_key is None:
-            raise KeyError
-        self.impl_delitem(w_key)
-        return w_key, w_value
 
-    # _________________________________________________________________
-    # fallback implementation methods
+class EmptyDictStrategy(DictStrategy):
 
-    def impl_fallback_setdefault(self, w_key, w_default):
-        return self.r_dict_content.setdefault(w_key, w_default)
+    erase, unerase = rerased.new_erasing_pair("empty")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
 
-    def impl_fallback_setitem(self, w_key, w_value):
-        self.r_dict_content[w_key] = w_value
+    def get_empty_storage(self):
+       return self.erase(None)
 
-    def impl_fallback_setitem_str(self, key, w_value):
-        return self.impl_fallback_setitem(self.space.wrap(key), w_value)
+    def switch_to_correct_strategy(self, w_dict, w_key):
+        #XXX implement other strategies later
+        if type(w_key) is self.space.StringObjectCls:
+            self.switch_to_string_strategy(w_dict)
+        elif self.space.is_w(self.space.type(w_key), self.space.w_int):
+            self.switch_to_int_strategy(w_dict)
+        else:
+            self.switch_to_object_strategy(w_dict)
 
-    def impl_fallback_delitem(self, w_key):
-        del self.r_dict_content[w_key]
+    def switch_to_string_strategy(self, w_dict):
+        strategy = self.space.fromcache(StringDictStrategy)
+        storage = strategy.get_empty_storage()
+        w_dict.strategy = strategy
+        w_dict.dstorage = storage
 
-    def impl_fallback_length(self):
-        return len(self.r_dict_content)
+    def switch_to_int_strategy(self, w_dict):
+        strategy = self.space.fromcache(IntDictStrategy)
+        storage = strategy.get_empty_storage()
+        w_dict.strategy = strategy
+        w_dict.dstorage = storage
 
-    def impl_fallback_getitem(self, w_key):
-        return self.r_dict_content.get(w_key, None)
+    def switch_to_object_strategy(self, w_dict):
+        strategy = self.space.fromcache(ObjectDictStrategy)
+        storage = strategy.get_empty_storage()
+        w_dict.strategy = strategy
+        w_dict.dstorage = storage
 
-    def impl_fallback_getitem_str(self, key):
-        return self.r_dict_content.get(self.space.wrap(key), None)
+    def getitem(self, w_dict, w_key):
+        #return w_value or None
+        # in case the key is unhashable, try to hash it
+        self.space.hash(w_key)
+        # return None anyway
+        return None
 
-    def impl_fallback_iter(self):
-        return RDictIteratorImplementation(self.space, self)
+    def getitem_str(self, w_dict, key):
+        #return w_value or None
+        return None
 
-    def impl_fallback_keys(self):
-        return self.r_dict_content.keys()
-    def impl_fallback_values(self):
-        return self.r_dict_content.values()
-    def impl_fallback_items(self):
-        return [self.space.newtuple([w_key, w_val])
-                    for w_key, w_val in self.r_dict_content.iteritems()]
+    def setdefault(self, w_dict, w_key, w_default):
+        # here the dict is always empty
+        self.switch_to_correct_strategy(w_dict, w_key)
+        w_dict.setitem(w_key, w_default)
+        return w_default
 
-    def impl_fallback_clear(self):
-        self.r_dict_content.clear()
+    def setitem(self, w_dict, w_key, w_value):
+        self.switch_to_correct_strategy(w_dict, w_key)
+        w_dict.setitem(w_key, w_value)
 
-    def impl_fallback_get_builtin_indexed(self, i):
-        key = OPTIMIZED_BUILTINS[i]
-        return self.impl_fallback_getitem_str(key)
+    def setitem_str(self, w_dict, key, w_value):
+        self.switch_to_string_strategy(w_dict)
+        w_dict.setitem_str(key, w_value)
 
-    def impl_fallback_popitem(self):
-        return self.r_dict_content.popitem()
+    def delitem(self, w_dict, w_key):
+        # in case the key is unhashable, try to hash it
+        self.space.hash(w_key)
+        raise KeyError
 
+    def length(self, w_dict):
+        return 0
 
-implementation_methods = [
-    ("getitem", 1),
-    ("getitem_str", 1),
-    ("length", 0),
-    ("setitem_str", 2),
-    ("setitem", 2),
-    ("setdefault", 2),
-    ("delitem", 1),
-    ("iter", 0),
-    ("items", 0),
-    ("values", 0),
-    ("keys", 0),
-    ("clear", 0),
-    ("get_builtin_indexed", 1),
-    ("popitem", 0),
-]
+    def iter(self, w_dict):
+        return EmptyIteratorImplementation(self.space, w_dict)
 
+    def clear(self, w_dict):
+        return
 
-def _make_method(name, implname, fallback, numargs):
-    args = ", ".join(["a" + str(i) for i in range(numargs)])
-    code = """def %s(self, %s):
-        if self.r_dict_content is not None:
-            return self.%s(%s)
-        return self.%s(%s)""" % (name, args, fallback, args, implname, args)
-    d = {}
-    exec py.code.Source(code).compile() in d
-    implementation_method = d[name]
-    implementation_method.func_defaults = getattr(W_DictMultiObject, implname).func_defaults
-    return implementation_method
-
-def _install_methods():
-    for name, numargs in implementation_methods:
-        implname = "impl_" + name
-        fallbackname = "impl_fallback_" + name
-        func = _make_method(name, implname, fallbackname, numargs)
-        setattr(W_DictMultiObject, name, func)
-_install_methods()
+    def popitem(self, w_dict):
+        raise KeyError
 
 registerimplementation(W_DictMultiObject)
 
@@ -300,319 +265,255 @@
             return self.len - self.pos
         return 0
 
+class EmptyIteratorImplementation(IteratorImplementation):
+    def next(self):
+        return (None, None)
+
 
 
 # concrete subclasses of the above
 
-class StrDictImplementation(W_DictMultiObject):
-    def __init__(self, space):
-        self.space = space
-        self.content = {}
+class AbstractTypedStrategy(object):
+    _mixin_ = True
 
-    def impl_setitem(self, w_key, w_value):
+    @staticmethod
+    def erase(storage):
+        raise NotImplementedError("abstract base class")
+
+    @staticmethod
+    def unerase(obj):
+        raise NotImplementedError("abstract base class")
+
+    def wrap(self, unwrapped):
+        raise NotImplementedError
+
+    def unwrap(self, wrapped):
+        raise NotImplementedError
+
+    def is_correct_type(self, w_obj):
+        raise NotImplementedError("abstract base class")
+
+    def get_empty_storage(self):
+        raise NotImplementedError("abstract base class")
+
+    def _never_equal_to(self, w_lookup_type):
+        raise NotImplementedError("abstract base class")
+
+    def setitem(self, w_dict, w_key, w_value):
         space = self.space
-        if space.is_w(space.type(w_key), space.w_str):
-            self.impl_setitem_str(self.space.str_w(w_key), w_value)
+        if self.is_correct_type(w_key):
+            self.unerase(w_dict.dstorage)[self.unwrap(w_key)] = w_value
+            return
         else:
-            self._as_rdict().impl_fallback_setitem(w_key, w_value)
+            self.switch_to_object_strategy(w_dict)
+            w_dict.setitem(w_key, w_value)
 
-    def impl_setitem_str(self, key, w_value):
-        self.content[key] = w_value
+    def setitem_str(self, w_dict, key, w_value):
+        self.switch_to_object_strategy(w_dict)
+        w_dict.setitem(self.space.wrap(key), w_value)
 
-    def impl_setdefault(self, w_key, w_default):
+    def setdefault(self, w_dict, w_key, w_default):
         space = self.space
-        if space.is_w(space.type(w_key), space.w_str):
-            return self.content.setdefault(space.str_w(w_key), w_default)
+        if self.is_correct_type(w_key):
+            return self.unerase(w_dict.dstorage).setdefault(self.unwrap(w_key), w_default)
         else:
-            return self._as_rdict().impl_fallback_setdefault(w_key, w_default)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.setdefault(w_key, w_default)
 
-
-    def impl_delitem(self, w_key):
+    def delitem(self, w_dict, w_key):
         space = self.space
         w_key_type = space.type(w_key)
-        if space.is_w(w_key_type, space.w_str):
-            del self.content[space.str_w(w_key)]
+        if self.is_correct_type(w_key):
+            del self.unerase(w_dict.dstorage)[self.unwrap(w_key)]
             return
-        elif _is_sane_hash(space, w_key_type):
-            raise KeyError
         else:
-            self._as_rdict().impl_fallback_delitem(w_key)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.delitem(w_key)
 
-    def impl_length(self):
-        return len(self.content)
+    def length(self, w_dict):
+        return len(self.unerase(w_dict.dstorage))
 
-    def impl_getitem_str(self, key):
-        return self.content.get(key, None)
+    def getitem_str(self, w_dict, key):
+        return self.getitem(w_dict, self.space.wrap(key))
 
-    def impl_getitem(self, w_key):
+    def getitem(self, w_dict, w_key):
+        space = self.space
+
+        if self.is_correct_type(w_key):
+            return self.unerase(w_dict.dstorage).get(self.unwrap(w_key), None)
+        elif self._never_equal_to(space.type(w_key)):
+            return None
+        else:
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.getitem(w_key)
+
+    def keys(self, w_dict):
+        return [self.wrap(key) for key in self.unerase(w_dict.dstorage).iterkeys()]
+
+    def values(self, w_dict):
+        return self.unerase(w_dict.dstorage).values()
+
+    def items(self, w_dict):
+        space = self.space
+        dict_w = self.unerase(w_dict.dstorage)
+        return [space.newtuple([self.wrap(key), w_value])
+                    for (key, w_value) in dict_w.iteritems()]
+
+    def popitem(self, w_dict):
+        key, value = self.unerase(w_dict.dstorage).popitem()
+        return (self.wrap(key), value)
+
+    def clear(self, w_dict):
+        self.unerase(w_dict.dstorage).clear()
+
+    def switch_to_object_strategy(self, w_dict):
+        d = self.unerase(w_dict.dstorage)
+        strategy = self.space.fromcache(ObjectDictStrategy)
+        d_new = strategy.unerase(strategy.get_empty_storage())
+        for key, value in d.iteritems():
+            d_new[self.wrap(key)] = value
+        w_dict.strategy = strategy
+        w_dict.dstorage = strategy.erase(d_new)
+
+class ObjectDictStrategy(AbstractTypedStrategy, DictStrategy):
+
+    erase, unerase = rerased.new_erasing_pair("object")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def wrap(self, unwrapped):
+        return unwrapped
+
+    def unwrap(self, wrapped):
+        return wrapped
+
+    def is_correct_type(self, w_obj):
+        return True
+
+    def get_empty_storage(self):
+       new_dict = r_dict(self.space.eq_w, self.space.hash_w,
+                         force_non_null=True)
+       return self.erase(new_dict)
+
+    def _never_equal_to(self, w_lookup_type):
+        return False
+
+    def iter(self, w_dict):
+        return ObjectIteratorImplementation(self.space, self, w_dict)
+
+    def keys(self, w_dict):
+        return self.unerase(w_dict.dstorage).keys()
+
+class StringDictStrategy(AbstractTypedStrategy, DictStrategy):
+
+    erase, unerase = rerased.new_erasing_pair("string")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def wrap(self, unwrapped):
+        return self.space.wrap(unwrapped)
+
+    def unwrap(self, wrapped):
+        return self.space.str_w(wrapped)
+
+    def is_correct_type(self, w_obj):
+        space = self.space
+        return space.is_w(space.type(w_obj), space.w_str)
+
+    def get_empty_storage(self):
+        res = {}
+        mark_dict_non_null(res)
+        return self.erase(res)
+
+    def _never_equal_to(self, w_lookup_type):
+        return _never_equal_to_string(self.space, w_lookup_type)
+
+    def setitem_str(self, w_dict, key, w_value):
+        assert key is not None
+        self.unerase(w_dict.dstorage)[key] = w_value
+
+    def getitem(self, w_dict, w_key):
         space = self.space
         # -- This is called extremely often.  Hack for performance --
         if type(w_key) is space.StringObjectCls:
-            return self.impl_getitem_str(w_key.unwrap(space))
+            return self.getitem_str(w_dict, w_key.unwrap(space))
         # -- End of performance hack --
-        w_lookup_type = space.type(w_key)
-        if space.is_w(w_lookup_type, space.w_str):
-            return self.impl_getitem_str(space.str_w(w_key))
-        elif _is_sane_hash(space, w_lookup_type):
-            return None
-        else:
-            return self._as_rdict().impl_fallback_getitem(w_key)
+        return AbstractTypedStrategy.getitem(self, w_dict, w_key)
 
-    def impl_iter(self):
-        return StrIteratorImplementation(self.space, self)
+    def getitem_str(self, w_dict, key):
+        assert key is not None
+        return self.unerase(w_dict.dstorage).get(key, None)
 
-    def impl_keys(self):
-        space = self.space
-        return [space.wrap(key) for key in self.content.iterkeys()]
+    def iter(self, w_dict):
+        return StrIteratorImplementation(self.space, self, w_dict)
 
-    def impl_values(self):
-        return self.content.values()
-
-    def impl_items(self):
-        space = self.space
-        return [space.newtuple([space.wrap(key), w_value])
-                    for (key, w_value) in self.content.iteritems()]
-
-    def impl_clear(self):
-        self.content.clear()
-
-
-    def _as_rdict(self):
-        r_dict_content = self.initialize_as_rdict()
-        for k, w_v in self.content.items():
-            r_dict_content[self.space.wrap(k)] = w_v
-        self._clear_fields()
-        return self
-
-    def _clear_fields(self):
-        self.content = None
 
 class StrIteratorImplementation(IteratorImplementation):
-    def __init__(self, space, dictimplementation):
+    def __init__(self, space, strategy, dictimplementation):
         IteratorImplementation.__init__(self, space, dictimplementation)
-        self.iterator = dictimplementation.content.iteritems()
+        self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
 
     def next_entry(self):
         # note that this 'for' loop only runs once, at most
-        for str, w_value in self.iterator:
-            return self.space.wrap(str), w_value
+        for key, w_value in self.iterator:
+            return self.space.wrap(key), w_value
         else:
             return None, None
 
 
-class WaryDictImplementation(StrDictImplementation):
-    def __init__(self, space):
-        StrDictImplementation.__init__(self, space)
-        self.shadowed = [None] * len(BUILTIN_TO_INDEX)
+class IntDictStrategy(AbstractTypedStrategy, DictStrategy):
+    erase, unerase = rerased.new_erasing_pair("int")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
 
-    def impl_setitem_str(self, key, w_value):
-        i = BUILTIN_TO_INDEX.get(key, -1)
-        if i != -1:
-            self.shadowed[i] = w_value
-        self.content[key] = w_value
+    def wrap(self, unwrapped):
+        return self.space.wrap(unwrapped)
 
-    def impl_delitem(self, w_key):
+    def unwrap(self, wrapped):
+        return self.space.int_w(wrapped)
+
+    def get_empty_storage(self):
+        return self.erase({})
+
+    def is_correct_type(self, w_obj):
         space = self.space
-        w_key_type = space.type(w_key)
-        if space.is_w(w_key_type, space.w_str):
-            key = space.str_w(w_key)
-            del self.content[key]
-            i = BUILTIN_TO_INDEX.get(key, -1)
-            if i != -1:
-                self.shadowed[i] = None
-        elif _is_sane_hash(space, w_key_type):
-            raise KeyError
-        else:
-            self._as_rdict().impl_fallback_delitem(w_key)
+        return space.is_w(space.type(w_obj), space.w_int)
 
-    def impl_get_builtin_indexed(self, i):
-        return self.shadowed[i]
+    def _never_equal_to(self, w_lookup_type):
+        space = self.space
+        # XXX there are many more types
+        return (space.is_w(w_lookup_type, space.w_NoneType) or
+                space.is_w(w_lookup_type, space.w_str) or
+                space.is_w(w_lookup_type, space.w_unicode)
+                )
 
+    def iter(self, w_dict):
+        return IntIteratorImplementation(self.space, self, w_dict)
 
-class RDictIteratorImplementation(IteratorImplementation):
-    def __init__(self, space, dictimplementation):
+class IntIteratorImplementation(IteratorImplementation):
+    def __init__(self, space, strategy, dictimplementation):
         IteratorImplementation.__init__(self, space, dictimplementation)
-        self.iterator = dictimplementation.r_dict_content.iteritems()
+        self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
 
     def next_entry(self):
         # note that this 'for' loop only runs once, at most
-        for item in self.iterator:
-            return item
+        for key, w_value in self.iterator:
+            return self.space.wrap(key), w_value
         else:
             return None, None
 
 
+class ObjectIteratorImplementation(IteratorImplementation):
+    def __init__(self, space, strategy, dictimplementation):
+        IteratorImplementation.__init__(self, space, dictimplementation)
+        self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
 
-# XXX fix this thing
-import time
-
-class DictInfo(object):
-    _dict_infos = []
-    def __init__(self):
-        self.id = len(self._dict_infos)
-
-        self.setitem_strs = 0; self.setitems = 0;  self.delitems = 0
-        self.lengths = 0;   self.gets = 0
-        self.iteritems = 0; self.iterkeys = 0; self.itervalues = 0
-        self.keys = 0;      self.values = 0;   self.items = 0
-
-        self.maxcontents = 0
-
-        self.reads = 0
-        self.hits = self.misses = 0
-        self.writes = 0
-        self.iterations = 0
-        self.listings = 0
-
-        self.seen_non_string_in_write = 0
-        self.seen_non_string_in_read_first = 0
-        self.size_on_non_string_seen_in_read = -1
-        self.size_on_non_string_seen_in_write = -1
-
-        self.createtime = time.time()
-        self.lifetime = -1.0
-
-        if not we_are_translated():
-            # very probable stack from here:
-            # 0 - us
-            # 1 - MeasuringDictImplementation.__init__
-            # 2 - W_DictMultiObject.__init__
-            # 3 - space.newdict
-            # 4 - newdict's caller.  let's look at that
-            try:
-                frame = sys._getframe(4)
-            except ValueError:
-                pass # might be at import time
-            else:
-                self.sig = '(%s:%s)%s'%(frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name)
-
-        self._dict_infos.append(self)
-    def __repr__(self):
-        args = []
-        for k in sorted(self.__dict__):
-            v = self.__dict__[k]
-            if v != 0:
-                args.append('%s=%r'%(k, v))
-        return '<DictInfo %s>'%(', '.join(args),)
-
-class OnTheWayOut:
-    def __init__(self, info):
-        self.info = info
-    def __del__(self):
-        self.info.lifetime = time.time() - self.info.createtime
-
-class MeasuringDictImplementation(W_DictMultiObject):
-    def __init__(self, space):
-        self.space = space
-        self.content = r_dict(space.eq_w, space.hash_w)
-        self.info = DictInfo()
-        self.thing_with_del = OnTheWayOut(self.info)
-
-    def __repr__(self):
-        return "%s<%s>" % (self.__class__.__name__, self.content)
-
-    def _is_str(self, w_key):
-        space = self.space
-        return space.is_true(space.isinstance(w_key, space.w_str))
-    def _read(self, w_key):
-        self.info.reads += 1
-        if not self.info.seen_non_string_in_write \
-               and not self.info.seen_non_string_in_read_first \
-               and not self._is_str(w_key):
-            self.info.seen_non_string_in_read_first = True
-            self.info.size_on_non_string_seen_in_read = len(self.content)
-        hit = w_key in self.content
-        if hit:
-            self.info.hits += 1
+    def next_entry(self):
+        # note that this 'for' loop only runs once, at most
+        for w_key, w_value in self.iterator:
+            return w_key, w_value
         else:
-            self.info.misses += 1
-
-    def impl_setitem(self, w_key, w_value):
-        if not self.info.seen_non_string_in_write and not self._is_str(w_key):
-            self.info.seen_non_string_in_write = True
-            self.info.size_on_non_string_seen_in_write = len(self.content)
-        self.info.setitems += 1
-        self.info.writes += 1
-        self.content[w_key] = w_value
-        self.info.maxcontents = max(self.info.maxcontents, len(self.content))
-    def impl_setitem_str(self, key, w_value):
-        self.info.setitem_strs += 1
-        self.impl_setitem(self.space.wrap(key), w_value)
-    def impl_delitem(self, w_key):
-        if not self.info.seen_non_string_in_write \
-               and not self.info.seen_non_string_in_read_first \
-               and not self._is_str(w_key):
-            self.info.seen_non_string_in_read_first = True
-            self.info.size_on_non_string_seen_in_read = len(self.content)
-        self.info.delitems += 1
-        self.info.writes += 1
-        del self.content[w_key]
-
-    def impl_length(self):
-        self.info.lengths += 1
-        return len(self.content)
-    def impl_getitem_str(self, key):
-        return self.impl_getitem(self.space.wrap(key))
-    def impl_getitem(self, w_key):
-        self.info.gets += 1
-        self._read(w_key)
-        return self.content.get(w_key, None)
-
-    def impl_iteritems(self):
-        self.info.iteritems += 1
-        self.info.iterations += 1
-        return RDictItemIteratorImplementation(self.space, self)
-    def impl_iterkeys(self):
-        self.info.iterkeys += 1
-        self.info.iterations += 1
-        return RDictKeyIteratorImplementation(self.space, self)
-    def impl_itervalues(self):
-        self.info.itervalues += 1
-        self.info.iterations += 1
-        return RDictValueIteratorImplementation(self.space, self)
-
-    def impl_keys(self):
-        self.info.keys += 1
-        self.info.listings += 1
-        return self.content.keys()
-    def impl_values(self):
-        self.info.values += 1
-        self.info.listings += 1
-        return self.content.values()
-    def impl_items(self):
-        self.info.items += 1
-        self.info.listings += 1
-        return [self.space.newtuple([w_key, w_val])
-                    for w_key, w_val in self.content.iteritems()]
-
-
-_example = DictInfo()
-del DictInfo._dict_infos[-1]
-tmpl = 'os.write(fd, "%(attr)s" + ": " + str(info.%(attr)s) + "\\n")'
-bodySrc = []
-for attr in sorted(_example.__dict__):
-    if attr == 'sig':
-        continue
-    bodySrc.append(tmpl%locals())
-exec py.code.Source('''
-from pypy.rlib.objectmodel import current_object_addr_as_int
-def _report_one(fd, info):
-    os.write(fd, "_address" + ": " + str(current_object_addr_as_int(info))
-                 + "\\n")
-    %s
-'''%'\n    '.join(bodySrc)).compile()
-
-def report():
-    if not DictInfo._dict_infos:
-        return
-    os.write(2, "Starting multidict report.\n")
-    fd = os.open('dictinfo.txt', os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0644)
-    for info in DictInfo._dict_infos:
-        os.write(fd, '------------------\n')
-        _report_one(fd, info)
-    os.close(fd)
-    os.write(2, "Reporting done.\n")
-
+            return None, None
 
 
 init_signature = Signature(['seq_or_map'], None, 'kwargs')
@@ -919,7 +820,7 @@
 def repr__DictViewKeys(space, w_dictview):
     w_seq = space.call_function(space.w_list, w_dictview)
     w_repr = space.repr(w_seq)
-    return space.wrap("%s(%s)" % (space.type(w_dictview).getname(space, "?"),
+    return space.wrap("%s(%s)" % (space.type(w_dictview).getname(space),
                                   space.str_w(w_repr)))
 repr__DictViewItems  = repr__DictViewKeys
 repr__DictViewValues = repr__DictViewKeys
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -1,96 +1,98 @@
 from pypy.objspace.std.model import registerimplementation, W_Object
 from pypy.objspace.std.register_all import register_all
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject, IteratorImplementation
+from pypy.objspace.std.dictmultiobject import DictStrategy
 from pypy.objspace.std.typeobject import unwrap_cell
 from pypy.interpreter.error import OperationError
 
+from pypy.rlib import rerased
 
-class W_DictProxyObject(W_DictMultiObject):
-    def __init__(w_self, space, w_type):
-        W_DictMultiObject.__init__(w_self, space)
-        w_self.w_type = w_type
 
-    def impl_getitem(self, w_lookup):
+class DictProxyStrategy(DictStrategy):
+
+    erase, unerase = rerased.new_erasing_pair("dictproxy")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def __init__(w_self, space):
+        DictStrategy.__init__(w_self, space)
+
+    def getitem(self, w_dict, w_key):
         space = self.space
-        w_lookup_type = space.type(w_lookup)
+        w_lookup_type = space.type(w_key)
         if space.is_w(w_lookup_type, space.w_str):
-            return self.impl_getitem_str(space.str_w(w_lookup))
+            return self.getitem_str(w_dict, space.str_w(w_key))
         else:
             return None
 
-    def impl_getitem_str(self, lookup):
-        return self.w_type.getdictvalue(self.space, lookup)
+    def getitem_str(self, w_dict, key):
+        return self.unerase(w_dict.dstorage).getdictvalue(self.space, key)
 
-    def impl_setitem(self, w_key, w_value):
+    def setitem(self, w_dict, w_key, w_value):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
-            self.impl_setitem_str(self.space.str_w(w_key), w_value)
+            self.setitem_str(w_dict, self.space.str_w(w_key), w_value)
         else:
             raise OperationError(space.w_TypeError, space.wrap("cannot add non-string keys to dict of a type"))
 
-    def impl_setitem_str(self, name, w_value):
+    def setitem_str(self, w_dict, key, w_value):
+        w_type = self.unerase(w_dict.dstorage)
         try:
-            self.w_type.setdictvalue(self.space, name, w_value)
+            w_type.setdictvalue(self.space, key, w_value)
         except OperationError, e:
             if not e.match(self.space, self.space.w_TypeError):
                 raise
-            w_type = self.w_type
             if not w_type.is_cpytype():
                 raise
             # xxx obscure workaround: allow cpyext to write to type->tp_dict.
             # xxx like CPython, we assume that this is only done early after
             # xxx the type is created, and we don't invalidate any cache.
-            w_type.dict_w[name] = w_value
+            w_type.dict_w[key] = w_value
 
-    def impl_setdefault(self, w_key, w_default):
+    def setdefault(self, w_dict, w_key, w_default):
         space = self.space
-        w_result = self.impl_getitem(w_key)
+        w_result = self.getitem(w_dict, w_key)
         if w_result is not None:
             return w_result
-        self.impl_setitem(w_key, w_default)
+        self.setitem(w_dict, w_key, w_default)
         return w_default
 
-    def impl_delitem(self, w_key):
+    def delitem(self, w_dict, w_key):
         space = self.space
         w_key_type = space.type(w_key)
         if space.is_w(w_key_type, space.w_str):
-            if not self.w_type.deldictvalue(space, w_key):
+            if not self.unerase(w_dict.dstorage).deldictvalue(space, w_key):
                 raise KeyError
         else:
             raise KeyError
 
-    def impl_length(self):
-        return len(self.w_type.dict_w)
+    def length(self, w_dict):
+        return len(self.unerase(w_dict.dstorage).dict_w)
 
-    def impl_iter(self):
-        return DictProxyIteratorImplementation(self.space, self)
+    def iter(self, w_dict):
+        return DictProxyIteratorImplementation(self.space, self, w_dict)
 
-    def impl_keys(self):
+    def keys(self, w_dict):
         space = self.space
-        return [space.wrap(key) for key in self.w_type.dict_w.iterkeys()]
+        return [space.wrap(key) for key in self.unerase(w_dict.dstorage).dict_w.iterkeys()]
 
-    def impl_values(self):
-        return [unwrap_cell(self.space, w_value) for w_value in self.w_type.dict_w.itervalues()]
+    def values(self, w_dict):
+        return [unwrap_cell(self.space, w_value) for w_value in self.unerase(w_dict.dstorage).dict_w.itervalues()]
 
-    def impl_items(self):
+    def items(self, w_dict):
         space = self.space
         return [space.newtuple([space.wrap(key), unwrap_cell(self.space, w_value)])
-                    for (key, w_value) in self.w_type.dict_w.iteritems()]
+                    for (key, w_value) in self.unerase(w_dict.dstorage).dict_w.iteritems()]
 
-    def impl_clear(self):
-        self.w_type.dict_w.clear()
-        self.w_type.mutated()
-
-    def _as_rdict(self):
-        assert 0, "should be unreachable"
-
-    def _clear_fields(self):
-        assert 0, "should be unreachable"
+    def clear(self, w_dict):
+        self.unerase(w_dict.dstorage).dict_w.clear()
+        self.unerase(w_dict.dstorage).mutated()
 
 class DictProxyIteratorImplementation(IteratorImplementation):
-    def __init__(self, space, dictimplementation):
+    def __init__(self, space, strategy, dictimplementation):
         IteratorImplementation.__init__(self, space, dictimplementation)
-        self.iterator = dictimplementation.w_type.dict_w.iteritems()
+        w_type = strategy.unerase(dictimplementation.dstorage)
+        self.iterator = w_type.dict_w.iteritems()
 
     def next_entry(self):
         for key, w_value in self.iterator:
diff --git a/pypy/objspace/std/frame.py b/pypy/objspace/std/frame.py
--- a/pypy/objspace/std/frame.py
+++ b/pypy/objspace/std/frame.py
@@ -6,7 +6,7 @@
 from pypy.interpreter import pyopcode, function
 from pypy.interpreter.pyframe import PyFrame
 from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.module.__builtin__ import OPTIMIZED_BUILTINS, Module
+from pypy.module.__builtin__ import Module
 from pypy.objspace.std import intobject, smallintobject
 from pypy.objspace.std.multimethod import FailedToImplement
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject
@@ -66,41 +66,6 @@
         w_result = f.space.getitem(w_1, w_2)
     f.pushvalue(w_result)
 
-def CALL_LIKELY_BUILTIN(f, oparg, next_instr):
-    w_globals = f.w_globals
-    num = oparg >> 8
-    assert isinstance(w_globals, W_DictMultiObject)
-    w_value = w_globals.get_builtin_indexed(num)
-    if w_value is None:
-        builtins = f.get_builtin()
-        assert isinstance(builtins, Module)
-        w_builtin_dict = builtins.getdict(f.space)
-        assert isinstance(w_builtin_dict, W_DictMultiObject)
-        w_value = w_builtin_dict.get_builtin_indexed(num)
-    if w_value is None:
-        varname = OPTIMIZED_BUILTINS[num]
-        message = "global name '%s' is not defined"
-        raise operationerrfmt(f.space.w_NameError,
-                              message, varname)
-    nargs = oparg & 0xff
-    w_function = w_value
-    try:
-        w_result = call_likely_builtin(f, w_function, nargs)
-    finally:
-        f.dropvalues(nargs)
-    f.pushvalue(w_result)
-
-def call_likely_builtin(f, w_function, nargs):
-    if isinstance(w_function, function.Function):
-        executioncontext = f.space.getexecutioncontext()
-        executioncontext.c_call_trace(f, w_function)
-        res = w_function.funccall_valuestack(nargs, f)
-        executioncontext.c_return_trace(f, w_function)
-        return res
-    args = f.make_arguments(nargs)
-    return f.space.call_args(w_function, args)
-
-
 compare_table = [
     "lt",   # "<"
     "le",   # "<="
@@ -145,8 +110,6 @@
             StdObjSpaceFrame.BINARY_ADD = int_BINARY_ADD
     if space.config.objspace.std.optimized_list_getitem:
         StdObjSpaceFrame.BINARY_SUBSCR = list_BINARY_SUBSCR
-    if space.config.objspace.opcodes.CALL_LIKELY_BUILTIN:
-        StdObjSpaceFrame.CALL_LIKELY_BUILTIN = CALL_LIKELY_BUILTIN
     if space.config.objspace.opcodes.CALL_METHOD:
         from pypy.objspace.std.callmethod import LOOKUP_METHOD, CALL_METHOD
         StdObjSpaceFrame.LOOKUP_METHOD = LOOKUP_METHOD
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -4,9 +4,9 @@
 from pypy.rlib import rerased
 
 from pypy.interpreter.baseobjspace import W_Root
-from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+from pypy.objspace.std.dictmultiobject import W_DictMultiObject, DictStrategy, ObjectDictStrategy
 from pypy.objspace.std.dictmultiobject import IteratorImplementation
-from pypy.objspace.std.dictmultiobject import _is_sane_hash
+from pypy.objspace.std.dictmultiobject import _never_equal_to_string
 from pypy.objspace.std.objectobject import W_ObjectObject
 from pypy.objspace.std.typeobject import TypeCell
 
@@ -53,7 +53,7 @@
         else:
             return self._index_indirection(selector)
 
-    @jit.purefunction
+    @jit.elidable
     def _index_jit_pure(self, name, index):
         return self._index_indirection((name, index))
 
@@ -113,14 +113,14 @@
     def set_terminator(self, obj, terminator):
         raise NotImplementedError("abstract base class")
 
-    @jit.purefunction
+    @jit.elidable
     def size_estimate(self):
         return self._size_estimate >> NUM_DIGITS
 
     def search(self, attrtype):
         return None
 
-    @jit.purefunction
+    @jit.elidable
     def _get_new_attr(self, name, index):
         selector = name, index
         cache = self.cache_attrs
@@ -154,7 +154,7 @@
         obj._set_mapdict_map(attr)
         obj._mapdict_write_storage(attr.position, w_value)
 
-    def materialize_r_dict(self, space, obj, w_d):
+    def materialize_r_dict(self, space, obj, dict_w):
         raise NotImplementedError("abstract base class")
 
     def remove_dict_entries(self, obj):
@@ -205,7 +205,7 @@
         Terminator.__init__(self, space, w_cls)
         self.devolved_dict_terminator = DevolvedDictTerminator(space, w_cls)
 
-    def materialize_r_dict(self, space, obj, w_d):
+    def materialize_r_dict(self, space, obj, dict_w):
         result = Object()
         result.space = space
         result._init_empty(self.devolved_dict_terminator)
@@ -297,11 +297,11 @@
             return self
         return self.back.search(attrtype)
 
-    def materialize_r_dict(self, space, obj, w_d):
-        new_obj = self.back.materialize_r_dict(space, obj, w_d)
+    def materialize_r_dict(self, space, obj, dict_w):
+        new_obj = self.back.materialize_r_dict(space, obj, dict_w)
         if self.selector[1] == DICT:
             w_attr = space.wrap(self.selector[0])
-            w_d.r_dict_content[w_attr] = obj._mapdict_read_storage(self.position)
+            dict_w[w_attr] = obj._mapdict_read_storage(self.position)
         else:
             self._copy_attr(obj, new_obj)
         return new_obj
@@ -357,7 +357,7 @@
         self._set_mapdict_storage_and_map(new_obj.storage, new_obj.map)
 
     def _get_mapdict_map(self):
-        return jit.hint(self.map, promote=True)
+        return jit.promote(self.map)
     def _set_mapdict_map(self, map):
         self.map = map
     # _____________________________________________
@@ -382,7 +382,10 @@
         if w_dict is not None:
             assert isinstance(w_dict, W_DictMultiObject)
             return w_dict
-        w_dict = MapDictImplementation(space, self)
+
+        strategy = space.fromcache(MapDictStrategy)
+        storage = strategy.erase(self)
+        w_dict = W_DictMultiObject(space, strategy, storage)
         flag = self._get_mapdict_map().write(self, ("dict", SPECIAL), w_dict)
         assert flag
         return w_dict
@@ -392,8 +395,8 @@
         w_dict = check_new_dictionary(space, w_dict)
         w_olddict = self.getdict(space)
         assert isinstance(w_dict, W_DictMultiObject)
-        if w_olddict.r_dict_content is None:
-            w_olddict._as_rdict()
+        if type(w_olddict.strategy) is not ObjectDictStrategy:
+            w_olddict.strategy.switch_to_object_strategy(w_olddict)
         flag = self._get_mapdict_map().write(self, ("dict", SPECIAL), w_dict)
         assert flag
 
@@ -575,105 +578,121 @@
 # ____________________________________________________________
 # dict implementation
 
+class MapDictStrategy(DictStrategy):
 
-class MapDictImplementation(W_DictMultiObject):
-    def __init__(self, space, w_obj):
+    erase, unerase = rerased.new_erasing_pair("map")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def __init__(self, space):
         self.space = space
-        self.w_obj = w_obj
 
-    def impl_getitem(self, w_lookup):
+    def switch_to_object_strategy(self, w_dict):
+        w_obj = self.unerase(w_dict.dstorage)
+        strategy = self.space.fromcache(ObjectDictStrategy)
+        dict_w = strategy.unerase(strategy.get_empty_storage())
+        w_dict.strategy = strategy
+        w_dict.dstorage = strategy.erase(dict_w)
+        assert w_obj.getdict(self.space) is w_dict
+        materialize_r_dict(self.space, w_obj, dict_w)
+
+    def getitem(self, w_dict, w_key):
         space = self.space
-        w_lookup_type = space.type(w_lookup)
+        w_lookup_type = space.type(w_key)
         if space.is_w(w_lookup_type, space.w_str):
-            return self.impl_getitem_str(space.str_w(w_lookup))
-        elif _is_sane_hash(space, w_lookup_type):
+            return self.getitem_str(w_dict, space.str_w(w_key))
+        elif _never_equal_to_string(space, w_lookup_type):
             return None
         else:
-            return self._as_rdict().impl_fallback_getitem(w_lookup)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.getitem(w_key)
 
-    def impl_getitem_str(self, key):
-        return self.w_obj.getdictvalue(self.space, key)
+    def getitem_str(self, w_dict, key):
+        w_obj = self.unerase(w_dict.dstorage)
+        return w_obj.getdictvalue(self.space, key)
 
-    def impl_setitem_str(self,  key, w_value):
-        flag = self.w_obj.setdictvalue(self.space, key, w_value)
+    def setitem_str(self, w_dict, key, w_value):
+        w_obj = self.unerase(w_dict.dstorage)
+        flag = w_obj.setdictvalue(self.space, key, w_value)
         assert flag
 
-    def impl_setitem(self,  w_key, w_value):
+    def setitem(self, w_dict, w_key, w_value):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
-            self.impl_setitem_str(self.space.str_w(w_key), w_value)
+            self.setitem_str(w_dict, self.space.str_w(w_key), w_value)
         else:
-            self._as_rdict().impl_fallback_setitem(w_key, w_value)
+            self.switch_to_object_strategy(w_dict)
+            w_dict.setitem(w_key, w_value)
 
-    def impl_setdefault(self,  w_key, w_default):
+    def setdefault(self, w_dict, w_key, w_default):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
             key = space.str_w(w_key)
-            w_result = self.impl_getitem_str(key)
+            w_result = self.getitem_str(w_dict, key)
             if w_result is not None:
                 return w_result
-            self.impl_setitem_str(key, w_default)
+            self.setitem_str(w_dict, key, w_default)
             return w_default
         else:
-            return self._as_rdict().impl_fallback_setdefault(w_key, w_default)
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.setdefault(w_key, w_default)
 
-    def impl_delitem(self, w_key):
+    def delitem(self, w_dict, w_key):
         space = self.space
         w_key_type = space.type(w_key)
+        w_obj = self.unerase(w_dict.dstorage)
         if space.is_w(w_key_type, space.w_str):
-            flag = self.w_obj.deldictvalue(space, w_key)
+            flag = w_obj.deldictvalue(space, w_key)
             if not flag:
                 raise KeyError
-        elif _is_sane_hash(space, w_key_type):
+        elif _never_equal_to_string(space, w_key_type):
             raise KeyError
         else:
-            self._as_rdict().impl_fallback_delitem(w_key)
+            self.switch_to_object_strategy(w_dict)
+            w_dict.delitem(w_key)
 
-    def impl_length(self):
+    def length(self, w_dict):
         res = 0
-        curr = self.w_obj._get_mapdict_map().search(DICT)
+        curr = self.unerase(w_dict.dstorage)._get_mapdict_map().search(DICT)
         while curr is not None:
             curr = curr.back
             curr = curr.search(DICT)
             res += 1
         return res
 
-    def impl_iter(self):
-        return MapDictIteratorImplementation(self.space, self)
+    def iter(self, w_dict):
+        return MapDictIteratorImplementation(self.space, self, w_dict)
 
-    def impl_clear(self):
-        w_obj = self.w_obj
+    def clear(self, w_dict):
+        w_obj = self.unerase(w_dict.dstorage)
         new_obj = w_obj._get_mapdict_map().remove_dict_entries(w_obj)
         _become(w_obj, new_obj)
 
-    def _clear_fields(self):
-        self.w_obj = None
+    def popitem(self, w_dict):
+        curr = self.unerase(w_dict.dstorage)._get_mapdict_map().search(DICT)
+        if curr is None:
+            raise KeyError
+        key = curr.selector[0]
+        w_value = self.getitem_str(w_dict, key)
+        w_key = self.space.wrap(key)
+        self.delitem(w_dict, w_key)
+        return (w_key, w_value)
 
-    def _as_rdict(self):
-        self.initialize_as_rdict()
-        space = self.space
-        w_obj = self.w_obj
-        materialize_r_dict(space, w_obj, self)
-        self._clear_fields()
-        return self
-
-
-def materialize_r_dict(space, obj, w_d):
+def materialize_r_dict(space, obj, dict_w):
     map = obj._get_mapdict_map()
-    assert obj.getdict(space) is w_d
-    new_obj = map.materialize_r_dict(space, obj, w_d)
+    new_obj = map.materialize_r_dict(space, obj, dict_w)
     _become(obj, new_obj)
 
 class MapDictIteratorImplementation(IteratorImplementation):
-    def __init__(self, space, dictimplementation):
+    def __init__(self, space, strategy, dictimplementation):
         IteratorImplementation.__init__(self, space, dictimplementation)
-        w_obj = dictimplementation.w_obj
+        w_obj = strategy.unerase(dictimplementation.dstorage)
         self.w_obj = w_obj
         self.orig_map = self.curr_map = w_obj._get_mapdict_map()
 
     def next_entry(self):
         implementation = self.dictimplementation
-        assert isinstance(implementation, MapDictImplementation)
+        assert isinstance(implementation.strategy, MapDictStrategy)
         if self.orig_map is not self.w_obj._get_mapdict_map():
             return None, None
         if self.curr_map:
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -11,7 +11,7 @@
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.rarithmetic import base_int, widen
 from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.jit import hint
+from pypy.rlib import jit
 from pypy.rlib.rbigint import rbigint
 from pypy.tool.sourcetools import func_with_new_name
 
@@ -255,7 +255,7 @@
             w_result = self.wrap_exception_cls(x)
             if w_result is not None:
                 return w_result
-        from fake import fake_object
+        from pypy.objspace.std.fake import fake_object
         return fake_object(self, x)
 
     def wrap_exception_cls(self, x):
@@ -311,6 +311,10 @@
                 classofinstance=classofinstance,
                 strdict=strdict)
 
+    def newset(self):
+        from pypy.objspace.std.setobject import newset
+        return W_SetObject(self, newset(self))
+
     def newslice(self, w_start, w_end, w_step):
         return W_SliceObject(w_start, w_end, w_step)
 
@@ -318,7 +322,7 @@
         return W_SeqIterObject(w_obj)
 
     def type(self, w_obj):
-        hint(w_obj.__class__, promote=True)
+        jit.promote(w_obj.__class__)
         return w_obj.getclass(self)
 
     def lookup(self, w_obj, name):
diff --git a/pypy/objspace/std/ropeunicodeobject.py b/pypy/objspace/std/ropeunicodeobject.py
--- a/pypy/objspace/std/ropeunicodeobject.py
+++ b/pypy/objspace/std/ropeunicodeobject.py
@@ -986,7 +986,7 @@
 ##        return space.wrap(0)
 ##    return space.wrap(result)
 
-import unicodetype
+from pypy.objspace.std import unicodetype
 register_all(vars(), unicodetype)
 
 # str.strip(unicode) needs to convert self to unicode and call unicode.strip we
@@ -997,7 +997,7 @@
 # methods?
 
 class str_methods:
-    import stringtype
+    from pypy.objspace.std import stringtype
     W_RopeUnicodeObject = W_RopeUnicodeObject
     from pypy.objspace.std.ropeobject import W_RopeObject
     def str_strip__Rope_RopeUnicode(space, w_self, w_chars):
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -112,7 +112,7 @@
 # some helper functions
 
 def newset(space):
-    return r_dict(space.eq_w, space.hash_w)
+    return r_dict(space.eq_w, space.hash_w, force_non_null=True)
 
 def make_setdata_from_w_iterable(space, w_iterable=None):
     """Return a new r_dict with the content of w_iterable."""
@@ -466,12 +466,11 @@
     return space.wrap(hash)
 
 def set_pop__Set(space, w_left):
-    for w_key in w_left.setdata:
-        break
-    else:
+    try:
+        w_key, _ = w_left.setdata.popitem()
+    except KeyError:
         raise OperationError(space.w_KeyError,
                                 space.wrap('pop from an empty set'))
-    del w_left.setdata[w_key]
     return w_key
 
 def and__Set_Set(space, w_left, w_other):
diff --git a/pypy/objspace/std/test/test_celldict.py b/pypy/objspace/std/test/test_celldict.py
--- a/pypy/objspace/std/test/test_celldict.py
+++ b/pypy/objspace/std/test/test_celldict.py
@@ -1,6 +1,7 @@
 import py
 from pypy.conftest import gettestobjspace, option
-from pypy.objspace.std.celldict import ModuleCell, ModuleDictImplementation
+from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+from pypy.objspace.std.celldict import ModuleCell, ModuleDictStrategy
 from pypy.objspace.std.test.test_dictmultiobject import FakeSpace
 from pypy.interpreter import gateway
 
@@ -8,7 +9,15 @@
 
 class TestCellDict(object):
     def test_basic_property(self):
-        d = ModuleDictImplementation(space)
+        strategy = ModuleDictStrategy(space)
+        storage = strategy.get_empty_storage()
+        d = W_DictMultiObject(space, strategy, storage)
+
+        # replace getcell with getcell from strategy
+        def f(key, makenew):
+            return strategy.getcell(d, key, makenew)
+        d.getcell = f
+
         d.setitem("a", 1)
         assert d.getcell("a", False) is d.getcell("a", False)
         acell = d.getcell("a", False)
@@ -29,3 +38,33 @@
         assert d.getitem("a") is None
         assert d.getcell("a", False) is acell
         assert d.length() == 0
+
+class AppTestCellDict(object):
+    OPTIONS = {"objspace.std.withcelldict": True}
+
+    def setup_class(cls):
+        if option.runappdirect:
+            py.test.skip("__repr__ doesn't work on appdirect")
+        strategy = ModuleDictStrategy(cls.space)
+        storage = strategy.get_empty_storage()
+        cls.w_d = W_DictMultiObject(cls.space, strategy, storage)
+
+    def test_popitem(self):
+        import __pypy__
+
+        d = self.d
+        assert "ModuleDict" in __pypy__.internal_repr(d)
+        raises(KeyError, d.popitem)
+        d["a"] = 3
+        x = d.popitem()
+        assert x == ("a", 3)
+
+    def test_degenerate(self):
+        import __pypy__
+
+        d = self.d
+        assert "ModuleDict" in __pypy__.internal_repr(d)
+        d["a"] = 3
+        del d["a"]
+        d[object()] = 5
+        assert d.values() == [5]
\ No newline at end of file
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -1,12 +1,13 @@
+import py
 import sys
 from pypy.interpreter.error import OperationError
 from pypy.objspace.std.dictmultiobject import \
      W_DictMultiObject, setitem__DictMulti_ANY_ANY, getitem__DictMulti_ANY, \
-     StrDictImplementation
+     StringDictStrategy, ObjectDictStrategy
 
-from pypy.objspace.std.celldict import ModuleDictImplementation
+from pypy.objspace.std.celldict import ModuleDictStrategy
 from pypy.conftest import gettestobjspace
-
+from pypy.conftest import option
 
 class TestW_DictObject:
 
@@ -17,7 +18,7 @@
         space = self.space
         d = self.space.newdict()
         assert not self.space.is_true(d)
-        assert d.r_dict_content is None
+        assert type(d.strategy) is not ObjectDictStrategy
 
     def test_nonempty(self):
         space = self.space
@@ -137,31 +138,31 @@
         cls.w_on_pypy = cls.space.wrap("__pypy__" in sys.builtin_module_names)
 
     def test_equality(self):
-        d = {1:2}
-        f = {1:2}
+        d = {1: 2}
+        f = {1: 2}
         assert d == f
-        assert d != {1:3}
+        assert d != {1: 3}
 
     def test_clear(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         d.clear()
         assert len(d) == 0
 
     def test_copy(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         assert d == dd
         assert not d is dd
 
     def test_get(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         assert d.get(1) == 2
-        assert d.get(1,44) == 2
+        assert d.get(1, 44) == 2
         assert d.get(33) == None
-        assert d.get(33,44) == 44
+        assert d.get(33, 44) == 44
 
     def test_pop(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         result = dd.pop(1)
         assert result == 2
@@ -176,18 +177,18 @@
         raises(KeyError, dd.pop, 33)
 
     def test_has_key(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         assert d.has_key(1)
         assert not d.has_key(33)
 
     def test_items(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         its = d.items()
         its.sort()
-        assert its == [(1,2),(3,4)]
+        assert its == [(1, 2), (3, 4)]
 
     def test_iteritems(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         for k, v in d.iteritems():
             assert v == dd[k]
@@ -195,33 +196,33 @@
         assert not dd
 
     def test_iterkeys(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         for k in d.iterkeys():
             del dd[k]
         assert not dd
 
     def test_itervalues(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         values = []
         for k in d.itervalues():
             values.append(k)
         assert values == d.values()
 
     def test_keys(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         kys = d.keys()
         kys.sort()
-        assert kys == [1,3]
+        assert kys == [1, 3]
 
     def test_popitem(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         it = d.popitem()
         assert len(d) == 1
-        assert it==(1,2) or it==(3,4)
+        assert it == (1, 2) or it == (3, 4)
         it1 = d.popitem()
         assert len(d) == 0
-        assert (it!=it1) and (it1==(1,2) or it1==(3,4))
+        assert (it != it1) and (it1 == (1, 2) or it1 == (3, 4))
         raises(KeyError, d.popitem)
 
     def test_popitem_2(self):
@@ -233,8 +234,33 @@
         assert it1 == ('x', 5)
         raises(KeyError, d.popitem)
 
+    def test_popitem3(self):
+        #object
+        d = {"a": 1, 2: 2, "c": 3}
+        l = []
+        while True:
+            try:
+                l.append(d.popitem())
+            except KeyError:
+                break;
+        assert ("a", 1) in l
+        assert (2, 2) in l
+        assert ("c", 3) in l
+
+        #string
+        d = {"a": 1, "b":2, "c":3}
+        l = []
+        while True:
+            try:
+                l.append(d.popitem())
+            except KeyError:
+                break;
+        assert ("a", 1) in l
+        assert ("b", 2) in l
+        assert ("c", 3) in l
+
     def test_setdefault(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         x = dd.setdefault(1, 99)
         assert d == dd
@@ -267,12 +293,12 @@
             assert k.calls == 1
 
     def test_update(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         dd = d.copy()
         d.update({})
         assert d == dd
-        d.update({3:5, 6:7})
-        assert d == {1:2, 3:5, 6:7}
+        d.update({3: 5, 6: 7})
+        assert d == {1: 2, 3: 5, 6: 7}
 
     def test_update_iterable(self):
         d = {}
@@ -297,15 +323,15 @@
         assert d == {'foo': 'bar', 'baz': 1}
 
     def test_values(self):
-        d = {1:2, 3:4}
+        d = {1: 2, 3: 4}
         vals = d.values()
         vals.sort()
         assert vals == [2,4]
 
     def test_eq(self):
-        d1 = {1:2, 3:4}
-        d2 = {1:2, 3:4}
-        d3 = {1:2}
+        d1 = {1: 2, 3: 4}
+        d2 = {1: 2, 3: 4}
+        d3 = {1: 2}
         bool = d1 == d2
         assert bool == True
         bool = d1 == d3
@@ -316,10 +342,10 @@
         assert bool == True
 
     def test_lt(self):
-        d1 = {1:2, 3:4}
-        d2 = {1:2, 3:4}
-        d3 = {1:2, 3:5}
-        d4 = {1:2}
+        d1 = {1: 2, 3: 4}
+        d2 = {1: 2, 3: 4}
+        d3 = {1: 2, 3: 5}
+        d4 = {1: 2}
         bool = d1 < d2
         assert bool == False
         bool = d1 < d3
@@ -366,21 +392,17 @@
     def test_new(self):
         d = dict()
         assert d == {}
-        args = [['a',2], [23,45]]
+        args = [['a', 2], [23, 45]]
         d = dict(args)
-        assert d == {'a':2, 23:45}
+        assert d == {'a': 2, 23: 45}
         d = dict(args, a=33, b=44)
-        assert d == {'a':33, 'b':44, 23:45}
+        assert d == {'a': 33, 'b': 44, 23: 45}
         d = dict(a=33, b=44)
-        assert d == {'a':33, 'b':44}
-        d = dict({'a':33, 'b':44})
-        assert d == {'a':33, 'b':44}
-        try: d = dict(23)
-        except (TypeError, ValueError): pass
-        else: self.fail("dict(23) should raise!")
-        try: d = dict([[1,2,3]])
-        except (TypeError, ValueError): pass
-        else: self.fail("dict([[1,2,3]]) should raise!")
+        assert d == {'a': 33, 'b': 44}
+        d = dict({'a': 33, 'b': 44})
+        assert d == {'a': 33, 'b': 44}
+        raises((TypeError, ValueError), dict, 23)
+        raises((TypeError, ValueError), dict, [[1, 2, 3]])
 
     def test_fromkeys(self):
         assert {}.fromkeys([1, 2], 1) == {1: 1, 2: 1}
@@ -527,6 +549,12 @@
             __missing__ = SpecialDescr(missing)
         assert X()['hi'] == 42
 
+    def test_empty_dict(self):
+        d = {}
+        raises(KeyError, d.popitem)
+        assert d.items() == []
+        assert d.values() == []
+        assert d.keys() == []
 
 class AppTest_DictMultiObject(AppTest_DictObject):
 
@@ -706,10 +734,12 @@
 class AppTestModuleDict(object):
     def setup_class(cls):
         cls.space = gettestobjspace(**{"objspace.std.withcelldict": True})
+        if option.runappdirect:
+            py.test.skip("__repr__ doesn't work on appdirect")
 
     def w_impl_used(self, obj):
         import __pypy__
-        assert "ModuleDictImplementation" in __pypy__.internal_repr(obj)
+        assert "ModuleDictStrategy" in __pypy__.internal_repr(obj)
 
     def test_check_module_uses_module_dict(self):
         m = type(__builtins__)("abc")
@@ -719,6 +749,64 @@
         d = type(__builtins__)("abc").__dict__
         raises(KeyError, "d['def']")
 
+    def test_fallback_evil_key(self):
+        class F(object):
+            def __hash__(self):
+                return hash("s")
+            def __eq__(self, other):
+                return other == "s"
+        d = type(__builtins__)("abc").__dict__
+        d["s"] = 12
+        assert d["s"] == 12
+        assert d[F()] == d["s"]
+
+        d = type(__builtins__)("abc").__dict__
+        x = d.setdefault("s", 12)
+        assert x == 12
+        x = d.setdefault(F(), 12)
+        assert x == 12
+
+        d = type(__builtins__)("abc").__dict__
+        x = d.setdefault(F(), 12)
+        assert x == 12
+
+        d = type(__builtins__)("abc").__dict__
+        d["s"] = 12
+        del d[F()]
+
+        assert "s" not in d
+        assert F() not in d
+
+class AppTestStrategies(object):
+    def setup_class(cls):
+        if option.runappdirect:
+            py.test.skip("__repr__ doesn't work on appdirect")
+
+    def w_get_strategy(self, obj):
+        import __pypy__
+        r = __pypy__.internal_repr(obj)
+        return r[r.find("(") + 1: r.find(")")]
+
+    def test_empty_to_string(self):
+        d = {}
+        assert "EmptyDictStrategy" in self.get_strategy(d)
+        d["a"] = 1
+        assert "StringDictStrategy" in self.get_strategy(d)
+
+        class O(object):
+            pass
+        o = O()
+        d = o.__dict__ = {}
+        assert "EmptyDictStrategy" in self.get_strategy(d)
+        o.a = 1
+        assert "StringDictStrategy" in self.get_strategy(d)
+
+    def test_empty_to_int(self):
+        import sys
+        d = {}
+        d[1] = "hi"
+        assert "IntDictStrategy" in self.get_strategy(d)
+        assert d[1L] == "hi"
 
 
 class FakeString(str):
@@ -759,6 +847,10 @@
         assert isinstance(string, str)
         return string
 
+    def int_w(self, integer):
+        assert isinstance(integer, int)
+        return integer
+
     def wrap(self, obj):
         return obj
 
@@ -790,6 +882,10 @@
 
     w_StopIteration = StopIteration
     w_None = None
+    w_NoneType = type(None, None)
+    w_int = int
+    w_bool = bool
+    w_float = float
     StringObjectCls = FakeString
     w_dict = W_DictMultiObject
     iter = iter
@@ -799,12 +895,9 @@
 class Config:
     class objspace:
         class std:
-            withdictmeasurement = False
             withsmalldicts = False
             withcelldict = False
             withmethodcache = False
-        class opcodes:
-            CALL_LIKELY_BUILTIN = False
 
 FakeSpace.config = Config()
 
@@ -834,14 +927,20 @@
         self.impl = self.get_impl()
 
     def get_impl(self):
-        return self.ImplementionClass(self.fakespace)
+        strategy = self.StrategyClass(self.fakespace)
+        storage = strategy.get_empty_storage()
+        w_dict = self.fakespace.allocate_instance(W_DictMultiObject, None)
+        W_DictMultiObject.__init__(w_dict, self.fakespace, strategy, storage)
+        return w_dict
 
     def fill_impl(self):
         self.impl.setitem(self.string, 1000)
         self.impl.setitem(self.string2, 2000)
 
     def check_not_devolved(self):
-        assert self.impl.r_dict_content is None
+        #XXX check if strategy changed!?
+        assert type(self.impl.strategy) is self.StrategyClass
+        #assert self.impl.r_dict_content is None
 
     def test_setitem(self):
         self.impl.setitem(self.string, 1000)
@@ -913,7 +1012,7 @@
         for x in xrange(100):
             impl.setitem(self.fakespace.str_w(str(x)), x)
             impl.setitem(x, x)
-        assert impl.r_dict_content is not None
+        assert type(impl.strategy) is ObjectDictStrategy
 
     def test_setdefault_fast(self):
         on_pypy = "__pypy__" in sys.builtin_module_names
@@ -928,8 +1027,38 @@
         if on_pypy:
             assert key.hash_count == 2
 
+    def test_fallback_evil_key(self):
+        class F(object):
+            def __hash__(self):
+                return hash("s")
+            def __eq__(self, other):
+                return other == "s"
+
+        d = self.get_impl()
+        d.setitem("s", 12)
+        assert d.getitem("s") == 12
+        assert d.getitem(F()) == d.getitem("s")
+
+        d = self.get_impl()
+        x = d.setdefault("s", 12)
+        assert x == 12
+        x = d.setdefault(F(), 12)
+        assert x == 12
+
+        d = self.get_impl()
+        x = d.setdefault(F(), 12)
+        assert x == 12
+
+        d = self.get_impl()
+        d.setitem("s", 12)
+        d.delitem(F())
+
+        assert "s" not in d.keys()
+        assert F() not in d.keys()
+
 class TestStrDictImplementation(BaseTestRDictImplementation):
-    ImplementionClass = StrDictImplementation
+    StrategyClass = StringDictStrategy
+    #ImplementionClass = StrDictImplementation
 
     def test_str_shortcut(self):
         self.fill_impl()
@@ -942,10 +1071,10 @@
 ##     DevolvedClass = MeasuringDictImplementation
 
 class TestModuleDictImplementation(BaseTestRDictImplementation):
-    ImplementionClass = ModuleDictImplementation
+    StrategyClass = ModuleDictStrategy
 
 class TestModuleDictImplementationWithBuiltinNames(BaseTestRDictImplementation):
-    ImplementionClass = ModuleDictImplementation
+    StrategyClass = ModuleDictStrategy
 
     string = "int"
     string2 = "isinstance"
@@ -954,19 +1083,19 @@
 class BaseTestDevolvedDictImplementation(BaseTestRDictImplementation):
     def fill_impl(self):
         BaseTestRDictImplementation.fill_impl(self)
-        self.impl._as_rdict()
+        self.impl.strategy.switch_to_object_strategy(self.impl)
 
     def check_not_devolved(self):
         pass
 
 class TestDevolvedStrDictImplementation(BaseTestDevolvedDictImplementation):
-    ImplementionClass = StrDictImplementation
+    StrategyClass = StringDictStrategy
 
 class TestDevolvedModuleDictImplementation(BaseTestDevolvedDictImplementation):
-    ImplementionClass = ModuleDictImplementation
+    StrategyClass = ModuleDictStrategy
 
 class TestDevolvedModuleDictImplementationWithBuiltinNames(BaseTestDevolvedDictImplementation):
-    ImplementionClass = ModuleDictImplementation
+    StrategyClass = ModuleDictStrategy
 
     string = "int"
     string2 = "isinstance"
@@ -975,5 +1104,4 @@
 def test_module_uses_strdict():
     fakespace = FakeSpace()
     d = fakespace.newdict(module=True)
-    assert isinstance(d, StrDictImplementation)
-
+    assert type(d.strategy) is StringDictStrategy
diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -250,13 +250,18 @@
 
     class FakeDict(W_DictMultiObject):
         def __init__(self, d):
-            self.r_dict_content = d
+            self.dstorage = d
+
+        class strategy:
+            def unerase(self, x):
+                return d
+        strategy = strategy()
 
     d = {}
     w_d = FakeDict(d)
     flag = obj.map.write(obj, ("dict", SPECIAL), w_d)
     assert flag
-    materialize_r_dict(space, obj, w_d)
+    materialize_r_dict(space, obj, d)
     assert d == {"a": 5, "b": 6, "c": 7}
     assert obj.storage == [50, 60, 70, w_d]
 
@@ -291,18 +296,18 @@
     w_obj = cls.instantiate(self.fakespace)
     return w_obj.getdict(self.fakespace)
 class TestMapDictImplementation(BaseTestRDictImplementation):
-    ImplementionClass = MapDictImplementation
+    StrategyClass = MapDictStrategy
     get_impl = get_impl
 class TestDevolvedMapDictImplementation(BaseTestDevolvedDictImplementation):
     get_impl = get_impl
-    ImplementionClass = MapDictImplementation
+    StrategyClass = MapDictStrategy
 
 # ___________________________________________________________
 # tests that check the obj interface after the dict has devolved
 
 def devolve_dict(space, obj):
     w_d = obj.getdict(space)
-    w_d._as_rdict()
+    w_d.strategy.switch_to_object_strategy(w_d)
 
 def test_get_setdictvalue_after_devolve():
     cls = Class()
@@ -463,6 +468,20 @@
         d['dd'] = 43
         assert a.dd == 41
 
+    def test_popitem(self):
+        class A(object):
+            pass
+        a = A()
+        a.x = 5
+        a.y = 6
+        it1 = a.__dict__.popitem()
+        assert it1 == ("y", 6)
+        it2 = a.__dict__.popitem()
+        assert it2 == ("x", 5)
+        assert a.__dict__ == {}
+        raises(KeyError, a.__dict__.popitem)
+
+
 
     def test_slot_name_conflict(self):
         class A(object):
@@ -604,6 +623,14 @@
         assert a.__dict__ is d
         assert isinstance(a, B)
 
+    def test_setdict(self):
+        class A(object):
+            pass
+
+        a = A()
+        a.__dict__ = {}
+        a.__dict__ = {}
+
 
 class AppTestWithMapDictAndCounters(object):
     def setup_class(cls):
diff --git a/pypy/objspace/std/test/test_setobject.py b/pypy/objspace/std/test/test_setobject.py
--- a/pypy/objspace/std/test/test_setobject.py
+++ b/pypy/objspace/std/test/test_setobject.py
@@ -50,6 +50,10 @@
         u = self.space.wrap(set('simsalabim'))
         assert self.space.eq_w(s,u)
 
+    def test_space_newset(self):
+        s = self.space.newset()
+        assert self.space.str_w(self.space.repr(s)) == 'set([])'
+
 class AppTestAppSetTest:
     def test_subtype(self):
         class subset(set):pass
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -9,8 +9,8 @@
 from pypy.objspace.std.objecttype import object_typedef
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.objectmodel import current_object_addr_as_int, compute_hash
-from pypy.rlib.jit import hint, purefunction_promote, we_are_jitted
-from pypy.rlib.jit import purefunction, dont_look_inside, unroll_safe
+from pypy.rlib.jit import promote, elidable_promote, we_are_jitted
+from pypy.rlib.jit import elidable, dont_look_inside, unroll_safe
 from pypy.rlib.rarithmetic import intmask, r_uint
 
 class TypeCell(W_Root):
@@ -177,7 +177,7 @@
         # prebuilt objects cannot get their version_tag changed
         return w_self._pure_version_tag()
 
-    @purefunction_promote()
+    @elidable_promote()
     def _pure_version_tag(w_self):
         return w_self._version_tag
 
@@ -247,7 +247,7 @@
                     return w_value
         return w_value
 
-    @purefunction
+    @elidable
     def _pure_getdictvalue_no_unwrapping(w_self, space, version_tag, attr):
         return w_self._getdictvalue_no_unwrapping(space, attr)
 
@@ -351,16 +351,16 @@
 
     def lookup_where_with_method_cache(w_self, name):
         space = w_self.space
-        w_self = hint(w_self, promote=True)
+        promote(w_self)
         assert space.config.objspace.std.withmethodcache
-        version_tag = hint(w_self.version_tag(), promote=True)
+        version_tag = promote(w_self.version_tag())
         if version_tag is None:
             tup = w_self._lookup_where(name)
             return tup
         w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, version_tag)
         return w_class, unwrap_cell(space, w_value)
 
-    @purefunction
+    @elidable
     def _pure_lookup_where_with_method_cache(w_self, name, version_tag):
         space = w_self.space
         cache = space.fromcache(MethodCache)
@@ -423,10 +423,13 @@
         return False
 
     def getdict(w_self, space): # returning a dict-proxy!
-        from pypy.objspace.std.dictproxyobject import W_DictProxyObject
+        from pypy.objspace.std.dictproxyobject import DictProxyStrategy
+        from pypy.objspace.std.dictmultiobject import W_DictMultiObject
         if w_self.lazyloaders:
             w_self._freeze_()    # force un-lazification
-        return W_DictProxyObject(space, w_self)
+        strategy = space.fromcache(DictProxyStrategy)
+        storage = strategy.erase(w_self)
+        return W_DictMultiObject(space, strategy, storage)
 
     def unwrap(w_self, space):
         if w_self.instancetypedef.fakedcpytype is not None:
@@ -447,8 +450,8 @@
         w_self.flag_abstract = bool(abstract)
 
     def issubtype(w_self, w_type):
-        w_self = hint(w_self, promote=True)
-        w_type = hint(w_type, promote=True)
+        promote(w_self)
+        promote(w_type)
         if w_self.space.config.objspace.std.withtypeversion and we_are_jitted():
             version_tag1 = w_self.version_tag()
             version_tag2 = w_type.version_tag()
@@ -774,7 +777,7 @@
 # ____________________________________________________________
 
 def call__Type(space, w_type, __args__):
-    w_type = hint(w_type, promote=True)
+    promote(w_type)
     # special case for type(x)
     if space.is_w(w_type, space.w_type):
         try:
@@ -820,7 +823,7 @@
 def _issubtype(w_sub, w_type):
     return w_type in w_sub.mro_w
 
- at purefunction_promote()
+ at elidable_promote()
 def _pure_issubtype(w_sub, w_type, version_tag1, version_tag2):
     return _issubtype(w_sub, w_type)
 
diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py
--- a/pypy/objspace/std/unicodeobject.py
+++ b/pypy/objspace/std/unicodeobject.py
@@ -937,7 +937,7 @@
     return formatter.format_string(space.unicode_w(w_unicode))
 
 
-import unicodetype
+from pypy.objspace.std import unicodetype
 register_all(vars(), unicodetype)
 
 # str.strip(unicode) needs to convert self to unicode and call unicode.strip we
@@ -948,7 +948,7 @@
 # methods?
 
 class str_methods:
-    import stringtype
+    from pypy.objspace.std import stringtype
     W_UnicodeObject = W_UnicodeObject
     from pypy.objspace.std.stringobject import W_StringObject
     from pypy.objspace.std.ropeobject import W_RopeObject
diff --git a/pypy/objspace/taint.py b/pypy/objspace/taint.py
--- a/pypy/objspace/taint.py
+++ b/pypy/objspace/taint.py
@@ -92,8 +92,8 @@
     w_realtype = space.type(w_obj)
     if not space.is_w(w_realtype, w_expectedtype):
         #msg = "expected an object of type '%s'" % (
-        #    w_expectedtype.getname(space, '?'),)
-        #    #w_realtype.getname(space, '?'))
+        #    w_expectedtype.getname(space),)
+        #    #w_realtype.getname(space))
         raise OperationError(space.w_TaintError, space.w_None)
     return w_obj
 app_untaint = gateway.interp2app(untaint)
diff --git a/pypy/rlib/debug.py b/pypy/rlib/debug.py
--- a/pypy/rlib/debug.py
+++ b/pypy/rlib/debug.py
@@ -262,6 +262,28 @@
         return hop.inputarg(hop.args_r[0], arg=0)
 
 
+def mark_dict_non_null(d):
+    """ Mark dictionary as having non-null keys and values. A warning would
+    be emitted (not an error!) in case annotation disagrees.
+    """
+    assert isinstance(d, dict)
+    return d
+
+
+class DictMarkEntry(ExtRegistryEntry):
+    _about_ = mark_dict_non_null
+    
+    def compute_result_annotation(self, s_dict):
+        from pypy.annotation.model import SomeDict, s_None
+
+        assert isinstance(s_dict, SomeDict)
+        s_dict.dictdef.force_non_null = True
+        return s_dict
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        return hop.inputarg(hop.args_r[0], arg=0)
+
 class IntegerCanBeNegative(Exception):
     pass
 
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -6,21 +6,26 @@
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.nonconst import NonConstant
 
-def purefunction(func):
-    """ Decorate a function as pure. Pure means precisely that:
+def elidable(func):
+    """ Decorate a function as "trace-elidable". This means precisely that:
 
     (1) the result of the call should not change if the arguments are
         the same (same numbers or same pointers)
     (2) it's fine to remove the call completely if we can guess the result
     according to rule 1
 
-    Most importantly it doesn't mean that pure function has no observable
-    side effect, but those side effects can be ommited (ie caching).
+    Most importantly it doesn't mean that an elidable function has no observable
+    side effect, but those side effects are idempotent (ie caching).
     For now, such a function should never raise an exception.
     """
-    func._pure_function_ = True
+    func._elidable_function_ = True
     return func
 
+def purefunction(*args, **kwargs):
+    import warnings
+    warnings.warn("purefunction is deprecated, use elidable instead", DeprecationWarning)
+    return elidable(*args, **kwargs)
+
 def hint(x, **kwds):
     """ Hint for the JIT
 
@@ -36,6 +41,10 @@
     """
     return x
 
+ at specialize.argtype(0)
+def promote(x):
+    return hint(x, promote=True)
+
 def dont_look_inside(func):
     """ Make sure the JIT does not trace inside decorated function
     (it becomes a call instead)
@@ -60,13 +69,13 @@
     func._jit_loop_invariant_ = True
     return func
 
-def purefunction_promote(promote_args='all'):
+def elidable_promote(promote_args='all'):
     """ A decorator that promotes all arguments and then calls the supplied
     function
     """
     def decorator(func):
         import inspect
-        purefunction(func)
+        elidable(func)
         args, varargs, varkw, defaults = inspect.getargspec(func)
         args = ["v%s" % (i, ) for i in range(len(args))]
         assert varargs is None and varkw is None
@@ -85,6 +94,12 @@
         return result
     return decorator
 
+def purefunction_promote(*args, **kwargs):
+    import warnings
+    warnings.warn("purefunction_promote is deprecated, use elidable_promote instead", DeprecationWarning)
+    return elidable_promote(*args, **kwargs)
+
+
 def oopspec(spec):
     def decorator(func):
         func.oopspec = spec
@@ -277,12 +292,13 @@
               'function_threshold': 1617, # slightly more than one above 
               'trace_eagerness': 200,
               'trace_limit': 12000,
-              'inlining': 0,
+              'inlining': 1,
               'loop_longevity': 1000,
               'retrace_limit': 5,
-              'enable_opts': None, # patched later by optimizeopt/__init__.py
+              'enable_opts': 'all',
               }
 unroll_parameters = unrolling_iterable(PARAMETERS.items())
+DEFAULT = object()
 
 # ____________________________________________________________
 
@@ -337,22 +353,33 @@
     def _set_param(self, name, value):
         # special-cased by ExtRegistryEntry
         # (internal, must receive a constant 'name')
+        # if value is DEFAULT, sets the default value.
         assert name in PARAMETERS
 
     @specialize.arg(0, 1)
     def set_param(self, name, value):
         """Set one of the tunable JIT parameter."""
-        for name1, _ in unroll_parameters:
-            if name1 == name:
-                self._set_param(name1, value)
-                return
-        raise ValueError("no such parameter")
+        self._set_param(name, value)
+
+    @specialize.arg(0, 1)
+    def set_param_to_default(self, name):
+        """Reset one of the tunable JIT parameters to its default value."""
+        self._set_param(name, DEFAULT)
 
     def set_user_param(self, text):
         """Set the tunable JIT parameters from a user-supplied string
-        following the format 'param=value,param=value'.  For programmatic
-        setting of parameters, use directly JitDriver.set_param().
+        following the format 'param=value,param=value', or 'off' to
+        disable the JIT.  For programmatic setting of parameters, use
+        directly JitDriver.set_param().
         """
+        if text == 'off':
+            self.set_param('threshold', -1)
+            self.set_param('function_threshold', -1)
+            return
+        if text == 'default':
+            for name1, _ in unroll_parameters:
+                self.set_param_to_default(name1)
+            return
         for s in text.split(','):
             s = s.strip(' ')
             parts = s.split('=')
@@ -575,15 +602,17 @@
     def compute_result_annotation(self, s_name, s_value):
         from pypy.annotation import model as annmodel
         assert s_name.is_constant()
-        if s_name.const == 'enable_opts':
-            assert annmodel.SomeString(can_be_None=True).contains(s_value)
-        else:
-            assert annmodel.SomeInteger().contains(s_value)
+        if not self.bookkeeper.immutablevalue(DEFAULT).contains(s_value):
+            if s_name.const == 'enable_opts':
+                assert annmodel.SomeString(can_be_None=True).contains(s_value)
+            else:
+                assert annmodel.SomeInteger().contains(s_value)
         return annmodel.s_None
 
     def specialize_call(self, hop):
         from pypy.rpython.lltypesystem import lltype
         from pypy.rpython.lltypesystem.rstr import string_repr
+        from pypy.objspace.flow.model import Constant
 
         hop.exception_cannot_occur()
         driver = self.instance.im_self
@@ -592,7 +621,12 @@
             repr = string_repr
         else:
             repr = lltype.Signed
-        v_value = hop.inputarg(repr, arg=1)
+        if (isinstance(hop.args_v[1], Constant) and
+            hop.args_v[1].value is DEFAULT):
+            value = PARAMETERS[name]
+            v_value = hop.inputconst(repr, value)
+        else:
+            v_value = hop.inputarg(repr, arg=1)
         vlist = [hop.inputconst(lltype.Void, "set_param"),
                  hop.inputconst(lltype.Void, driver),
                  hop.inputconst(lltype.Void, name),
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
--- a/pypy/rlib/libffi.py
+++ b/pypy/rlib/libffi.py
@@ -40,7 +40,7 @@
         del cls._import
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def getkind(ffi_type):
         """Returns 'v' for void, 'f' for float, 'i' for signed integer,
         and 'u' for unsigned integer.
@@ -74,7 +74,7 @@
         raise KeyError
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def is_struct(ffi_type):
         return intmask(ffi_type.c_type) == intmask(FFI_TYPE_STRUCT)
 
@@ -253,7 +253,7 @@
         # the optimizer will fail to recognize the pattern and won't turn it
         # into a fast CALL.  Note that "arg = arg.next" is optimized away,
         # assuming that archain is completely virtual.
-        self = jit.hint(self, promote=True)
+        self = jit.promote(self)
         if argchain.numargs != len(self.argtypes):
             raise TypeError, 'Wrong number of arguments: %d expected, got %d' %\
                 (argchain.numargs, len(self.argtypes))
diff --git a/pypy/rlib/longlong2float.py b/pypy/rlib/longlong2float.py
--- a/pypy/rlib/longlong2float.py
+++ b/pypy/rlib/longlong2float.py
@@ -49,9 +49,9 @@
 longlong2float = rffi.llexternal(
     "pypy__longlong2float", [rffi.LONGLONG], rffi.DOUBLE,
     _callable=longlong2float_emulator, compilation_info=eci,
-    _nowrapper=True, pure_function=True)
+    _nowrapper=True, elidable_function=True)
 
 float2longlong = rffi.llexternal(
     "pypy__float2longlong", [rffi.DOUBLE], rffi.LONGLONG,
     _callable=float2longlong_emulator, compilation_info=eci,
-    _nowrapper=True, pure_function=True)
+    _nowrapper=True, elidable_function=True)
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -448,10 +448,11 @@
     The functions key_eq() and key_hash() are used by the key comparison
     algorithm."""
 
-    def __init__(self, key_eq, key_hash):
+    def __init__(self, key_eq, key_hash, force_non_null=False):
         self._dict = {}
         self.key_eq = key_eq
         self.key_hash = key_hash
+        self.force_non_null = force_non_null
 
     def __getitem__(self, key):
         return self._dict[_r_dictkey(self, key)]
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -124,7 +124,7 @@
         return len(self._digits)
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def fromint(intval):
         # This function is marked as pure, so you must not call it and
         # then modify the result.
@@ -156,7 +156,7 @@
         return v
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def frombool(b):
         # This function is marked as pure, so you must not call it and
         # then modify the result.
@@ -179,7 +179,7 @@
             raise OverflowError
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def _fromfloat_finite(dval):
         sign = 1
         if dval < 0.0:
@@ -201,7 +201,7 @@
         return v
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     @specialize.argtype(0)
     def fromrarith_int(i):
         # This function is marked as pure, so you must not call it and
@@ -209,7 +209,7 @@
         return rbigint(*args_from_rarith_int(i))
 
     @staticmethod
-    @jit.purefunction
+    @jit.elidable
     def fromdecimalstr(s):
         # This function is marked as pure, so you must not call it and
         # then modify the result.
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -272,7 +272,9 @@
     if isinstance(TP.OF, lltype.Ptr) and TP.OF.TO._gckind == 'gc':
         # perform a write barrier that copies necessary flags from
         # source to dest
-        if not llop.gc_writebarrier_before_copy(lltype.Bool, source, dest):
+        if not llop.gc_writebarrier_before_copy(lltype.Bool, source, dest,
+                                                source_start, dest_start,
+                                                length):
             # if the write barrier is not supported, copy by hand
             for i in range(length):
                 dest[i + dest_start] = source[i + source_start]
diff --git a/pypy/rlib/rmd5.py b/pypy/rlib/rmd5.py
--- a/pypy/rlib/rmd5.py
+++ b/pypy/rlib/rmd5.py
@@ -51,7 +51,7 @@
     _rotateLeft = rffi.llexternal(
         "pypy__rotateLeft", [lltype.Unsigned, lltype.Signed], lltype.Unsigned,
         _callable=_rotateLeft_emulator, compilation_info=eci,
-        _nowrapper=True, pure_function=True)
+        _nowrapper=True, elidable_function=True)
     # we expect the function _rotateLeft to be actually inlined
 
 
diff --git a/pypy/rlib/ropenssl.py b/pypy/rlib/ropenssl.py
--- a/pypy/rlib/ropenssl.py
+++ b/pypy/rlib/ropenssl.py
@@ -151,7 +151,7 @@
                 [rffi.INT, rffi.INT, rffi.CCHARP, rffi.INT], lltype.Void))],
              lltype.Void)
 ssl_external('CRYPTO_set_id_callback',
-             [lltype.Ptr(lltype.FuncType([], rffi.INT))],
+             [lltype.Ptr(lltype.FuncType([], rffi.LONG))],
              lltype.Void)
              
 if HAVE_OPENSSL_RAND:
diff --git a/pypy/rlib/rsdl/RMix.py b/pypy/rlib/rsdl/RMix.py
--- a/pypy/rlib/rsdl/RMix.py
+++ b/pypy/rlib/rsdl/RMix.py
@@ -52,7 +52,8 @@
                                ChunkPtr)
 
 def LoadWAV(filename_ccharp):
-    return LoadWAV_RW(RSDL.RWFromFile(filename_ccharp, rffi.str2charp('rb')), 1)
+    with rffi.scoped_str2charp('rb') as mode:
+        return LoadWAV_RW(RSDL.RWFromFile(filename_ccharp, mode), 1)
 
 
 PlayChannelTimed    = external('Mix_PlayChannelTimed',
@@ -64,4 +65,4 @@
 
 """Returns zero if the channel is not playing. 
 Otherwise if you passed in -1, the number of channels playing is returned"""
-ChannelPlaying  = external('Mix_Playing', [ rffi.INT])
\ No newline at end of file
+ChannelPlaying  = external('Mix_Playing', [rffi.INT], rffi.INT)
diff --git a/pypy/rlib/test/test_debug.py b/pypy/rlib/test/test_debug.py
--- a/pypy/rlib/test/test_debug.py
+++ b/pypy/rlib/test/test_debug.py
@@ -1,11 +1,12 @@
 
 import py
-from pypy.rlib.debug import check_annotation, make_sure_not_resized
-from pypy.rlib.debug import debug_print, debug_start, debug_stop
-from pypy.rlib.debug import have_debug_prints, debug_offset, debug_flush
-from pypy.rlib.debug import check_nonneg, IntegerCanBeNegative
+from pypy.rlib.debug import (check_annotation, make_sure_not_resized,
+                             debug_print, debug_start, debug_stop,
+                             have_debug_prints, debug_offset, debug_flush,
+                             check_nonneg, IntegerCanBeNegative,
+                             mark_dict_non_null)
 from pypy.rlib import debug
-from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.test.test_llinterp import interpret, gengraph
 
 def test_check_annotation():
     class Error(Exception):
@@ -52,8 +53,17 @@
     py.test.raises(ListChangeUnallowed, interpret, f, [], 
                    list_comprehension_operations=True)
 
+def test_mark_dict_non_null():
+    def f():
+        d = {"ac": "bx"}
+        mark_dict_non_null(d)
+        return d
 
-class DebugTests:
+    t, typer, graph = gengraph(f, [])
+    assert sorted(graph.returnblock.inputargs[0].concretetype.TO.entries.TO.OF._flds.keys()) == ['key', 'value']
+
+
+class DebugTests(object):
 
     def test_debug_print_start_stop(self):
         def f(x):
diff --git a/pypy/rlib/test/test_jit.py b/pypy/rlib/test/test_jit.py
--- a/pypy/rlib/test/test_jit.py
+++ b/pypy/rlib/test/test_jit.py
@@ -1,6 +1,6 @@
 import py
 from pypy.conftest import option
-from pypy.rlib.jit import hint, we_are_jitted, JitDriver, purefunction_promote
+from pypy.rlib.jit import hint, we_are_jitted, JitDriver, elidable_promote
 from pypy.rlib.jit import JitHintError, oopspec
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
@@ -31,8 +31,8 @@
         res = self.interpret(f, [4])
         assert res == 5
 
-    def test_purefunction_promote(self):
-        @purefunction_promote()
+    def test_elidable_promote(self):
+        @elidable_promote()
         def g(func):
             return func + 1
         def f(x):
@@ -40,8 +40,8 @@
         res = self.interpret(f, [2])
         assert res == 5
 
-    def test_purefunction_promote_args(self):
-        @purefunction_promote(promote_args='0')
+    def test_elidable_promote_args(self):
+        @elidable_promote(promote_args='0')
         def g(func, x):
             return func + 1
         def f(x):
diff --git a/pypy/rpython/llinterp.py b/pypy/rpython/llinterp.py
--- a/pypy/rpython/llinterp.py
+++ b/pypy/rpython/llinterp.py
@@ -737,9 +737,12 @@
     def op_zero_gc_pointers_inside(self, obj):
         raise NotImplementedError("zero_gc_pointers_inside")
 
-    def op_gc_writebarrier_before_copy(self, source, dest):
+    def op_gc_writebarrier_before_copy(self, source, dest,
+                                       source_start, dest_start, length):
         if hasattr(self.heap, 'writebarrier_before_copy'):
-            return self.heap.writebarrier_before_copy(source, dest)
+            return self.heap.writebarrier_before_copy(source, dest,
+                                                      source_start, dest_start,
+                                                      length)
         else:
             return True
 
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -37,7 +37,9 @@
     if far_regions:
         import random
         pieces = far_regions._ll2ctypes_pieces
-        num = random.randrange(len(pieces))
+        num = random.randrange(len(pieces)+1)
+        if num == len(pieces):
+            return ctype()
         i1, stop = pieces[num]
         i2 = i1 + ((ctypes.sizeof(ctype) or 1) + 7) & ~7
         if i2 > stop:
diff --git a/pypy/rpython/lltypesystem/ll_str.py b/pypy/rpython/lltypesystem/ll_str.py
--- a/pypy/rpython/lltypesystem/ll_str.py
+++ b/pypy/rpython/lltypesystem/ll_str.py
@@ -1,12 +1,13 @@
 from pypy.rpython.lltypesystem.lltype import GcArray, Array, Char, malloc
 from pypy.rpython.annlowlevel import llstr
 from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
+from pypy.rlib import jit
 
 CHAR_ARRAY = GcArray(Char)
 
+ at jit.elidable
 def ll_int_str(repr, i):
     return ll_int2dec(i)
-ll_int_str._pure_function_ = True
 
 def ll_unsigned(i):
     if isinstance(i, r_longlong) or isinstance(i, r_ulonglong):
@@ -14,6 +15,7 @@
     else:
         return r_uint(i)
 
+ at jit.elidable
 def ll_int2dec(i):
     from pypy.rpython.lltypesystem.rstr import mallocstr
     temp = malloc(CHAR_ARRAY, 20)
@@ -44,13 +46,13 @@
         result.chars[j] = temp[len-j-1]
         j += 1
     return result
-ll_int2dec._pure_function_ = True
 
 hex_chars = malloc(Array(Char), 16, immortal=True)
 
 for i in range(16):
     hex_chars[i] = "%x"%i
 
+ at jit.elidable
 def ll_int2hex(i, addPrefix):
     from pypy.rpython.lltypesystem.rstr import mallocstr
     temp = malloc(CHAR_ARRAY, 20)
@@ -86,8 +88,8 @@
         result.chars[j] = temp[len-j-1]
         j += 1
     return result
-ll_int2hex._pure_function_ = True
 
+ at jit.elidable
 def ll_int2oct(i, addPrefix):
     from pypy.rpython.lltypesystem.rstr import mallocstr
     if i == 0:
@@ -123,9 +125,8 @@
         result.chars[j] = temp[len-j-1]
         j += 1
     return result
-ll_int2oct._pure_function_ = True
 
+ at jit.elidable
 def ll_float_str(repr, f):
     from pypy.rlib.rfloat import formatd
     return llstr(formatd(f, 'f', 6))
-ll_float_str._pure_function_ = True
diff --git a/pypy/rpython/lltypesystem/module/ll_math.py b/pypy/rpython/lltypesystem/module/ll_math.py
--- a/pypy/rpython/lltypesystem/module/ll_math.py
+++ b/pypy/rpython/lltypesystem/module/ll_math.py
@@ -58,7 +58,7 @@
 math_log10 = llexternal('log10', [rffi.DOUBLE], rffi.DOUBLE)
 math_copysign = llexternal(underscore + 'copysign',
                            [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE,
-                           pure_function=True)
+                           elidable_function=True)
 math_atan2 = llexternal('atan2', [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
 math_frexp = llexternal('frexp', [rffi.DOUBLE, rffi.INTP], rffi.DOUBLE)
 math_modf  = llexternal('modf',  [rffi.DOUBLE, rffi.DOUBLEP], rffi.DOUBLE)
@@ -67,11 +67,11 @@
 math_fmod  = llexternal('fmod',  [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
 math_hypot = llexternal(underscore + 'hypot',
                         [rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
-math_floor = llexternal('floor', [rffi.DOUBLE], rffi.DOUBLE, pure_function=True)
+math_floor = llexternal('floor', [rffi.DOUBLE], rffi.DOUBLE, elidable_function=True)
 
 math_sqrt = llexternal('sqrt', [rffi.DOUBLE], rffi.DOUBLE)
 
- at jit.purefunction
+ at jit.elidable
 def sqrt_nonneg(x):
     return math_sqrt(x)
 sqrt_nonneg.oopspec = "math.sqrt_nonneg(x)"
diff --git a/pypy/rpython/lltypesystem/opimpl.py b/pypy/rpython/lltypesystem/opimpl.py
--- a/pypy/rpython/lltypesystem/opimpl.py
+++ b/pypy/rpython/lltypesystem/opimpl.py
@@ -473,12 +473,16 @@
     checkadr(addr2)
     return addr1 - addr2
 
-def op_gc_writebarrier_before_copy(source, dest):
+def op_gc_writebarrier_before_copy(source, dest,
+                                   source_start, dest_start, length):
     A = lltype.typeOf(source)
     assert A == lltype.typeOf(dest)
     assert isinstance(A.TO, lltype.GcArray)
     assert isinstance(A.TO.OF, lltype.Ptr)
     assert A.TO.OF.TO._gckind == 'gc'
+    assert type(source_start) is int
+    assert type(dest_start) is int
+    assert type(length) is int
     return True
 
 def op_getfield(p, name):
diff --git a/pypy/rpython/lltypesystem/rdict.py b/pypy/rpython/lltypesystem/rdict.py
--- a/pypy/rpython/lltypesystem/rdict.py
+++ b/pypy/rpython/lltypesystem/rdict.py
@@ -9,6 +9,7 @@
 from pypy.rpython import robject
 from pypy.rlib import objectmodel, jit
 from pypy.rpython import rmodel
+from pypy.rpython.error import TyperError
 
 HIGHEST_BIT = intmask(1 << (LONG_BIT - 1))
 MASK = intmask(HIGHEST_BIT - 1)
@@ -42,7 +43,7 @@
 class DictRepr(AbstractDictRepr):
 
     def __init__(self, rtyper, key_repr, value_repr, dictkey, dictvalue,
-                 custom_eq_hash=None):
+                 custom_eq_hash=None, force_non_null=False):
         self.rtyper = rtyper
         self.DICT = lltype.GcForwardReference()
         self.lowleveltype = lltype.Ptr(self.DICT)
@@ -61,6 +62,7 @@
         self.dictvalue = dictvalue
         self.dict_cache = {}
         self._custom_eq_hash_repr = custom_eq_hash
+        self.force_non_null = force_non_null
         # setup() needs to be called to finish this initialization
 
     def _externalvsinternal(self, rtyper, item_repr):
@@ -97,6 +99,13 @@
             s_value = self.dictvalue.s_value
             nullkeymarker = not self.key_repr.can_ll_be_null(s_key)
             nullvaluemarker = not self.value_repr.can_ll_be_null(s_value)
+            if self.force_non_null:
+                if not nullkeymarker:
+                    rmodel.warning("%s can be null, but forcing non-null in dict key" % s_key)
+                    nullkeymarker = True
+                if not nullvaluemarker:
+                    rmodel.warning("%s can be null, but forcing non-null in dict value" % s_value)
+                    nullvaluemarker = True
             dummykeyobj = self.key_repr.get_ll_dummyval_obj(self.rtyper,
                                                             s_key)
             dummyvalueobj = self.value_repr.get_ll_dummyval_obj(self.rtyper,
@@ -640,12 +649,15 @@
     pass
 
 
-def rtype_r_dict(hop):
+def rtype_r_dict(hop, i_force_non_null=None):
     r_dict = hop.r_result
     if not r_dict.custom_eq_hash:
         raise TyperError("r_dict() call does not return an r_dict instance")
-    v_eqfn, v_hashfn = hop.inputargs(r_dict.r_rdict_eqfn,
-                                     r_dict.r_rdict_hashfn)
+    v_eqfn = hop.inputarg(r_dict.r_rdict_eqfn, arg=0)
+    v_hashfn = hop.inputarg(r_dict.r_rdict_hashfn, arg=1)
+    if i_force_non_null is not None:
+        assert i_force_non_null == 2
+        hop.inputarg(lltype.Void, arg=2)
     cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
     hop.exception_cannot_occur()
     v_result = hop.gendirectcall(ll_newdict, cDICT)
@@ -833,10 +845,16 @@
 POPITEMINDEX = lltype.Struct('PopItemIndex', ('nextindex', lltype.Signed))
 global_popitem_index = lltype.malloc(POPITEMINDEX, zero=True, immortal=True)
 
-def ll_popitem(ELEM, dic):
+def _ll_getnextitem(dic):
     entries = dic.entries
+    ENTRY = lltype.typeOf(entries).TO.OF
     dmask = len(entries) - 1
-    base = global_popitem_index.nextindex
+    if hasattr(ENTRY, 'f_hash'):
+        if entries.valid(0):
+            return 0
+        base = entries[0].f_hash
+    else:
+        base = global_popitem_index.nextindex
     counter = 0
     while counter <= dmask:
         i = (base + counter) & dmask
@@ -845,8 +863,16 @@
             break
     else:
         raise KeyError
-    global_popitem_index.nextindex += counter
-    entry = entries[i]
+    if hasattr(ENTRY, 'f_hash'):
+        entries[0].f_hash = base + counter
+    else:
+        global_popitem_index.nextindex = base + counter
+    return i
+
+ at jit.dont_look_inside
+def ll_popitem(ELEM, dic):
+    i = _ll_getnextitem(dic)
+    entry = dic.entries[i]
     r = lltype.malloc(ELEM.TO)
     r.item0 = recast(ELEM.TO.item0, entry.key)
     r.item1 = recast(ELEM.TO.item1, entry.value)
diff --git a/pypy/rpython/lltypesystem/rffi.py b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -55,7 +55,7 @@
                compilation_info=ExternalCompilationInfo(),
                sandboxsafe=False, threadsafe='auto',
                _nowrapper=False, calling_conv='c',
-               oo_primitive=None, pure_function=False,
+               oo_primitive=None, elidable_function=False,
                macro=None):
     """Build an external function that will invoke the C function 'name'
     with the given 'args' types and 'result' type.
@@ -87,8 +87,8 @@
                 name, macro, ext_type, compilation_info)
         else:
             _callable = ll2ctypes.LL2CtypesCallable(ext_type, calling_conv)
-    if pure_function:
-        _callable._pure_function_ = True
+    if elidable_function:
+        _callable._elidable_function_ = True
     kwds = {}
     if oo_primitive:
         kwds['oo_primitive'] = oo_primitive
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -4,7 +4,7 @@
 from pypy.rlib.objectmodel import malloc_zero_filled, we_are_translated
 from pypy.rlib.objectmodel import _hash_string, enforceargs
 from pypy.rlib.debug import ll_assert
-from pypy.rlib.jit import purefunction, we_are_jitted, dont_look_inside
+from pypy.rlib.jit import elidable, we_are_jitted, dont_look_inside
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rpython.robject import PyObjRepr, pyobj_repr
 from pypy.rpython.rmodel import inputconst, IntegerRepr
@@ -144,7 +144,7 @@
         self.ll = LLHelpers
         self.malloc = mallocunicode
 
-    @purefunction
+    @elidable
     def ll_str(self, s):
         # XXX crazy that this is here, but I don't want to break
         #     rmodel logic
@@ -159,7 +159,7 @@
             result.chars[i] = cast_primitive(Char, c)
         return result
 
-    @purefunction
+    @elidable
     def ll_encode_latin1(self, s):
         length = len(s.chars)
         result = mallocstr(length)
@@ -258,7 +258,7 @@
 
 
 class LLHelpers(AbstractLLHelpers):
-    @purefunction
+    @elidable
     def ll_str_mul(s, times):
         if times < 0:
             times = 0
@@ -280,7 +280,7 @@
             i += j
         return newstr
 
-    @purefunction
+    @elidable
     def ll_char_mul(ch, times):
         if typeOf(ch) is Char:
             malloc = mallocstr
@@ -325,8 +325,7 @@
         return s
     ll_str2unicode.oopspec = 'str.str2unicode(str)'
 
-    # it's pure but it does not look like it
-    @purefunction
+    @elidable
     def ll_strhash(s):
         # unlike CPython, there is no reason to avoid to return -1
         # but our malloc initializes the memory to zero, so we use zero as the
@@ -342,7 +341,7 @@
     def ll_strfasthash(s):
         return s.hash     # assumes that the hash is already computed
 
-    @purefunction
+    @elidable
     def ll_strconcat(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -352,7 +351,7 @@
         return newstr
     ll_strconcat.oopspec = 'stroruni.concat(s1, s2)'
 
-    @purefunction
+    @elidable
     def ll_strip(s, ch, left, right):
         s_len = len(s.chars)
         if s_len == 0:
@@ -370,7 +369,7 @@
         s.copy_contents(s, result, lpos, 0, r_len)
         return result
 
-    @purefunction
+    @elidable
     def ll_upper(s):
         s_chars = s.chars
         s_len = len(s_chars)
@@ -387,7 +386,7 @@
             i += 1
         return result
 
-    @purefunction
+    @elidable
     def ll_lower(s):
         s_chars = s.chars
         s_len = len(s_chars)
@@ -428,7 +427,7 @@
             i += 1
         return result
 
-    @purefunction
+    @elidable
     def ll_strcmp(s1, s2):
         if not s1 and not s2:
             return True
@@ -451,7 +450,7 @@
             i += 1
         return len1 - len2
 
-    @purefunction
+    @elidable
     def ll_streq(s1, s2):
         if s1 == s2:       # also if both are NULLs
             return True
@@ -471,7 +470,7 @@
         return True
     ll_streq.oopspec = 'stroruni.equal(s1, s2)'
 
-    @purefunction
+    @elidable
     def ll_startswith(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -487,7 +486,7 @@
 
         return True
 
-    @purefunction
+    @elidable
     def ll_endswith(s1, s2):
         len1 = len(s1.chars)
         len2 = len(s2.chars)
@@ -504,7 +503,7 @@
 
         return True
 
-    @purefunction
+    @elidable
     def ll_find_char(s, ch, start, end):
         i = start
         if end > len(s.chars):
@@ -516,7 +515,7 @@
         return -1
     ll_find_char._annenforceargs_ = [None, None, int, int]
 
-    @purefunction
+    @elidable
     def ll_rfind_char(s, ch, start, end):
         if end > len(s.chars):
             end = len(s.chars)
@@ -527,7 +526,7 @@
                 return i
         return -1
 
-    @purefunction
+    @elidable
     def ll_count_char(s, ch, start, end):
         count = 0
         i = start
@@ -595,7 +594,7 @@
             res = 0
         return res
 
-    @purefunction
+    @elidable
     def ll_search(s1, s2, start, end, mode):
         count = 0
         n = end - start
@@ -718,7 +717,7 @@
             i += 1
         return result
 
-    @purefunction
+    @elidable
     def _ll_stringslice(s1, start, stop):
         lgt = stop - start
         assert start >= 0
@@ -816,7 +815,7 @@
         item.copy_contents(s, item, j, 0, i - j)
         return res
 
-    @purefunction
+    @elidable
     def ll_replace_chr_chr(s, c1, c2):
         length = len(s.chars)
         newstr = s.malloc(length)
@@ -831,7 +830,7 @@
             j += 1
         return newstr
 
-    @purefunction
+    @elidable
     def ll_contains(s, c):
         chars = s.chars
         strlen = len(chars)
@@ -842,7 +841,7 @@
             i += 1
         return False
 
-    @purefunction
+    @elidable
     def ll_int(s, base):
         if not 2 <= base <= 36:
             raise ValueError
diff --git a/pypy/rpython/memory/gc/generation.py b/pypy/rpython/memory/gc/generation.py
--- a/pypy/rpython/memory/gc/generation.py
+++ b/pypy/rpython/memory/gc/generation.py
@@ -517,7 +517,8 @@
             objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
             self.last_generation_root_objects.append(addr_struct)
 
-    def writebarrier_before_copy(self, source_addr, dest_addr):
+    def writebarrier_before_copy(self, source_addr, dest_addr,
+                                 source_start, dest_start, length):
         """ This has the same effect as calling writebarrier over
         each element in dest copied from source, except it might reset
         one of the following flags a bit too eagerly, which means we'll have
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -75,10 +75,16 @@
 
 first_gcflag = 1 << (LONG_BIT//2)
 
-# The following flag is never set on young objects.  It is initially set
-# on all prebuilt and old objects, and gets cleared by the write_barrier()
-# when we write in them a pointer to a young object.
-GCFLAG_NO_YOUNG_PTRS = first_gcflag << 0
+# The following flag is set on objects if we need to do something to
+# track the young pointers that it might contain.  The flag is not set
+# on young objects (unless they are large arrays, see below), and we
+# simply assume that any young object can point to any other young object.
+# For old and prebuilt objects, the flag is usually set, and is cleared
+# when we write a young pointer to it.  For large arrays with
+# GCFLAG_HAS_CARDS, we rely on card marking to track where the
+# young pointers are; the flag GCFLAG_TRACK_YOUNG_PTRS is set in this
+# case too, to speed up the write barrier.
+GCFLAG_TRACK_YOUNG_PTRS = first_gcflag << 0
 
 # The following flag is set on some prebuilt objects.  The flag is set
 # unless the object is already listed in 'prebuilt_root_objects'.
@@ -246,17 +252,23 @@
         self.ac = ArenaCollectionClass(arena_size, page_size,
                                        small_request_threshold)
         #
-        # Used by minor collection: a list of non-young objects that
+        # Used by minor collection: a list of (mostly non-young) objects that
         # (may) contain a pointer to a young object.  Populated by
-        # the write barrier.
-        self.old_objects_pointing_to_young = self.AddressStack()
+        # the write barrier: when we clear GCFLAG_TRACK_YOUNG_PTRS, we
+        # add it to this list.
+        class Cls(self.AddressStack):
+            def append(self2, addr):
+                assert addr not in self2.tolist()
+                self.AddressStack.append(self2, addr)
+        self.objects_pointing_to_young = self.AddressStack()
         #
-        # Similar to 'old_objects_pointing_to_young', but lists objects
+        # Similar to 'objects_pointing_to_young', but lists objects
         # that have the GCFLAG_CARDS_SET bit.  For large arrays.  Note
         # that it is possible for an object to be listed both in here
-        # and in 'old_objects_pointing_to_young', in which case we
+        # and in 'objects_pointing_to_young', in which case we
         # should just clear the cards and trace it fully, as usual.
-        self.old_objects_with_cards_set = self.AddressStack()
+        # Note also that young array objects may be added to this list.
+        self.objects_with_cards_set = self.AddressStack()
         #
         # A list of all prebuilt GC objects that contain pointers to the heap
         self.prebuilt_root_objects = self.AddressStack()
@@ -625,7 +637,7 @@
             # if 'can_make_young'.  The interesting case of 'can_make_young'
             # is for large objects, bigger than the 'large_objects' threshold,
             # which are raw-malloced but still young.
-            extra_flags = GCFLAG_NO_YOUNG_PTRS
+            extra_flags = GCFLAG_TRACK_YOUNG_PTRS
             #
         else:
             # No, so proceed to allocate it externally with raw_malloc().
@@ -643,7 +655,7 @@
                 # Reserve N extra words containing card bits before the object.
                 extra_words = self.card_marking_words_for_length(length)
                 cardheadersize = WORD * extra_words
-                extra_flags = GCFLAG_HAS_CARDS
+                extra_flags = GCFLAG_HAS_CARDS | GCFLAG_TRACK_YOUNG_PTRS
                 # note that if 'can_make_young', then card marking will only
                 # be used later, after (and if) the object becomes old
             #
@@ -686,7 +698,7 @@
                 self.young_rawmalloced_objects.add(result + size_gc_header)
             else:
                 self.old_rawmalloced_objects.append(result + size_gc_header)
-                extra_flags |= GCFLAG_NO_YOUNG_PTRS
+                extra_flags |= GCFLAG_TRACK_YOUNG_PTRS
         #
         # Common code to fill the header and length of the object.
         self.init_gc_object(result, typeid, extra_flags)
@@ -777,7 +789,7 @@
     def init_gc_object_immortal(self, addr, typeid16, flags=0):
         # For prebuilt GC objects, the flags must contain
         # GCFLAG_NO_xxx_PTRS, at least initially.
-        flags |= GCFLAG_NO_HEAP_PTRS | GCFLAG_NO_YOUNG_PTRS
+        flags |= GCFLAG_NO_HEAP_PTRS | GCFLAG_TRACK_YOUNG_PTRS
         self.init_gc_object(addr, typeid16, flags)
 
     def is_in_nursery(self, addr):
@@ -870,8 +882,8 @@
         ll_assert(not self.is_in_nursery(obj),
                   "object in nursery after collection")
         # similarily, all objects should have this flag:
-        ll_assert(self.header(obj).tid & GCFLAG_NO_YOUNG_PTRS,
-                  "missing GCFLAG_NO_YOUNG_PTRS")
+        ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS,
+                  "missing GCFLAG_TRACK_YOUNG_PTRS")
         # the GCFLAG_VISITED should not be set between collections
         ll_assert(self.header(obj).tid & GCFLAG_VISITED == 0,
                   "unexpected GCFLAG_VISITED")
@@ -910,7 +922,7 @@
     # for the JIT: a minimal description of the write_barrier() method
     # (the JIT assumes it is of the shape
     #  "if addr_struct.int0 & JIT_WB_IF_FLAG: remember_young_pointer()")
-    JIT_WB_IF_FLAG = GCFLAG_NO_YOUNG_PTRS
+    JIT_WB_IF_FLAG = GCFLAG_TRACK_YOUNG_PTRS
 
     @classmethod
     def JIT_max_size_of_young_obj(cls):
@@ -921,11 +933,11 @@
         return cls.minimal_size_in_nursery
 
     def write_barrier(self, newvalue, addr_struct):
-        if self.header(addr_struct).tid & GCFLAG_NO_YOUNG_PTRS:
+        if self.header(addr_struct).tid & GCFLAG_TRACK_YOUNG_PTRS:
             self.remember_young_pointer(addr_struct, newvalue)
 
     def write_barrier_from_array(self, newvalue, addr_array, index):
-        if self.header(addr_array).tid & GCFLAG_NO_YOUNG_PTRS:
+        if self.header(addr_array).tid & GCFLAG_TRACK_YOUNG_PTRS:
             if self.card_page_indices > 0:     # <- constant-folded
                 self.remember_young_pointer_from_array2(addr_array, index)
             else:
@@ -943,20 +955,23 @@
         def remember_young_pointer(addr_struct, newvalue):
             # 'addr_struct' is the address of the object in which we write.
             # 'newvalue' is the address that we are going to write in there.
+            # We know that 'addr_struct' has GCFLAG_TRACK_YOUNG_PTRS so far.
+            #
             if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
-                ll_assert(self.debug_is_old_object(addr_struct),
-                          "young object with GCFLAG_NO_YOUNG_PTRS")
+                ll_assert(self.debug_is_old_object(addr_struct) or
+                          self.header(addr_struct).tid & GCFLAG_HAS_CARDS != 0,
+                      "young object with GCFLAG_TRACK_YOUNG_PTRS and no cards")
             #
-            # If it seems that what we are writing is a pointer to the nursery
+            # If it seems that what we are writing is a pointer to a young obj
             # (as checked with appears_to_be_young()), then we need
-            # to remove the flag GCFLAG_NO_YOUNG_PTRS and add the old object
-            # to the list 'old_objects_pointing_to_young'.  We know that
+            # to remove the flag GCFLAG_TRACK_YOUNG_PTRS and add the object
+            # to the list 'objects_pointing_to_young'.  We know that
             # 'addr_struct' cannot be in the nursery, because nursery objects
-            # never have the flag GCFLAG_NO_YOUNG_PTRS to start with.
+            # never have the flag GCFLAG_TRACK_YOUNG_PTRS to start with.
             objhdr = self.header(addr_struct)
             if self.appears_to_be_young(newvalue):
-                self.old_objects_pointing_to_young.append(addr_struct)
-                objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
+                self.objects_pointing_to_young.append(addr_struct)
+                objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
             #
             # Second part: if 'addr_struct' is actually a prebuilt GC
             # object and it's the first time we see a write to it, we
@@ -980,16 +995,18 @@
             # 'addr_array' is the address of the object in which we write,
             # which must have an array part;  'index' is the index of the
             # item that is (or contains) the pointer that we write.
-            if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
-                ll_assert(self.debug_is_old_object(addr_array),
-                          "young array with GCFLAG_NO_YOUNG_PTRS")
+            # We know that 'addr_array' has GCFLAG_TRACK_YOUNG_PTRS so far.
+            #
             objhdr = self.header(addr_array)
             if objhdr.tid & GCFLAG_HAS_CARDS == 0:
                 #
+                if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
+                    ll_assert(self.debug_is_old_object(addr_array),
+                        "young array with no card but GCFLAG_TRACK_YOUNG_PTRS")
+                #
                 # no cards, use default logic.  Mostly copied from above.
-                self.old_objects_pointing_to_young.append(addr_array)
-                objhdr = self.header(addr_array)
-                objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
+                self.objects_pointing_to_young.append(addr_array)
+                objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
                 if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
                     objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
                     self.prebuilt_root_objects.append(addr_array)
@@ -1002,9 +1019,7 @@
             bitmask = 1 << (bitindex & 7)
             #
             # If the bit is already set, leave now.
-            size_gc_header = self.gcheaderbuilder.size_gc_header
-            addr_byte = addr_array - size_gc_header
-            addr_byte = llarena.getfakearenaaddress(addr_byte) + (~byteindex)
+            addr_byte = self.get_card(addr_array, byteindex)
             byte = ord(addr_byte.char[0])
             if byte & bitmask:
                 return
@@ -1016,7 +1031,7 @@
             addr_byte.char[0] = chr(byte | bitmask)
             #
             if objhdr.tid & GCFLAG_CARDS_SET == 0:
-                self.old_objects_with_cards_set.append(addr_array)
+                self.objects_with_cards_set.append(addr_array)
                 objhdr.tid |= GCFLAG_CARDS_SET
 
         remember_young_pointer_from_array2._dont_inline_ = True
@@ -1026,9 +1041,6 @@
 
         # xxx trying it out for the JIT: a 3-arguments version of the above
         def remember_young_pointer_from_array3(addr_array, index, newvalue):
-            if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
-                ll_assert(self.debug_is_old_object(addr_array),
-                          "young array with GCFLAG_NO_YOUNG_PTRS")
             objhdr = self.header(addr_array)
             #
             # a single check for the common case of neither GCFLAG_HAS_CARDS
@@ -1044,8 +1056,8 @@
             else:
                 # case with cards.
                 #
-                # If the newly written address does not actually point to the
-                # nursery, leave now.
+                # If the newly written address does not actually point to a
+                # young object, leave now.
                 if not self.appears_to_be_young(newvalue):
                     return
                 #
@@ -1056,46 +1068,53 @@
                 bitmask = 1 << (bitindex & 7)
                 #
                 # If the bit is already set, leave now.
-                size_gc_header = self.gcheaderbuilder.size_gc_header
-                addr_byte = addr_array - size_gc_header
-                addr_byte = llarena.getfakearenaaddress(addr_byte) + \
-                            (~byteindex)
+                addr_byte = self.get_card(addr_array, byteindex)
                 byte = ord(addr_byte.char[0])
                 if byte & bitmask:
                     return
                 addr_byte.char[0] = chr(byte | bitmask)
                 #
                 if objhdr.tid & GCFLAG_CARDS_SET == 0:
-                    self.old_objects_with_cards_set.append(addr_array)
+                    self.objects_with_cards_set.append(addr_array)
                     objhdr.tid |= GCFLAG_CARDS_SET
                 return
             #
             # Logic for the no-cards case, put here to minimize the number
             # of checks done at the start of the function
+            if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
+                ll_assert(self.debug_is_old_object(addr_array),
+                        "young array with no card but GCFLAG_TRACK_YOUNG_PTRS")
+            #
             if self.appears_to_be_young(newvalue):
-                self.old_objects_pointing_to_young.append(addr_array)
-                objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
+                self.objects_pointing_to_young.append(addr_array)
+                objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
 
         remember_young_pointer_from_array3._dont_inline_ = True
         assert self.card_page_indices > 0
         self.remember_young_pointer_from_array3 = (
             remember_young_pointer_from_array3)
 
+    def get_card(self, obj, byteindex):
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        addr_byte = obj - size_gc_header
+        return llarena.getfakearenaaddress(addr_byte) + (~byteindex)
+
 
     def assume_young_pointers(self, addr_struct):
         """Called occasionally by the JIT to mean ``assume that 'addr_struct'
         may now contain young pointers.''
         """
         objhdr = self.header(addr_struct)
-        if objhdr.tid & GCFLAG_NO_YOUNG_PTRS:
-            self.old_objects_pointing_to_young.append(addr_struct)
-            objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
+        if objhdr.tid & GCFLAG_TRACK_YOUNG_PTRS:
+            self.objects_pointing_to_young.append(addr_struct)
+            objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
             #
             if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
                 objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
                 self.prebuilt_root_objects.append(addr_struct)
 
-    def writebarrier_before_copy(self, source_addr, dest_addr):
+    def writebarrier_before_copy(self, source_addr, dest_addr,
+                                 source_start, dest_start, length):
         """ This has the same effect as calling writebarrier over
         each element in dest copied from source, except it might reset
         one of the following flags a bit too eagerly, which means we'll have
@@ -1103,15 +1122,36 @@
         """
         source_hdr = self.header(source_addr)
         dest_hdr = self.header(dest_addr)
-        if dest_hdr.tid & GCFLAG_NO_YOUNG_PTRS == 0:
+        if dest_hdr.tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
             return True
         # ^^^ a fast path of write-barrier
         #
-        if (source_hdr.tid & GCFLAG_NO_YOUNG_PTRS == 0 or
-            source_hdr.tid & GCFLAG_CARDS_SET != 0):
+        if source_hdr.tid & GCFLAG_HAS_CARDS != 0:
+            #
+            if source_hdr.tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
+                # The source object may have random young pointers.
+                # Return False to mean "do it manually in ll_arraycopy".
+                return False
+            #
+            if source_hdr.tid & GCFLAG_CARDS_SET == 0:
+                # The source object has no young pointers at all.  Done.
+                return True
+            #
+            if dest_hdr.tid & GCFLAG_HAS_CARDS == 0:
+                # The dest object doesn't have cards.  Do it manually.
+                return False
+            #
+            if source_start != 0 or dest_start != 0:
+                # Misaligned.  Do it manually.
+                return False
+            #
+            self.manually_copy_card_bits(source_addr, dest_addr, length)
+            return True
+        #
+        if source_hdr.tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
             # there might be in source a pointer to a young object
-            self.old_objects_pointing_to_young.append(dest_addr)
-            dest_hdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
+            self.objects_pointing_to_young.append(dest_addr)
+            dest_hdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
         #
         if dest_hdr.tid & GCFLAG_NO_HEAP_PTRS:
             if source_hdr.tid & GCFLAG_NO_HEAP_PTRS == 0:
@@ -1119,6 +1159,22 @@
                 self.prebuilt_root_objects.append(dest_addr)
         return True
 
+    def manually_copy_card_bits(self, source_addr, dest_addr, length):
+        # manually copy the individual card marks from source to dest
+        bytes = self.card_marking_bytes_for_length(length)
+        #
+        i = 0
+        while i < bytes:
+            addr_srcbyte = self.get_card(source_addr, i)
+            addr_dstbyte = self.get_card(dest_addr, i)
+            byte = ord(addr_srcbyte.char[0])
+            addr_dstbyte.char[0] = chr(ord(addr_dstbyte.char[0]) | byte)
+            i += 1
+        #
+        dest_hdr = self.header(dest_addr)
+        if dest_hdr.tid & GCFLAG_CARDS_SET == 0:
+            self.objects_with_cards_set.append(dest_addr)
+            dest_hdr.tid |= GCFLAG_CARDS_SET
 
     # ----------
     # Nursery collection
@@ -1135,20 +1191,28 @@
         # Note that during this step, we ignore references to further
         # young objects; only objects directly referenced by roots
         # are copied out or flagged.  They are also added to the list
-        # 'old_objects_pointing_to_young'.
+        # 'objects_pointing_to_young'.
         self.collect_roots_in_nursery()
         #
-        # If we are using card marking, do a partial trace of the arrays
-        # that are flagged with GCFLAG_CARDS_SET.
-        if self.card_page_indices > 0:
-            self.collect_cardrefs_to_nursery()
-        #
-        # Now trace objects from 'old_objects_pointing_to_young'.
-        # All nursery objects they reference are copied out of the
-        # nursery, and again added to 'old_objects_pointing_to_young'.
-        # All young raw-malloced object found is flagged GCFLAG_VISITED.
-        # We proceed until 'old_objects_pointing_to_young' is empty.
-        self.collect_oldrefs_to_nursery()
+        while True:
+            # If we are using card marking, do a partial trace of the arrays
+            # that are flagged with GCFLAG_CARDS_SET.
+            if self.card_page_indices > 0:
+                self.collect_cardrefs_to_nursery()
+            #
+            # Now trace objects from 'objects_pointing_to_young'.
+            # All nursery objects they reference are copied out of the
+            # nursery, and again added to 'objects_pointing_to_young'.
+            # All young raw-malloced object found is flagged GCFLAG_VISITED.
+            # We proceed until 'objects_pointing_to_young' is empty.
+            self.collect_oldrefs_to_nursery()
+            #
+            # We have to loop back if collect_oldrefs_to_nursery caused
+            # new objects to show up in objects_with_cards_set
+            if self.card_page_indices > 0:
+                if self.objects_with_cards_set.non_empty():
+                    continue
+            break
         #
         # Now all live nursery objects should be out.  Update the young
         # weakrefs' targets.
@@ -1181,7 +1245,7 @@
         # we don't need to trace prebuilt GcStructs during a minor collect:
         # if a prebuilt GcStruct contains a pointer to a young object,
         # then the write_barrier must have ensured that the prebuilt
-        # GcStruct is in the list self.old_objects_pointing_to_young.
+        # GcStruct is in the list self.objects_pointing_to_young.
         self.root_walker.walk_roots(
             MiniMarkGC._trace_drag_out1,  # stack roots
             MiniMarkGC._trace_drag_out1,  # static in prebuilt non-gc
@@ -1189,7 +1253,7 @@
 
     def collect_cardrefs_to_nursery(self):
         size_gc_header = self.gcheaderbuilder.size_gc_header
-        oldlist = self.old_objects_with_cards_set
+        oldlist = self.objects_with_cards_set
         while oldlist.non_empty():
             obj = oldlist.pop()
             #
@@ -1205,11 +1269,11 @@
             bytes = self.card_marking_bytes_for_length(length)
             p = llarena.getfakearenaaddress(obj - size_gc_header)
             #
-            # If the object doesn't have GCFLAG_NO_YOUNG_PTRS, then it
-            # means that it is in 'old_objects_pointing_to_young' and
+            # If the object doesn't have GCFLAG_TRACK_YOUNG_PTRS, then it
+            # means that it is in 'objects_pointing_to_young' and
             # will be fully traced by collect_oldrefs_to_nursery() just
             # afterwards.
-            if self.header(obj).tid & GCFLAG_NO_YOUNG_PTRS == 0:
+            if self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
                 #
                 # In that case, we just have to reset all card bits.
                 while bytes > 0:
@@ -1245,19 +1309,30 @@
 
 
     def collect_oldrefs_to_nursery(self):
-        # Follow the old_objects_pointing_to_young list and move the
+        # Follow the objects_pointing_to_young list and move the
         # young objects they point to out of the nursery.
-        oldlist = self.old_objects_pointing_to_young
+        oldlist = self.objects_pointing_to_young
         while oldlist.non_empty():
             obj = oldlist.pop()
             #
-            # Add the flag GCFLAG_NO_YOUNG_PTRS.  All live objects should have
-            # this flag set after a nursery collection.
-            self.header(obj).tid |= GCFLAG_NO_YOUNG_PTRS
+            # Check (somehow) that the flags are correct: we must not have
+            # GCFLAG_TRACK_YOUNG_PTRS so far.  But in a rare case, it's
+            # possible that the same obj is appended twice to the list
+            # (see _trace_drag_out, GCFLAG_VISITED case).  Filter it out
+            # here.
+            if self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS != 0:
+                ll_assert(self.header(obj).tid & GCFLAG_VISITED != 0,
+                          "objects_pointing_to_young contains obj with "
+                          "GCFLAG_TRACK_YOUNG_PTRS and not GCFLAG_VISITED")
+                continue
+            #
+            # Add the flag GCFLAG_TRACK_YOUNG_PTRS.  All live objects should
+            # have this flag set after a nursery collection.
+            self.header(obj).tid |= GCFLAG_TRACK_YOUNG_PTRS
             #
             # Trace the 'obj' to replace pointers to nursery with pointers
             # outside the nursery, possibly forcing nursery objects out
-            # and adding them to 'old_objects_pointing_to_young' as well.
+            # and adding them to 'objects_pointing_to_young' as well.
             self.trace_and_drag_out_of_nursery(obj)
 
     def trace_and_drag_out_of_nursery(self, obj):
@@ -1296,7 +1371,19 @@
                 # 'obj' points to a young, raw-malloced object
                 if (self.header(obj).tid & GCFLAG_VISITED) == 0:
                     self.header(obj).tid |= GCFLAG_VISITED
-                    self.old_objects_pointing_to_young.append(obj)
+                    #
+                    # we just made 'obj' old, so we may need to add it
+                    # in the correct list:
+                    if self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
+                        # common case: GCFLAG_TRACK_YOUNG_PTRS is not set, so
+                        # the object may contain young pointers anywhere
+                        self.objects_pointing_to_young.append(obj)
+                    else:
+                        # large array case: the object contains card marks
+                        # that tell us where young pointers are, and it
+                        # is already in objects_with_cards_set.
+                        ll_assert(self.header(obj).tid & GCFLAG_HAS_CARDS != 0,
+                                  "neither YOUNG_PTRS nor HAS_CARDS??")
             return
         #
         # If 'obj' was already forwarded, change it to its forwarding address.
@@ -1343,11 +1430,11 @@
         # Change the original pointer to this object.
         root.address[0] = newobj
         #
-        # Add the newobj to the list 'old_objects_pointing_to_young',
+        # Add the newobj to the list 'objects_pointing_to_young',
         # because it can contain further pointers to other young objects.
         # We will fix such references to point to the copy of the young
-        # objects when we walk 'old_objects_pointing_to_young'.
-        self.old_objects_pointing_to_young.append(newobj)
+        # objects when we walk 'objects_pointing_to_young'.
+        self.objects_pointing_to_young.append(newobj)
 
 
     def _malloc_out_of_nursery(self, totalsize):
diff --git a/pypy/rpython/memory/gc/test/test_direct.py b/pypy/rpython/memory/gc/test/test_direct.py
--- a/pypy/rpython/memory/gc/test/test_direct.py
+++ b/pypy/rpython/memory/gc/test/test_direct.py
@@ -522,5 +522,78 @@
             self.stackroots.pop()
     test_card_marker.GC_PARAMS = {"card_page_indices": 4}
 
+    def test_writebarrier_before_copy(self):
+        from pypy.rpython.memory.gc import minimark
+        largeobj_size =  self.gc.nonlarge_max + 1
+        p_src = self.malloc(VAR, largeobj_size)
+        p_dst = self.malloc(VAR, largeobj_size)
+        # make them old
+        self.stackroots.append(p_src)
+        self.stackroots.append(p_dst)
+        self.gc.collect()
+        p_dst = self.stackroots.pop()
+        p_src = self.stackroots.pop()
+        #
+        addr_src = llmemory.cast_ptr_to_adr(p_src)
+        addr_dst = llmemory.cast_ptr_to_adr(p_dst)
+        hdr_src = self.gc.header(addr_src)
+        hdr_dst = self.gc.header(addr_dst)
+        #
+        assert hdr_src.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        #
+        res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
+        assert res
+        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS
+        #
+        hdr_src.tid &= ~minimark.GCFLAG_TRACK_YOUNG_PTRS  # pretend we have young ptrs
+        res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
+        assert res # we optimized it
+        assert hdr_dst.tid & minimark.GCFLAG_TRACK_YOUNG_PTRS == 0 # and we copied the flag
+        #
+        hdr_src.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
+        hdr_dst.tid |= minimark.GCFLAG_TRACK_YOUNG_PTRS
+        hdr_src.tid |= minimark.GCFLAG_HAS_CARDS
+        hdr_src.tid |= minimark.GCFLAG_CARDS_SET
+        # hdr_dst.tid does not have minimark.GCFLAG_HAS_CARDS
+        res = self.gc.writebarrier_before_copy(addr_src, addr_dst, 0, 0, 10)
+        assert not res # there might be young ptrs, let ll_arraycopy to find them
+
+    def test_writebarrier_before_copy_preserving_cards(self):
+        from pypy.rpython.lltypesystem import llarena
+        from pypy.rpython.memory.gc import minimark
+        tid = self.get_type_id(VAR)
+        largeobj_size =  self.gc.nonlarge_max + 1
+        addr_src = self.gc.external_malloc(tid, largeobj_size)
+        addr_dst = self.gc.external_malloc(tid, largeobj_size)
+        hdr_src = self.gc.header(addr_src)
+        hdr_dst = self.gc.header(addr_dst)
+        #
+        assert hdr_src.tid & minimark.GCFLAG_HAS_CARDS
+        assert hdr_dst.tid & minimark.GCFLAG_HAS_CARDS
+        #
+        young_p = self.malloc(S)
+        self.gc.write_barrier_from_array(young_p, addr_src, 0)
+        index_in_third_page = int(2.5 * self.gc.card_page_indices)
+        assert index_in_third_page < largeobj_size
+        self.gc.write_barrier_from_array(young_p, addr_src,
+                                         index_in_third_page)
+        #
+        assert hdr_src.tid & minimark.GCFLAG_CARDS_SET
+        addr_byte = self.gc.get_card(addr_src, 0)
+        assert ord(addr_byte.char[0]) == 0x01 | 0x04  # bits 0 and 2
+        #
+        res = self.gc.writebarrier_before_copy(addr_src, addr_dst,
+                                             0, 0, 2*self.gc.card_page_indices)
+        assert res
+        #
+        assert hdr_dst.tid & minimark.GCFLAG_CARDS_SET
+        addr_byte = self.gc.get_card(addr_dst, 0)
+        assert ord(addr_byte.char[0]) == 0x01 | 0x04  # bits 0 and 2
+
+    test_writebarrier_before_copy_preserving_cards.GC_PARAMS = {
+        "card_page_indices": 4}
+
+
 class TestMiniMarkGCFull(DirectGCTest):
     from pypy.rpython.memory.gc.minimark import MiniMarkGC as GCClass
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -322,7 +322,8 @@
         if hasattr(GCClass, 'writebarrier_before_copy'):
             self.wb_before_copy_ptr = \
                     getfn(GCClass.writebarrier_before_copy.im_func,
-                    [s_gc] + [annmodel.SomeAddress()] * 2, annmodel.SomeBool())
+                    [s_gc] + [annmodel.SomeAddress()] * 2 +
+                    [annmodel.SomeInteger()] * 3, annmodel.SomeBool())
         elif GCClass.needs_write_barrier:
             raise NotImplementedError("GC needs write barrier, but does not provide writebarrier_before_copy functionality")
 
@@ -884,7 +885,7 @@
         dest_addr = hop.genop('cast_ptr_to_adr', [op.args[1]],
                                 resulttype=llmemory.Address)
         hop.genop('direct_call', [self.wb_before_copy_ptr, self.c_const_gc,
-                                  source_addr, dest_addr],
+                                  source_addr, dest_addr] + op.args[2:],
                   resultvar=op.result)
 
     def gct_weakref_create(self, hop):
diff --git a/pypy/rpython/memory/gctransform/test/test_framework.py b/pypy/rpython/memory/gctransform/test/test_framework.py
--- a/pypy/rpython/memory/gctransform/test/test_framework.py
+++ b/pypy/rpython/memory/gctransform/test/test_framework.py
@@ -163,7 +163,8 @@
     GC_PARAMS = {}
     class GCClass(MarkSweepGC):
         needs_write_barrier = True
-        def writebarrier_before_copy(self, source, dest):
+        def writebarrier_before_copy(self, source, dest,
+                                     source_start, dest_start, length):
             return True
 
 def write_barrier_check(spaceop, needs_write_barrier=True):
diff --git a/pypy/rpython/memory/gcwrapper.py b/pypy/rpython/memory/gcwrapper.py
--- a/pypy/rpython/memory/gcwrapper.py
+++ b/pypy/rpython/memory/gcwrapper.py
@@ -136,11 +136,14 @@
         ptr = lltype.cast_opaque_ptr(llmemory.GCREF, ptr)
         return self.gc.id(ptr)
 
-    def writebarrier_before_copy(self, source, dest):
+    def writebarrier_before_copy(self, source, dest,
+                                 source_start, dest_start, length):
         if self.gc.needs_write_barrier:
             source_addr = llmemory.cast_ptr_to_adr(source)
             dest_addr   = llmemory.cast_ptr_to_adr(dest)
-            return self.gc.writebarrier_before_copy(source_addr, dest_addr)
+            return self.gc.writebarrier_before_copy(source_addr, dest_addr,
+                                                    source_start, dest_start,
+                                                    length)
         else:
             return True
 
diff --git a/pypy/rpython/memory/support.py b/pypy/rpython/memory/support.py
--- a/pypy/rpython/memory/support.py
+++ b/pypy/rpython/memory/support.py
@@ -140,6 +140,14 @@
             self.foreach(_add_in_dict, result)
             return result
 
+        def tolist(self):
+            """NOT_RPYTHON.  Returns the content as a list."""
+            lst = []
+            def _add(obj, lst):
+                lst.append(obj)
+            self.foreach(_add, lst)
+            return lst
+
         def remove(self, addr):
             """Remove 'addr' from the stack.  The addr *must* be in the list,
             and preferrably near the top.
diff --git a/pypy/rpython/ootypesystem/rclass.py b/pypy/rpython/ootypesystem/rclass.py
--- a/pypy/rpython/ootypesystem/rclass.py
+++ b/pypy/rpython/ootypesystem/rclass.py
@@ -264,7 +264,8 @@
 
         for name, attrdef in selfattrs.iteritems():
             if not attrdef.readonly and self.is_quasi_immutable(name):
-                ootype.addFields(self.lowleveltype, {'mutable_'+name: OBJECT})
+                name = mangle('mutable_' + name, self.rtyper.getconfig())
+                ootype.addFields(self.lowleveltype, {name: OBJECT})
 
         classattributes = {}
         baseInstance = self.lowleveltype._superclass
diff --git a/pypy/rpython/ootypesystem/rdict.py b/pypy/rpython/ootypesystem/rdict.py
--- a/pypy/rpython/ootypesystem/rdict.py
+++ b/pypy/rpython/ootypesystem/rdict.py
@@ -18,7 +18,7 @@
 
 class DictRepr(AbstractDictRepr):
     def __init__(self, rtyper, key_repr, value_repr, dictkey, dictvalue,
-                 custom_eq_hash=None):
+                 custom_eq_hash=None, force_non_null=False):
         self.rtyper = rtyper
         self.custom_eq_hash = custom_eq_hash is not None
 
diff --git a/pypy/rpython/ootypesystem/test/test_oopbc.py b/pypy/rpython/ootypesystem/test/test_oopbc.py
--- a/pypy/rpython/ootypesystem/test/test_oopbc.py
+++ b/pypy/rpython/ootypesystem/test/test_oopbc.py
@@ -81,3 +81,18 @@
     res = interpret(f, [1], type_system='ootype')
     assert res == 2
 
+def test_quasi_immutable():
+    class A(object):
+        _immutable_fields_ = ['x?']
+        def __init__(self):
+            self.x = 3
+        def foo(self):
+            return self.x
+
+    a = A()
+
+    def f():
+        return a.foo()
+    
+    res = interpret(f, [], type_system='ootype')
+    assert res == 3
diff --git a/pypy/rpython/rdict.py b/pypy/rpython/rdict.py
--- a/pypy/rpython/rdict.py
+++ b/pypy/rpython/rdict.py
@@ -15,6 +15,7 @@
         dictvalue = self.dictdef.dictvalue
         s_key     = dictkey  .s_value
         s_value   = dictvalue.s_value
+        force_non_null = self.dictdef.force_non_null
         if (s_key.__class__ is annmodel.SomeObject and s_key.knowntype == object and
             s_value.__class__ is annmodel.SomeObject and s_value.knowntype == object):
             return robject.pyobj_repr
@@ -29,7 +30,8 @@
                                                      lambda: rtyper.getrepr(s_value),
                                                      dictkey,
                                                      dictvalue,
-                                                     custom_eq_hash)
+                                                     custom_eq_hash,
+                                                     force_non_null)
 
     def rtyper_makekey(self):
         self.dictdef.dictkey  .dont_change_any_more = True
diff --git a/pypy/rpython/test/test_rdict.py b/pypy/rpython/test/test_rdict.py
--- a/pypy/rpython/test/test_rdict.py
+++ b/pypy/rpython/test/test_rdict.py
@@ -598,6 +598,29 @@
         res = self.interpret(func, [])
         assert res in [5263, 6352]
 
+    def test_dict_popitem_hash(self):
+        def deq(n, m):
+            return n == m
+        def dhash(n):
+            return ~n
+        def func():
+            d = r_dict(deq, dhash)
+            d[5] = 2
+            d[6] = 3
+            k1, v1 = d.popitem()
+            assert len(d) == 1
+            k2, v2 = d.popitem()
+            try:
+                d.popitem()
+            except KeyError:
+                pass
+            else:
+                assert 0, "should have raised KeyError"
+            assert len(d) == 0
+            return k1*1000 + v1*100 + k2*10 + v2
+
+        res = self.interpret(func, [])
+        assert res in [5263, 6352]
 
 class TestLLtype(BaseTestRdict, LLRtypeMixin):
     def test_dict_but_not_with_char_keys(self):
@@ -860,6 +883,25 @@
         res = f()
         assert res == 1
 
+    def test_nonnull_hint(self):
+        def eq(a, b):
+            return a == b
+        def rhash(a):
+            return 3
+        
+        def func(i):
+            d = r_dict(eq, rhash, force_non_null=True)
+            if not i:
+                d[None] = i
+            else:
+                d[str(i)] = i
+            return "12" in d, d
+
+        llres = self.interpret(func, [12])
+        assert llres.item0 == 1
+        DICT = lltype.typeOf(llres.item1)
+        assert sorted(DICT.TO.entries.TO.OF._flds) == ['f_hash', 'key', 'value']
+
     # ____________________________________________________________
 
 
diff --git a/pypy/tool/jitlogparser/parser.py b/pypy/tool/jitlogparser/parser.py
--- a/pypy/tool/jitlogparser/parser.py
+++ b/pypy/tool/jitlogparser/parser.py
@@ -121,6 +121,9 @@
     def getcode(self):
         return self.code
 
+    def has_valid_code(self):
+        return self.code is not None
+
     def getopcode(self):
         return self.code.map[self.bytecode_no]
 
@@ -220,6 +223,12 @@
         return self._lineset
     lineset = property(getlineset)
 
+    def has_valid_code(self):
+        for chunk in self.chunks:
+            if not chunk.has_valid_code():
+                return False
+        return True
+
     def _compute_linerange(self):
         self._lineset = set()
         minline = sys.maxint
diff --git a/pypy/tool/jitlogparser/test/test_parser.py b/pypy/tool/jitlogparser/test/test_parser.py
--- a/pypy/tool/jitlogparser/test/test_parser.py
+++ b/pypy/tool/jitlogparser/test/test_parser.py
@@ -168,7 +168,7 @@
     []
     int_add(0, 1)
     ''')
-    loops = LoopStorage().reconnect_loops([main, bridge])
+    LoopStorage().reconnect_loops([main, bridge])
     assert adjust_bridges(main, {})[1].name == 'guard_true'
     assert adjust_bridges(main, {'loop-13': True})[1].name == 'int_add'
 
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -83,7 +83,7 @@
     def __init__(self, space, operr):
         self.space = space
         self.operr = operr
-        self.typename = operr.w_type.getname(space, "?")
+        self.typename = operr.w_type.getname(space)
         self.traceback = AppTraceback(space, self.operr.get_traceback())
         debug_excs = getattr(operr, 'debug_excs', [])
         if debug_excs:
diff --git a/pypy/translator/goal/app_main.py b/pypy/translator/goal/app_main.py
--- a/pypy/translator/goal/app_main.py
+++ b/pypy/translator/goal/app_main.py
@@ -143,6 +143,7 @@
     for key, value in items:
         print '  --jit %s=N %slow-level JIT parameter (default %s)' % (
             key, ' '*(18-len(key)), value)
+    print '  --jit off                  turn off the JIT'
 
 def print_version(*args):
     print "Python", sys.version
diff --git a/pypy/translator/goal/translate.py b/pypy/translator/goal/translate.py
--- a/pypy/translator/goal/translate.py
+++ b/pypy/translator/goal/translate.py
@@ -186,7 +186,7 @@
             print "\n\nTarget specific help:\n\n"
             targetspec_dic['print_help'](config)
         print "\n\nFor detailed descriptions of the command line options see"
-        print "http://codespeak.net/pypy/dist/pypy/doc/config/commandline.html"
+        print "http://pypy.readthedocs.org/en/latest/config/commandline.html"
         sys.exit(0)
     
     return targetspec_dic, translateconfig, config, args
diff --git a/pypy/translator/jvm/opcodes.py b/pypy/translator/jvm/opcodes.py
--- a/pypy/translator/jvm/opcodes.py
+++ b/pypy/translator/jvm/opcodes.py
@@ -98,6 +98,7 @@
     'jit_marker':               Ignore,
     'jit_force_virtualizable':  Ignore,
     'jit_force_virtual':        DoNothing,
+    'jit_force_quasi_immutable': Ignore,
 
     'debug_assert':              [], # TODO: implement?
     'debug_start_traceback':    Ignore,
diff --git a/pypy/translator/jvm/src/pypy/PyPy.java b/pypy/translator/jvm/src/pypy/PyPy.java
--- a/pypy/translator/jvm/src/pypy/PyPy.java
+++ b/pypy/translator/jvm/src/pypy/PyPy.java
@@ -964,12 +964,15 @@
         return a + File.separator + b;
     }
 
-    public String ll_strtod_formatd(String format, double d)
+    public String ll_strtod_formatd(double d, char code, int precision, int flags)
     {
         // XXX: this is really a quick hack to make things work.
         // it should disappear, because this function is not
         // supported by ootypesystem.
-        return Double.toString(d); // XXX: we are ignoring "format"
+        DecimalFormat format = new DecimalFormat("0.###");
+        format.setMinimumFractionDigits(precision);
+        format.setMaximumFractionDigits(precision);
+        return format.format(d);
     }
 
     // ----------------------------------------------------------------------
diff --git a/pypy/translator/jvm/test/test_float.py b/pypy/translator/jvm/test/test_float.py
--- a/pypy/translator/jvm/test/test_float.py
+++ b/pypy/translator/jvm/test/test_float.py
@@ -22,3 +22,14 @@
 
     def test_r_singlefloat(self):
         py.test.skip("not implemented: single-precision floats")
+
+    def test_format_float(self):
+        from pypy.rlib.rfloat import _formatd
+        def fn(precision):
+            return _formatd(10.01, 'd', precision, 0)
+
+        res = self.interpret(fn, [2])
+        assert res == "10.01"
+
+        res = self.interpret(fn, [1])
+        assert res == "10.0"


More information about the pypy-commit mailing list