[pypy-commit] pypy stacklet: hg merge default

arigo noreply at buildbot.pypy.org
Sat Aug 27 11:03:58 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46816:0ba9950d0d47
Date: 2011-08-27 11:09 +0200
http://bitbucket.org/pypy/pypy/changeset/0ba9950d0d47/

Log:	hg merge default

diff --git a/ctypes_configure/configure.py b/ctypes_configure/configure.py
--- a/ctypes_configure/configure.py
+++ b/ctypes_configure/configure.py
@@ -559,7 +559,9 @@
 C_HEADER = """
 #include <stdio.h>
 #include <stddef.h>   /* for offsetof() */
-#include <stdint.h>   /* FreeBSD: for uint64_t */
+#ifndef _WIN32
+#  include <stdint.h>   /* FreeBSD: for uint64_t */
+#endif
 
 void dump(char* key, int value) {
     printf("%s: %d\\n", key, value);
diff --git a/lib-python/modified-2.7/distutils/unixccompiler.py b/lib-python/modified-2.7/distutils/unixccompiler.py
--- a/lib-python/modified-2.7/distutils/unixccompiler.py
+++ b/lib-python/modified-2.7/distutils/unixccompiler.py
@@ -324,7 +324,7 @@
             # On OSX users can specify an alternate SDK using
             # '-isysroot', calculate the SDK root if it is specified
             # (and use it further on)
-            cflags = sysconfig.get_config_var('CFLAGS')
+            cflags = sysconfig.get_config_var('CFLAGS') or ''
             m = re.search(r'-isysroot\s+(\S+)', cflags)
             if m is None:
                 sysroot = '/'
diff --git a/lib-python/modified-2.7/test/test_bz2.py b/lib-python/modified-2.7/test/test_bz2.py
--- a/lib-python/modified-2.7/test/test_bz2.py
+++ b/lib-python/modified-2.7/test/test_bz2.py
@@ -50,6 +50,7 @@
         self.filename = TESTFN
 
     def tearDown(self):
+        test_support.gc_collect()
         if os.path.isfile(self.filename):
             os.unlink(self.filename)
 
diff --git a/lib_pypy/_subprocess.py b/lib_pypy/_subprocess.py
--- a/lib_pypy/_subprocess.py
+++ b/lib_pypy/_subprocess.py
@@ -35,7 +35,7 @@
 _DuplicateHandle.restype = ctypes.c_int
     
 _WaitForSingleObject = _kernel32.WaitForSingleObject
-_WaitForSingleObject.argtypes = [ctypes.c_int, ctypes.c_int]
+_WaitForSingleObject.argtypes = [ctypes.c_int, ctypes.c_uint]
 _WaitForSingleObject.restype = ctypes.c_int
 
 _GetExitCodeProcess = _kernel32.GetExitCodeProcess
diff --git a/lib_pypy/pyrepl/readline.py b/lib_pypy/pyrepl/readline.py
--- a/lib_pypy/pyrepl/readline.py
+++ b/lib_pypy/pyrepl/readline.py
@@ -33,7 +33,7 @@
 from pyrepl.unix_console import UnixConsole, _error
 
 
-ENCODING = 'latin1'     # XXX hard-coded
+ENCODING = sys.getfilesystemencoding() or 'latin1'     # XXX review
 
 __all__ = ['add_history',
            'clear_history',
diff --git a/pypy/config/makerestdoc.py b/pypy/config/makerestdoc.py
--- a/pypy/config/makerestdoc.py
+++ b/pypy/config/makerestdoc.py
@@ -134,7 +134,7 @@
         for child in self._children:
             subpath = fullpath + "." + child._name
             toctree.append(subpath)
-        content.add(Directive("toctree", *toctree, maxdepth=4))
+        content.add(Directive("toctree", *toctree, **{'maxdepth': 4}))
         content.join(
             ListItem(Strong("name:"), self._name),
             ListItem(Strong("description:"), self.doc))
diff --git a/pypy/config/test/test_config.py b/pypy/config/test/test_config.py
--- a/pypy/config/test/test_config.py
+++ b/pypy/config/test/test_config.py
@@ -1,5 +1,5 @@
 from pypy.config.config import *
-import py
+import py, sys
 
 def make_description():
     gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
@@ -69,13 +69,15 @@
     attrs = dir(config)
     assert '__repr__' in attrs        # from the type
     assert '_cfgimpl_values' in attrs # from self
-    assert 'gc' in attrs              # custom attribute
-    assert 'objspace' in attrs        # custom attribute
+    if sys.version_info >= (2, 6):
+        assert 'gc' in attrs              # custom attribute
+        assert 'objspace' in attrs        # custom attribute
     #
     attrs = dir(config.gc)
-    assert 'name' in attrs
-    assert 'dummy' in attrs
-    assert 'float' in attrs
+    if sys.version_info >= (2, 6):
+        assert 'name' in attrs
+        assert 'dummy' in attrs
+        assert 'float' in attrs
 
 def test_arbitrary_option():
     descr = OptionDescription("top", "", [
diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py
--- a/pypy/doc/conf.py
+++ b/pypy/doc/conf.py
@@ -45,9 +45,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '1.5'
+version = '1.6'
 # The full version, including alpha/beta/rc tags.
-release = '1.5'
+release = '1.6'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
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
@@ -287,7 +287,14 @@
   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``.
+* Do not compare immutable objects with ``is``.  For example on CPython
+  it is true that ``x is 0`` works, i.e. does the same as ``type(x) is
+  int and x == 0``, but it is so by accident.  If you do instead
+  ``x is 1000``, then it stops working, because 1000 is too large and
+  doesn't come from the internal cache.  In PyPy it fails to work in
+  both cases, because we have no need for a cache at all.
+
+* Also, object identity of immutable keys in dictionaries is not necessarily
+  preserved.
 
 .. include:: _ref.txt
diff --git a/pypy/doc/getting-started-python.rst b/pypy/doc/getting-started-python.rst
--- a/pypy/doc/getting-started-python.rst
+++ b/pypy/doc/getting-started-python.rst
@@ -105,7 +105,7 @@
 
     $ ./pypy-c
     Python 2.7.0 (61ef2a11b56a, Mar 02 2011, 03:00:11)
-    [PyPy 1.5.0-alpha0 with GCC 4.4.3] on linux2
+    [PyPy 1.6.0 with GCC 4.4.3] on linux2
     Type "help", "copyright", "credits" or "license" for more information.
     And now for something completely different: ``this sentence is false''
     >>>> 46 - 4
@@ -165,7 +165,7 @@
 
     $ ./pypy-cli
     Python 2.7.0 (61ef2a11b56a, Mar 02 2011, 03:00:11)
-    [PyPy 1.5.0-alpha0] on linux2
+    [PyPy 1.6.0] on linux2
     Type "help", "copyright", "credits" or "license" for more information.
     And now for something completely different: ``distopian and utopian chairs''
     >>>> 
@@ -202,7 +202,7 @@
 
         $ ./pypy-jvm 
         Python 2.7.0 (61ef2a11b56a, Mar 02 2011, 03:00:11)
-        [PyPy 1.5.0-alpha0] on linux2
+        [PyPy 1.6.0] on linux2
         Type "help", "copyright", "credits" or "license" for more information.
         And now for something completely different: ``# assert did not crash''
         >>>> 
@@ -241,7 +241,7 @@
 the ``bin/pypy`` executable.
 
 To install PyPy system wide on unix-like systems, it is recommended to put the
-whole hierarchy alone (e.g. in ``/opt/pypy1.5``) and put a symlink to the
+whole hierarchy alone (e.g. in ``/opt/pypy1.6``) and put a symlink to the
 ``pypy`` executable into ``/usr/bin`` or ``/usr/local/bin``
 
 If the executable fails to find suitable libraries, it will report
diff --git a/pypy/doc/getting-started.rst b/pypy/doc/getting-started.rst
--- a/pypy/doc/getting-started.rst
+++ b/pypy/doc/getting-started.rst
@@ -53,11 +53,11 @@
 PyPy is ready to be executed as soon as you unpack the tarball or the zip
 file, with no need to install it in any specific location::
 
-    $ tar xf pypy-1.5-linux.tar.bz2
+    $ tar xf pypy-1.6-linux.tar.bz2
 
-    $ ./pypy-1.5-linux/bin/pypy
+    $ ./pypy-1.6/bin/pypy
     Python 2.7.1 (?, Apr 27 2011, 12:44:21)
-    [PyPy 1.5.0-alpha0 with GCC 4.4.3] on linux2
+    [PyPy 1.6.0 with GCC 4.4.3] on linux2
     Type "help", "copyright", "credits" or "license" for more information.
     And now for something completely different: ``implementing LOGO in LOGO:
     "turtles all the way down"''
@@ -73,16 +73,16 @@
 
     $ curl -O http://python-distribute.org/distribute_setup.py
 
-    $ curl -O https://github.com/pypa/pip/raw/master/contrib/get-pip.py
+    $ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
 
-    $ ./pypy-1.5-linux/bin/pypy distribute_setup.py
+    $ ./pypy-1.6/bin/pypy distribute_setup.py
 
-    $ ./pypy-1.5-linux/bin/pypy get-pip.py
+    $ ./pypy-1.6/bin/pypy get-pip.py
 
-    $ ./pypy-1.5-linux/bin/pip install pygments  # for example
+    $ ./pypy-1.6/bin/pip install pygments  # for example
 
-3rd party libraries will be installed in ``pypy-1.5-linux/site-packages``, and
-the scripts in ``pypy-1.5-linux/bin``.
+3rd party libraries will be installed in ``pypy-1.6/site-packages``, and
+the scripts in ``pypy-1.6/bin``.
 
 Installing using virtualenv
 ---------------------------
diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst
--- a/pypy/doc/index-of-release-notes.rst
+++ b/pypy/doc/index-of-release-notes.rst
@@ -16,3 +16,4 @@
    release-1.4.0beta.rst
    release-1.4.1.rst
    release-1.5.0.rst
+   release-1.6.0.rst
diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst
--- a/pypy/doc/index.rst
+++ b/pypy/doc/index.rst
@@ -15,7 +15,7 @@
 
 * `FAQ`_: some frequently asked questions.
 
-* `Release 1.5`_: the latest official release
+* `Release 1.6`_: the latest official release
 
 * `PyPy Blog`_: news and status info about PyPy 
 
@@ -77,7 +77,7 @@
 .. _`Getting Started`: getting-started.html
 .. _`Papers`: extradoc.html
 .. _`Videos`: video-index.html
-.. _`Release 1.5`: http://pypy.org/download.html
+.. _`Release 1.6`: http://pypy.org/download.html
 .. _`speed.pypy.org`: http://speed.pypy.org
 .. _`RPython toolchain`: translation.html
 .. _`potential project ideas`: project-ideas.html
@@ -122,9 +122,9 @@
 Windows, on top of .NET, and on top of Java.
 To dig into PyPy it is recommended to try out the current
 Mercurial default branch, which is always working or mostly working,
-instead of the latest release, which is `1.5`__.
+instead of the latest release, which is `1.6`__.
 
-.. __: release-1.5.0.html
+.. __: release-1.6.0.html
 
 PyPy is mainly developed on Linux and Mac OS X.  Windows is supported,
 but platform-specific bugs tend to take longer before we notice and fix
diff --git a/pypy/doc/release-1.6.0.rst b/pypy/doc/release-1.6.0.rst
--- a/pypy/doc/release-1.6.0.rst
+++ b/pypy/doc/release-1.6.0.rst
@@ -1,6 +1,6 @@
-=========================================================
-PyPy 1.6 - XXX thing something out (I like kickass panda)
-=========================================================
+========================
+PyPy 1.6 - kickass panda
+========================
 
 We're pleased to announce the 1.6 release of PyPy. This release brings a lot
 of bugfixes and performance improvements over 1.5, and improves support for
@@ -22,7 +22,7 @@
 far).  Windows 64 is not yet supported.
 
 The main topics of this release are speed and stability: on average on
-our benchmark suite, PyPy 1.6 is between 20% and 30% faster than PyPy 1.5,
+our benchmark suite, PyPy 1.6 is between **20% and 30%** faster than PyPy 1.5,
 which was already much faster than CPython on our set of benchmarks.
 
 The speed improvements have been made possible by optimizing many of the
@@ -30,6 +30,8 @@
 the JIT warmup time, the optimizations performed by the JIT, the quality of
 the generated machine code and the implementation of our Python interpreter.
 
+.. _`pypy 1.5 and cpython 2.6.2`: http://speed.pypy.org
+
 
 Highlights
 ==========
@@ -38,7 +40,7 @@
 
   - better GC behavior when dealing with very large objects and arrays
 
-  - `fast ctypes`_: now calls to ctypes functions are seen and optimized
+  - **fast ctypes:** now calls to ctypes functions are seen and optimized
     by the JIT, and they are up to 60 times faster than PyPy 1.5 and 10 times
     faster than CPython
 
@@ -60,10 +62,10 @@
 
 * JitViewer: this is the first official release which includes the JitViewer,
   a web-based tool which helps you to see which parts of your Python code have
-  been compiled by the JIT, down until the assembler. XXX: publish a public
-  demo?
+  been compiled by the JIT, down until the assembler. The `jitviewer`_ 0.1 has
+  already been release and works well with PyPy 1.6.
 
-- The CPython extension module API has been improved and now supports many
+* The CPython extension module API has been improved and now supports many
   more extensions. For information on which one are supported, please refer to
   our `compatibility wiki`_.
 
@@ -86,3 +88,8 @@
 Hakan Ardo, Carl Friedrich Bolz, Laura Creighton, Antonio Cuni,
 Maciej Fijalkowski, Amaury Forgeot d'Arc, Alex Gaynor,
 Armin Rigo and the PyPy team
+
+.. _`jitviewer`: http://morepypy.blogspot.com/2011/08/visualization-of-jitted-code.html
+.. _`bug tracker`: https://bugs.pypy.org
+.. _`compatibility wiki`: https://bitbucket.org/pypy/compatibility/wiki/Home
+
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
@@ -571,6 +571,7 @@
                 pure = '_pure'
         else:
             pure = ''
+        self.check_field_access(v_inst.concretetype.TO)
         argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
         descr = self.cpu.fielddescrof(v_inst.concretetype.TO,
                                       c_fieldname.value)
@@ -604,6 +605,7 @@
             return [SpaceOperation('-live-', [], None),
                     SpaceOperation('setfield_vable_%s' % kind,
                                    [v_inst, descr, v_value], None)]
+        self.check_field_access(v_inst.concretetype.TO)
         argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
         descr = self.cpu.fielddescrof(v_inst.concretetype.TO,
                                       c_fieldname.value)
@@ -616,6 +618,22 @@
         return (op.args[1].value == 'typeptr' and
                 op.args[0].concretetype.TO._hints.get('typeptr'))
 
+    def check_field_access(self, STRUCT):
+        # check against a GcStruct with a nested GcStruct as a first argument
+        # but which is not an object at all; see metainterp/test/test_loop,
+        # test_regular_pointers_in_short_preamble.
+        if not isinstance(STRUCT, lltype.GcStruct):
+            return
+        if STRUCT._first_struct() == (None, None):
+            return
+        PARENT = STRUCT
+        while not PARENT._hints.get('typeptr'):
+            _, PARENT = PARENT._first_struct()
+            if PARENT is None:
+                raise NotImplementedError("%r is a GcStruct using nesting but "
+                                          "not inheriting from object" %
+                                          (STRUCT,))
+
     def get_vinfo(self, v_virtualizable):
         if self.callcontrol is None:      # for tests
             return None
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
@@ -1014,3 +1014,13 @@
         assert op1.opname == 'jit_force_quasi_immutable'
         assert op1.args[0] == v_x
         assert op1.args[1] == ('fielddescr', STRUCT, 'mutate_x')
+
+def test_no_gcstruct_nesting_outside_of_OBJECT():
+    PARENT = lltype.GcStruct('parent')
+    STRUCT = lltype.GcStruct('struct', ('parent', PARENT),
+                                       ('x', lltype.Signed))
+    v_x = varoftype(lltype.Ptr(STRUCT))
+    op = SpaceOperation('getfield', [v_x, Constant('x', lltype.Void)],
+                        varoftype(lltype.Signed))
+    tr = Transformer(None, None)
+    raises(NotImplementedError, tr.rewrite_operation, op)
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
@@ -137,6 +137,10 @@
             jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp(
                 greenkey, loop.preamble.token)
             record_loop_or_bridge(metainterp_sd, loop.preamble)
+        elif token.short_preamble:
+            short = token.short_preamble[-1]
+            metainterp_sd.logger_ops.log_short_preamble(short.inputargs,
+                                                        short.operations)
         return token
     else:
         send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop,
@@ -637,6 +641,7 @@
         debug_print("compile_new_bridge: got an InvalidLoop")
         # XXX I am fairly convinced that optimize_bridge cannot actually raise
         # InvalidLoop
+        debug_print('InvalidLoop in compile_new_bridge')
         return None
     # Did it work?
     if target_loop_token is not None:
diff --git a/pypy/jit/metainterp/optimize.py b/pypy/jit/metainterp/optimize.py
--- a/pypy/jit/metainterp/optimize.py
+++ b/pypy/jit/metainterp/optimize.py
@@ -1,4 +1,4 @@
-from pypy.rlib.debug import debug_start, debug_stop
+from pypy.rlib.debug import debug_start, debug_stop, debug_print
 from pypy.jit.metainterp.jitexc import JitException
 
 class InvalidLoop(JitException):
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
@@ -33,10 +33,6 @@
         if name in enable_opts:
             if opt is not None:
                 o = opt()
-                if unroll and name == 'string':
-                    o.enabled = False
-                # FIXME: Workaround to disable string optimisation
-                # during preamble but to keep it during the loop
                 optimizations.append(o)
             elif name == 'ffi' and config.translation.jit_ffi:
                 # we cannot put the class directly in the unrolling_iterable,
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
@@ -75,10 +75,9 @@
         else:
             self.logops = None
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+    def new(self):
         return OptFfiCall()
-        # FIXME: Should any status be saved for next iteration?
-
+    
     def begin_optimization(self, funcval, op):
         self.rollback_maybe('begin_optimization', op)
         self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)
diff --git a/pypy/jit/metainterp/optimizeopt/generalize.py b/pypy/jit/metainterp/optimizeopt/generalize.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/generalize.py
@@ -0,0 +1,19 @@
+from pypy.jit.metainterp.optimizeopt.optimizer import MININT, MAXINT
+
+class GeneralizationStrategy(object):
+    def __init__(self, optimizer):
+        self.optimizer = optimizer
+
+    def apply(self):
+        raise NotImplementedError
+
+class KillHugeIntBounds(GeneralizationStrategy):
+    def apply(self):
+        for v in self.optimizer.values.values():
+            if v.is_constant():
+                continue
+            if v.intbound.lower < MININT/2:
+                v.intbound.lower = MININT
+            if v.intbound.upper > MAXINT/2:
+                v.intbound.upper = MAXINT
+          
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,9 +1,10 @@
 import os
 
 from pypy.jit.metainterp.jitexc import JitException
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY
+from pypy.jit.metainterp.history import ConstInt, Const
 from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
-from pypy.jit.metainterp.resoperation import rop
+from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.rlib.objectmodel import we_are_translated
 
 
@@ -24,6 +25,7 @@
         #      'cached_fields'.
         #
         self._cached_fields = {}
+        self._cached_fields_getfield_op = {}        
         self._lazy_setfield = None
         self._lazy_setfield_registered = False
 
@@ -70,9 +72,10 @@
         else:
             return self._cached_fields.get(structvalue, None)
 
-    def remember_field_value(self, structvalue, fieldvalue):
+    def remember_field_value(self, structvalue, fieldvalue, getfield_op=None):
         assert self._lazy_setfield is None
         self._cached_fields[structvalue] = fieldvalue
+        self._cached_fields_getfield_op[structvalue] = getfield_op        
 
     def force_lazy_setfield(self, optheap, can_cache=True):
         op = self._lazy_setfield
@@ -81,7 +84,7 @@
             # Now we clear _cached_fields, because actually doing the
             # setfield might impact any of the stored result (because of
             # possible aliasing).
-            self._cached_fields.clear()
+            self.clear()
             self._lazy_setfield = None
             optheap.next_optimization.propagate_forward(op)
             if not can_cache:
@@ -91,19 +94,49 @@
             # field.
             structvalue = optheap.getvalue(op.getarg(0))
             fieldvalue  = optheap.getvalue(op.getarglist()[-1])
-            self.remember_field_value(structvalue, fieldvalue)
+            self.remember_field_value(structvalue, fieldvalue, op)
         elif not can_cache:
-            self._cached_fields.clear()
+            self.clear()
 
-    def get_reconstructed(self, optimizer, valuemap):
-        assert self._lazy_setfield is None
-        cf = CachedField()
-        for structvalue, fieldvalue in self._cached_fields.iteritems():
-            structvalue2 = structvalue.get_reconstructed(optimizer, valuemap)
-            fieldvalue2  = fieldvalue .get_reconstructed(optimizer, valuemap)
-            cf._cached_fields[structvalue2] = fieldvalue2
-        return cf
+    def clear(self):
+        self._cached_fields.clear()
+        self._cached_fields_getfield_op.clear()
 
+    def turned_constant(self, newvalue, value):
+        if newvalue not in self._cached_fields and value in self._cached_fields:
+            self._cached_fields[newvalue] = self._cached_fields[value]
+            op = self._cached_fields_getfield_op[value].clone()
+            constbox = value.box
+            assert isinstance(constbox, Const)
+            op.setarg(0, constbox)
+            self._cached_fields_getfield_op[newvalue] = op
+        for structvalue in self._cached_fields.keys():
+            if self._cached_fields[structvalue] is value:
+                self._cached_fields[structvalue] = newvalue
+
+    def produce_potential_short_preamble_ops(self, optimizer, shortboxes, descr):
+        if self._lazy_setfield is not None:
+            return
+        for structvalue in self._cached_fields_getfield_op.keys():
+            op = self._cached_fields_getfield_op[structvalue]
+            if not op:
+                continue
+            if optimizer.getvalue(op.getarg(0)) in optimizer.opaque_pointers:
+                continue
+            if structvalue in self._cached_fields:
+                if op.getopnum() == rop.SETFIELD_GC:
+                    result = op.getarg(1)
+                    if isinstance(result, Const):
+                        newresult = result.clonebox()
+                        optimizer.make_constant(newresult, result)
+                        result = newresult
+                    getop = ResOperation(rop.GETFIELD_GC, [op.getarg(0)],
+                                         result, op.getdescr())
+                    getop = shortboxes.add_potential(getop)
+                    self._cached_fields_getfield_op[structvalue] = getop
+                    self._cached_fields[structvalue] = optimizer.getvalue(result)
+                elif op.result is not None:
+                    shortboxes.add_potential(op)
 
 class BogusPureField(JitException):
     pass
@@ -122,24 +155,32 @@
         self._remove_guard_not_invalidated = False
         self._seen_guard_not_invalidated = False
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        new = OptHeap()
+    def force_at_end_of_preamble(self):
+        self.force_all_lazy_setfields_and_arrayitems()
 
-        if True:
-            self.force_all_lazy_setfields_and_arrayitems()
-        else:
-            assert 0   # was: new.lazy_setfields = self.lazy_setfields
+    def flush(self):
+        self.force_all_lazy_setfields_and_arrayitems()
 
-        for descr, d in self.cached_fields.items():
-            new.cached_fields[descr] = d.get_reconstructed(optimizer, valuemap)
+    def new(self):
+        return OptHeap()
+        
+    def produce_potential_short_preamble_ops(self, sb):
+        descrkeys = self.cached_fields.keys()
+        if not we_are_translated():
+            # XXX Pure operation of boxes that are cached in several places will
+            #     only be removed from the peeled loop when red from the first
+            #     place discovered here. This is far from ideal, as it makes
+            #     the effectiveness of our optimization a bit random. It should
+            #     howevere always generate correct results. For tests we dont
+            #     want this randomness.
+            descrkeys.sort(key=str, reverse=True)
+        for descr in descrkeys:
+            d = self.cached_fields[descr]
+            d.produce_potential_short_preamble_ops(self.optimizer, sb, descr)
 
         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
+                d.produce_potential_short_preamble_ops(self.optimizer, sb, descr)
 
     def clean_caches(self):
         del self._lazy_setfields_and_arrayitems[:]
@@ -227,12 +268,10 @@
         newvalue = self.getvalue(value.box)
         if value is not newvalue:
             for cf in self.cached_fields.itervalues():
-                if value in cf._cached_fields:
-                    cf._cached_fields[newvalue] = cf._cached_fields[value]
+                cf.turned_constant(newvalue, 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]
+                    cf.turned_constant(newvalue, value)
 
     def force_lazy_setfield(self, descr, can_cache=True):
         try:
@@ -335,7 +374,7 @@
         self.emit_operation(op)
         # then remember the result of reading the field
         fieldvalue = self.getvalue(op.result)
-        cf.remember_field_value(structvalue, fieldvalue)
+        cf.remember_field_value(structvalue, fieldvalue, op)
 
     def optimize_SETFIELD_GC(self, op):
         if self.has_pure_result(rop.GETFIELD_GC_PURE, [op.getarg(0)],
@@ -352,6 +391,7 @@
         indexvalue = self.getvalue(op.getarg(1))
         cf = None
         if indexvalue.is_constant():
+            arrayvalue.make_len_gt(MODE_ARRAY, op.getdescr(), indexvalue.box.getint())
             # 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)
@@ -367,7 +407,7 @@
         # 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)
+            cf.remember_field_value(arrayvalue, fieldvalue, op)
 
     def optimize_SETARRAYITEM_GC(self, op):
         if self.has_pure_result(rop.GETARRAYITEM_GC_PURE, [op.getarg(0),
@@ -379,6 +419,8 @@
         #
         indexvalue = self.getvalue(op.getarg(1))
         if indexvalue.is_constant():
+            arrayvalue = self.getvalue(op.getarg(0))
+            arrayvalue.make_len_gt(MODE_ARRAY, op.getdescr(), indexvalue.box.getint())
             # use the cache on (arraydescr, index), which is a constant
             cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
             cf.do_setfield(self, op)
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,8 @@
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, CONST_1, CONST_0, \
+                                                  MODE_ARRAY, MODE_STR, MODE_UNICODE
 from pypy.jit.metainterp.history import ConstInt
 from pypy.jit.metainterp.optimizeopt.intutils import (IntBound, IntLowerBound,
     IntUpperBound)
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, CONST_1, CONST_0
 from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from pypy.jit.metainterp.resoperation import rop
 
@@ -14,18 +15,17 @@
         self.posponedop = None
         self.nextop = None
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+    def new(self):
         assert self.posponedop is None
-        return self
+        return OptIntBounds()
+        
+    def flush(self):
+        assert self.posponedop is None
 
     def setup(self):
         self.posponedop = None
         self.nextop = None
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        assert self.posponedop is None
-        return self
-
     def propagate_forward(self, op):
         if op.is_ovf():
             self.posponedop = op
@@ -286,10 +286,27 @@
 
     def optimize_ARRAYLEN_GC(self, op):
         self.emit_operation(op)
-        v1 = self.getvalue(op.result)
-        v1.intbound.make_ge(IntLowerBound(0))
+        array  = self.getvalue(op.getarg(0))
+        result = self.getvalue(op.result)
+        array.make_len_gt(MODE_ARRAY, op.getdescr(), -1)
+        array.lenbound.bound.intersect(result.intbound)
+        result.intbound = array.lenbound.bound
 
-    optimize_STRLEN = optimize_UNICODELEN = optimize_ARRAYLEN_GC
+    def optimize_STRLEN(self, op):
+        self.emit_operation(op)
+        array  = self.getvalue(op.getarg(0))
+        result = self.getvalue(op.result)
+        array.make_len_gt(MODE_STR, op.getdescr(), -1)
+        array.lenbound.bound.intersect(result.intbound)
+        result.intbound = array.lenbound.bound
+
+    def optimize_UNICODELEN(self, op):
+        self.emit_operation(op)
+        array  = self.getvalue(op.getarg(0))
+        result = self.getvalue(op.result)
+        array.make_len_gt(MODE_UNICODE, op.getdescr(), -1)
+        array.lenbound.bound.intersect(result.intbound)
+        result.intbound = array.lenbound.bound
 
     def optimize_STRGETITEM(self, op):
         self.emit_operation(op)
diff --git a/pypy/jit/metainterp/optimizeopt/intutils.py b/pypy/jit/metainterp/optimizeopt/intutils.py
--- a/pypy/jit/metainterp/optimizeopt/intutils.py
+++ b/pypy/jit/metainterp/optimizeopt/intutils.py
@@ -1,4 +1,9 @@
 from pypy.rlib.rarithmetic import ovfcheck, ovfcheck_lshift, LONG_BIT
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.history import BoxInt, ConstInt
+import sys
+MAXINT = sys.maxint
+MININT = -sys.maxint - 1
 
 class IntBound(object):
     _attrs_ = ('has_upper', 'has_lower', 'upper', 'lower')
@@ -210,11 +215,11 @@
         
     def __repr__(self):
         if self.has_lower:
-            l = '%4d' % self.lower
+            l = '%d' % self.lower
         else:
             l = '-Inf'
         if self.has_upper:
-            u = '%3d' % self.upper
+            u = '%d' % self.upper
         else:
             u = 'Inf'
         return '%s <= x <= %s' % (l, u)
@@ -224,7 +229,24 @@
         res.has_lower = self.has_lower
         res.has_upper = self.has_upper
         return res
+
+    def make_guards(self, box, guards):
+        if self.has_lower and self.lower > MININT:
+            bound = self.lower
+            res = BoxInt()
+            op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res)
+            guards.append(op)
+            op = ResOperation(rop.GUARD_TRUE, [res], None)
+            guards.append(op)
+        if self.has_upper and self.upper < MAXINT:
+            bound = self.upper
+            res = BoxInt()
+            op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res)
+            guards.append(op)
+            op = ResOperation(rop.GUARD_TRUE, [res], None)
+            guards.append(op)
     
+
 class IntUpperBound(IntBound):
     def __init__(self, upper):
         self.has_upper = True
@@ -244,7 +266,23 @@
         self.has_upper = False
         self.has_lower = False
         self.upper = 0
-        self.lower = 0        
+        self.lower = 0
+
+class ImmutableIntUnbounded(IntUnbounded):
+    def _raise(self):
+        raise TypeError('ImmutableIntUnbounded is immutable')
+    def make_le(self, other):
+        self._raise()
+    def make_lt(self, other):
+        self._raise()
+    def make_ge(self, other):
+        self._raise()
+    def make_gt(self, other):
+        self._raise()
+    def make_constant(self, value):
+        self._raise()
+    def intersect(self, other):        
+        self._raise()
 
 def min4(t):
     return min(min(t[0], t[1]), min(t[2], t[3]))
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
@@ -1,64 +1,105 @@
 from pypy.jit.metainterp import jitprof, resume, compile
 from pypy.jit.metainterp.executor import execute_nonspec
 from pypy.jit.metainterp.history import BoxInt, BoxFloat, Const, ConstInt, REF
-from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded, \
+                                                     ImmutableIntUnbounded, \
+                                                     IntLowerBound, MININT, MAXINT
 from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
     args_dict)
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
 from pypy.tool.pairtype import extendabletype
+from pypy.rlib.debug import debug_start, debug_stop, debug_print
 
 LEVEL_UNKNOWN    = '\x00'
 LEVEL_NONNULL    = '\x01'
 LEVEL_KNOWNCLASS = '\x02'     # might also mean KNOWNARRAYDESCR, for arrays
 LEVEL_CONSTANT   = '\x03'
 
-import sys
-MAXINT = sys.maxint
-MININT = -sys.maxint - 1
+MODE_ARRAY   = '\x00'
+MODE_STR     = '\x01'
+MODE_UNICODE = '\x02'
+class LenBound(object):
+    def __init__(self, mode, descr, bound):
+        self.mode = mode
+        self.descr = descr
+        self.bound = bound
 
 class OptValue(object):
     __metaclass__ = extendabletype
-    _attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound')
+    _attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound', 'lenbound')
     last_guard_index = -1
 
     level = LEVEL_UNKNOWN
     known_class = None
-    intbound = None
+    intbound = ImmutableIntUnbounded()
+    lenbound = None
 
-    def __init__(self, box):
+    def __init__(self, box, level=None, known_class=None, intbound=None):
         self.box = box
-        self.intbound = IntBound(MININT, MAXINT) #IntUnbounded()
+        if level is not None:
+            self.level = level
+        self.known_class = known_class
+        if intbound:
+            self.intbound = intbound
+        else:
+            if isinstance(box, BoxInt):
+                self.intbound = IntBound(MININT, MAXINT)
+            else:
+                self.intbound = IntUnbounded()
+
         if isinstance(box, Const):
             self.make_constant(box)
         # invariant: box is a Const if and only if level == LEVEL_CONSTANT
 
+    def make_len_gt(self, mode, descr, val):
+        if self.lenbound:
+            assert self.lenbound.mode == mode
+            assert self.lenbound.descr == descr
+            self.lenbound.bound.make_gt(IntBound(val, val))
+        else:
+            self.lenbound = LenBound(mode, descr, IntLowerBound(val + 1))
+
+    def make_guards(self, box):
+        guards = []
+        if self.level == LEVEL_CONSTANT:
+            op = ResOperation(rop.GUARD_VALUE, [box, self.box], None)
+            guards.append(op)
+        elif self.level == LEVEL_KNOWNCLASS:
+            op = ResOperation(rop.GUARD_NONNULL, [box], None)
+            guards.append(op)            
+            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+            guards.append(op)
+        else:
+            if self.level == LEVEL_NONNULL:
+                op = ResOperation(rop.GUARD_NONNULL, [box], None)
+                guards.append(op)
+            self.intbound.make_guards(box, guards)
+            if self.lenbound:
+                lenbox = BoxInt()
+                if self.lenbound.mode == MODE_ARRAY:
+                    op = ResOperation(rop.ARRAYLEN_GC, [box], lenbox, self.lenbound.descr)
+                elif self.lenbound.mode == MODE_STR:
+                    op = ResOperation(rop.STRLEN, [box], lenbox, self.lenbound.descr)
+                elif self.lenbound.mode == MODE_UNICODE:
+                    op = ResOperation(rop.UNICODELEN, [box], lenbox, self.lenbound.descr)
+                else:
+                    debug_print("Unknown lenbound mode")
+                    assert False
+                guards.append(op)
+                self.lenbound.bound.make_guards(lenbox, guards)
+
+        return guards
+
     def force_box(self):
         return self.box
 
     def get_key_box(self):
         return self.box
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key not in already_seen:
-            boxes.append(self.force_box())
-            already_seen[self.get_key_box()] = None
-
-    def get_reconstructed(self, optimizer, valuemap):
-        if self in valuemap:
-            return valuemap[self]
-        new = self.reconstruct_for_next_iteration(optimizer)
-        valuemap[self] = new
-        self.reconstruct_childs(new, valuemap)
-        return new
-
-    def reconstruct_for_next_iteration(self, optimizer):
+    def force_at_end_of_preamble(self, already_forced):
         return self
 
-    def reconstruct_childs(self, new, valuemap):
-        pass
-
     def get_args_for_fail(self, modifier):
         pass
 
@@ -82,6 +123,7 @@
         assert isinstance(constbox, Const)
         self.box = constbox
         self.level = LEVEL_CONSTANT
+        
         if isinstance(constbox, ConstInt):
             val = constbox.getint()
             self.intbound = IntBound(val, val)
@@ -222,7 +264,9 @@
 
     def pure(self, opnum, args, result):
         op = ResOperation(opnum, args, result)
-        self.optimizer.pure_operations[self.optimizer.make_args_key(op)] = op
+        key = self.optimizer.make_args_key(op)
+        if key not in self.optimizer.pure_operations:
+            self.optimizer.pure_operations[key] = op
 
     def has_pure_result(self, opnum, args, descr):
         op = ResOperation(opnum, args, None, descr)
@@ -235,16 +279,22 @@
     def setup(self):
         pass
 
+    def turned_constant(self, value):
+        pass
+
     def force_at_end_of_preamble(self):
         pass
 
-    def turned_constant(self, value):
+    # It is too late to force stuff here, it must be done in force_at_end_of_preamble
+    def new(self):
+        raise NotImplementedError
+
+    # Called after last operation has been propagated to flush out any posponed ops
+    def flush(self):
         pass
 
-    def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
-        #return self.__class__()
-        raise NotImplementedError
-
+    def produce_potential_short_preamble_ops(self, potential_ops):
+        pass
 
 class Optimizer(Optimization):
 
@@ -257,8 +307,8 @@
         self.interned_refs = self.cpu.ts.new_ref_dict()
         self.resumedata_memo = resume.ResumeDataLoopMemo(metainterp_sd)
         self.bool_boxes = {}
-        self.loop_invariant_results = {}
         self.pure_operations = args_dict()
+        self.emitted_pure_operations = {}
         self.producer = {}
         self.pendingfields = []
         self.posponedop = None
@@ -266,6 +316,8 @@
         self.quasi_immutable_deps = None
         self.opaque_pointers = {}
         self.newoperations = []
+        self.emitting_dissabled = False
+        self.emitted_guards = 0        
         if loop is not None:
             self.call_pure_results = loop.call_pure_results
 
@@ -287,39 +339,32 @@
         self.optimizations  = optimizations
 
     def force_at_end_of_preamble(self):
-        self.resumedata_memo = resume.ResumeDataLoopMemo(self.metainterp_sd)
         for o in self.optimizations:
             o.force_at_end_of_preamble()
 
-    def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
-        assert optimizer is None
-        assert valuemap is None
-        valuemap = {}
+    def flush(self):
+        for o in self.optimizations:
+            o.flush()
+        assert self.posponedop is None
+
+    def new(self):
+        assert self.posponedop is None
         new = Optimizer(self.metainterp_sd, self.loop)
-        optimizations = [o.reconstruct_for_next_iteration(new, valuemap) for o in
-                         self.optimizations]
+        optimizations = [o.new() for o in self.optimizations]
         new.set_optimizations(optimizations)
-
-        new.values = {}
-        for box, value in self.values.items():
-            new.values[box] = value.get_reconstructed(new, valuemap)
-        new.interned_refs = self.interned_refs
-        new.bool_boxes = {}
-        for value in new.bool_boxes.keys():
-            new.bool_boxes[value.get_reconstructed(new, valuemap)] = None
-
-        # FIXME: Move to rewrite.py
-        new.loop_invariant_results = {}
-        for key, value in self.loop_invariant_results.items():
-            new.loop_invariant_results[key] = \
-                                 value.get_reconstructed(new, valuemap)
-
-        new.pure_operations = self.pure_operations
-        new.producer = self.producer
-        assert self.posponedop is None
         new.quasi_immutable_deps = self.quasi_immutable_deps
-
         return new
+        
+    def produce_potential_short_preamble_ops(self, sb):
+        for op in self.emitted_pure_operations:
+            if op.getopnum() == rop.GETARRAYITEM_GC_PURE or \
+               op.getopnum() == rop.STRGETITEM or \
+               op.getopnum() == rop.UNICODEGETITEM:
+                if not self.getvalue(op.getarg(1)).is_constant():
+                    continue
+            sb.add_potential(op)
+        for opt in self.optimizations:
+            opt.produce_potential_short_preamble_ops(sb)
 
     def turned_constant(self, value):
         for o in self.optimizations:
@@ -434,10 +479,11 @@
         return True
 
     def emit_operation(self, op):
-        ###self.heap_op_optimizer.emitting_operation(op)
-        self._emit_operation(op)
-
-    def _emit_operation(self, op):
+        if op.returns_bool_result():
+            self.bool_boxes[self.getvalue(op.result)] = None
+        if self.emitting_dissabled:
+            return
+        
         for i in range(op.numargs()):
             arg = op.getarg(i)
             if arg in self.values:
@@ -446,11 +492,10 @@
         self.metainterp_sd.profiler.count(jitprof.OPT_OPS)
         if op.is_guard():
             self.metainterp_sd.profiler.count(jitprof.OPT_GUARDS)
+            self.emitted_guards += 1 # FIXME: can we reuse above counter?
             op = self.store_final_boxes_in_guard(op)
         elif op.can_raise():
             self.exception_might_have_happened = True
-        elif op.returns_bool_result():
-            self.bool_boxes[self.getvalue(op.result)] = None
         self.newoperations.append(op)
 
     def store_final_boxes_in_guard(self, op):
@@ -534,6 +579,7 @@
                 return
             else:
                 self.pure_operations[args] = op
+                self.emitted_pure_operations[op] = True
 
         # otherwise, the operation remains
         self.emit_operation(op)
@@ -561,6 +607,30 @@
         self.opaque_pointers[value] = True
         self.make_equal_to(op.result, value)
 
+    def optimize_GETARRAYITEM_GC_PURE(self, op):
+        indexvalue = self.getvalue(op.getarg(1))
+        if indexvalue.is_constant():
+            arrayvalue = self.getvalue(op.getarg(0))
+            arrayvalue.make_len_gt(MODE_ARRAY, op.getdescr(), indexvalue.box.getint())
+        self.optimize_default(op)
+
+    def optimize_STRGETITEM(self, op):
+        indexvalue = self.getvalue(op.getarg(1))
+        if indexvalue.is_constant():
+            arrayvalue = self.getvalue(op.getarg(0))
+            arrayvalue.make_len_gt(MODE_STR, op.getdescr(), indexvalue.box.getint())
+        self.optimize_default(op)
+
+    def optimize_UNICODEGETITEM(self, op):
+        indexvalue = self.getvalue(op.getarg(1))
+        if indexvalue.is_constant():
+            arrayvalue = self.getvalue(op.getarg(0))
+            arrayvalue.make_len_gt(MODE_UNICODE, op.getdescr(), indexvalue.box.getint())
+        self.optimize_default(op)
+        
+
+    
+
 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
@@ -13,9 +13,16 @@
     """Rewrite operations into equivalent, cheaper operations.
        This includes already executed operations and constants.
     """
+    def __init__(self):
+        self.loop_invariant_results = {}
+        self.loop_invariant_producer = {}
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
+    def new(self):
+        return OptRewrite()
+        
+    def produce_potential_short_preamble_ops(self, sb):
+        for op in self.loop_invariant_producer.values():
+            sb.add_potential(op)
 
     def propagate_forward(self, op):
         args = self.optimizer.make_args_key(op)
@@ -344,16 +351,18 @@
         # expects a compile-time constant
         assert isinstance(arg, Const)
         key = make_hashable_int(arg.getint())
-        resvalue = self.optimizer.loop_invariant_results.get(key, None)
+        
+        resvalue = self.loop_invariant_results.get(key, None)
         if resvalue is not None:
             self.make_equal_to(op.result, resvalue)
             return
         # change the op to be a normal call, from the backend's point of view
         # there is no reason to have a separate operation for this
+        self.loop_invariant_producer[key] = op
         op = op.copy_and_change(rop.CALL)
         self.emit_operation(op)
         resvalue = self.getvalue(op.result)
-        self.optimizer.loop_invariant_results[key] = resvalue
+        self.loop_invariant_results[key] = resvalue
 
     def _optimize_nullness(self, op, box, expect_nonnull):
         value = self.getvalue(box)
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
@@ -2742,11 +2742,11 @@
     def test_residual_call_invalidate_some_arrays(self):
         ops = """
         [p1, p2, i1]
-        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
         p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
         i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
         i3 = call(i1, descr=writearraydescr)
-        p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p5 = getarrayitem_gc(p2, 0, descr=arraydescr2)
         p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
         i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
         escape(p3)
@@ -2759,7 +2759,7 @@
         """
         expected = """
         [p1, p2, i1]
-        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
         p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
         i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
         i3 = call(i1, descr=writearraydescr)
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
@@ -61,7 +61,9 @@
             boxes = []
         boxes = []
     def clone_if_mutable(self):
-        return self
+        return FakeDescr()
+    def __eq__(self, other):
+        return isinstance(other, Storage) or isinstance(other, FakeDescr)
 
 
 class BaseTestWithUnroll(BaseTest):
@@ -69,13 +71,14 @@
     enable_opts = "intbounds:rewrite:virtualize:string:heap:unroll"
 
     def optimize_loop(self, ops, expected, expected_preamble=None,
-                      call_pure_results=None):
+                      call_pure_results=None, expected_short=None):
         loop = self.parse(ops)
         if expected != "crash!":
             expected = self.parse(expected)
         if expected_preamble:
             expected_preamble = self.parse(expected_preamble)
-
+        if expected_short:
+            expected_short = self.parse(expected_short)
         loop.preamble = TreeLoop('preamble')
         loop.preamble.inputargs = loop.inputargs
         loop.preamble.token = LoopToken()
@@ -84,17 +87,33 @@
         self._do_optimize_loop(loop, call_pure_results)
         #
         print
+        print "Preamble:"
         print loop.preamble.inputargs
-        print '\n'.join([str(o) for o in loop.preamble.operations])
+        if loop.preamble.operations:
+            print '\n'.join([str(o) for o in loop.preamble.operations])
+        else:
+            print 'Failed!'
         print
+        print "Loop:"
         print loop.inputargs
         print '\n'.join([str(o) for o in loop.operations])
         print
+        if expected_short:
+            print "Short Preamble:"
+            short = loop.preamble.token.short_preamble[0]
+            print short.inputargs
+            print '\n'.join([str(o) for o in short.operations])        
+            print
+        
         assert expected != "crash!", "should have raised an exception"
         self.assert_equal(loop, expected)
         if expected_preamble:
             self.assert_equal(loop.preamble, expected_preamble,
                               text_right='expected preamble')
+        if expected_short:
+            self.assert_equal(short, expected_short,
+                              text_right='expected short preamble')
+            
         return loop
 
 class OptimizeOptTest(BaseTestWithUnroll):
@@ -840,7 +859,13 @@
         p3sub = new_with_vtable(ConstClass(node_vtable2))
         setfield_gc(p3sub, i1, descr=valuedescr)
         setfield_gc(p1, p3sub, descr=nextdescr)
-        jump(i1, p1, p3sub)
+        # XXX: We get two extra operations here because the setfield
+        #      above is the result of forcing p1 and thus not 
+        #      registered with the heap optimizer. I've makred tests
+        #      below with VIRTUALHEAP if they suffer from this issue
+        p3sub2 = getfield_gc(p1, descr=nextdescr) 
+        guard_nonnull_class(p3sub2, ConstClass(node_vtable2)) []
+        jump(i1, p1, p3sub2)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -871,7 +896,9 @@
         guard_true(i2b) []
         p3 = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p3, i2, descr=nextdescr)
-        jump(p3, i2)
+        # XXX: VIRTUALHEAP (see above)
+        i3 = getfield_gc(p3, descr=nextdescr)
+        jump(p3, i3)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -1166,6 +1193,29 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_virtual_field_forced_by_lazy_setfield(self):
+        ops = """
+        [i0, p1, p3]
+        i28 = int_add(i0, 1)
+        p30 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p30, i28, descr=nextdescr)
+        setfield_gc(p3, p30, descr=valuedescr)
+        p45 = getfield_gc(p3, descr=valuedescr)
+        i29 = int_add(i28, 1)
+        jump(i29, p45, p3)
+        """
+        preamble = """
+        [i0, p1, p3]
+        i28 = int_add(i0, 1)
+        i29 = int_add(i28, 1)
+        p30 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p30, i28, descr=nextdescr)
+        setfield_gc(p3, p30, descr=valuedescr)
+        jump(i29, p30, p3)
+        """
+        expected = preamble
+        self.optimize_loop(ops, expected, preamble)
+
     def test_nonvirtual_1(self):
         ops = """
         [i]
@@ -1308,15 +1358,78 @@
         ops = """
         [i]
         i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr)
-        jump(i1)
-        """
-        preamble = ops
-        expected = """
+        call(i1, descr=nonwritedescr)
+        jump(i)
+        """
+        preamble = """
         [i]
-        jump(i)
+        i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr)
+        call(i1, descr=nonwritedescr)
+        jump(i, i1)
+        """
+        expected = """
+        [i, i1]
+        call(i1, descr=nonwritedescr)
+        jump(i, i1)
         """
         self.optimize_loop(ops, expected, preamble)
 
+    def test_varray_boxed1(self):
+        ops = """
+        [p0, p8]
+        p11 = getfield_gc(p0, descr=otherdescr)
+        guard_nonnull(p11) [p0, p8]
+        guard_class(p11, ConstClass(node_vtable2)) [p0, p8]
+        p14 = getfield_gc(p11, descr=otherdescr)
+        guard_isnull(p14) [p0, p8]
+        p18 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        guard_isnull(p18) [p0, p8]
+        p31 = new(descr=ssize)
+        setfield_gc(p31, 0, descr=adescr)
+        p33 = new_array(0, descr=arraydescr)
+        setfield_gc(p31, p33, descr=bdescr)
+        p35 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p35, p31, descr=valuedescr)
+        jump(p0, p35)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_varray_boxed_simplified(self):
+        ops = """
+        [p0, p8]
+        p18 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        guard_isnull(p18) [p0, p8]
+        p31 = new(descr=ssize)
+        p35 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p35, p31, descr=valuedescr)        
+        jump(p0, p35)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_varray_boxed_noconst(self):
+        ops = """
+        [p0, p8, p18, p19]
+        guard_isnull(p18) [p0, p8]
+        p31 = new(descr=ssize)
+        p35 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p35, p31, descr=valuedescr)        
+        jump(p0, p35, p19, p18)
+        """
+        expected = """
+        [p0, p19]
+        guard_isnull(p19) [p0]
+        jump(p0, NULL)
+        """
+        self.optimize_loop(ops, expected)
+        
     def test_varray_1(self):
         ops = """
         [i1]
@@ -1552,6 +1665,24 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_duplicate_getfield_2(self):
+        ops = """
+        [p1, p2, i0]
+        i1 = getfield_gc(p1, descr=valuedescr)
+        i2 = getfield_gc(p2, descr=valuedescr)
+        i3 = getfield_gc(p1, descr=valuedescr)
+        i4 = getfield_gc(p2, descr=valuedescr)
+        i5 = int_add(i3, i4)
+        i6 = int_add(i0, i5)
+        jump(p1, p2, i6)
+        """
+        expected = """
+        [p1, p2, i0, i5]
+        i6 = int_add(i0, i5)
+        jump(p1, p2, i6, i5)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_getfield_after_setfield(self):
         ops = """
         [p1, i1]
@@ -1728,6 +1859,7 @@
         """
         expected = """
         [p1, i1, i2]
+        setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2)
         """
         # in this case, all setfields are removed, because we can prove
@@ -1872,14 +2004,14 @@
         guard_true(i3) []
         i4 = int_neg(i2)
         setfield_gc(p1, i2, descr=valuedescr)
-        jump(p1, i1, i2, i4)
-        """
-        expected = """
-        [p1, i1, i2, i4]
+        jump(p1, i1, i2, i4, i4)
+        """
+        expected = """
+        [p1, i1, i2, i4, i5]
         setfield_gc(p1, i1, descr=valuedescr)
         guard_true(i4) []
         setfield_gc(p1, i2, descr=valuedescr)
-        jump(p1, i1, i2, 1)
+        jump(p1, i1, i2, i5, i5)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -1902,14 +2034,14 @@
         i4 = int_neg(i2)
         setfield_gc(p1, NULL, descr=nextdescr)
         escape()
-        jump(p1, i2, i4)
-        """
-        expected = """
-        [p1, i2, i4]
+        jump(p1, i2, i4, i4)
+        """
+        expected = """
+        [p1, i2, i4, i5]
         guard_true(i4) [p1]
         setfield_gc(p1, NULL, descr=nextdescr)
         escape()
-        jump(p1, i2, 1)
+        jump(p1, i2, i5, i5)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -1931,14 +2063,14 @@
         i4 = int_neg(i2)
         setfield_gc(p1, NULL, descr=nextdescr)
         escape()
-        jump(p1, i2, i4)
-        """
-        expected = """
-        [p1, i2, i4]
+        jump(p1, i2, i4, i4)
+        """
+        expected = """
+        [p1, i2, i4, i5]
         guard_true(i4) [i2, p1]
         setfield_gc(p1, NULL, descr=nextdescr)
         escape()
-        jump(p1, i2, 1)
+        jump(p1, i2, i5, i5)
         """
         self.optimize_loop(ops, expected)
 
@@ -1954,14 +2086,22 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2, i4)
         """
-        preamble = ops
-        expected = """
-        [p1, i1, i2, i4]
+        preamble = """
+        [p1, i1, i2, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i5 = int_eq(i3, 5)
+        guard_true(i5) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, i4, i4)
+        """
+        expected = """
+        [p1, i1, i2, i4, i7]
         setfield_gc(p1, i1, descr=valuedescr)
         i5 = int_eq(i4, 5)
         guard_true(i5) []
         setfield_gc(p1, i2, descr=valuedescr)
-        jump(p1, i1, i2, 5)
+        jump(p1, i1, i2, i7, i7)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -2035,7 +2175,25 @@
         jump(p1)
         """
         self.optimize_loop(ops, expected)
-
+        
+    def test_duplicate_getarrayitem_2(self):
+        ops = """
+        [p1, i0]
+        i2 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        i3 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+        i4 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        i5 = getarrayitem_gc(p1, 1, descr=arraydescr2)
+        i6 = int_add(i3, i4)
+        i7 = int_add(i0, i6)
+        jump(p1, i7)
+        """
+        expected = """
+        [p1, i0, i6]
+        i7 = int_add(i0, i6)
+        jump(p1, i7, i6)
+        """
+        self.optimize_loop(ops, expected)
+        
     def test_duplicate_getarrayitem_after_setarrayitem_1(self):
         ops = """
         [p1, p2]
@@ -2161,15 +2319,15 @@
         p2 = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p2, p4, descr=nextdescr)
         setfield_gc(p1, p2, descr=nextdescr)
-        jump(p1, i2, i4, p4)
-        """
-        expected = """
-        [p1, i2, i4, p4]
+        jump(p1, i2, i4, p4, i4)
+        """
+        expected = """
+        [p1, i2, i4, p4, i5]
         guard_true(i4) [p1, p4]
         p2 = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p2, p4, descr=nextdescr)
         setfield_gc(p1, p2, descr=nextdescr)
-        jump(p1, i2, 1, p4)
+        jump(p1, i2, i5, p4, i5)
         """
         self.optimize_loop(ops, expected, preamble)
 
@@ -2625,6 +2783,101 @@
         """
         self.optimize_loop(ops, expected, preamble)
 
+    def test_remove_duplicate_pure_op_ovf_with_lazy_setfield(self):
+        py.test.skip('this optimization is not yet supprted')
+        ops = """
+        [i1, p1]
+        i3 = int_add_ovf(i1, 1)
+        guard_no_overflow() []
+        i3b = int_is_true(i3)
+        guard_true(i3b) []
+        setfield_gc(p1, i1, descr=valuedescr)
+        i4 = int_add_ovf(i1, 1)
+        guard_no_overflow() []
+        i4b = int_is_true(i4)
+        guard_true(i4b) []
+        escape(i3)
+        escape(i4)
+        jump(i1, p1)
+        """
+        preamble = """
+        [i1, p1]
+        i3 = int_add_ovf(i1, 1)
+        guard_no_overflow() []
+        i3b = int_is_true(i3)
+        guard_true(i3b) []
+        setfield_gc(p1, i1, descr=valuedescr)        
+        escape(i3)
+        escape(i3)
+        jump(i1, p1, i3)
+        """
+        expected = """
+        [i1, p1, i3]
+        setfield_gc(p1, i1, descr=valuedescr)        
+        escape(i3)
+        escape(i3)
+        jump(i1, p1, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_ovf_guard_in_short_preamble1(self):
+        ops = """
+        [p8, p11, i24]
+        p26 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p26, i24, descr=adescr)        
+        i34 = getfield_gc_pure(p11, descr=valuedescr)
+        i35 = getfield_gc_pure(p26, descr=adescr)
+        i36 = int_add_ovf(i34, i35)
+        guard_no_overflow() []
+        jump(p8, p11, i35)
+        """
+        expected = """
+        [p8, p11, i26]
+        jump(p8, p11, i26)        
+        """
+        self.optimize_loop(ops, expected)
+        
+    def test_ovf_guard_in_short_preamble2(self):
+        ops = """
+        [p8, p11, p12]
+        p16 = getfield_gc(p8, descr=valuedescr)
+        i17 = getfield_gc(p8, descr=nextdescr)
+        i19 = getfield_gc(p16, descr=valuedescr)
+        i20 = int_ge(i17, i19)
+        guard_false(i20) []
+        i21 = getfield_gc(p16, descr=otherdescr)
+        i22 = getfield_gc(p16, descr=nextdescr)
+        i23 = int_mul(i17, i22)
+        i24 = int_add(i21, i23)
+        p26 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p26, i24, descr=adescr)
+        i28 = int_add(i17, 1)
+        setfield_gc(p8, i28, descr=nextdescr)
+        i34 = getfield_gc_pure(p11, descr=valuedescr)
+        i35 = getfield_gc_pure(p26, descr=adescr)
+        guard_nonnull(p12) []
+        i36 = int_add_ovf(i34, i35)
+        guard_no_overflow() []
+        p38 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p38, i36, descr=adescr)
+        jump(p8, p11, p26)
+        """
+        expected = """
+        [p8, p11, i24, i39, i19, p16, i21, i34]
+        i40 = int_ge(i39, i19)
+        guard_false(i40) []
+        i41 = getfield_gc(p16, descr=nextdescr)
+        i42 = int_mul(i39, i41)
+        i43 = int_add(i21, i42)
+        i44 = int_add(i39, 1)
+        setfield_gc(p8, i44, descr=nextdescr)
+        i45 = int_add_ovf(i34, i43)
+        guard_no_overflow() []
+        jump(p8, p11, i43, i44, i19, p16, i21, i34)
+        """
+        self.optimize_loop(ops, expected)
+
+
     def test_int_and_or_with_zero(self):
         ops = """
         [i0, i1]
@@ -3899,11 +4152,13 @@
         jump(p4364)
         """
         expected = """
-        [i0, i1]
+        [i0]
+        i1 = int_sub_ovf(i0, 1)
+        guard_no_overflow() []
         escape(i1)
         i2 = int_add_ovf(i0, 1)
         guard_no_overflow() []
-        jump(i2, i0)
+        jump(i2)
         """
         self.optimize_loop(ops, expected)
 
@@ -4867,32 +5122,38 @@
 
     def test_invariant_ovf(self):
         ops = """
-        [i0, i1, i10, i11, i12]
+        [i0, i1, i10, i11, i20, i21]
         i2 = int_add_ovf(i0, i1)
         guard_no_overflow() []
         i3 = int_sub_ovf(i0, i1)
         guard_no_overflow() []
         i4 = int_mul_ovf(i0, i1)
         guard_no_overflow() []
+        escape(i2)
+        escape(i3)
+        escape(i4)
         i24 = int_mul_ovf(i10, i11)
         guard_no_overflow() []
         i23 = int_sub_ovf(i10, i11)
         guard_no_overflow() []
         i22 = int_add_ovf(i10, i11)
         guard_no_overflow() []
-        jump(i0, i1, i2, i3, i4)
-        """
-        expected = """
-        [i0, i1, i10, i11, i12]
+        jump(i0, i1, i20, i21, i20, i21)
+        """
+        expected = """
+        [i0, i1, i10, i11, i2, i3, i4]
+        escape(i2)
+        escape(i3)
+        escape(i4)        
         i24 = int_mul_ovf(i10, i11)
         guard_no_overflow() []
         i23 = int_sub_ovf(i10, i11)
         guard_no_overflow() []
         i22 = int_add_ovf(i10, i11)
         guard_no_overflow() []
-        jump(i0, i1, i10, i11, i12)
-        """
-        self.optimize_loop(ops, expected, ops)
+        jump(i0, i1, i10, i11, i2, i3, i4) 
+        """
+        self.optimize_loop(ops, expected)
 
     def test_value_proven_to_be_constant_after_two_iterations(self):
         class FakeDescr(AbstractDescr):
@@ -4908,8 +5169,8 @@
         ops = """
         [p0, p1, p2, p3, i4, p5, i6, p7, p8, p9, p14]
         guard_value(i4, 3) []
-        guard_class(p9, 17278984) []
-        guard_class(p9, 17278984) []
+        guard_class(p9, ConstClass(node_vtable)) []
+        guard_class(p9, ConstClass(node_vtable)) []
         p22 = getfield_gc(p9, descr=inst_w_seq)
         guard_nonnull(p22) []
         i23 = getfield_gc(p9, descr=inst_index)
@@ -4924,11 +5185,11 @@
         guard_class(p14, 17273920) []
         guard_class(p14, 17273920) []
 
-        p75 = new_with_vtable(17278984)
+        p75 = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p75, p14, descr=inst_w_seq)
         setfield_gc(p75, 0, descr=inst_index)
-        guard_class(p75, 17278984) []
-        guard_class(p75, 17278984) []
+        guard_class(p75, ConstClass(node_vtable)) []
+        guard_class(p75, ConstClass(node_vtable)) []
         p79 = getfield_gc(p75, descr=inst_w_seq)
         guard_nonnull(p79) []
         i80 = getfield_gc(p75, descr=inst_index)
@@ -4974,6 +5235,7 @@
         """
         expected = """
         [p0]
+        setfield_gc(p0, p0, descr=valuedescr)
         jump(p0)
         """
         self.optimize_loop(ops, expected, preamble)
@@ -5060,9 +5322,7 @@
         self.optimize_loop(ops, expected)
 
     # ----------
-    def optimize_strunicode_loop(self, ops, optops, preamble=None):
-        if not preamble:
-            preamble = ops # FIXME: Force proper testing of preamble
+    def optimize_strunicode_loop(self, ops, optops, preamble):
         # check with the arguments passed in
         self.optimize_loop(ops, optops, preamble)
         # check with replacing 'str' with 'unicode' everywhere
@@ -5082,7 +5342,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_newstr_2(self):
         ops = """
@@ -5098,7 +5358,7 @@
         [i0, i1]
         jump(i1, i0)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_concat_1(self):
         ops = """
@@ -5106,7 +5366,7 @@
         p3 = call(0, p1, p2, descr=strconcatdescr)
         jump(p2, p3)
         """
-        expected = """
+        preamble = """
         [p1, p2]
         i1 = strlen(p1)
         i2 = strlen(p2)
@@ -5114,9 +5374,18 @@
         p3 = newstr(i3)
         copystrcontent(p1, p3, 0, 0, i1)
         copystrcontent(p2, p3, 0, i1, i2)
-        jump(p2, p3)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p2, p3, i2)
+        """
+        expected = """
+        [p1, p2, i1]
+        i2 = strlen(p2)
+        i3 = int_add(i1, i2)
+        p3 = newstr(i3)
+        copystrcontent(p1, p3, 0, 0, i1)
+        copystrcontent(p2, p3, 0, i1, i2)
+        jump(p2, p3, i2)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_concat_vstr2_str(self):
         ops = """
@@ -5137,7 +5406,7 @@
         copystrcontent(p2, p3, 0, 2, i2)
         jump(i1, i0, p3)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_concat_str_vstr2(self):
         ops = """
@@ -5160,7 +5429,7 @@
         i6 = int_add(i5, 1)      # will be killed by the backend
         jump(i1, i0, p3)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_concat_str_str_str(self):
         ops = """
@@ -5169,7 +5438,7 @@
         p5 = call(0, p4, p3, descr=strconcatdescr)
         jump(p2, p3, p5)
         """
-        expected = """
+        preamble = """
         [p1, p2, p3]
         i1 = strlen(p1)
         i2 = strlen(p2)
@@ -5180,9 +5449,20 @@
         copystrcontent(p1, p5, 0, 0, i1)
         copystrcontent(p2, p5, 0, i1, i2)
         copystrcontent(p3, p5, 0, i12, i3)
-        jump(p2, p3, p5)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p2, p3, p5, i2, i3)
+        """
+        expected = """
+        [p1, p2, p3, i1, i2]
+        i12 = int_add(i1, i2)
+        i3 = strlen(p3)
+        i123 = int_add(i12, i3)
+        p5 = newstr(i123)
+        copystrcontent(p1, p5, 0, 0, i1)
+        copystrcontent(p2, p5, 0, i1, i2)
+        copystrcontent(p3, p5, 0, i12, i3)
+        jump(p2, p3, p5, i2, i3)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_concat_str_cstr1(self):
         ops = """
@@ -5199,7 +5479,7 @@
         strsetitem(p3, i2, 120)     # == ord('x')
         jump(p3)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_concat_consts(self):
         ops = """
@@ -5210,17 +5490,58 @@
         escape(p3)
         jump()
         """
-        preamble = """
-        []
-        p3 = call(0, s"ab", s"cde", descr=strconcatdescr)
-        escape(p3)
-        jump()
-        """
         expected = """
         []
         escape(s"abcde")
         jump()
         """
+        self.optimize_strunicode_loop(ops, expected, expected)
+
+    def test_str_slice_len_surviving1(self):
+        ops = """
+        [p1, i1, i2, i3]
+        escape(i3)
+        p2 = call(0, p1, i1, i2, descr=strslicedescr)
+        i4 = strlen(p2)
+        jump(p1, i1, i2, i4)
+        """
+        preamble = """
+        [p1, i1, i2, i3]
+        escape(i3)
+        i4 = int_sub(i2, i1)
+        jump(p1, i1, i2, i4, i4)
+        """
+        expected = """
+        [p1, i1, i2, i3, i4]
+        escape(i3)
+        jump(p1, i1, i2, i4, i4)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
+
+    def test_str_slice_len_surviving2(self):
+        ops = """
+        [p1, i1, i2, p2]
+        i5 = getfield_gc(p2, descr=valuedescr)
+        escape(i5)
+        p3 = call(0, p1, i1, i2, descr=strslicedescr)
+        i4 = strlen(p3)
+        setfield_gc(p2, i4, descr=valuedescr)
+        jump(p1, i1, i2, p2)
+        """
+        preamble = """
+        [p1, i1, i2, p2]
+        i5 = getfield_gc(p2, descr=valuedescr)
+        escape(i5)
+        i4 = int_sub(i2, i1)
+        setfield_gc(p2, i4, descr=valuedescr)
+        jump(p1, i1, i2, p2, i4, i4)
+        """
+        expected = """
+        [p1, i1, i2, p2, i5, i6]
+        escape(i5)
+        setfield_gc(p2, i6, descr=valuedescr)
+        jump(p1, i1, i2, p2, i6, i6)
+        """
         self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_slice_1(self):
@@ -5229,14 +5550,20 @@
         p2 = call(0, p1, i1, i2, descr=strslicedescr)
         jump(p2, i1, i2)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2]
         i3 = int_sub(i2, i1)
         p2 = newstr(i3)
         copystrcontent(p1, p2, i1, 0, i3)
-        jump(p2, i1, i2)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p2, i1, i2, i3)
+        """
+        expected = """
+        [p1, i1, i2, i3]
+        p2 = newstr(i3)
+        copystrcontent(p1, p2, i1, 0, i3)
+        jump(p2, i1, i2, i3)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_slice_2(self):
         ops = """
@@ -5250,7 +5577,7 @@
         copystrcontent(p1, p2, 0, 0, i2)
         jump(p2, i2)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_slice_3(self):
         ops = """
@@ -5259,16 +5586,22 @@
         p3 = call(0, p2, i3, i4, descr=strslicedescr)
         jump(p3, i1, i2, i3, i4)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, i3, i4]
         i0 = int_sub(i2, i1)     # killed by the backend
         i5 = int_sub(i4, i3)
         i6 = int_add(i1, i3)
         p3 = newstr(i5)
         copystrcontent(p1, p3, i6, 0, i5)
-        jump(p3, i1, i2, i3, i4)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p3, i1, i2, i3, i4, i5, i6)
+        """
+        expected = """
+        [p1, i1, i2, i3, i4, i5, i6]
+        p3 = newstr(i5)
+        copystrcontent(p1, p3, i6, 0, i5)
+        jump(p3, i1, i2, i3, i4, i5, i6)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_slice_getitem1(self):
         ops = """
@@ -5278,15 +5611,21 @@
         escape(i4)
         jump(p1, i1, i2, i3)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, i3]
         i6 = int_sub(i2, i1)      # killed by the backend
         i5 = int_add(i1, i3)
         i4 = strgetitem(p1, i5)
         escape(i4)
-        jump(p1, i1, i2, i3)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p1, i1, i2, i3, i5)
+        """
+        expected = """
+        [p1, i1, i2, i3, i5]
+        i4 = strgetitem(p1, i5)
+        escape(i4)
+        jump(p1, i1, i2, i3, i5)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_slice_plain(self):
         ops = """
@@ -5304,7 +5643,7 @@
         escape(i4)
         jump(i3, i4)
         """
-        self.optimize_strunicode_loop(ops, expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_str_slice_concat(self):
         ops = """
@@ -5313,7 +5652,7 @@
         p4 = call(0, p3, p2, descr=strconcatdescr)
         jump(p4, i1, i2, p2)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, p2]
         i3 = int_sub(i2, i1)     # length of p3
         i4 = strlen(p2)
@@ -5321,14 +5660,22 @@
         p4 = newstr(i5)
         copystrcontent(p1, p4, i1, 0, i3)
         copystrcontent(p2, p4, 0, i3, i4)
-        jump(p4, i1, i2, p2)
-        """
-        self.optimize_strunicode_loop(ops, expected)
+        jump(p4, i1, i2, p2, i5, i3, i4)
+        """
+        expected = """
+        [p1, i1, i2, p2, i5, i3, i4]
+        p4 = newstr(i5)
+        copystrcontent(p1, p4, i1, 0, i3)
+        copystrcontent(p2, p4, 0, i3, i4)
+        jump(p4, i1, i2, p2, i5, i3, i4)
+        """
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_strgetitem_bounds(self):
         ops = """
         [p0, i0]
         i1 = strgetitem(p0, i0)
+        i10 = strgetitem(p0, i0)
         i2 = int_lt(i1, 256)
         guard_true(i2) []
         i3 = int_ge(i1, 0)
@@ -5337,6 +5684,7 @@
         """
         expected = """
         [p0, i0]
+        i1 = strgetitem(p0, i0)
         jump(p0, i0)
         """
         self.optimize_loop(ops, expected)
@@ -5345,12 +5693,14 @@
         ops = """
         [p0, i0]
         i1 = unicodegetitem(p0, i0)
+        i10 = unicodegetitem(p0, i0)        
         i2 = int_lt(i1, 0)
         guard_false(i2) []
         jump(p0, i0)
         """
         expected = """
         [p0, i0]
+        i1 = unicodegetitem(p0, i0)        
         jump(p0, i0)
         """
         self.optimize_loop(ops, expected)
@@ -5387,7 +5737,7 @@
         self.optimize_loop(ops, expected)
 
     # ----------
-    def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None):
+    def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble):
         class FakeCallInfoCollection:
             def callinfo_for_oopspec(self, oopspecindex):
                 calldescrtype = type(LLtypeMixin.strequaldescr)
@@ -5410,7 +5760,7 @@
         escape(i0)
         jump(p1, p2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, ops)
+        self.optimize_strunicode_loop_extradescrs(ops, ops, ops)
 
     def test_str_equal_noop2(self):
         ops = """
@@ -5420,7 +5770,7 @@
         escape(i0)
         jump(p1, p2, p3)
         """
-        expected = """
+        preamble = """
         [p1, p2, p3]
         i1 = strlen(p1)
         i2 = strlen(p2)
@@ -5430,10 +5780,19 @@
         copystrcontent(p2, p4, 0, i1, i2)
         i0 = call(0, p3, p4, descr=strequaldescr)
         escape(i0)
-        jump(p1, p2, p3)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected)
+        jump(p1, p2, p3, i3, i1, i2)
+        """
+        expected = """
+        [p1, p2, p3, i3, i1, i2]
+        p4 = newstr(i3)
+        copystrcontent(p1, p4, 0, 0, i1)
+        copystrcontent(p2, p4, 0, i1, i2)
+        i0 = call(0, p3, p4, descr=strequaldescr)
+        escape(i0)
+        jump(p1, p2, p3, i3, i1, i2)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected,
+                                                  preamble)
 
     def test_str_equal_slice1(self):
         ops = """
@@ -5443,15 +5802,21 @@
         escape(i0)
         jump(p1, i1, i2, p3)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, p3]
         i3 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i3, p3, descr=streq_slice_checknull_descr)
         escape(i0)
-        jump(p1, i1, i2, p3)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected)
+        jump(p1, i1, i2, p3, i3)
+        """
+        expected = """
+        [p1, i1, i2, p3, i3]
+        i0 = call(0, p1, i1, i3, p3, descr=streq_slice_checknull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3, i3)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected,
+                                                  preamble)
 
     def test_str_equal_slice2(self):
         ops = """
@@ -5461,15 +5826,21 @@
         escape(i0)
         jump(p1, i1, i2, p3)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, p3]
         i4 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i4, p3, descr=streq_slice_checknull_descr)
         escape(i0)
-        jump(p1, i1, i2, p3)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected)
+        jump(p1, i1, i2, p3, i4)
+        """
+        expected = """
+        [p1, i1, i2, p3, i4]
+        i0 = call(0, p1, i1, i4, p3, descr=streq_slice_checknull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3, i4)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected,
+                                                  preamble)
 
     def test_str_equal_slice3(self):
         ops = """
@@ -5481,14 +5852,21 @@
         jump(p1, i1, i2, p3)
         """
         expected = """
+        [p1, i1, i2, p3, i4]
+        i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr)
+        escape(i0)
+        jump(p1, i1, i2, p3, i4)
+        """
+        preamble = """
         [p1, i1, i2, p3]
+        guard_nonnull(p3) []        
         i4 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr)
         escape(i0)
-        jump(p1, i1, i2, p3)
+        jump(p1, i1, i2, p3, i4)
         """
         self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected, ops)
+                                                  expected, preamble)
 
     def test_str_equal_slice4(self):
         ops = """
@@ -5498,15 +5876,21 @@
         escape(i0)
         jump(p1, i1, i2)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2]
         i3 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i3, 120, descr=streq_slice_char_descr)
         escape(i0)
-        jump(p1, i1, i2)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected)
+        jump(p1, i1, i2, i3)
+        """
+        expected = """
+        [p1, i1, i2, i3]
+        i0 = call(0, p1, i1, i3, 120, descr=streq_slice_char_descr)
+        escape(i0)
+        jump(p1, i1, i2, i3)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected,
+                                                  preamble)
 
     def test_str_equal_slice5(self):
         ops = """
@@ -5518,15 +5902,21 @@
         escape(i0)
         jump(p1, i1, i2, i3)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, i3]
         i4 = int_sub(i2, i1)
         i0 = call(0, p1, i1, i4, i3, descr=streq_slice_char_descr)
         escape(i0)
-        jump(p1, i1, i2, i3)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops,
-                                                  expected)
+        jump(p1, i1, i2, i3, i4)
+        """
+        expected = """
+        [p1, i1, i2, i3, i4]
+        i0 = call(0, p1, i1, i4, i3, descr=streq_slice_char_descr)
+        escape(i0)
+        jump(p1, i1, i2, i3, i4)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected,
+                                                  preamble)
 
     def test_str_equal_none1(self):
         ops = """
@@ -5541,7 +5931,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str_equal_none2(self):
         ops = """
@@ -5556,7 +5946,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str_equal_nonnull1(self):
         ops = """
@@ -5572,7 +5962,14 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        preamble = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, s"hello world", descr=streq_nonnull_descr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
 
     def test_str_equal_nonnull2(self):
         ops = """
@@ -5583,13 +5980,19 @@
         jump(p1)
         """
         expected = """
+        [p1, i0]
+        escape(i0)
+        jump(p1, i0)
+        """
+        preamble = """
         [p1]
+        guard_nonnull(p1) []
         i1 = strlen(p1)
         i0 = int_eq(i1, 0)
         escape(i0)
-        jump(p1)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        jump(p1, i0)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
 
     def test_str_equal_nonnull3(self):
         ops = """
@@ -5605,7 +6008,14 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        preamble = """
+        [p1]
+        guard_nonnull(p1) []
+        i0 = call(0, p1, 120, descr=streq_nonnull_char_descr)
+        escape(i0)
+        jump(p1)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
 
     def test_str_equal_nonnull4(self):
         ops = """
@@ -5615,7 +6025,7 @@
         escape(i0)
         jump(p1, p2)
         """
-        expected = """
+        preamble = """
         [p1, p2]
         i1 = strlen(p1)
         i2 = strlen(p2)
@@ -5625,9 +6035,18 @@
         copystrcontent(p2, p4, 0, i1, i2)
         i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
         escape(i0)
-        jump(p1, p2)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        jump(p1, p2, i3, i1, i2)
+        """
+        expected = """
+        [p1, p2, i3, i1, i2]
+        p4 = newstr(i3)
+        copystrcontent(p1, p4, 0, 0, i1)
+        copystrcontent(p2, p4, 0, i1, i2)
+        i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
+        escape(i0)
+        jump(p1, p2, i3, i1, i2)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
 
     def test_str_equal_chars0(self):
         ops = """
@@ -5642,7 +6061,7 @@
         escape(1)
         jump(i1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str_equal_chars1(self):
         ops = """
@@ -5653,13 +6072,42 @@
         escape(i0)
         jump(i1)
         """
-        expected = """
+        preamble = """
         [i1]
         i0 = int_eq(i1, 120)     # ord('x')
         escape(i0)
-        jump(i1)
-        """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        jump(i1, i0)
+        """
+        expected = """
+        [i1, i0]
+        escape(i0)
+        jump(i1, i0)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
+
+    def test_str_equal_nonconst(self):
+        ops = """
+        [i1, i2]
+        p1 = newstr(1)
+        strsetitem(p1, 0, i1)
+        p2 = newstr(1)
+        strsetitem(p2, 0, i2)
+        i0 = call(0, p1, p2, descr=strequaldescr)
+        escape(i0)
+        jump(i1, i2)
+        """
+        preamble = """
+        [i1, i2]
+        i0 = int_eq(i1, i2)
+        escape(i0)
+        jump(i1, i2, i0)
+        """
+        expected = """
+        [i1, i2, i0]
+        escape(i0)
+        jump(i1, i2, i0)
+        """
+        self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
 
     def test_str_equal_chars2(self):
         ops = """
@@ -5680,7 +6128,7 @@
         escape(i0)
         jump(i1, i2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str_equal_chars3(self):
         ops = """
@@ -5695,7 +6143,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str_equal_lengthmismatch1(self):
         ops = """
@@ -5711,7 +6159,7 @@
         escape(0)
         jump(i1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str2unicode_constant(self):
         ops = """
@@ -5725,7 +6173,7 @@
         escape(u"xy")
         jump()
         """
-        self.optimize_strunicode_loop_extradescrs(ops, expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
 
     def test_str2unicode_nonconstant(self):
         ops = """
@@ -5734,7 +6182,7 @@
         escape(p1)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, ops)
+        self.optimize_strunicode_loop_extradescrs(ops, ops, ops)
         # more generally, supporting non-constant but virtual cases is
         # not obvious, because of the exception UnicodeDecodeError that
         # can be raised by ll_str2unicode()
@@ -5858,6 +6306,213 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_constant_getfield1(self):
+        ops = """
+        [p1, p187, i184]
+        p188 = getarrayitem_gc(p187, 42, descr=<GcPtrArrayDescr>)
+        guard_value(p188, ConstPtr(myptr)) []
+        p25 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        jump(p25, p187, i184)
+        """
+        preamble = """
+        [p1, p187, i184]
+        p188 = getarrayitem_gc(p187, 42, descr=<GcPtrArrayDescr>)
+        guard_value(p188, ConstPtr(myptr)) []
+        p25 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        jump(p25, p187, i184, p25)
+        """
+        short = """
+        [p1, p187, i184]
+        p188 = getarrayitem_gc(p187, 42, descr=<GcPtrArrayDescr>)
+        guard_value(p188, ConstPtr(myptr)) []
+        p25 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        jump(p1, p187, i184, p25)
+        """
+        expected = """
+        [p25, p187, i184, p189]
+        jump(p189, p187, i184, p189)
+        """
+        self.optimize_loop(ops, expected, preamble, expected_short=short)
+
+    def test_constant_getfield1bis(self):
+        ops = """
+        [p1, p187, i184]
+        p188 = getarrayitem_gc(p187, 42, descr=<GcPtrArrayDescr>)
+        guard_value(p188, ConstPtr(myptr)) []
+        p25 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        p26 = call(p25, descr=nonwritedescr)
+        jump(p26, p187, i184)
+        """
+        expected = """
+        [p24, p187, i184, p25]
+        p26 = call(p25, descr=nonwritedescr)
+        jump(p26, p187, i184, p25)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constant_getfield2(self):
+        ops = """
+        [p19]
+        p22 = getfield_gc(p19, descr=otherdescr)
+        guard_value(p19, ConstPtr(myptr)) []
+        jump(p19)
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constant_getfield3(self):
+        ops = """
+        [p19, p20, p21]
+        p22 = getfield_gc(p19, descr=otherdescr)
+        guard_value(p19, ConstPtr(myptr)) []
+        p23 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        jump(p20, p21, p21)
+        """
+        expected = """
+        [p20, p21]
+        p22 = getfield_gc(p20, descr=otherdescr)
+        guard_value(p20, ConstPtr(myptr)) []
+        jump(p21, p21)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constant_getfield4(self):
+        ops = """
+        [p19, p20, p21]
+        p22 = getfield_gc(p19, descr=otherdescr)
+        p23 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        guard_value(p19, ConstPtr(myptr)) []
+        jump(p20, p21, p21)
+        """
+        expected = """
+        [p20, p21]
+        p22 = getfield_gc(p20, descr=otherdescr)
+        guard_value(p20, ConstPtr(myptr)) []
+        jump(p21, p21)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constnats_among_virtual_fileds(self):
+        ops = """
+        [p19, p20, p21]
+        p1 = getfield_gc(p20, descr=valuedescr)
+        p2 = getfield_gc(p1, descr=otherdescr)
+        pv = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(pv, p19, descr=valuedescr)
+        p22 = getfield_gc(p19, descr=otherdescr)
+        guard_value(p19, ConstPtr(myptr)) []
+        p23 = getfield_gc(ConstPtr(myptr), descr=otherdescr)
+        jump(p21, pv, p21)
+        """
+        expected = """
+        [p20]
+        p22 = getfield_gc(p20, descr=otherdescr)
+        guard_value(p20, ConstPtr(myptr)) []
+        jump(ConstPtr(myptr))
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_dont_cache_setfields(self):
+        # Naivly caching the last two getfields here would specialize
+        # the loop to the state where the first two getfields return
+        # the same value. That state would need to be guarded for
+        # in the short preamble. Instead we make sure to keep the
+        # results of the two getfields as separate boxes.
+        ops = """
+        [p0, p1, ii, ii2]
+        i1 = getfield_gc(p0, descr=valuedescr)
+        i2 = getfield_gc(p1, descr=otherdescr)
+        i3 = int_add(i1, i2)
+        setfield_gc(p0, ii, descr=valuedescr)
+        setfield_gc(p1, ii, descr=otherdescr)
+        i4 = getfield_gc(p0, descr=valuedescr)
+        i5 = getfield_gc(p1, descr=otherdescr)
+        jump(p0, p1, ii2, ii)
+        """
+        preamble = """
+        [p0, p1, ii, ii2]
+        i1 = getfield_gc(p0, descr=valuedescr)
+        i2 = getfield_gc(p1, descr=otherdescr)
+        i3 = int_add(i1, i2)
+        setfield_gc(p0, ii, descr=valuedescr)
+        setfield_gc(p1, ii, descr=otherdescr)
+        jump(p0, p1, ii2, ii, ii, ii)
+        """
+        expected = """
+        [p0, p1, ii, ii2, i1, i2]
+        i3 = int_add(i1, i2)
+        setfield_gc(p0, ii, descr=valuedescr)
+        setfield_gc(p1, ii, descr=otherdescr)
+        jump(p0, p1, ii2, ii, ii, ii)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_dont_specialize_on_boxes_equal(self):
+        ops = """
+        [p0, p1, p3, ii, ii2]
+        i1 = getfield_gc(p0, descr=valuedescr)
+        i2 = getfield_gc(p1, descr=otherdescr)
+        setfield_gc(p3, i1, descr=adescr)
+        setfield_gc(p3, i2, descr=bdescr)
+        i4 = int_eq(i1, i2)
+        guard_true(i4) []
+        i5 = int_gt(ii, 42)
+        guard_true(i5) []
+        jump(p0, p1, p3, ii2, ii)
+        """
+        expected = """
+        [p0, p1, p3, ii, ii2, i1, i2]
+        setfield_gc(p3, i1, descr=adescr)
+        setfield_gc(p3, i2, descr=bdescr)
+        i5 = int_gt(ii, 42)
+        guard_true(i5) []        
+        jump(p0, p1, p3, ii2, ii, i1, i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_lazy_setfield_forced_by_jump_needing_additionall_inputargs(self):
+        ops = """
+        [p0, p3]
+        i1 = getfield_gc(p0, descr=valuedescr)
+        setfield_gc(p3, i1, descr=otherdescr)
+        jump(p0, p3)
+        """
+        expected = """
+        [p0, p3, i1]
+        setfield_gc(p3, i1, descr=otherdescr)
+        jump(p0, p3, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_guards_before_getfields_in_short_preamble(self):
+        ops = """
+        [p0]
+        guard_nonnull_class(p0, ConstClass(node_vtable)) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull_class(p1, ConstClass(node_vtable)) []
+        p2 = getfield_gc(p1, descr=nextdescr)
+        guard_nonnull_class(p2, ConstClass(node_vtable)) []        
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        short = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        guard_class(p1, ConstClass(node_vtable)) []
+        p2 = getfield_gc(p1, descr=nextdescr)
+        guard_nonnull(p2) []
+        guard_class(p2, ConstClass(node_vtable)) []        
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, expected_short=short)
+        
     def test_forced_virtual_pure_getfield(self):
         ops = """
         [p0]
@@ -5906,5 +6561,533 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_setgetfield_counter(self):
+        ops = """
+        [p1]
+        i2 = getfield_gc(p1, descr=valuedescr)
+        i3 = int_add(i2, 1)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1)
+        """
+        expected = """
+        [p1, i1]
+        i2 = int_add(i1, 1)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i2)
+        """
+        self.optimize_loop(ops, expected)
+        
+    def test_loopinvariant_strlen(self):
+        ops = """
+        [p9]
+        i843 = strlen(p9)
+        call(i843, descr=nonwritedescr)
+        jump(p9)
+        """
+        preamble = """
+        [p9]
+        i843 = strlen(p9)
+        call(i843, descr=nonwritedescr)
+        jump(p9, i843)
+        """
+        short = """
+        [p9]
+        i843 = strlen(p9)
+        i848 = int_ge(i843, 0)
+        guard_true(i848)[]
+        jump(p9, i843)
+        """
+        expected = """
+        [p9, i2]
+        call(i2, descr=nonwritedescr)
+        jump(p9, i2)
+        """
+        self.optimize_loop(ops, expected, preamble, expected_short=short)
+
+    def test_loopinvariant_strlen_with_bound(self):
+        ops = """
+        [p9]
+        i843 = strlen(p9)
+        i1 = int_gt(i843, 7)
+        guard_true(i1) []
+        call(i843, descr=nonwritedescr)
+        jump(p9)
+        """
+        expected = """
+        [p9, i2]
+        call(i2, descr=nonwritedescr)
+        jump(p9, i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_loopinvariant_strgetitem(self):
+        ops = """
+        [p9, i1]
+        i843 = strgetitem(p9, i1)
+        call(i843, descr=nonwritedescr)
+        jump(p9, i1)
+        """
+        self.optimize_loop(ops, ops)
+
+    def test_loopinvariant_unicodelen(self):
+        ops = """
+        [p9]
+        i843 = unicodelen(p9)
+        call(i843, descr=nonwritedescr)
+        jump(p9)
+        """
+        expected = """
+        [p9, i2]
+        call(i2, descr=nonwritedescr)
+        jump(p9, i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_loopinvariant_unicodegetitem(self):
+        ops = """
+        [p9, i1]
+        i843 = unicodegetitem(p9, i1)
+        call(i843, descr=nonwritedescr)
+        jump(p9, i1)
+        """
+        self.optimize_loop(ops, ops)
+
+    def test_loopinvariant_arraylen(self):
+        ops = """
+        [p9]
+        i843 = arraylen_gc(p9)
+        call(i843, descr=nonwritedescr)
+        jump(p9)
+        """
+        expected = """
+        [p9, i2]
+        call(i2, descr=nonwritedescr)
+        jump(p9, i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_loopinvariant_getarrayitem(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        p2 = getarrayitem_gc(p1, 7, descr=<GcPtrArrayDescr>)
+        call(p2, descr=nonwritedescr)
+        jump(p0)
+        """
+        short = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        i1 = arraylen_gc(p1)
+        i2 = int_ge(i1, 8)
+        guard_true(i2) []
+        p2 = getarrayitem_gc(p1, 7, descr=<GcPtrArrayDescr>)
+        jump(p0, p2, p1)
+        """
+        expected = """
+        [p0, p2, p1]
+        call(p2, descr=nonwritedescr)
+        i3 = arraylen_gc(p1) # Should be killed by backend
+        jump(p0, p2, p1)
+        """
+        self.optimize_loop(ops, expected, expected_short=short)
+
+    def test_duplicated_virtual(self):
+        ops = """
+        [p1, p2]
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        jump(p3, p3)
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_chained_virtuals(self):
+        ops = """
+        [p0, p1]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p3, descr=nextdescr) 
+        jump(p2, p3)
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_arraylen_bound(self):
+        ops = """
+        [p1, i]
+        p2 = getarrayitem_gc(p1, 7, descr=<GcPtrArrayDescr>)
+        i1 = arraylen_gc(p1)
+        i2 = int_ge(i1, 8)
+        guard_true(i2) []
+        jump(p2, i2)
+        """
+        expected = """
+        [p1]        
+        p2 = getarrayitem_gc(p1, 7, descr=<GcPtrArrayDescr>)
+        i1 = arraylen_gc(p1)
+        jump(p2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_loopinvariant_getarrayitem_gc_pure(self):
+        ops = """
+        [p9, i1]
+        i843 = getarrayitem_gc_pure(p9, i1)
+        call(i843, descr=nonwritedescr)
+        jump(p9, i1)
+        """
+        self.optimize_loop(ops, ops)
+
+    def test_loopinvariant_constant_getarrayitem_pure(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        p2 = getarrayitem_gc_pure(p1, 7, descr=<GcPtrArrayDescr>)
+        call(p2, descr=nonwritedescr)
+        jump(p0)
+        """
+        short = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        i1 = arraylen_gc(p1)
+        i2 = int_ge(i1, 8)
+        guard_true(i2) []
+        p2 = getarrayitem_gc_pure(p1, 7, descr=<GcPtrArrayDescr>)
+        jump(p0, p2, p1)
+        """
+        expected = """
+        [p0, p2, p1]
+        call(p2, descr=nonwritedescr)
+        i3 = arraylen_gc(p1) # Should be killed by backend
+        jump(p0, p2, p1)
+        """
+        self.optimize_loop(ops, expected, expected_short=short)
+        
+        
+    def test_loopinvariant_constant_strgetitem(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i22 = strgetitem(p1, 7)
+        call(i22, descr=nonwritedescr)
+        jump(p0)
+        """
+        short = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        i1 = strlen(p1)
+        i2 = int_ge(i1, 8)
+        guard_true(i2) []
+        i22 = strgetitem(p1, 7, descr=<GcPtrArrayDescr>)
+        i8 = int_ge(i22, 0)
+        guard_true(i8) []
+        i9 = int_le(i22, 255)
+        guard_true(i9) []
+        jump(p0, i22, p1)
+        """
+        expected = """
+        [p0, i22, p1]
+        call(i22, descr=nonwritedescr)
+        i3 = strlen(p1) # Should be killed by backend
+        jump(p0, i22, p1)
+        """
+        self.optimize_loop(ops, expected, expected_short=short)
+
+    def test_loopinvariant_constant_unicodegetitem(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i22 = unicodegetitem(p1, 7)
+        call(i22, descr=nonwritedescr)
+        jump(p0)
+        """
+        short = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        i1 = unicodelen(p1)
+        i2 = int_ge(i1, 8)
+        guard_true(i2) []
+        i22 = unicodegetitem(p1, 7, descr=<GcPtrArrayDescr>)
+        i8 = int_ge(i22, 0)
+        guard_true(i8) []
+        jump(p0, i22, p1)
+        """
+        expected = """
+        [p0, i22, p1]
+        call(i22, descr=nonwritedescr)
+        i3 = unicodelen(p1) # Should be killed by backend        
+        jump(p0, i22, p1)
+        """
+        self.optimize_loop(ops, expected, expected_short=short)
+        
+    def test_propagate_virtual_arryalen(self):
+        ops = """
+        [p0]
+        p404 = new_array(2, descr=arraydescr)
+        p403 = new_array(3, descr=arraydescr)
+        i405 = arraylen_gc(p404, descr=arraydescr)
+        i406 = arraylen_gc(p403, descr=arraydescr)
+        i407 = int_add_ovf(i405, i406)
+        guard_no_overflow() []
+        call(i407, descr=nonwritedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        call(5, descr=nonwritedescr)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_propagate_virtual_strunicodelen(self):
+        ops = """
+        [p0]
+        p404 = newstr(2)
+        p403 = newunicode(3)
+        i405 = strlen(p404)
+        i406 = unicodelen(p403)
+        i407 = int_add_ovf(i405, i406)
+        guard_no_overflow() []
+        call(i407, descr=nonwritedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        call(5, descr=nonwritedescr)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_keep_getfields_and_inputargs_separate(self):
+        ops = """
+        [p0]
+        call(p0, descr=nonwritedescr)
+        p1 = getfield_gc(ConstPtr(myptr), descr=nextdescr)
+        call(p1, descr=writeadescr)
+        jump(p1)
+        """
+        expected = """
+        [p0, p1]
+        call(p0, descr=nonwritedescr)
+        call(p1, descr=writeadescr)
+        jump(p1, p1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_value_guard_arraylen_reused(self):
+        ops = """
+        [p0, p1]
+        p10 = getfield_gc(p0, descr=nextdescr)
+        p11 = getfield_gc(p1, descr=nextdescr)
+        i1 = arraylen_gc(p10, descr=arraydescr)
+        getarrayitem_gc(p11, 1, descr=arraydescr)
+        call(i1, descr=nonwritedescr)
+        jump(p1, p0)
+        """
+        expected = """
+        [p0, p1, p10, p11]
+        i1 = arraylen_gc(p10, descr=arraydescr)
+        getarrayitem_gc(p11, 1, descr=arraydescr)
+        call(i1, descr=nonwritedescr)        
+        jump(p1, p0, p11, p10)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_cache_constant_setfield(self):
+        ops = """
+        [p5]
+        i10 = getfield_gc(p5, descr=valuedescr)
+        call(i10, descr=nonwritedescr) 
+        setfield_gc(p5, 1, descr=valuedescr)
+        jump(p5)
+        """
+        preamble = """
+        [p5]
+        i10 = getfield_gc(p5, descr=valuedescr)
+        call(i10, descr=nonwritedescr) 
+        setfield_gc(p5, 1, descr=valuedescr)
+        jump(p5)
+        """
+        expected = """
+        [p5]
+        call(1, descr=nonwritedescr) 
+        jump(p5)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_dont_mixup_equal_boxes(self):
+        ops = """
+        [p8]
+        i9 = getfield_gc_pure(p8, descr=valuedescr)
+        i10 = int_gt(i9, 0)
+        guard_true(i10) []
+        i29 = int_lshift(i9, 1)
+        i30 = int_rshift(i29, 1)
+        i40 = int_ne(i30, i9)
+        guard_false(i40) []
+        jump(p8)
+        """
+        expected = """
+        [p8]
+        jump(p8)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_specialized_to_cached_constant_guard(self):
+        ops = """
+        [p9]
+        i16 = getfield_gc(p9, descr=valuedescr)
+        i17 = int_is_true(i16)
+        guard_false(i17) []
+        call_assembler(i17, descr=asmdescr)
+        i18 = getfield_gc(p9, descr=valuedescr)
+        guard_value(i18, 0) []
+        jump(p9)
+        """
+        expected = """
+        [p9]
+        call_assembler(0, descr=asmdescr)
+        i18 = getfield_gc(p9, descr=valuedescr)
+        guard_value(i18, 0) []        
+        jump(p9)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_specialized_to_cached_constant_setfield(self):
+        ops = """
+        [p9]
+        i16 = getfield_gc(p9, descr=valuedescr)
+        i17 = int_is_true(i16)
+        guard_false(i17) []
+        call_assembler(i17, descr=asmdescr)
+        i18 = setfield_gc(p9, 0, descr=valuedescr)
+        jump(p9)
+        """
+        expected = """
+        [p9]
+        call_assembler(0, descr=asmdescr)
+        i18 = setfield_gc(p9, 0, descr=valuedescr)
+        jump(p9)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_cached_equal_fields(self):
+        ops = """
+        [p5, p6]
+        i10 = getfield_gc(p5, descr=valuedescr)
+        i11 = getfield_gc(p6, descr=nextdescr)
+        call(i10, i11, descr=nonwritedescr)
+        setfield_gc(p6, i10, descr=nextdescr)        
+        jump(p5, p6)
+        """
+        expected = """
+        [p5, p6, i10, i11]
+        call(i10, i11, descr=nonwritedescr)
+        setfield_gc(p6, i10, descr=nextdescr)        
+        jump(p5, p6, i10, i10)
+        """
+        self.optimize_loop(ops, expected)
+        
+    def test_forced_counter(self):
+        # XXX: VIRTUALHEAP (see above)
+        py.test.skip("would be fixed by make heap optimizer aware of virtual setfields")
+        ops = """
+        [p5, p8]
+        i9 = getfield_gc_pure(p5, descr=valuedescr)
+        call(i9, descr=nonwritedescr)
+        i11 = getfield_gc_pure(p8, descr=valuedescr)
+        i13 = int_add_ovf(i11, 1)
+        guard_no_overflow() []
+        p22 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p22, i13, descr=valuedescr)
+        setfield_gc(ConstPtr(myptr), p22, descr=adescr)
+        jump(p22, p22)
+        """
+        expected = """
+        [p8, i9]
+        call(i9, descr=nonwritedescr)
+        i13 = int_add_ovf(i9, 1)
+        guard_no_overflow() []
+        p22 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p22, i13, descr=valuedescr)
+        setfield_gc(ConstPtr(myptr), p22, descr=adescr)
+        jump(p22, i13)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constptr_samebox_getfield_setfield(self):
+        ops = """
+        [p0]
+        p10 = getfield_gc(ConstPtr(myptr), descr=valuedescr)
+        call(p10, descr=nonwritedescr)
+        setfield_gc(ConstPtr(myptr), p10, descr=valuedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0, p10]
+        call(p10, descr=nonwritedescr)
+        jump(p0, p10)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_constptr_constptr_getfield_setfield(self):
+        ops = """
+        [p0]
+        p10 = getfield_gc(ConstPtr(myptr), descr=valuedescr)
+        guard_value(p10, ConstPtr(myptr2)) []
+        call(p10, descr=nonwritedescr)
+        setfield_gc(ConstPtr(myptr), ConstPtr(myptr2), descr=valuedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        call(ConstPtr(myptr2), descr=nonwritedescr)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_box_samebox_getfield_setfield(self):
+        ops = """
+        [p0]
+        p10 = getfield_gc(p0, descr=valuedescr)
+        call(p10, descr=nonwritedescr)
+        setfield_gc(p0, p10, descr=valuedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0, p10]
+        call(p10, descr=nonwritedescr)
+        jump(p0, p10)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_box_constptr_getfield_setfield(self):
+        ops = """
+        [p0]
+        p10 = getfield_gc(p0, descr=valuedescr)
+        guard_value(p10, ConstPtr(myptr2)) []
+        call(p10, descr=nonwritedescr)
+        setfield_gc(p0, ConstPtr(myptr2), descr=valuedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        call(ConstPtr(myptr2), descr=nonwritedescr)
+        jump(p0)
+        """
+        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
@@ -95,10 +95,13 @@
                                      ('other', lltype.Ptr(NODE)))
     node = lltype.malloc(NODE)
     node.parent.typeptr = node_vtable
+    node2 = lltype.malloc(NODE2)
+    node2.parent.parent.typeptr = node_vtable2
     nodebox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node))
     myptr = nodebox.value
     myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(NODE))
-    nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node))
+    nullptr = lltype.nullptr(llmemory.GCREF.TO)
+    nodebox2 = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, node2))
     nodesize = cpu.sizeof(NODE)
     nodesize2 = cpu.sizeof(NODE2)
     valuedescr = cpu.fielddescrof(NODE, 'value')
@@ -318,6 +321,10 @@
         self.config = get_pypy_config(translating=True)
         self.config.translation.jit_ffi = True
 
+    class warmrunnerdesc:
+        class memory_manager:
+            retrace_limit = 5
+            max_retrace_guards = 15
 
 class Storage(compile.ResumeGuardDescr):
     "for tests."
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
@@ -1,74 +1,15 @@
 from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.jit.metainterp.optimizeopt.virtualstate import VirtualStateAdder, ShortBoxes
 from pypy.jit.metainterp.compile import ResumeGuardDescr
 from pypy.jit.metainterp.history import TreeLoop, LoopToken
 from pypy.jit.metainterp.jitexc import JitException
 from pypy.jit.metainterp.optimize import InvalidLoop, RetraceLoop
 from pypy.jit.metainterp.optimizeopt.optimizer import *
+from pypy.jit.metainterp.optimizeopt.generalize import KillHugeIntBounds
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.metainterp.resume import Snapshot
 from pypy.rlib.debug import debug_print
-
-# Assumptions
-# ===========
-#
-# For this to work some assumptions had to be made about the
-# optimizations performed. At least for the optimizations that are
-# allowed to operate across the loop boundaries. To enforce this, the
-# optimizer chain is recreated at the end of the preamble and only the
-# state of the optimizations that fulfill those assumptions are kept.
-# Since part of this state is stored in virtuals all OptValue objects
-# are also recreated to allow virtuals not supported to be forced.
-#
-# First of all, the optimizations are not allowed to introduce new
-# boxes. It is the unoptimized version of the trace that is inlined to
-# form the second iteration of the loop. Otherwise the
-# state of the virtuals would not be updated correctly. Whenever some
-# box from the first iteration is reused in the second iteration, it
-# is added to the input arguments of the loop as well as to the
-# arguments of the jump at the end of the preamble. This means that
-# inlining the jump from the unoptimized trace will not work since it
-# contains too few arguments.  Instead the jump at the end of the
-# preamble is inlined. If the arguments of that jump contains boxes
-# that were produced by one of the optimizations, and thus never seen
-# by the inliner, the inliner will not be able to inline them. There
-# is no way of known what these boxes are supposed to contain in the
-# third iteration.
-#
-# The second assumption is that the state of the optimizer should be the
-# same after the second iteration as after the first. This have forced
-# us to disable store sinking across loop boundaries. Consider the
-# following trace
-#
-#         [p1, p2]
-#         i1 = getfield_gc(p1, descr=nextdescr)
-#         i2 = int_sub(i1, 1)
-#         i2b = int_is_true(i2)
-#         guard_true(i2b) []
-#         setfield_gc(p2, i2, descr=nextdescr)
-#         p3 = new_with_vtable(ConstClass(node_vtable))
-#         jump(p2, p3)
-#
-# At the start of the preamble, p1 and p2 will be pointers. The
-# setfield_gc will be removed by the store sinking heap optimizer, and
-# p3 will become a virtual. Jumping to the loop will make p1 a pointer
-# and p2 a virtual at the start of the loop. The setfield_gc will now
-# be absorbed into the virtual p2 and never seen by the heap
-# optimizer. At the end of the loop both p2 and p3 are virtuals, but
-# the loop needs p2 to be a pointer to be able to call itself. So it
-# is forced producing the operations
-#
-#         p2 = new_with_vtable(ConstClass(node_vtable))
-#         setfield_gc(p2, i2, descr=nextdescr)
-#
-# In this case the setfield_gc is not store sinked, which means we are
-# not in the same state at the end of the loop as at the end of the
-# preamble. When we now call the loop again, the first 4 operations of
-# the trace were optimized under the wrong assumption that the
-# setfield_gc was store sinked which could lead to errors. In this
-# case what would happen is that it would be inserted once more in
-# front of the guard.
-
-
+import sys, os
 
 # FIXME: Introduce some VirtualOptimizer super class instead
 
@@ -81,7 +22,10 @@
         assert len(inputargs) == len(jump_args)
         self.argmap = {}
         for i in range(len(inputargs)):
-           self.argmap[inputargs[i]] = jump_args[i]
+            if inputargs[i] in self.argmap:
+                assert self.argmap[inputargs[i]] == jump_args[i]
+            else:
+                self.argmap[inputargs[i]] = jump_args[i]
         self.snapshot_map = {None: None}
 
     def inline_op(self, newop, ignore_result=False, clone=True,
@@ -126,112 +70,6 @@
         self.snapshot_map[snapshot] = new_snapshot
         return new_snapshot
 
-class VirtualState(object):
-    def __init__(self, state):
-        self.state = state
-
-    def generalization_of(self, other):
-        assert len(self.state) == len(other.state)
-        for i in range(len(self.state)):
-            if not self.state[i].generalization_of(other.state[i]):
-                return False
-        return True
-
-    def generate_guards(self, other, args, cpu, extra_guards):
-        assert len(self.state) == len(other.state) == len(args)
-        for i in range(len(self.state)):
-            self.state[i].generate_guards(other.state[i], args[i],
-                                          cpu, extra_guards)
-
-class VirtualStateAdder(resume.ResumeDataVirtualAdder):
-    def __init__(self, optimizer):
-        self.fieldboxes = {}
-        self.optimizer = optimizer
-        self.info = {}
-
-    def register_virtual_fields(self, keybox, fieldboxes):
-        self.fieldboxes[keybox] = fieldboxes
-
-    def already_seen_virtual(self, keybox):
-        return keybox in self.fieldboxes
-
-    def getvalue(self, box):
-        return self.optimizer.getvalue(box)
-
-    def state(self, box):
-        value = self.getvalue(box)
-        box = value.get_key_box()
-        try:
-            info = self.info[box]
-        except KeyError:
-            if value.is_virtual():
-                self.info[box] = info = value.make_virtual_info(self, None)
-                flds = self.fieldboxes[box]
-                info.fieldstate = [self.state(b) for b in flds]
-            else:
-                self.info[box] = info = self.make_not_virtual(value)
-        return info
-
-    def get_virtual_state(self, jump_args):
-        for box in jump_args:
-            value = self.getvalue(box)
-            value.get_args_for_fail(self)
-        return VirtualState([self.state(box) for box in jump_args])
-
-
-    def make_not_virtual(self, value):
-        return NotVirtualInfo(value)
-
-class NotVirtualInfo(resume.AbstractVirtualInfo):
-    def __init__(self, value):
-        self.known_class = value.known_class
-        self.level = value.level
-        if value.intbound is None:
-            self.intbound = IntBound(MININT, MAXINT)
-        else:
-            self.intbound = value.intbound.clone()
-        if value.is_constant():
-            self.constbox = value.box
-        else:
-            self.constbox = None
-
-    def generalization_of(self, other):
-        # XXX This will always retrace instead of forcing anything which
-        # might be what we want sometimes?
-        if not isinstance(other, NotVirtualInfo):
-            return False
-        if other.level < self.level:
-            return False
-        if self.level == LEVEL_CONSTANT:
-            if not self.constbox.same_constant(other.constbox):
-                return False
-        elif self.level == LEVEL_KNOWNCLASS:
-            if self.known_class != other.known_class: # FIXME: use issubclass?
-                return False
-        return self.intbound.contains_bound(other.intbound)
-
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        if not isinstance(other, NotVirtualInfo):
-            raise InvalidLoop
-        if self.level == LEVEL_KNOWNCLASS and \
-           box.nonnull() and \
-           self.known_class.same_constant(cpu.ts.cls_of_box(box)):
-            # Note: This is only a hint on what the class of box was
-            # during the trace. There are actually no guarentees that this
-            # box realy comes from a trace. The hint is used here to choose
-            # between either eimtting a guard_class and jumping to an
-            # excisting compiled loop or retracing the loop. Both
-            # alternatives will always generate correct behaviour, but
-            # performace will differ.
-            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
-            extra_guards.append(op)
-            return
-        # Remaining cases are probably not interesting
-        raise InvalidLoop
-        if self.level == LEVEL_CONSTANT:
-            import pdb; pdb.set_trace()
-            raise NotImplementedError
-
 
 class UnrollOptimizer(Optimization):
     """Unroll the loop into two iterations. The first one will
@@ -245,6 +83,17 @@
             newop = op.clone()
             self.cloned_operations.append(newop)
 
+    def fix_snapshot(self, loop, jump_args, snapshot):
+        if snapshot is None:
+            return None
+        snapshot_args = snapshot.boxes 
+        new_snapshot_args = []
+        for a in snapshot_args:
+            a = self.getvalue(a).get_key_box()
+            new_snapshot_args.append(a)
+        prev = self.fix_snapshot(loop, jump_args, snapshot.prev)
+        return Snapshot(prev, new_snapshot_args)
+            
     def propagate_all_forward(self):
         loop = self.optimizer.loop
         jumpop = loop.operations[-1]
@@ -260,43 +109,119 @@
             assert jumpop.getdescr() is loop.token
             jump_args = jumpop.getarglist()
             jumpop.initarglist([])
-            #virtual_state = [self.getvalue(a).is_virtual() for a in jump_args]
+            self.optimizer.flush()
+
+            KillHugeIntBounds(self.optimizer).apply()
+            
+            loop.preamble.operations = self.optimizer.newoperations
+            jump_args = [self.getvalue(a).get_key_box() for a in jump_args]
+
+            start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable()
+            self.start_resumedescr = start_resumedescr
+            assert isinstance(start_resumedescr, ResumeGuardDescr)
+            start_resumedescr.rd_snapshot = self.fix_snapshot(loop, jump_args,
+                                                              start_resumedescr.rd_snapshot)
+
             modifier = VirtualStateAdder(self.optimizer)
             virtual_state = modifier.get_virtual_state(jump_args)
+            
+            values = [self.getvalue(arg) for arg in jump_args]
+            inputargs = virtual_state.make_inputargs(values)
+            short_inputargs = virtual_state.make_inputargs(values, keyboxes=True)
 
-            loop.preamble.operations = self.optimizer.newoperations
+            self.constant_inputargs = {}
+            for box in jump_args: 
+                const = self.get_constant_box(box)
+                if const:
+                    self.constant_inputargs[box] = const
+
+            sb = ShortBoxes(self.optimizer, inputargs + self.constant_inputargs.keys())
+            self.short_boxes = sb
+            preamble_optimizer = self.optimizer
             loop.preamble.quasi_immutable_deps = (
                 self.optimizer.quasi_immutable_deps)
-            self.optimizer = self.optimizer.reconstruct_for_next_iteration()
-            inputargs = self.inline(self.cloned_operations,
-                                    loop.inputargs, jump_args)
+            self.optimizer = self.optimizer.new()
+            loop.quasi_immutable_deps = self.optimizer.quasi_immutable_deps
+
+            logops = self.optimizer.loop.logops
+            if logops:
+                args = ", ".join([logops.repr_of_arg(arg) for arg in inputargs])
+                debug_print('inputargs:       ' + args)
+                args = ", ".join([logops.repr_of_arg(arg) for arg in short_inputargs])
+                debug_print('short inputargs: ' + args)
+                self.short_boxes.debug_print(logops)
+
+            # Force virtuals amoung the jump_args of the preamble to get the
+            # operations needed to setup the proper state of those virtuals
+            # in the peeled loop
+            inputarg_setup_ops = []
+            preamble_optimizer.newoperations = []
+            seen = {}
+            for box in inputargs:
+                if box in seen:
+                    continue
+                seen[box] = True
+                value = preamble_optimizer.getvalue(box)
+                inputarg_setup_ops.extend(value.make_guards(box))
+            for box in short_inputargs:
+                if box in seen:
+                    continue
+                seen[box] = True
+                value = preamble_optimizer.getvalue(box)
+                value.force_box()
+            preamble_optimizer.flush()
+            inputarg_setup_ops += preamble_optimizer.newoperations
+
+            # Setup the state of the new optimizer by emiting the
+            # short preamble operations and discarding the result
+            self.optimizer.emitting_dissabled = True
+            for op in inputarg_setup_ops:
+                self.optimizer.send_extra_operation(op)
+            seen = {}
+            for op in self.short_boxes.operations():
+                self.ensure_short_op_emitted(op, self.optimizer, seen)
+                if op and op.result:
+                    # The order of these guards is not important as 
+                    # self.optimizer.emitting_dissabled is False
+                    value = preamble_optimizer.getvalue(op.result)
+                    for guard in value.make_guards(op.result):
+                        self.optimizer.send_extra_operation(guard)
+                    newresult = self.optimizer.getvalue(op.result).get_key_box()
+                    if newresult is not op.result:
+                        self.short_boxes.alias(newresult, op.result)
+            self.optimizer.flush()
+            self.optimizer.emitting_dissabled = False
+
+            # XXX Hack to prevent the arraylen/strlen/unicodelen ops generated
+            #     by value.make_guards() from ending up in pure_operations
+            for key, op in self.optimizer.pure_operations.items():
+                if not self.short_boxes.has_producer(op.result):
+                    del self.optimizer.pure_operations[key]
+
+            initial_inputargs_len = len(inputargs)
+            self.inliner = Inliner(loop.inputargs, jump_args)
+
+
+            short = self.inline(inputargs, self.cloned_operations,
+                                loop.inputargs, short_inputargs,
+                                virtual_state)
+            
             loop.inputargs = inputargs
-            jmp = ResOperation(rop.JUMP, loop.inputargs[:], None)
+            args = [preamble_optimizer.getvalue(self.short_boxes.original(a)).force_box()\
+                    for a in inputargs]
+            jmp = ResOperation(rop.JUMP, args, None)
             jmp.setdescr(loop.token)
             loop.preamble.operations.append(jmp)
 
             loop.operations = self.optimizer.newoperations
-            loop.quasi_immutable_deps = self.optimizer.quasi_immutable_deps
+            maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards
+            
+            if self.optimizer.emitted_guards > maxguards:
+                loop.preamble.token.retraced_count = sys.maxint
 
-            start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable()
-            assert isinstance(start_resumedescr, ResumeGuardDescr)
-            snapshot = start_resumedescr.rd_snapshot
-            while snapshot is not None:
-                snapshot_args = snapshot.boxes
-                new_snapshot_args = []
-                for a in snapshot_args:
-                    if not isinstance(a, Const):
-                        a = loop.preamble.inputargs[jump_args.index(a)]
-                    new_snapshot_args.append(a)
-                snapshot.boxes = new_snapshot_args
-                snapshot = snapshot.prev
-
-            short = self.create_short_preamble(loop.preamble, loop)
             if short:
-                if False:
-                    # FIXME: This should save some memory but requires
-                    # a lot of tests to be fixed...
-                    loop.preamble.operations = short[:]
+                assert short[-1].getopnum() == rop.JUMP
+                short[-1].setdescr(loop.token)
 
                 # Turn guards into conditional jumps to the preamble
                 for i in range(len(short)):
@@ -304,20 +229,31 @@
                     if op.is_guard():
                         op = op.clone()
                         op.setfailargs(None)
-                        op.setdescr(start_resumedescr.clone_if_mutable())
+                        descr = self.start_resumedescr.clone_if_mutable()
+                        op.setdescr(descr)
                         short[i] = op
 
                 short_loop = TreeLoop('short preamble')
-                short_loop.inputargs = loop.preamble.inputargs[:]
+                short_loop.inputargs = short_inputargs
                 short_loop.operations = short
 
                 # Clone ops and boxes to get private versions and
-                newargs = [a.clonebox() for a in short_loop.inputargs]
+                boxmap = {}
+                newargs = [None] * len(short_loop.inputargs)
+                for i in range(len(short_loop.inputargs)):
+                    a = short_loop.inputargs[i]
+                    if a in boxmap:
+                        newargs[i] = boxmap[a]
+                    else:
+                        newargs[i] = a.clonebox()
+                        boxmap[a] = newargs[i]
                 inliner = Inliner(short_loop.inputargs, newargs)
+                for box, const in self.constant_inputargs.items():
+                    inliner.argmap[box] = const
                 short_loop.inputargs = newargs
                 ops = [inliner.inline_op(op) for op in short_loop.operations]
                 short_loop.operations = ops
-                descr = start_resumedescr.clone_if_mutable()
+                descr = self.start_resumedescr.clone_if_mutable()
                 inliner.inline_descr_inplace(descr)
                 short_loop.start_resumedescr = descr
 
@@ -335,324 +271,208 @@
                     if op.result:
                         op.result.forget_value()
 
-    def inline(self, loop_operations, loop_args, jump_args):
-        self.inliner = inliner = Inliner(loop_args, jump_args)
+    def inline(self, inputargs, loop_operations, loop_args, short_inputargs, virtual_state):
+        inliner = self.inliner
 
-        for v in self.optimizer.values.values():
-            v.last_guard_index = -1 # FIXME: Are there any more indexes stored?
+        short_jumpargs = inputargs[:]
 
-        inputargs = []
-        seen_inputargs = {}
-        for arg in jump_args:
-            boxes = []
-            self.getvalue(arg).enum_forced_boxes(boxes, seen_inputargs)
-            for a in boxes:
-                if not isinstance(a, Const):
-                    inputargs.append(a)
+        short = []
+        short_seen = {}
+        for box, const in self.constant_inputargs.items():
+            short_seen[box] = True
+
+        for op in self.short_boxes.operations():
+            if op is not None:
+                if len(self.getvalue(op.result).make_guards(op.result)) > 0:
+                    self.add_op_to_short(op, short, short_seen, False, True)
 
         # This loop is equivalent to the main optimization loop in
         # Optimizer.propagate_all_forward
+        jumpop = None
         for newop in loop_operations:
+            newop = inliner.inline_op(newop, clone=False)
             if newop.getopnum() == rop.JUMP:
-                newop.initarglist(inputargs)
-            newop = inliner.inline_op(newop, clone=False)
+                jumpop = newop
+                break
 
-            self.optimizer.first_optimization.propagate_forward(newop)
+            #self.optimizer.first_optimization.propagate_forward(newop)
+            self.optimizer.send_extra_operation(newop)
 
-        # Remove jump to make sure forced code are placed before it
-        newoperations = self.optimizer.newoperations
-        jmp = newoperations[-1]
-        assert jmp.getopnum() == rop.JUMP
-        self.optimizer.newoperations = newoperations[:-1]
+        self.boxes_created_this_iteration = {}
 
-        boxes_created_this_iteration = {}
-        jumpargs = jmp.getarglist()
+        assert jumpop
+        original_jumpargs = jumpop.getarglist()[:]
+        values = [self.getvalue(arg) for arg in jumpop.getarglist()]
+        jumpargs = virtual_state.make_inputargs(values)
+        jumpop.initarglist(jumpargs)
+        jmp_to_short_args = virtual_state.make_inputargs(values, keyboxes=True)
+        self.short_inliner = Inliner(short_inputargs, jmp_to_short_args)
+        
+        for box, const in self.constant_inputargs.items():
+            self.short_inliner.argmap[box] = const
 
-        # FIXME: Should also loop over operations added by forcing things in this loop
-        for op in newoperations:
-            boxes_created_this_iteration[op.result] = True
-            args = op.getarglist()
-            if op.is_guard():
-                args = args + op.getfailargs()
+        for op in short:
+            newop = self.short_inliner.inline_op(op)
+            self.optimizer.send_extra_operation(newop)
+        
+        self.optimizer.flush()
 
-            for a in args:
-                if not isinstance(a, Const) and not a in boxes_created_this_iteration:
-                    if a not in inputargs:
-                        inputargs.append(a)
-                        box = inliner.inline_arg(a)
-                        if box in self.optimizer.values:
-                            box = self.optimizer.values[box].force_box()
-                        jumpargs.append(box)
+        i = j = 0
+        while i < len(self.optimizer.newoperations) or j < len(jumpargs):
+            if i == len(self.optimizer.newoperations):
+                while j < len(jumpargs):
+                    a = jumpargs[j]
+                    if self.optimizer.loop.logops:
+                        debug_print('J:  ' + self.optimizer.loop.logops.repr_of_arg(a))
+                    self.import_box(a, inputargs, short, short_jumpargs,
+                                    jumpargs, short_seen)
+                    j += 1
+            else:
+                op = self.optimizer.newoperations[i]
 
-        jmp.initarglist(jumpargs)
-        self.optimizer.newoperations.append(jmp)
-        return inputargs
+                self.boxes_created_this_iteration[op.result] = True
+                args = op.getarglist()
+                if op.is_guard():
+                    args = args + op.getfailargs()
 
-    def sameop(self, op1, op2):
-        if op1.getopnum() != op2.getopnum():
-            return False
+                if self.optimizer.loop.logops:
+                    debug_print('OP: ' + self.optimizer.loop.logops.repr_of_resop(op))
+                for a in args:
+                    if self.optimizer.loop.logops:
+                        debug_print('A:  ' + self.optimizer.loop.logops.repr_of_arg(a))
+                    self.import_box(a, inputargs, short, short_jumpargs,
+                                    jumpargs, short_seen)
+                i += 1
 
-        args1 = op1.getarglist()
-        args2 = op2.getarglist()
-        if len(args1) != len(args2):
-            return False
-        for i in range(len(args1)):
-            box1, box2 = args1[i], args2[i]
-            val1 = self.optimizer.getvalue(box1)
-            val2 = self.optimizer.getvalue(box2)
-            if val1.is_constant() and val2.is_constant():
-                if not val1.box.same_constant(val2.box):
-                    return False
-            elif val1 is not val2:
-                return False
+        jumpop.initarglist(jumpargs)
+        self.optimizer.send_extra_operation(jumpop)
+        short.append(ResOperation(rop.JUMP, short_jumpargs, None))
 
-        if not op1.is_guard():
-            descr1 = op1.getdescr()
-            descr2 = op2.getdescr()
-            if descr1 is not descr2:
-                return False
+        modifier = VirtualStateAdder(self.optimizer)
+        final_virtual_state = modifier.get_virtual_state(original_jumpargs)
+        debug_start('jit-log-virtualstate')
+        virtual_state.debug_print('Closed loop with ')
+        bad = {}
+        if not virtual_state.generalization_of(final_virtual_state, bad):
+            # We ended up with a virtual state that is not compatible
+            # and we are thus unable to jump to the start of the loop
+            # XXX Is it possible to end up here? If so, consider:
+            #    - Fallback on having the preamble jump to itself?
+            #    - Would virtual_state.generate_guards make sense here?
+            final_virtual_state.debug_print("Bad virtual state at end of loop, ",
+                                            bad)
+            debug_stop('jit-log-virtualstate')
+            raise InvalidLoop
+        debug_stop('jit-log-virtualstate')
+        
+        return short
 
-        return True
+    def ensure_short_op_emitted(self, op, optimizer, seen):
+        if op is None:
+            return
+        if op.result is not None and op.result in seen:
+            return
+        for a in op.getarglist():
+            if not isinstance(a, Const) and a not in seen:
+                self.ensure_short_op_emitted(self.short_boxes.producer(a), optimizer, seen)
+        optimizer.send_extra_operation(op)
+        seen[op.result] = True
+        if op.is_ovf():
+            guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
+            optimizer.send_extra_operation(guard)
+        
+    def add_op_to_short(self, op, short, short_seen, emit=True, guards_needed=False):
+        if op is None:
+            return None
+        if op.result is not None and op.result in short_seen:
+            if emit:
+                return self.short_inliner.inline_arg(op.result)
+            else:
+                return None
+        
+        for a in op.getarglist():
+            if not isinstance(a, Const) and a not in short_seen:
+                self.add_op_to_short(self.short_boxes.producer(a), short, short_seen,
+                                     emit, guards_needed)
+        if op.is_guard():
+            descr = self.start_resumedescr.clone_if_mutable()
+            op.setdescr(descr)
 
-    def create_short_preamble(self, preamble, loop):
-        #return None # Dissable
+        if guards_needed and self.short_boxes.has_producer(op.result):
+            value_guards = self.getvalue(op.result).make_guards(op.result)
+        else:
+            value_guards = []            
 
-        preamble_ops = preamble.operations
-        loop_ops = loop.operations
+        short.append(op)
+        short_seen[op.result] = True
+        if emit:
+            newop = self.short_inliner.inline_op(op)
+            self.optimizer.send_extra_operation(newop)
+        else:
+            newop = None
 
-        boxmap = BoxMap()
-        state = ExeState(self.optimizer)
-        short_preamble = []
-        loop_i = preamble_i = 0
-        while preamble_i < len(preamble_ops):
+        if op.is_ovf():
+            # FIXME: ensure that GUARD_OVERFLOW:ed ops not end up here
+            guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
+            self.add_op_to_short(guard, short, short_seen, emit, guards_needed)
+        for guard in value_guards:
+            self.add_op_to_short(guard, short, short_seen, emit, guards_needed)
 
-            op = preamble_ops[preamble_i]
-            try:
-                newop = self.inliner.inline_op(op, ignore_result=True,
-                                               ignore_failargs=True)
-            except KeyError:
-                debug_print("create_short_preamble failed due to",
-                            "new boxes created during optimization.",
-                            "op:", op.getopnum(),
-                            "at preamble position: ", preamble_i,
-                            "loop position: ", loop_i)
-                return None
+        if newop:
+            return newop.result
+        return None
+        
+    def import_box(self, box, inputargs, short, short_jumpargs,
+                   jumpargs, short_seen):
+        if isinstance(box, Const) or box in inputargs:
+            return
+        if box in self.boxes_created_this_iteration:
+            return
 
-            if self.sameop(newop, loop_ops[loop_i]) \
-               and loop_i < len(loop_ops):
-                try:
-                    boxmap.link_ops(op, loop_ops[loop_i])
-                except ImpossibleLink:
-                    debug_print("create_short_preamble failed due to",
-                                "impossible link of "
-                                "op:", op.getopnum(),
-                                "at preamble position: ", preamble_i,
-                                "loop position: ", loop_i)
-                    return None
-                loop_i += 1
-            else:
-                if not state.safe_to_move(op):
-                    debug_print("create_short_preamble failed due to",
-                                "unsafe op:", op.getopnum(),
-                                "at preamble position: ", preamble_i,
-                                "loop position: ", loop_i)
-                    return None
-                short_preamble.append(op)
+        short_op = self.short_boxes.producer(box)
+        newresult = self.add_op_to_short(short_op, short, short_seen)
 
-            state.update(op)
-            preamble_i += 1
-
-        if loop_i < len(loop_ops):
-            debug_print("create_short_preamble failed due to",
-                        "loop contaning ops not in preamble"
-                        "at position", loop_i)
-            return None
-
-
-        jumpargs = []
-        for i in range(len(loop.inputargs)):
-            try:
-                jumpargs.append(boxmap.get_preamblebox(loop.inputargs[i]))
-            except KeyError:
-                debug_print("create_short_preamble failed due to",
-                            "input arguments not located")
-                return None
-
-        jmp = ResOperation(rop.JUMP, jumpargs[:], None)
-        jmp.setdescr(loop.token)
-        short_preamble.append(jmp)
-
-        # Check that boxes used as arguemts are produced.
-        seen = {}
-        for box in preamble.inputargs:
-            seen[box] = True
-        for op in short_preamble:
-            for box in op.getarglist():
-                if isinstance(box, Const):
-                    continue
-                if box not in seen:
-                    debug_print("create_short_preamble failed due to",
-                                "op arguments not produced")
-                    return None
-            if op.result:
-                seen[op.result] = True
-
-        return short_preamble
-
-class ExeState(object):
-    def __init__(self, optimizer):
-        self.optimizer = optimizer
-        self.heap_dirty = False
-        self.unsafe_getitem = {}
-        self.unsafe_getarrayitem = {}
-        self.unsafe_getarrayitem_indexes = {}
-
-    # Make sure it is safe to move the instrucions in short_preamble
-    # to the top making short_preamble followed by loop equvivalent
-    # to preamble
-    def safe_to_move(self, op):
-        opnum = op.getopnum()
-        descr = op.getdescr()
-        for box in op.getarglist():
-            if self.optimizer.getvalue(box) in self.optimizer.opaque_pointers:
-                return False
-        if op.is_always_pure() or op.is_foldable_guard():
-            return True
-        elif opnum == rop.JUMP:
-            return True
-        elif (opnum == rop.GETFIELD_GC or
-              opnum == rop.GETFIELD_RAW):
-            if self.heap_dirty:
-                return False
-            if descr in self.unsafe_getitem:
-                return False
-            return True
-        elif (opnum == rop.GETARRAYITEM_GC or
-              opnum == rop.GETARRAYITEM_RAW):
-            if self.heap_dirty:
-                return False
-            if descr in self.unsafe_getarrayitem:
-                return False
-            index = op.getarg(1)
-            if isinstance(index, Const):
-                d = self.unsafe_getarrayitem_indexes.get(descr, None)
-                if d is not None:
-                    if index.getint() in d:
-                        return False
-            else:
-                if descr in self.unsafe_getarrayitem_indexes:
-                    return False
-            return True
-        elif opnum == rop.CALL:
-            effectinfo = descr.get_extra_info()
-            ef = effectinfo.extraeffect
-            if ef == EffectInfo.EF_LOOPINVARIANT or \
-               ef == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
-               ef == EffectInfo.EF_ELIDABLE_CAN_RAISE:
-                return True
-        return False
-
-    def update(self, op):
-        if (op.has_no_side_effect() or
-            op.is_ovf() or
-            op.is_guard()):
-            return
-        opnum = op.getopnum()
-        descr = op.getdescr()
-        if (opnum == rop.DEBUG_MERGE_POINT):
-            return
-        if (opnum == rop.SETFIELD_GC or
-            opnum == rop.SETFIELD_RAW):
-            self.unsafe_getitem[descr] = True
-            return
-        if (opnum == rop.SETARRAYITEM_GC or
-            opnum == rop.SETARRAYITEM_RAW):
-            index = op.getarg(1)
-            if isinstance(index, Const):
-                d = self.unsafe_getarrayitem_indexes.get(descr, None)
-                if d is None:
-                    d = self.unsafe_getarrayitem_indexes[descr] = {}
-                d[index.getint()] = True
-            else:
-                self.unsafe_getarrayitem[descr] = True
-            return
-        if opnum == rop.CALL:
-            effectinfo = descr.get_extra_info()
-            if not effectinfo.has_random_effects():
-                for fielddescr in effectinfo.write_descrs_fields:
-                    self.unsafe_getitem[fielddescr] = True
-                for arraydescr in effectinfo.write_descrs_arrays:
-                    self.unsafe_getarrayitem[arraydescr] = True
-                return
-        debug_print("heap dirty due to op ", opnum)
-        self.heap_dirty = True
-
-class ImpossibleLink(JitException):
-    pass
-
-class BoxMap(object):
-    def __init__(self):
-        self.map = {}
-
-
-    def link_ops(self, preambleop, loopop):
-        pargs = preambleop.getarglist()
-        largs = loopop.getarglist()
-        if len(pargs) != len(largs):
-            raise ImpossibleLink
-        for i in range(len(largs)):
-            pbox, lbox = pargs[i], largs[i]
-            self.link_boxes(pbox, lbox)
-
-        if preambleop.result:
-            if not loopop.result:
-                raise ImpossibleLink
-            self.link_boxes(preambleop.result, loopop.result)
-
-
-    def link_boxes(self, pbox, lbox):
-        if lbox in self.map:
-            if self.map[lbox] is not pbox:
-                raise ImpossibleLink
-        else:
-            if isinstance(lbox, Const):
-                if not isinstance(pbox, Const) or not pbox.same_constant(lbox):
-                    raise ImpossibleLink
-            else:
-                self.map[lbox] = pbox
-
-
-    def get_preamblebox(self, loopbox):
-        return self.map[loopbox]
+        short_jumpargs.append(short_op.result)
+        inputargs.append(box)
+        box = newresult
+        if box in self.optimizer.values:
+            box = self.optimizer.values[box].force_box()
+        jumpargs.append(box)
+        
 
 class OptInlineShortPreamble(Optimization):
     def __init__(self, retraced):
         self.retraced = retraced
-        self.inliner = None
 
-
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
+    def new(self):
+        return OptInlineShortPreamble(self.retraced)
 
     def propagate_forward(self, op):
         if op.getopnum() == rop.JUMP:
-            descr = op.getdescr()
-            assert isinstance(descr, LoopToken)
+            loop_token = op.getdescr()
+            assert isinstance(loop_token, LoopToken)
             # FIXME: Use a tree, similar to the tree formed by the full
             # preamble and it's bridges, instead of a list to save time and
             # memory. This should also allow better behaviour in
             # situations that the is_emittable() chain currently cant
             # handle and the inlining fails unexpectedly belwo.
-            short = descr.short_preamble
+            short = loop_token.short_preamble
             if short:
                 args = op.getarglist()
                 modifier = VirtualStateAdder(self.optimizer)
                 virtual_state = modifier.get_virtual_state(args)
+                debug_start('jit-log-virtualstate')
+                virtual_state.debug_print("Looking for ")
+
                 for sh in short:
                     ok = False
                     extra_guards = []
-                    if sh.virtual_state.generalization_of(virtual_state):
+
+                    bad = {}
+                    debugmsg = 'Did not match '
+                    if sh.virtual_state.generalization_of(virtual_state, bad):
                         ok = True
+                        debugmsg = 'Matched '
                     else:
                         try:
                             cpu = self.optimizer.cpu
@@ -661,38 +481,46 @@
                                                              extra_guards)
 
                             ok = True
+                            debugmsg = 'Guarded to match '
                         except InvalidLoop:
                             pass
+                    sh.virtual_state.debug_print(debugmsg, bad)
+                    
                     if ok:
-                        # FIXME: Do we still need the dry run
-                        #if self.inline(sh.operations, sh.inputargs,
-                        #               op.getarglist(), dryrun=True):
+                        debug_stop('jit-log-virtualstate')
+
+                        values = [self.getvalue(arg)
+                                  for arg in op.getarglist()]
+                        args = sh.virtual_state.make_inputargs(values,
+                                                               keyboxes=True)
+                        inliner = Inliner(sh.inputargs, args)
+                        
+                        for guard in extra_guards:
+                            if guard.is_guard():
+                                descr = sh.start_resumedescr.clone_if_mutable()
+                                inliner.inline_descr_inplace(descr)
+                                guard.setdescr(descr)
+                            self.emit_operation(guard)
+                        
                         try:
-                            self.inline(sh.operations, sh.inputargs,
-                                        op.getarglist())
+                            for shop in sh.operations:
+                                newop = inliner.inline_op(shop)
+                                self.emit_operation(newop)
                         except InvalidLoop:
                             debug_print("Inlining failed unexpectedly",
                                         "jumping to preamble instead")
                             self.emit_operation(op)
-                        else:
-                            jumpop = self.optimizer.newoperations.pop()
-                            assert jumpop.getopnum() == rop.JUMP
-                            for guard in extra_guards:
-                                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 = descr.retraced_count
-                descr.retraced_count += 1
+                debug_stop('jit-log-virtualstate')
+                retraced_count = loop_token.retraced_count
                 limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit
                 if not self.retraced and retraced_count<limit:
-                    if not descr.failed_states:
+                    loop_token.retraced_count += 1
+                    if not loop_token.failed_states:
                         debug_print("Retracing (%d of %d)" % (retraced_count,
                                                               limit))
                         raise RetraceLoop
-                    for failed in descr.failed_states:
+                    for failed in loop_token.failed_states:
                         if failed.generalization_of(virtual_state):
                             # Retracing once more will most likely fail again
                             break
@@ -702,29 +530,12 @@
 
                         raise RetraceLoop
                 else:
-                    if not descr.failed_states:
-                        descr.failed_states=[virtual_state]
+                    if not loop_token.failed_states:
+                        loop_token.failed_states=[virtual_state]
                     else:
-                        descr.failed_states.append(virtual_state)
+                        loop_token.failed_states.append(virtual_state)
         self.emit_operation(op)
 
 
 
-    def inline(self, loop_operations, loop_args, jump_args, dryrun=False):
-        self.inliner = inliner = Inliner(loop_args, jump_args)
 
-        for op in loop_operations:
-            newop = inliner.inline_op(op)
-
-            if not dryrun:
-                self.emit_operation(newop)
-            else:
-                if not self.is_emittable(newop):
-                    return False
-
-        return True
-
-    #def inline_arg(self, arg):
-    #    if isinstance(arg, Const):
-    #        return arg
-    #    return self.argmap[arg]
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
@@ -6,6 +6,7 @@
     descrlist_dict, sort_descrs)
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.rlib.objectmodel import we_are_translated
+from pypy.jit.metainterp.optimizeopt.optimizer import OptValue
 
 
 class AbstractVirtualValue(optimizer.OptValue):
@@ -34,6 +35,12 @@
             self._really_force()
         return self.box
 
+    def force_at_end_of_preamble(self, already_forced):
+        value = already_forced.get(self, None)
+        if value:
+            return value
+        return OptValue(self.force_box())
+
     def make_virtual_info(self, modifier, fieldnums):
         if fieldnums is None:
             return self._make_virtual(modifier)
@@ -51,9 +58,6 @@
     def _really_force(self):
         raise NotImplementedError("abstract base")
 
-    def reconstruct_for_next_iteration(self, _optimizer):
-        return optimizer.OptValue(self.force_box())
-
 def get_fielddescrlist_cache(cpu):
     if not hasattr(cpu, '_optimizeopt_fielddescrlist_cache'):
         result = descrlist_dict()
@@ -90,6 +94,15 @@
                 return False
         return True
 
+    def force_at_end_of_preamble(self, already_forced):
+        if self in already_forced:
+            return self
+        already_forced[self] = self
+        if self._fields:
+            for ofs in self._fields.keys():
+                self._fields[ofs] = self._fields[ofs].force_at_end_of_preamble(already_forced)
+        return self
+
     def _really_force(self):
         op = self.source_op
         assert op is not None
@@ -161,30 +174,6 @@
                 fieldvalue = self._fields[ofs]
                 fieldvalue.get_args_for_fail(modifier)
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key in already_seen:
-            return
-        already_seen[key] = None
-        if self.box is None:
-            lst = self._get_field_descr_list()
-            for ofs in lst:
-                self._fields[ofs].enum_forced_boxes(boxes, already_seen)
-        else:
-            boxes.append(self.box)
-
-    def reconstruct_for_next_iteration(self, optimizer):
-        self.optimizer = optimizer
-        return self
-
-    def reconstruct_childs(self, new, valuemap):
-        assert isinstance(new, AbstractVirtualStructValue)
-        if new.box is None:
-            lst = self._get_field_descr_list()
-            for ofs in lst:
-                new._fields[ofs] = \
-                      self._fields[ofs].get_reconstructed(new.optimizer, valuemap)
-
 class VirtualValue(AbstractVirtualStructValue):
     level = optimizer.LEVEL_KNOWNCLASS
 
@@ -220,6 +209,7 @@
     def _get_descr(self):
         return self.structdescr
 
+
 class VArrayValue(AbstractVirtualValue):
 
     def __init__(self, optimizer, arraydescr, size, keybox, source_op=None):
@@ -239,6 +229,14 @@
         assert isinstance(itemvalue, optimizer.OptValue)
         self._items[index] = itemvalue
 
+    def force_at_end_of_preamble(self, already_forced):
+        if self in already_forced:
+            return self
+        already_forced[self] = self
+        for index in range(len(self._items)):
+            self._items[index] = self._items[index].force_at_end_of_preamble(already_forced)
+        return self
+    
     def _really_force(self):
         assert self.source_op is not None
         if not we_are_translated():
@@ -271,34 +269,12 @@
     def _make_virtual(self, modifier):
         return modifier.make_varray(self.arraydescr)
 
-    def enum_forced_boxes(self, boxes, already_seen):
-        key = self.get_key_box()
-        if key in already_seen:
-            return
-        already_seen[key] = None
-        if self.box is None:
-            for itemvalue in self._items:
-                itemvalue.enum_forced_boxes(boxes, already_seen)
-        else:
-            boxes.append(self.box)
-
-    def reconstruct_for_next_iteration(self, optimizer):
-        self.optimizer = optimizer
-        return self
-
-    def reconstruct_childs(self, new, valuemap):
-        assert isinstance(new, VArrayValue)
-        if new.box is None:
-            for i in range(len(self._items)):
-                new._items[i] = self._items[i].get_reconstructed(new.optimizer,
-                                                                 valuemap)
-
 class OptVirtualize(optimizer.Optimization):
     "Virtualize objects until they escape."
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
-
+    def new(self):
+        return OptVirtualize()
+        
     def make_virtual(self, known_class, box, source_op=None):
         vvalue = VirtualValue(self.optimizer, known_class, box, source_op)
         self.make_equal_to(box, vvalue)
diff --git a/pypy/jit/metainterp/optimizeopt/virtualstate.py b/pypy/jit/metainterp/optimizeopt/virtualstate.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/virtualstate.py
@@ -0,0 +1,537 @@
+from pypy.jit.metainterp import resume
+from pypy.jit.metainterp.optimizeopt import virtualize
+from pypy.jit.metainterp.optimizeopt.optimizer import LEVEL_CONSTANT, \
+                                                      LEVEL_KNOWNCLASS, \
+                                                      LEVEL_NONNULL, \
+                                                      LEVEL_UNKNOWN, \
+                                                      MININT, MAXINT, OptValue
+from pypy.jit.metainterp.history import BoxInt, ConstInt, BoxPtr, Const
+from pypy.jit.metainterp.optimize import InvalidLoop
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.debug import debug_start, debug_stop, debug_print
+from pypy.rlib.objectmodel import we_are_translated
+
+class AbstractVirtualStateInfo(resume.AbstractVirtualInfo):
+    position = -1
+    
+    def generalization_of(self, other, renum, bad):
+        raise NotImplementedError
+
+    def generate_guards(self, other, box, cpu, extra_guards, renum):
+        if self.generalization_of(other, renum, {}):
+            return
+        if renum[self.position] != other.position:
+            raise InvalidLoop
+        self._generate_guards(other, box, cpu, extra_guards)
+
+    def _generate_guards(self, other, box, cpu, extra_guards):
+        raise InvalidLoop
+
+    def enum_forced_boxes(self, boxes, value):
+        raise NotImplementedError
+
+    def enum(self, virtual_state):
+        if self.position != -1:
+            return
+        virtual_state.info_counter += 1
+        self.position = virtual_state.info_counter
+        self._enum(virtual_state)
+
+    def _enum(self, virtual_state):
+        raise NotImplementedError
+
+    def debug_print(self, indent, seen, bad):
+        mark = ''
+        if self in bad:
+            mark = '*'
+        self.debug_header(indent + mark)
+        if self not in seen:
+            seen[self] = True
+            for s in self.fieldstate:
+                s.debug_print(indent + "    ", seen, bad)
+        else:
+            debug_print(indent + "    ...")
+                
+
+    def debug_header(self, indent):
+        raise NotImplementedError
+
+
+class AbstractVirtualStructStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, fielddescrs):
+        self.fielddescrs = fielddescrs
+
+    def generalization_of(self, other, renum, bad):
+        assert self.position != -1
+        if self.position in renum:
+            if renum[self.position] == other.position:
+                return True
+            bad[self] = True
+            bad[other] = True
+            return False
+        renum[self.position] = other.position
+        if not self._generalization_of(other):
+            bad[self] = True
+            bad[other] = True
+            return False
+        assert len(self.fielddescrs) == len(self.fieldstate)
+        assert len(other.fielddescrs) == len(other.fieldstate)
+        if len(self.fielddescrs) != len(other.fielddescrs):
+            bad[self] = True
+            bad[other] = True
+            return False
+        
+        for i in range(len(self.fielddescrs)):
+            if other.fielddescrs[i] is not self.fielddescrs[i]:
+                bad[self] = True
+                bad[other] = True
+                return False
+            if not self.fieldstate[i].generalization_of(other.fieldstate[i],
+                                                        renum, bad):
+                bad[self] = True
+                bad[other] = True
+                return False
+
+        return True
+
+    def _generalization_of(self, other):
+        raise NotImplementedError
+
+    def enum_forced_boxes(self, boxes, value):
+        assert isinstance(value, virtualize.AbstractVirtualStructValue)
+        assert value.is_virtual()
+        for i in range(len(self.fielddescrs)):
+            v = value._fields[self.fielddescrs[i]]
+            s = self.fieldstate[i]
+            if s.position > self.position:
+                s.enum_forced_boxes(boxes, v)
+
+    def _enum(self, virtual_state):
+        for s in self.fieldstate:
+            s.enum(virtual_state)
+        
+        
+class VirtualStateInfo(AbstractVirtualStructStateInfo):
+    def __init__(self, known_class, fielddescrs):
+        AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
+        self.known_class = known_class
+
+    def _generalization_of(self, other):
+        if not isinstance(other, VirtualStateInfo):
+            return False
+        if not self.known_class.same_constant(other.known_class):
+            return False
+        return True
+
+    def debug_header(self, indent):
+        debug_print(indent + 'VirtualStateInfo(%d):' % self.position)
+        
+class VStructStateInfo(AbstractVirtualStructStateInfo):
+    def __init__(self, typedescr, fielddescrs):
+        AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
+        self.typedescr = typedescr
+
+    def _generalization_of(self, other):        
+        if not isinstance(other, VStructStateInfo):
+            return False
+        if self.typedescr is not other.typedescr:
+            return False
+        return True
+
+    def debug_header(self, indent):
+        debug_print(indent + 'VStructStateInfo(%d):' % self.position)
+        
+class VArrayStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, arraydescr):
+        self.arraydescr = arraydescr
+
+    def generalization_of(self, other, renum, bad):
+        assert self.position != -1
+        if self.position in renum:
+            if renum[self.position] == other.position:
+                return True
+            bad[self] = True
+            bad[other] = True
+            return False
+        renum[self.position] = other.position
+        if not isinstance(other, VArrayStateInfo):
+            bad[self] = True
+            bad[other] = True
+            return False
+        if self.arraydescr is not other.arraydescr:
+            bad[self] = True
+            bad[other] = True
+            return False
+        if len(self.fieldstate) != len(other.fieldstate):
+            bad[self] = True
+            bad[other] = True
+            return False
+        for i in range(len(self.fieldstate)):
+            if not self.fieldstate[i].generalization_of(other.fieldstate[i],
+                                                        renum, bad):
+                bad[self] = True
+                bad[other] = True
+                return False
+        return True
+
+    def enum_forced_boxes(self, boxes, value):
+        assert isinstance(value, virtualize.VArrayValue)
+        assert value.is_virtual()
+        for i in range(len(self.fieldstate)):
+            v = value._items[i]
+            s = self.fieldstate[i]
+            if s.position > self.position:
+                s.enum_forced_boxes(boxes, v)
+
+    def _enum(self, virtual_state):
+        for s in self.fieldstate:
+            s.enum(virtual_state)
+
+    def debug_header(self, indent):
+        debug_print(indent + 'VArrayStateInfo(%d):' % self.position)
+            
+        
+class NotVirtualStateInfo(AbstractVirtualStateInfo):
+    def __init__(self, value):
+        self.known_class = value.known_class
+        self.level = value.level
+        if value.intbound is None:
+            self.intbound = IntUnbounded()
+        else:
+            self.intbound = value.intbound.clone()
+        if value.is_constant():
+            self.constbox = value.box
+        else:
+            self.constbox = None
+        self.position_in_notvirtuals = -1
+        self.lenbound = value.lenbound
+
+    def generalization_of(self, other, renum, bad):
+        # XXX This will always retrace instead of forcing anything which
+        # might be what we want sometimes?
+        assert self.position != -1
+        if self.position in renum:
+            if renum[self.position] == other.position:
+                return True
+            bad[self] = True
+            bad[other] = True
+            return False
+        renum[self.position] = other.position
+        if not isinstance(other, NotVirtualStateInfo):
+            bad[self] = True
+            bad[other] = True
+            return False
+        if other.level < self.level:
+            bad[self] = True
+            bad[other] = True
+            return False
+        if self.level == LEVEL_CONSTANT:
+            if not self.constbox.same_constant(other.constbox):
+                bad[self] = True
+                bad[other] = True
+                return False
+        elif self.level == LEVEL_KNOWNCLASS:
+            if not self.known_class.same_constant(other.known_class):
+                bad[self] = True
+                bad[other] = True
+                return False
+        if not self.intbound.contains_bound(other.intbound):
+            bad[self] = True
+            bad[other] = True
+            return False
+        if self.lenbound and other.lenbound:
+            if self.lenbound.mode != other.lenbound.mode or \
+               self.lenbound.descr != other.lenbound.descr or \
+               not self.lenbound.bound.contains_bound(other.lenbound.bound):
+                bad[self] = True
+                bad[other] = True
+                return False
+        elif self.lenbound:
+            bad[self] = True
+            bad[other] = True
+            return False
+        return True
+
+    def _generate_guards(self, other, box, cpu, extra_guards):
+        if not isinstance(other, NotVirtualStateInfo):
+            raise InvalidLoop
+
+        if self.lenbound or other.lenbound:
+            raise InvalidLoop
+
+        if self.level == LEVEL_KNOWNCLASS and \
+           box.nonnull() and \
+           self.known_class.same_constant(cpu.ts.cls_of_box(box)):
+            # Note: This is only a hint on what the class of box was
+            # during the trace. There are actually no guarentees that this
+            # box realy comes from a trace. The hint is used here to choose
+            # between either eimtting a guard_class and jumping to an
+            # excisting compiled loop or retracing the loop. Both
+            # alternatives will always generate correct behaviour, but
+            # performace will differ.
+            op = ResOperation(rop.GUARD_NONNULL, [box], None)
+            extra_guards.append(op)
+            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+            extra_guards.append(op)
+            return
+        
+        if self.level == LEVEL_NONNULL and \
+               other.level == LEVEL_UNKNOWN and \
+               isinstance(box, BoxPtr) and \
+               box.nonnull():
+            op = ResOperation(rop.GUARD_NONNULL, [box], None)
+            extra_guards.append(op)
+            return
+        
+        if self.level == LEVEL_UNKNOWN and \
+               other.level == LEVEL_UNKNOWN and \
+               isinstance(box, BoxInt) and \
+               self.intbound.contains(box.getint()):
+            if self.intbound.has_lower:
+                bound = self.intbound.lower
+                if not (other.intbound.has_lower and \
+                        other.intbound.lower >= bound):
+                    res = BoxInt()
+                    op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res)
+                    extra_guards.append(op)
+                    op = ResOperation(rop.GUARD_TRUE, [res], None)
+                    extra_guards.append(op)
+            if self.intbound.has_upper:
+                bound = self.intbound.upper
+                if not (other.intbound.has_upper and \
+                        other.intbound.upper <= bound):
+                    res = BoxInt()
+                    op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res)
+                    extra_guards.append(op)
+                    op = ResOperation(rop.GUARD_TRUE, [res], None)
+                    extra_guards.append(op)
+            return
+        
+        # Remaining cases are probably not interesting
+        raise InvalidLoop
+        if self.level == LEVEL_CONSTANT:
+            import pdb; pdb.set_trace()
+            raise NotImplementedError
+
+    def enum_forced_boxes(self, boxes, value):
+        if self.level == LEVEL_CONSTANT:
+            return
+        assert 0 <= self.position_in_notvirtuals 
+        boxes[self.position_in_notvirtuals] = value.force_box()
+
+    def _enum(self, virtual_state):
+        if self.level == LEVEL_CONSTANT:
+            return
+        self.position_in_notvirtuals = len(virtual_state.notvirtuals)
+        virtual_state.notvirtuals.append(self)
+
+    def debug_print(self, indent, seen, bad):
+        mark = ''
+        if self in bad:
+            mark = '*'
+        if we_are_translated():
+            l = {LEVEL_UNKNOWN: 'Unknown',
+                 LEVEL_NONNULL: 'NonNull',
+                 LEVEL_KNOWNCLASS: 'KnownClass',
+                 LEVEL_CONSTANT: 'Constant',
+                 }[self.level]
+        else:
+            l = {LEVEL_UNKNOWN: 'Unknown',
+                 LEVEL_NONNULL: 'NonNull',
+                 LEVEL_KNOWNCLASS: 'KnownClass(%r)' % self.known_class,
+                 LEVEL_CONSTANT: 'Constant(%r)' % self.constbox,
+                 }[self.level]
+
+        lb = ''
+        if self.lenbound:
+            lb = ', ' + self.lenbound.bound.__repr__()
+        
+        debug_print(indent + mark + 'NotVirtualInfo(%d' % self.position +
+                    ', ' + l + ', ' + self.intbound.__repr__() + lb + ')')
+
+class VirtualState(object):
+    def __init__(self, state):
+        self.state = state
+        self.info_counter = -1
+        self.notvirtuals = [] # FIXME: We dont need this list, only it's length
+        for s in state:
+            s.enum(self)
+
+    def generalization_of(self, other, bad=None):
+        if bad is None:
+            bad = {}
+        assert len(self.state) == len(other.state)
+        renum = {}
+        for i in range(len(self.state)):
+            if not self.state[i].generalization_of(other.state[i], renum, bad):
+                return False
+        return True
+
+    def generate_guards(self, other, args, cpu, extra_guards):        
+        assert len(self.state) == len(other.state) == len(args)
+        renum = {}
+        for i in range(len(self.state)):
+            self.state[i].generate_guards(other.state[i], args[i],
+                                          cpu, extra_guards, renum)
+
+    def make_inputargs(self, values, keyboxes=False):
+        assert len(values) == len(self.state)
+        inputargs = [None] * len(self.notvirtuals)
+        for i in range(len(values)):
+            self.state[i].enum_forced_boxes(inputargs, values[i])
+
+        if keyboxes:
+            for i in range(len(values)):
+                if not isinstance(self.state[i], NotVirtualStateInfo):
+                    box = values[i].get_key_box()
+                    assert not isinstance(box, Const)
+                    inputargs.append(box)
+
+        assert None not in inputargs
+            
+        return inputargs
+
+    def debug_print(self, hdr='', bad=None):
+        if bad is None:
+            bad = {}
+        debug_print(hdr + "VirtualState():")
+        seen = {}
+        for s in self.state:
+            s.debug_print("    ", seen, bad)
+
+class VirtualStateAdder(resume.ResumeDataVirtualAdder):
+    def __init__(self, optimizer):
+        self.fieldboxes = {}
+        self.optimizer = optimizer
+        self.info = {}
+
+    def register_virtual_fields(self, keybox, fieldboxes):
+        self.fieldboxes[keybox] = fieldboxes
+        
+    def already_seen_virtual(self, keybox):
+        return keybox in self.fieldboxes
+
+    def getvalue(self, box):
+        return self.optimizer.getvalue(box)
+
+    def state(self, box):
+        value = self.getvalue(box)
+        box = value.get_key_box()
+        try:
+            info = self.info[box]
+        except KeyError:
+            if value.is_virtual():
+                self.info[box] = info = value.make_virtual_info(self, None)
+                flds = self.fieldboxes[box]
+                info.fieldstate = [self.state(b) for b in flds]
+            else:
+                self.info[box] = info = self.make_not_virtual(value)
+        return info
+
+    def get_virtual_state(self, jump_args):
+        self.optimizer.force_at_end_of_preamble()
+        already_forced = {}
+        values = [self.getvalue(box).force_at_end_of_preamble(already_forced)
+                  for box in jump_args]
+
+        for value in values:
+            if value.is_virtual():
+                value.get_args_for_fail(self)
+            else:
+                self.make_not_virtual(value)
+        return VirtualState([self.state(box) for box in jump_args])
+
+    def make_not_virtual(self, value):
+        return NotVirtualStateInfo(value)
+
+    def make_virtual(self, known_class, fielddescrs):
+        return VirtualStateInfo(known_class, fielddescrs)
+
+    def make_vstruct(self, typedescr, fielddescrs):
+        return VStructStateInfo(typedescr, fielddescrs)
+
+    def make_varray(self, arraydescr):
+        return VArrayStateInfo(arraydescr)
+
+class BoxNotProducable(Exception):
+    pass
+
+class ShortBoxes(object):
+    def __init__(self, optimizer, surviving_boxes):
+        self.potential_ops = {}
+        self.duplicates = {}
+        self.aliases = {}
+        self.optimizer = optimizer
+        for box in surviving_boxes:
+            self.potential_ops[box] = None
+        optimizer.produce_potential_short_preamble_ops(self)
+
+        self.short_boxes = {}
+
+        for box in self.potential_ops.keys():
+            try:
+                self.produce_short_preamble_box(box)
+            except BoxNotProducable:
+                pass
+
+    def produce_short_preamble_box(self, box):
+        if box in self.short_boxes:
+            return 
+        if isinstance(box, Const):
+            return 
+        if box in self.potential_ops:
+            op = self.potential_ops[box]
+            if op:
+                for arg in op.getarglist():
+                    self.produce_short_preamble_box(arg)
+            self.short_boxes[box] = op
+        else:
+            raise BoxNotProducable
+
+    def add_potential(self, op):
+        if op.result not in self.potential_ops:
+            self.potential_ops[op.result] = op
+            return op
+        newop = op.clone()
+        newop.result = op.result.clonebox()
+        self.potential_ops[newop.result] = newop
+        if op.result in self.duplicates:
+            self.duplicates[op.result].append(newop.result)
+        else:
+            self.duplicates[op.result] = [newop.result]
+        self.optimizer.make_equal_to(newop.result, self.optimizer.getvalue(op.result))
+        return newop
+
+    def debug_print(self, logops):
+        debug_start('jit-short-boxes')
+        for box, op in self.short_boxes.items():
+            if op:
+                debug_print(logops.repr_of_arg(box) + ': ' + logops.repr_of_resop(op))
+            else:
+                debug_print(logops.repr_of_arg(box) + ': None')
+        debug_stop('jit-short-boxes')
+        
+    def operations(self):
+        if not we_are_translated(): # For tests
+            ops = self.short_boxes.values()
+            ops.sort(key=str, reverse=True)
+            return ops
+        return self.short_boxes.values()
+
+    def producer(self, box):
+        return self.short_boxes[box]
+
+    def has_producer(self, box):
+        return box in self.short_boxes
+
+    def alias(self, newbox, oldbox):
+        if not isinstance(oldbox, Const) and newbox not in self.short_boxes:
+            self.short_boxes[newbox] = self.short_boxes[oldbox]
+        self.aliases[newbox] = oldbox
+        
+    def original(self, box):
+        while box in self.aliases:
+            box = self.aliases[box]
+        return box
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -57,7 +57,7 @@
         self.ensure_nonnull()
         box = self.force_box()
         lengthbox = BoxInt()
-        optimization.optimize_default(ResOperation(mode.STRLEN, [box], lengthbox))
+        optimization.propagate_forward(ResOperation(mode.STRLEN, [box], lengthbox))
         return lengthbox
 
     @specialize.arg(1)
@@ -332,7 +332,7 @@
     if optimizer is None:
         return None
     resbox = BoxInt()
-    optimizer.optimize_default(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+    optimizer.propagate_forward(ResOperation(rop.INT_ADD, [box1, box2], resbox))
     return resbox
 
 def _int_sub(optimizer, box1, box2):
@@ -342,7 +342,7 @@
         if isinstance(box1, ConstInt):
             return ConstInt(box1.value - box2.value)
     resbox = BoxInt()
-    optimizer.optimize_default(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+    optimizer.propagate_forward(ResOperation(rop.INT_SUB, [box1, box2], resbox))
     return resbox
 
 def _strgetitem(optimizer, strbox, indexbox, mode):
@@ -354,7 +354,7 @@
             s = strbox.getref(lltype.Ptr(rstr.UNICODE))
             return ConstInt(ord(s.chars[indexbox.getint()]))
     resbox = BoxInt()
-    optimizer.optimize_default(ResOperation(mode.STRGETITEM, [strbox, indexbox],
+    optimizer.propagate_forward(ResOperation(mode.STRGETITEM, [strbox, indexbox],
                                       resbox))
     return resbox
 
@@ -363,10 +363,9 @@
     "Handling of strings and unicodes."
     enabled = True
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        self.enabled = True
-        return self
-
+    def new(self):
+        return OptString()
+    
     def make_vstring_plain(self, box, source_op, mode):
         vvalue = VStringPlainValue(self.optimizer, box, source_op, mode)
         self.make_equal_to(box, vvalue)
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
@@ -15,7 +15,7 @@
 from pypy.jit.metainterp.jitprof import EmptyProfiler
 from pypy.jit.metainterp.jitprof import GUARDS, RECORDED_OPS, ABORT_ESCAPE
 from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE, \
-                                        ABORT_FORCE_QUASIIMMUT
+                                        ABORT_FORCE_QUASIIMMUT, ABORT_BAD_LOOP
 from pypy.jit.metainterp.jitexc import JitException, get_llexception
 from pypy.rlib.objectmodel import specialize
 from pypy.jit.codewriter.jitcode import JitCode, SwitchDictDescr
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
@@ -455,17 +455,6 @@
 
     def debug_prints(self):
         raise NotImplementedError
-
-    def generalization_of(self, other):
-        raise NotImplementedError
-
-    def generate_guards(self, other, box, cpu, extra_guards):
-        if self.generalization_of(other):
-            return
-        self._generate_guards(other, box, cpu, extra_guards)
-
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        raise InvalidLoop
         
 class AbstractVirtualStructInfo(AbstractVirtualInfo):
     def __init__(self, fielddescrs):
@@ -486,26 +475,6 @@
                         str(self.fielddescrs[i]),
                         str(untag(self.fieldnums[i])))
 
-    def generalization_of(self, other):
-        if not self._generalization_of(other):
-            return False
-        assert len(self.fielddescrs) == len(self.fieldstate)
-        assert len(other.fielddescrs) == len(other.fieldstate)
-        if len(self.fielddescrs) != len(other.fielddescrs):
-            return False
-        
-        for i in range(len(self.fielddescrs)):
-            if other.fielddescrs[i] is not self.fielddescrs[i]:
-                return False
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
-                return False
-
-        return True
-
-    def _generalization_of(self, other):
-        raise NotImplementedError
-
-
 class VirtualInfo(AbstractVirtualStructInfo):
     def __init__(self, known_class, fielddescrs):
         AbstractVirtualStructInfo.__init__(self, fielddescrs)
@@ -521,13 +490,6 @@
         debug_print("\tvirtualinfo", self.known_class.repr_rpython())
         AbstractVirtualStructInfo.debug_prints(self)
 
-    def _generalization_of(self, other):        
-        if not isinstance(other, VirtualInfo):
-            return False
-        if not self.known_class.same_constant(other.known_class):
-            return False
-        return True
-        
 
 class VStructInfo(AbstractVirtualStructInfo):
     def __init__(self, typedescr, fielddescrs):
@@ -544,14 +506,6 @@
         debug_print("\tvstructinfo", self.typedescr.repr_rpython())
         AbstractVirtualStructInfo.debug_prints(self)
 
-    def _generalization_of(self, other):        
-        if not isinstance(other, VStructInfo):
-            return False
-        if self.typedescr is not other.typedescr:
-            return False
-        return True
-        
-
 class VArrayInfo(AbstractVirtualInfo):
     def __init__(self, arraydescr):
         self.arraydescr = arraydescr
@@ -583,17 +537,6 @@
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
-    def generalization_of(self, other):
-        if self.arraydescr is not other.arraydescr:
-            return False
-        if len(self.fieldstate) != len(other.fieldstate):
-            return False
-        for i in range(len(self.fieldstate)):
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i]):
-                return False
-        return True
-
-
 class VStrPlainInfo(AbstractVirtualInfo):
     """Stands for the string made out of the characters of all fieldnums."""
 
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
@@ -90,6 +90,40 @@
                     found += 1
             assert found == 1
 
+    def test_loop_variant_mul1(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
+        def f(x, y):
+            res = 0
+            while y > 0:
+                myjitdriver.can_enter_jit(x=x, y=y, res=res)
+                myjitdriver.jit_merge_point(x=x, y=y, res=res)
+                res += x * x
+                x += 1
+                res += x * x                
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 7])
+        assert res == 1323
+        self.check_loop_count(1)
+        self.check_loops(int_mul=1)
+        
+    def test_loop_variant_mul_ovf(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
+        def f(x, y):
+            res = 0
+            while y > 0:
+                myjitdriver.can_enter_jit(x=x, y=y, res=res)
+                myjitdriver.jit_merge_point(x=x, y=y, res=res)
+                res += ovfcheck(x * x)
+                x += 1
+                res += ovfcheck(x * x)
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 7])
+        assert res == 1323
+        self.check_loop_count(1)
+        self.check_loops(int_mul_ovf=1)
+
     def test_loop_invariant_mul1(self):
         myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
         def f(x, y):
@@ -1338,8 +1372,8 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_value=2)
-        self.check_loops(guard_class=0, guard_value=5, everywhere=True)
+        self.check_loops(guard_class=0, guard_value=3)        
+        self.check_loops(guard_class=0, guard_value=6, everywhere=True)
 
     def test_merge_guardnonnull_guardclass(self):
         from pypy.rlib.objectmodel import instantiate
@@ -1367,10 +1401,10 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_nonnull=0,
-                         guard_nonnull_class=2, guard_isnull=0)
-        self.check_loops(guard_class=0, guard_nonnull=0,
-                         guard_nonnull_class=4, guard_isnull=1,
+        self.check_loops(guard_class=0, guard_nonnull=2,
+                         guard_nonnull_class=2, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=4,
+                         guard_nonnull_class=4, guard_isnull=2,
                          everywhere=True)
 
     def test_merge_guardnonnull_guardvalue(self):
@@ -1398,9 +1432,9 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=1,
+        self.check_loops(guard_class=0, guard_nonnull=2, guard_value=2,
                          guard_nonnull_class=0, guard_isnull=1)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=3,
+        self.check_loops(guard_class=0, guard_nonnull=4, guard_value=4,
                          guard_nonnull_class=0, guard_isnull=2,
                          everywhere=True)
 
@@ -1429,10 +1463,10 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
-                         guard_nonnull_class=0, guard_isnull=0)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=4,
-                         guard_nonnull_class=0, guard_isnull=1,
+        self.check_loops(guard_class=0, guard_nonnull=2, guard_value=2,
+                         guard_nonnull_class=0, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=4, guard_value=4,
+                         guard_nonnull_class=0, guard_isnull=2,
                          everywhere=True)
 
     def test_merge_guardnonnull_guardclass_guardvalue(self):
@@ -1463,10 +1497,10 @@
             return x
         res = self.meta_interp(f, [399], listops=True)
         assert res == f(399)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
-                         guard_nonnull_class=0, guard_isnull=0)
-        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=5,
-                         guard_nonnull_class=0, guard_isnull=1,
+        self.check_loops(guard_class=0, guard_nonnull=3, guard_value=3,
+                         guard_nonnull_class=0, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=6, guard_value=6,
+                         guard_nonnull_class=0, guard_isnull=2,
                          everywhere=True)
 
     def test_residual_call_doesnt_lose_info(self):
@@ -1754,10 +1788,8 @@
             return a1.val + b1.val
         res = self.meta_interp(g, [6, 14])
         assert res == g(6, 14)
-        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)
+        self.check_loop_count(9)
+        self.check_loops(getarrayitem_gc=8, everywhere=True)
 
     def test_multiple_specialied_versions_bridge(self):
         myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'z', 'res'])
@@ -2064,6 +2096,109 @@
         assert res == 12
         self.check_tree_loop_count(2)
 
+    def test_caching_setfield(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
+        class A:
+            pass
+        def f(n, a):
+            i = sa = 0
+            node = A()
+            node.val1 = node.val2 = 0
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, node=node)
+                sa += node.val1 + node.val2
+                if i < n/2:
+                    node.val1 = a
+                    node.val2 = a
+                else:
+                    node.val1 = a
+                    node.val2 = a + 1
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7])
+        assert res == f(32, 7)
+        
+    def test_caching_setarrayitem_fixed(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
+        def f(n, a):
+            i = sa = 0
+            node = [1, 2, 3]
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, node=node)
+                sa += node[0] + node[1]
+                if i < n/2:
+                    node[0] = a
+                    node[1] = a
+                else:
+                    node[0] = a
+                    node[1] = a + 1
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7])
+        assert res == f(32, 7)
+        
+    def test_caching_setarrayitem_var(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'b', 'node'])
+        def f(n, a, b):
+            i = sa = 0
+            node = [1, 2, 3]
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, b=b, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, b=b, node=node)
+                sa += node[0] + node[b]
+                if i < n/2:
+                    node[0] = a
+                    node[b] = a
+                else:
+                    node[0] = a
+                    node[b] = a + 1
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7, 2])
+        assert res == f(32, 7, 2)
+
+    def test_getfield_result_with_intbound(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
+        class A:
+            pass
+        def f(n, a):
+            i = sa = 0
+            node = A()
+            node.val1 = a
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, node=node)
+                if node.val1 > 0:
+                    sa += 1
+                if i > n/2:
+                    node.val1 = -a
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7])
+        assert res == f(32, 7)
+
+    def test_getfield_result_constant(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
+        class A:
+            pass
+        def f(n, a):
+            i = sa = 0
+            node = A()
+            node.val1 = 7
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, node=node)
+                if node.val1 == 7:
+                    sa += 1
+                if i > n/2:
+                    node.val1 = -7
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7])
+        assert res == f(32, 7)
+
     def test_overflowing_shift_pos(self):
         myjitdriver = JitDriver(greens = [], reds = ['a', 'b', 'n', 'sa'])
         def f1(a, b):
@@ -2154,32 +2289,16 @@
             assert self.meta_interp(f, [bigval, 5]) == 0
             self.check_loops(int_rshift=3, everywhere=True)
 
-    def notest_overflowing_shift2(self):
-        myjitdriver = JitDriver(greens = [], reds = ['a', 'b', 'n', 'sa'])
-        def f(a, b):
-            n = sa = 0
-            while n < 10:
-                myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
-                if 0 < a < promote(sys.maxint/2): pass
-                if 0 < b < 100: pass
-                sa += (a << b) >> b
-                n += 1
+    def test_pure_op_not_to_be_propagated(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'sa'])
+        def f(n):
+            sa = 0
+            while n > 0:
+                myjitdriver.jit_merge_point(n=n, sa=sa)
+                sa += n + 1
+                n -= 1
             return sa
-
-        assert self.meta_interp(f, [5, 5]) == 50
-        self.check_loops(int_rshift=0, everywhere=True)
-
-        assert self.meta_interp(f, [5, 10]) == 50
-        self.check_loops(int_rshift=1, everywhere=True)
-
-        assert self.meta_interp(f, [10, 5]) == 100
-        self.check_loops(int_rshift=1, everywhere=True)
-
-        assert self.meta_interp(f, [10, 10]) == 100
-        self.check_loops(int_rshift=1, everywhere=True)
-
-        assert self.meta_interp(f, [5, 100]) == 0
-        self.check_loops(int_rshift=1, everywhere=True)
+        assert self.meta_interp(f, [10]) == f(10)
 
     def test_inputarg_reset_bug(self):
         ## j = 0
@@ -2311,6 +2430,349 @@
         self.check_loops(getfield_gc_pure=0)
         self.check_loops(getfield_gc_pure=2, everywhere=True)
 
+    def test_args_becomming_equal(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a', 'b'])
+        def f(n, a, b):
+            sa = i = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, b=b)
+                sa += a
+                sa *= b
+                if i > n/2:
+                    a = b
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 1, 2]) == f(20, 1, 2)
+
+    def test_args_becomming_equal_boxed1(self):
+        class A(object):
+            def __init__(self, a, b):
+                self.a = a
+                self.b = b
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a', 'b', 'node'])
+
+        def f(n, a, b):
+            sa = i = 0
+            node = A(a,b)
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, b=b, node=node)
+                sa += node.a
+                sa *= node.b
+                if i > n/2:
+                    node = A(b, b)
+                else:
+                    node = A(a, b)
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 1, 2]) == f(20, 1, 2)
+
+    def test_args_becomming_not_equal_boxed1(self):
+        class A(object):
+            def __init__(self, a, b):
+                self.a = a
+                self.b = b
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a', 'b', 'node'])
+
+        def f(n, a, b):
+            sa = i = 0
+            node = A(b, b)
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, b=b, node=node)
+                sa += node.a
+                sa *= node.b
+                if i > n/2:
+                    node = A(a, b)
+                else:
+                    node = A(b, b)
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 1, 2]) == f(20, 1, 2)
+
+    def test_args_becomming_equal_boxed2(self):
+        class A(object):
+            def __init__(self, a, b):
+                self.a = a
+                self.b = b
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'node'])
+
+        def f(n, a, b):
+            sa = i = 0
+            node = A(a, b)
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, node=node)
+                sa += node.a
+                sa *= node.b
+                if i > n/2:
+                    node = A(node.b, node.b)
+                else:
+                    node = A(node.b, node.a)
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 1, 2]) == f(20, 1, 2)
+
+    def test_inlined_short_preamble_guard_needed_in_loop1(self):
+        class A(object):
+            def __init__(self, a):
+                self.a = a
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa',
+                                                     'a', 'b'])
+        def f(n, a, b):
+            sa = i = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, b=b)
+                if a.a < 10:
+                    sa += a.a
+                b.a = i
+                i += 1
+            return sa
+        def g(n):
+            return f(n, A(5), A(10))
+        assert self.meta_interp(g, [20]) == g(20)
+
+    def test_ovf_guard_in_short_preamble2(self):
+        class A(object):
+            def __init__(self, val):
+                self.val = val
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a', 'node1', 'node2'])
+        def f(n, a):
+            node1 = node2 = A(0)
+            sa = i = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, node1=node1, node2=node2)
+                node2.val = 7
+                if a >= 100:
+                    sa += 1
+                sa += ovfcheck(i + i)
+                node1 = A(i)
+                i += 1
+        assert self.meta_interp(f, [20, 7]) == f(20, 7)
+
+    def test_intbounds_generalized(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa'])
+
+        def f(n):
+            sa = i = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa)
+                if i > n/2:
+                    sa += 1
+                else:
+                    sa += 2
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20]) == f(20)
+        self.check_loops(int_gt=1, int_lt=2, int_ge=0, int_le=0)
+
+    def test_intbounds_not_generalized1(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa'])
+
+        def f(n):
+            sa = i = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa)
+                if i > n/2:
+                    sa += 1
+                else:
+                    sa += 2
+                    assert  -100 < i < 100
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20]) == f(20)
+        self.check_loops(int_gt=1, int_lt=3, int_ge=2, int_le=1)
+
+    def test_intbounds_not_generalized2(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'node'])
+        class A(object):
+            def __init__(self, val):
+                self.val = val
+        def f(n):
+            sa = i = 0
+            node = A(n)
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, node=node)
+                if i > n/2:
+                    sa += 1
+                else:
+                    sa += 2
+                    assert  -100 <= node.val <= 100
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20]) == f(20)
+        self.check_loops(int_gt=1, int_lt=2, int_ge=1, int_le=1)
+
+    def test_retrace_limit1(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a'])
+
+        def f(n, limit):
+            myjitdriver.set_param('retrace_limit', limit)
+            sa = i = a = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a)
+                a = i/4
+                a = hint(a, promote=True)
+                sa += a
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 2]) == f(20, 2)
+        self.check_tree_loop_count(4)
+        assert self.meta_interp(f, [20, 3]) == f(20, 3)
+        self.check_tree_loop_count(5)
+
+    def test_max_retrace_guards(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a'])
+
+        def f(n, limit):
+            myjitdriver.set_param('retrace_limit', 3)
+            myjitdriver.set_param('max_retrace_guards', limit)
+            sa = i = a = 0
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a)
+                a = i/4
+                a = hint(a, promote=True)
+                sa += a
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 1]) == f(20, 1)
+        self.check_tree_loop_count(2)
+        assert self.meta_interp(f, [20, 10]) == f(20, 10)
+        self.check_tree_loop_count(5)
+
+
+    def test_retrace_limit_with_extra_guards(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a',
+                                                     'node'])
+        def f(n, limit):
+            myjitdriver.set_param('retrace_limit', limit)
+            sa = i = a = 0
+            node = [1, 2, 3]
+            node[1] = n
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a, node=node)
+                a = i/4
+                a = hint(a, promote=True)
+                if i&1 == 0:
+                    sa += node[i%3]
+                sa += a
+                i += 1
+            return sa
+        assert self.meta_interp(f, [20, 2]) == f(20, 2)
+        self.check_tree_loop_count(4)
+        assert self.meta_interp(f, [20, 3]) == f(20, 3)
+        self.check_tree_loop_count(5)
+
+    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))
+
+    def test_nested_retrace(self):
+
+        myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'a', 'i', 'j', 'sa'])
+        bytecode = "ij+Jj+JI"
+        def f(n, a):
+            myjitdriver.set_param('threshold', 5)
+            myjitdriver.set_param('trace_eagerness', 1)
+            myjitdriver.set_param('retrace_limit', 2)
+            pc = sa = i = j = 0
+            while pc < len(bytecode):
+                myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i, j=j, a=a)
+                a = hint(a, promote=True)
+                op = bytecode[pc]
+                if op == 'i':
+                    i = 0
+                elif op == 'j':
+                    j = 0
+                elif op == '+':
+                    sa += a
+                elif op == 'J':
+                    j += 1
+                    if j < 3:
+                        pc -= 1
+                        myjitdriver.can_enter_jit(pc=pc, n=n, sa=sa, i=i, j=j, a=a)
+                        continue
+                elif op == 'I':
+                    i += 1
+                    if i < n:
+                        pc -= 6
+                        myjitdriver.can_enter_jit(pc=pc, n=n, sa=sa, i=i, j=j, a=a)
+                        continue
+                pc += 1
+            return sa
+
+        res = self.meta_interp(f, [10, 7])
+        assert res == f(10, 7)
+        self.check_tree_loop_count(4)
+
+        def g(n):
+            return f(n, 2) + f(n, 3)
+
+        res = self.meta_interp(g, [10])
+        assert res == g(10)
+        self.check_tree_loop_count(6)
+
+
+        def g(n):
+            return f(n, 2) + f(n, 3) + f(n, 4) + f(n, 5) + f(n, 6) + f(n, 7)
+
+        res = self.meta_interp(g, [10])
+        assert res == g(10)
+        self.check_tree_loop_count(8)
+
     def test_frame_finished_during_retrace(self):
         class Base(object):
             pass
@@ -2452,7 +2914,46 @@
         # 1 preamble and 6 speciealized versions of each loop
         self.check_tree_loop_count(2*(1 + 6))
 
+    def test_continue_tracing_with_boxes_in_start_snapshot_replaced_by_optimizer(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'a', 'b'])
+        def f(n):
+            sa = a = 0
+            b = 10
+            while n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, b=b)
+                sa += b
+                b += 1
+                if b > 7:
+                    pass
+                if a == 0:
+                    a = 1
+                elif a == 1:
+                    a = 2
+                elif a == 2:
+                    a = 0
+                sa += a
+                sa += 0
+                n -= 1
+            return sa
+        res = self.meta_interp(f, [16])
+        assert res == f(16)
 
+    def test_loopinvariant_array_shrinking1(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
+        def f(n):
+            sa = i = 0
+            a = [0, 1, 2, 3, 4]
+            while i < n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
+                if i < n/2:
+                    sa += a[4]
+                elif i == n/2:
+                    a.pop()
+                i += 1
+        res = self.meta_interp(f, [32])
+        assert res == f(32)
+        self.check_loops(arraylen_gc=2)
+        
 class TestOOtype(BasicTests, OOJitMixin):
 
     def test_oohash(self):
@@ -2630,6 +3131,98 @@
         self.meta_interp(f, [], enable_opts='')
         self.check_loops(new_with_vtable=1)
 
+    def test_two_loopinvariant_arrays1(self):
+        from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
+        TP = lltype.GcArray(lltype.Signed)
+        def f(n):
+            sa = i = 0
+            a = lltype.malloc(TP, 5)
+            a[4] = 7
+            while i < n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
+                if i < n/2:
+                    sa += a[4]
+                if i == n/2:
+                    a = lltype.malloc(TP, 3)
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32])
+        assert res == f(32)
+        self.check_tree_loop_count(3)
+
+    def test_two_loopinvariant_arrays2(self):
+        from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
+        TP = lltype.GcArray(lltype.Signed)
+        def f(n):
+            sa = i = 0
+            a = lltype.malloc(TP, 5)
+            a[4] = 7
+            while i < n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
+                if i < n/2:
+                    sa += a[4]
+                elif i > n/2:
+                    sa += a[2]
+                if i == n/2:
+                    a = lltype.malloc(TP, 3)
+                    a[2] = 42
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32])
+        assert res == f(32)
+        self.check_tree_loop_count(3)
+        
+    def test_two_loopinvariant_arrays3(self):
+        from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
+        TP = lltype.GcArray(lltype.Signed)
+        def f(n):
+            sa = i = 0
+            a = lltype.malloc(TP, 5)
+            a[2] = 7
+            while i < n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
+                if i < n/2:
+                    sa += a[2]
+                elif i > n/2:
+                    sa += a[3]
+                if i == n/2:
+                    a = lltype.malloc(TP, 7)
+                    a[3] = 10
+                    a[2] = 42
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32])
+        assert res == f(32)
+        self.check_tree_loop_count(2)
+        
+    def test_two_loopinvariant_arrays_boxed(self):
+        class A(object):
+            def __init__(self, a):
+                self.a  = a
+        from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'n', 'i', 'a'])
+        TP = lltype.GcArray(lltype.Signed)
+        a1 = A(lltype.malloc(TP, 5))
+        a2 = A(lltype.malloc(TP, 3))
+        def f(n):
+            sa = i = 0
+            a = a1
+            a.a[4] = 7
+            while i < n:
+                myjitdriver.jit_merge_point(sa=sa, n=n, a=a, i=i)
+                if i < n/2:
+                    sa += a.a[4]
+                if i == n/2:
+                    a = a2
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32])
+        assert res == f(32)
+        self.check_loops(arraylen_gc=2, everywhere=True)
+        
     def test_release_gil_flush_heap_cache(self):
         if sys.platform == "win32":
             py.test.skip("needs 'time'")
@@ -2683,6 +3276,7 @@
             return n
 
         self.meta_interp(f, [10], repeat=3)
+        
 
 class TestLLtype(BaseLLtypeTests, LLJitMixin):
     pass
diff --git a/pypy/jit/metainterp/test/test_jitdriver.py b/pypy/jit/metainterp/test/test_jitdriver.py
--- a/pypy/jit/metainterp/test/test_jitdriver.py
+++ b/pypy/jit/metainterp/test/test_jitdriver.py
@@ -123,7 +123,11 @@
         res = self.meta_interp(loop2, [4, 40], repeat=7, inline=True)
         assert res == loop2(4, 40)
         # we expect no loop at all for 'loop1': it should always be inlined
-        self.check_tree_loop_count(2)    # 1 x loop, 1 x enter bridge
+        # we do however get several version of 'loop2', all of which contains
+        # at least one int_add, while there are no int_add's in 'loop1'
+        self.check_tree_loop_count(5)
+        for loop in get_stats().loops:
+            assert loop.summary()['int_add'] >= 1
 
     def test_inactive_jitdriver(self):
         myjitdriver1 = JitDriver(greens=[], reds=['n', 'm'],
diff --git a/pypy/jit/metainterp/test/test_loop.py b/pypy/jit/metainterp/test/test_loop.py
--- a/pypy/jit/metainterp/test/test_loop.py
+++ b/pypy/jit/metainterp/test/test_loop.py
@@ -1,5 +1,5 @@
 import py
-from pypy.rlib.jit import JitDriver
+from pypy.rlib.jit import JitDriver, hint
 from pypy.rlib.objectmodel import compute_hash
 from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -798,11 +798,9 @@
                 some_fn(Stuff(n), k, z)
             return 0
 
-        res = self.meta_interp(f, [200])
+        res = self.meta_interp(f, [200])        
 
     def test_regular_pointers_in_short_preamble(self):
-        # XXX do we really care about this case?  If not, we should
-        # at least detect it and complain during codewriter/jtransform
         from pypy.rpython.lltypesystem import lltype
         BASE = lltype.GcStruct('BASE')
         A = lltype.GcStruct('A', ('parent', BASE), ('val', lltype.Signed))
@@ -829,9 +827,8 @@
                 assert n>0 and m>0
                 i += j
             return sa
-        expected = f(20, 10, 1)
-        res = self.meta_interp(f, [20, 10, 1])
-        assert res == expected
+        # This is detected as invalid by the codewriter, for now
+        py.test.raises(NotImplementedError, self.meta_interp, f, [20, 10, 1])
 
     def test_unerased_pointers_in_short_preamble(self):
         from pypy.rlib.rerased import new_erasing_pair
diff --git a/pypy/jit/metainterp/test/test_quasiimmut.py b/pypy/jit/metainterp/test/test_quasiimmut.py
--- a/pypy/jit/metainterp/test/test_quasiimmut.py
+++ b/pypy/jit/metainterp/test/test_quasiimmut.py
@@ -289,7 +289,7 @@
             return total
 
         res = self.meta_interp(main, [])
-        self.check_loop_count(7)
+        self.check_loop_count(9)
         assert res == main()
 
     def test_change_during_running(self):
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
@@ -384,9 +384,9 @@
         res = self.meta_interp(f, [198],
                                policy=StopAtXPolicy(State.externfn.im_func))
         assert res == f(198)
-        # we get two TreeLoops: an initial one, and one entering from
-        # the interpreter
-        self.check_tree_loop_count(2)
+        # we get four TreeLoops: one for each of the 3 getvalue functions,
+        # and one entering from the interpreter
+        self.check_tree_loop_count(4)
 
     def test_two_behaviors(self):
         py.test.skip("XXX fix me!!!!!!! problem in optimize.py")
@@ -436,10 +436,11 @@
             return x.value
         res = self.meta_interp(f, [len(cases)])
         assert res == 200
-        # we expect 1 loop, 1 entry bridge, and 1 bridge going from the
+        # we expect 2 versions of the loop, 1 entry bridge,
+        # and 1 bridge going from the
         # loop back to the start of the entry bridge
-        self.check_loop_count(2)        # 1 loop + 1 bridge
-        self.check_tree_loop_count(2)   # 1 loop + 1 entry bridge  (argh)
+        self.check_loop_count(3)        # 2 loop + 1 bridge
+        self.check_tree_loop_count(3)   # 2 loop + 1 entry bridge  (argh)
         self.check_aborted_count(0)
 
     def test_three_cases(self):
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -324,6 +324,163 @@
         self.meta_interp(f, [6, 7])
         self.check_loops(newstr=0, newunicode=0)
 
+    def test_str_slice_len_surviving(self):
+        _str = self._str
+        longstring = _str("Unrolling Trouble")
+        mydriver = JitDriver(reds = ['i', 'a', 'sa'], greens = []) 
+        def f(a):
+            i = sa = a
+            while i < len(longstring):
+                mydriver.jit_merge_point(i=i, a=a, sa=sa)
+                assert a >= 0 and i >= 0
+                i = len(longstring[a:i+1])
+                sa += i
+            return sa
+        assert self.meta_interp(f, [0]) == f(0)
+
+    def test_virtual_strings_direct(self):
+        _str = self._str
+        fillers = _str("abcdefghijklmnopqrstuvwxyz")
+        data = _str("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+        mydriver = JitDriver(reds = ['line', 'noise', 'res'], greens = []) 
+        def f():
+            line = data
+            noise = fillers
+            ratio = len(line) // len(noise)
+            res = data[0:0]
+            while line and noise:
+                mydriver.jit_merge_point(line=line, noise=noise, res=res)
+                if len(line) // len(noise) > ratio:
+                    c, line = line[0], line[1:]
+                else:
+                    c, noise = noise[0], noise[1:]
+                res += c
+            return res + noise + line
+        s1 = self.meta_interp(f, [])
+        s2 = f()
+        for c1, c2 in zip(s1.chars, s2):
+            assert c1==c2
+
+    def test_virtual_strings_boxed(self):
+        _str = self._str
+        fillers = _str("abcdefghijklmnopqrstuvwxyz")
+        data = _str("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+        class Str(object):
+            def __init__(self, value):
+                self.value = value
+        mydriver = JitDriver(reds = ['ratio', 'line', 'noise', 'res'],
+                             greens = []) 
+        def f():
+            line = Str(data)
+            noise = Str(fillers)
+            ratio = len(line.value) // len(noise.value)
+            res = Str(data[0:0])
+            while line.value and noise.value:
+                mydriver.jit_merge_point(line=line, noise=noise, res=res,
+                                         ratio=ratio)
+                if len(line.value) // len(noise.value) > ratio:
+                    c, line = line.value[0], Str(line.value[1:])
+                else:
+                    c, noise = noise.value[0], Str(noise.value[1:])
+                res = Str(res.value + c)
+            return res.value + noise.value + line.value
+        s1 = self.meta_interp(f, [])
+        s2 = f()
+        for c1, c2 in zip(s1.chars, s2):
+            assert c1==c2
+
+    def test_string_in_virtual_state(self):
+        _str = self._str
+        s1 = _str("a")
+        s2 = _str("AA")
+        mydriver = JitDriver(reds = ['i', 'n', 'sa'], greens = [])
+        def f(n):
+            sa = s1
+            i = 0
+            while i < n:
+                mydriver.jit_merge_point(i=i, n=n, sa=sa)
+                if i&4 == 0:
+                    sa += s1
+                else:
+                    sa += s2
+                i += 1
+            return len(sa)
+        assert self.meta_interp(f, [16]) == f(16)
+
+    def test_loop_invariant_string_slize(self):
+        _str = self._str
+        mydriver = JitDriver(reds = ['i', 'n', 'sa', 's', 's1'], greens = [])
+        def f(n, c):
+            s = s1 = _str(c*10)
+            sa = i = 0
+            while i < n:
+                mydriver.jit_merge_point(i=i, n=n, sa=sa, s=s, s1=s1)
+                sa += len(s)
+                if i < n/2:
+                    s = s1[1:3]
+                else:
+                    s = s1[2:3]
+                i += 1
+            return sa
+        assert self.meta_interp(f, [16, 'a']) == f(16, 'a')
+
+    def test_loop_invariant_string_slize_boxed(self):
+        class Str(object):
+            def __init__(self, value):
+                self.value = value
+        _str = self._str
+        mydriver = JitDriver(reds = ['i', 'n', 'sa', 's', 's1'], greens = [])
+        def f(n, c):
+            s = s1 = Str(_str(c*10))
+            sa = i = 0
+            while i < n:
+                mydriver.jit_merge_point(i=i, n=n, sa=sa, s=s, s1=s1)
+                sa += len(s.value)
+                if i < n/2:
+                    s = Str(s1.value[1:3])
+                else:
+                    s = Str(s1.value[2:3])
+                i += 1
+            return sa
+        assert self.meta_interp(f, [16, 'a']) == f(16, 'a')
+
+    def test_loop_invariant_string_slize_in_array(self):
+        _str = self._str
+        mydriver = JitDriver(reds = ['i', 'n', 'sa', 's', 's1'], greens = [])
+        def f(n, c):
+            s = s1 = [_str(c*10)]
+            sa = i = 0
+            while i < n:
+                mydriver.jit_merge_point(i=i, n=n, sa=sa, s=s, s1=s1)
+                sa += len(s[0])
+                if i < n/2:
+                    s = [s1[0][1:3]]
+                else:
+                    s = [s1[0][2:3]]
+                i += 1
+            return sa
+        assert self.meta_interp(f, [16, 'a']) == f(16, 'a')
+
+    def test_boxed_virtual_string_not_surviving(self):
+        class StrBox(object):
+            def __init__(self, val):
+                self.val = val
+        class IntBox(object):
+            def __init__(self, val):
+                self.val = val
+        _str = self._str
+        mydriver = JitDriver(reds = ['i', 'nt', 'sa'], greens = [])
+        def f(c):
+            nt = StrBox(_str(c*16))
+            sa = StrBox(_str(''))
+            i = IntBox(0)
+            while i.val < len(nt.val):
+                mydriver.jit_merge_point(i=i, nt=nt, sa=sa)
+                sa = StrBox(sa.val + StrBox(nt.val[i.val]).val)
+                i = IntBox(i.val + 1)
+            return len(sa.val)
+        assert self.meta_interp(f, ['a']) == f('a')
 
 #class TestOOtype(StringTests, OOJitMixin):
 #    CALL = "oosend"
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
@@ -898,6 +898,53 @@
         res = self.meta_interp(f, [], repeat=7)
         assert res == f()
 
+    def test_virtual_attribute_pure_function(self):
+        mydriver = JitDriver(reds = ['i', 'sa', 'n', 'node'], greens = [])
+        class A(object):
+            def __init__(self, v1, v2):
+                self.v1 = v1
+                self.v2 = v2
+        def f(n):
+            i = sa = 0
+            node = A(1, 2)
+            while i < n:
+                mydriver.jit_merge_point(i=i, sa=sa, n=n, node=node)
+                sa += node.v1 + node.v2 + 2*node.v1
+                if i < n/2:
+                    node = A(n, 2*n)
+                else:
+                    node = A(n, 3*n)
+                i += 1
+            return sa
+
+        res = self.meta_interp(f, [16])
+        assert res == f(16)
+
+    def test_virtual_loop_invariant_getitem(self):
+        mydriver = JitDriver(reds = ['i', 'sa', 'n', 'node1', 'node2'], greens = [])
+        class A(object):
+            def __init__(self, v1, v2):
+                self.v1 = v1
+                self.v2 = v2
+        def f(n):
+            i = sa = 0
+            node1 = A(1, 2)
+            node2 = A(n, n)
+            while i < n:
+                mydriver.jit_merge_point(i=i, sa=sa, n=n, node1=node1, node2=node2)
+                sa += node1.v1 + node2.v1 + node2.v2
+                if i < n/2:
+                    node1 = A(node2.v1, 2)
+                else:
+                    node1 = A(i, 2)
+                i += 1
+            return sa
+
+        res = self.meta_interp(f, [16])
+        assert res == f(16)
+        self.check_loops(getfield_gc=2)
+        
+
 # ____________________________________________________________
 # Run 1: all the tests instantiate a real RPython class
 
diff --git a/pypy/jit/metainterp/test/test_virtualstate.py b/pypy/jit/metainterp/test/test_virtualstate.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_virtualstate.py
@@ -0,0 +1,911 @@
+import py
+from pypy.jit.metainterp.optimize import InvalidLoop
+from pypy.jit.metainterp.optimizeopt.virtualstate import VirtualStateInfo, VStructStateInfo, \
+     VArrayStateInfo, NotVirtualStateInfo, VirtualState
+from pypy.jit.metainterp.optimizeopt.optimizer import OptValue
+from pypy.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr, ConstInt, ConstPtr
+from pypy.rpython.lltypesystem import lltype
+from pypy.jit.metainterp.optimizeopt.test.test_util import LLtypeMixin, BaseTest, equaloplists
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound
+from pypy.jit.metainterp.history import TreeLoop, LoopToken
+from pypy.jit.metainterp.optimizeopt.test.test_optimizeopt import FakeDescr, FakeMetaInterpStaticData
+from pypy.jit.metainterp.optimize import RetraceLoop
+
+class TestBasic:
+    someptr1 = LLtypeMixin.myptr
+    someptr2 = LLtypeMixin.myptr2
+
+    def test_position_generalization(self):
+        def postest(info1, info2):
+            info1.position = 0
+            assert info1.generalization_of(info1, {}, {})
+            info2.position = 0
+            assert info1.generalization_of(info2, {}, {})
+            info2.position = 1
+            renum = {}
+            assert info1.generalization_of(info2, renum, {})
+            assert renum == {0:1}
+            assert info1.generalization_of(info2, {0:1}, {})
+            assert info1.generalization_of(info2, {1:1}, {})
+            bad = {}
+            assert not info1.generalization_of(info2, {0:0}, bad)
+            assert info1 in bad and info2 in bad
+
+        for BoxType in (BoxInt, BoxFloat, BoxPtr):
+            info1 = NotVirtualStateInfo(OptValue(BoxType()))
+            info2 = NotVirtualStateInfo(OptValue(BoxType()))
+            postest(info1, info2)
+            
+        info1, info2 = VArrayStateInfo(42), VArrayStateInfo(42)
+        info1.fieldstate = info2.fieldstate = []
+        postest(info1, info2)
+
+        info1, info2 = VStructStateInfo(42, []), VStructStateInfo(42, [])
+        info1.fieldstate = info2.fieldstate = []
+        postest(info1, info2)
+
+        info1, info2 = VirtualStateInfo(ConstInt(42), []), VirtualStateInfo(ConstInt(42), [])
+        info1.fieldstate = info2.fieldstate = []
+        postest(info1, info2)
+
+    def test_NotVirtualStateInfo_generalization(self):
+        def isgeneral(value1, value2):
+            info1 = NotVirtualStateInfo(value1)
+            info1.position = 0
+            info2 = NotVirtualStateInfo(value2)
+            info2.position = 0
+            return info1.generalization_of(info2, {}, {})
+
+        assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7)))
+        assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt()))
+
+        ptr = OptValue(BoxPtr())
+        nonnull = OptValue(BoxPtr())
+        nonnull.make_nonnull(0)
+        knownclass = OptValue(BoxPtr())
+        knownclass.make_constant_class(ConstPtr(self.someptr1), 0)
+        const = OptValue(BoxPtr)
+        const.make_constant_class(ConstPtr(self.someptr1), 0)
+        const.make_constant(ConstPtr(self.someptr1))
+        inorder = [ptr, nonnull, knownclass, const]
+        for i in range(len(inorder)):
+            for j in range(i, len(inorder)):
+                assert isgeneral(inorder[i], inorder[j])
+                if i != j:
+                    assert not isgeneral(inorder[j], inorder[i])
+
+        value1 = OptValue(BoxInt())
+        value2 = OptValue(BoxInt())
+        value2.intbound.make_lt(IntBound(10, 10))
+        assert isgeneral(value1, value2)
+        assert not isgeneral(value2, value1)
+
+    def test_field_matching_generalization(self):
+        const1 = NotVirtualStateInfo(OptValue(ConstInt(1)))
+        const2 = NotVirtualStateInfo(OptValue(ConstInt(2)))
+        const1.position = const2.position = 1
+        assert not const1.generalization_of(const2, {}, {})
+        assert not const2.generalization_of(const1, {}, {})
+
+        def fldtst(info1, info2):
+            info1.position = info2.position = 0
+            info1.fieldstate = [const1]
+            info2.fieldstate = [const2]
+            assert not info1.generalization_of(info2, {}, {})
+            assert not info2.generalization_of(info1, {}, {})
+            assert info1.generalization_of(info1, {}, {})
+            assert info2.generalization_of(info2, {}, {})
+        fldtst(VArrayStateInfo(42), VArrayStateInfo(42))
+        fldtst(VStructStateInfo(42, [7]), VStructStateInfo(42, [7]))
+        fldtst(VirtualStateInfo(ConstInt(42), [7]), VirtualStateInfo(ConstInt(42), [7]))
+
+    def test_known_class_generalization(self):
+        knownclass1 = OptValue(BoxPtr())
+        knownclass1.make_constant_class(ConstPtr(self.someptr1), 0)
+        info1 = NotVirtualStateInfo(knownclass1)
+        info1.position = 0
+        knownclass2 = OptValue(BoxPtr())
+        knownclass2.make_constant_class(ConstPtr(self.someptr1), 0)
+        info2 = NotVirtualStateInfo(knownclass2)
+        info2.position = 0
+        assert info1.generalization_of(info2, {}, {})
+        assert info2.generalization_of(info1, {}, {})
+
+        knownclass3 = OptValue(BoxPtr())
+        knownclass3.make_constant_class(ConstPtr(self.someptr2), 0)
+        info3 = NotVirtualStateInfo(knownclass3)
+        info3.position = 0
+        assert not info1.generalization_of(info3, {}, {})
+        assert not info2.generalization_of(info3, {}, {})
+        assert not info3.generalization_of(info2, {}, {})
+        assert not info3.generalization_of(info1, {}, {})
+
+
+    def test_circular_generalization(self):
+        for info in (VArrayStateInfo(42), VStructStateInfo(42, [7]),
+                     VirtualStateInfo(ConstInt(42), [7])):
+            info.position = 0
+            info.fieldstate = [info]
+            assert info.generalization_of(info, {}, {})
+
+class BaseTestGenerateGuards(BaseTest):
+    def guards(self, info1, info2, box, expected):
+        info1.position = info2.position = 0
+        guards = []
+        info1.generate_guards(info2, box, self.cpu, guards, {})
+        self.compare(guards, expected, [box])
+
+    def compare(self, guards, expected, inputargs):
+        loop = self.parse(expected)
+        boxmap = {}
+        assert len(loop.inputargs) == len(inputargs)
+        for a, b in zip(loop.inputargs, inputargs):
+            boxmap[a] = b
+        for op in loop.operations:
+            if op.is_guard():
+                op.setdescr(None)
+        assert equaloplists(guards, loop.operations, False,
+                            boxmap)        
+    def test_intbounds(self):
+        value1 = OptValue(BoxInt())
+        value1.intbound.make_ge(IntBound(0, 10))
+        value1.intbound.make_le(IntBound(20, 30))
+        info1 = NotVirtualStateInfo(value1)
+        info2 = NotVirtualStateInfo(OptValue(BoxInt()))
+        expected = """
+        [i0]
+        i1 = int_ge(i0, 0)
+        guard_true(i1) []
+        i2 = int_le(i0, 30)
+        guard_true(i2) []
+        """
+        self.guards(info1, info2, BoxInt(15), expected)
+        py.test.raises(InvalidLoop, self.guards,
+                       info1, info2, BoxInt(50), expected)
+
+
+    def test_known_class(self):
+        value1 = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value1.make_constant_class(classbox, -1)
+        info1 = NotVirtualStateInfo(value1)
+        info2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        expected = """
+        [p0]
+        guard_nonnull(p0) []        
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(info1, info2, self.nodebox, expected)
+        py.test.raises(InvalidLoop, self.guards,
+                       info1, info2, BoxPtr(), expected)
+
+    def test_known_class_value(self):
+        value1 = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value1.make_constant_class(classbox, -1)
+        box = self.nodebox
+        guards = value1.make_guards(box)
+        expected = """
+        [p0]
+        guard_nonnull(p0) []        
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.compare(guards, expected, [box])
+
+    def test_equal_inputargs(self):
+        value = OptValue(self.nodebox)        
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        vstate1 = VirtualState([knownclass_info, knownclass_info])
+        assert vstate1.generalization_of(vstate1)
+
+        unknown_info1 = NotVirtualStateInfo(OptValue(self.nodebox))
+        vstate2 = VirtualState([unknown_info1, unknown_info1])
+        assert vstate2.generalization_of(vstate2)
+        assert not vstate1.generalization_of(vstate2)
+        assert vstate2.generalization_of(vstate1)
+
+        unknown_info1 = NotVirtualStateInfo(OptValue(self.nodebox))
+        unknown_info2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        vstate3 = VirtualState([unknown_info1, unknown_info2])
+        assert vstate3.generalization_of(vstate2)
+        assert vstate3.generalization_of(vstate1)
+        assert not vstate2.generalization_of(vstate3)
+        assert not vstate1.generalization_of(vstate3)
+
+        expected = """
+        [p0]
+        guard_nonnull(p0) []        
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        guards = []
+        vstate1.generate_guards(vstate2, [self.nodebox, self.nodebox], self.cpu, guards)
+        self.compare(guards, expected, [self.nodebox])
+
+        with py.test.raises(InvalidLoop):
+            guards = []
+            vstate1.generate_guards(vstate3, [self.nodebox, self.nodebox],
+                                    self.cpu, guards)
+        with py.test.raises(InvalidLoop):
+            guards = []
+            vstate2.generate_guards(vstate3, [self.nodebox, self.nodebox],
+                                    self.cpu, guards)
+        
+    def test_virtuals_with_equal_fields(self):
+        info1 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VirtualStateInfo(ConstInt(42), [1, 2])
+        unknown_info1 = NotVirtualStateInfo(OptValue(self.nodebox))
+        info2.fieldstate = [unknown_info1, unknown_info1]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+        assert not vstate1.generalization_of(vstate2)
+        assert vstate2.generalization_of(vstate1)
+
+        info3 = VirtualStateInfo(ConstInt(42), [1, 2])
+        unknown_info1 = NotVirtualStateInfo(OptValue(self.nodebox))
+        unknown_info2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        info3.fieldstate = [unknown_info1, unknown_info2]
+        vstate3 = VirtualState([info3])        
+        assert vstate3.generalization_of(vstate2)
+        assert vstate3.generalization_of(vstate1)
+        assert not vstate2.generalization_of(vstate3)
+        assert not vstate1.generalization_of(vstate3)
+
+    def test_virtuals_with_nonmatching_fields(self):
+        info1 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info, knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+
+    def test_virtuals_with_nonmatching_descrs(self):
+        info1 = VirtualStateInfo(ConstInt(42), [10, 20])
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info, knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+        
+    def test_virtuals_with_nonmatching_classes(self):
+        info1 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VirtualStateInfo(ConstInt(7), [1, 2])
+        value = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info, knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+        
+    def test_nonvirtual_is_not_virtual(self):
+        info1 = VirtualStateInfo(ConstInt(42), [1, 2])
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = NotVirtualStateInfo(value)
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+
+    def test_arrays_with_nonmatching_fields(self):
+        info1 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info, knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+
+    def test_arrays_of_different_sizes(self):
+        info1 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+
+    def test_arrays_with_nonmatching_types(self):
+        info1 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = VArrayStateInfo(7)
+        value = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info2.fieldstate = [knownclass_info, knownclass_info]
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+        
+    def test_nonvirtual_is_not_array(self):
+        info1 = VArrayStateInfo(42)
+        value = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        value.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(value)
+        info1.fieldstate = [knownclass_info, knownclass_info]
+        vstate1 = VirtualState([info1])
+        assert vstate1.generalization_of(vstate1)
+
+        info2 = NotVirtualStateInfo(value)
+        vstate2 = VirtualState([info2])
+        assert vstate2.generalization_of(vstate2)
+
+        assert not vstate2.generalization_of(vstate1)
+        assert not vstate1.generalization_of(vstate2)
+        
+
+class BaseTestBridges(BaseTest):
+    enable_opts = "intbounds:rewrite:virtualize:string:heap:unroll"
+
+    def _do_optimize_bridge(self, bridge, call_pure_results):
+        from pypy.jit.metainterp.optimizeopt import optimize_bridge_1, build_opt_chain
+        from pypy.jit.metainterp.optimizeopt.util import args_dict
+
+        self.bridge = bridge
+        bridge.call_pure_results = args_dict()
+        if call_pure_results is not None:
+            for k, v in call_pure_results.items():
+                bridge.call_pure_results[list(k)] = v
+        metainterp_sd = FakeMetaInterpStaticData(self.cpu)
+        if hasattr(self, 'vrefinfo'):
+            metainterp_sd.virtualref_info = self.vrefinfo
+        if hasattr(self, 'callinfocollection'):
+            metainterp_sd.callinfocollection = self.callinfocollection
+        #
+        d = {}
+        for name in self.enable_opts.split(":"):
+            d[name] = None
+        optimize_bridge_1(metainterp_sd, bridge,  d)
+        
+    def optimize_bridge(self, loops, bridge, expected, expected_target='Loop', **boxvalues):
+        if isinstance(loops, str):
+            loops = (loops, )
+        loops = [self.parse(loop) for loop in loops]
+        bridge = self.parse(bridge)
+        for loop in loops:
+            loop.preamble = TreeLoop('preamble')
+            loop.preamble.inputargs = loop.inputargs
+            loop.preamble.token = LoopToken()
+            loop.preamble.start_resumedescr = FakeDescr()        
+            self._do_optimize_loop(loop, None)
+        preamble = loops[0].preamble
+        for loop in loops[1:]:
+            preamble.token.short_preamble.extend(loop.preamble.token.short_preamble)
+
+        boxes = {}
+        for b in bridge.inputargs + [op.result for op in bridge.operations]:
+            boxes[str(b)] = b
+        for b, v in boxvalues.items():
+            boxes[b].value = v
+        bridge.operations[-1].setdescr(preamble.token)
+        try:
+            self._do_optimize_bridge(bridge, None)
+        except RetraceLoop:
+            assert expected == 'RETRACE'
+            return
+
+        print '\n'.join([str(o) for o in bridge.operations])
+        expected = self.parse(expected)
+        self.assert_equal(bridge, expected)
+
+        if expected_target == 'Preamble':
+            assert bridge.operations[-1].getdescr() is preamble.token
+        elif expected_target == 'Loop':
+            assert len(loops) == 1
+            assert bridge.operations[-1].getdescr() is loops[0].token
+        elif expected_target.startswith('Loop'):
+            n = int(expected_target[4:])
+            assert bridge.operations[-1].getdescr() is loops[n].token
+        else:
+            assert False
+
+    def test_nonnull(self):
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE', p0=self.nullptr)
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+        self.optimize_bridge(loop, expected, expected, p0=self.myptr)
+        self.optimize_bridge(loop, expected, expected, p0=self.nullptr)
+
+    def test_cached_nonnull(self):
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        call(p1, descr=nonwritedescr)
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        jump(p0, p1)
+        """
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+    def test_cached_unused_nonnull(self):        
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        jump(p0)
+        """        
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+    def test_cached_invalid_nonnull(self):        
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_value(p1, ConstPtr(nullptr)) []        
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, bridge, 'Preamble', p0=self.myptr)
+
+    def test_multiple_nonnull(self):
+        loops = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        jump(p0)
+        """, """
+        [p0]
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_bridge(loops, bridge, expected, 'Loop1', p0=self.nullptr)
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        jump(p0)
+        """
+        self.optimize_bridge(loops, bridge, expected, 'Loop0', p0=self.myptr)
+
+    def test_constant(self):
+        loops = """
+        [p0]
+        p1 = same_as(ConstPtr(myptr))
+        jump(p1)
+        """, """
+        [p0]
+        p1 = same_as(ConstPtr(myptr2))
+        jump(p1)
+        """, """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump()
+        """
+        self.optimize_bridge(loops, loops[0], expected, 'Loop0')
+        self.optimize_bridge(loops, loops[1], expected, 'Loop1')
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_bridge(loops, loops[2], expected, 'Loop2')
+
+    def test_cached_constant(self):
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_value(p1, ConstPtr(myptr)) []
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_value(p1, ConstPtr(myptr)) []       
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+    def test_virtual(self):
+        loops = """
+        [p0, p1]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p1, descr=nextdescr)
+        setfield_gc(p2, 7, descr=adescr)
+        setfield_gc(p2, 42, descr=bdescr)
+        jump(p2, p1)
+        ""","""
+        [p0, p1]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p1, descr=nextdescr)
+        setfield_gc(p2, 9, descr=adescr)
+        jump(p2, p1)
+        """
+        expected = """
+        [p0, p1]
+        jump(p1)
+        """
+        self.optimize_bridge(loops, loops[0], expected, 'Loop0')
+        self.optimize_bridge(loops, loops[1], expected, 'Loop1')
+        bridge = """
+        [p0, p1]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p1, descr=nextdescr)
+        setfield_gc(p2, 42, descr=adescr)
+        setfield_gc(p2, 7, descr=bdescr)
+        jump(p2, p1)
+        """
+        self.optimize_bridge(loops, bridge, "RETRACE")
+        bridge = """
+        [p0, p1]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p1, descr=nextdescr)
+        setfield_gc(p2, 7, descr=adescr)
+        jump(p2, p1)
+        """
+        self.optimize_bridge(loops, bridge, "RETRACE")
+
+    def test_known_class(self):
+        loops = """
+        [p0]
+        guard_nonnull_class(p0, ConstClass(node_vtable)) []
+        jump(p0)
+        ""","""
+        [p0]
+        guard_nonnull_class(p0, ConstClass(node_vtable2)) []
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_bridge(loops, bridge, loops[0], 'Loop0', p0=self.nodebox.value)
+        self.optimize_bridge(loops, bridge, loops[1], 'Loop1', p0=self.nodebox2.value)
+        self.optimize_bridge(loops[0], bridge, 'RETRACE', p0=self.nodebox2.value)
+        self.optimize_bridge(loops, loops[0], loops[0], 'Loop0', p0=self.nullptr)
+        self.optimize_bridge(loops, loops[1], loops[1], 'Loop1', p0=self.nullptr)
+
+    def test_cached_known_class(self):
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_class(p1, ConstClass(node_vtable)) []
+        jump(p0)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull_class(p1, ConstClass(node_vtable)) []
+        jump(p0)        
+        """
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+
+    def test_lenbound_array(self):
+        loop = """
+        [p0]
+        i2 = getarrayitem_gc(p0, 10, descr=arraydescr)
+        call(i2, descr=nonwritedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        i2 = getarrayitem_gc(p0, 10, descr=arraydescr)
+        call(i2, descr=nonwritedescr)
+        jump(p0, i2)
+        """
+        self.optimize_bridge(loop, loop, expected, 'Loop0')
+        bridge = """
+        [p0]
+        i2 = getarrayitem_gc(p0, 15, descr=arraydescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        i2 = getarrayitem_gc(p0, 15, descr=arraydescr)
+        i3 = getarrayitem_gc(p0, 10, descr=arraydescr)
+        jump(p0, i3)
+        """        
+        self.optimize_bridge(loop, bridge, expected, 'Loop0')
+        bridge = """
+        [p0]
+        i2 = getarrayitem_gc(p0, 5, descr=arraydescr)
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE')
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE')
+
+    def test_cached_lenbound_array(self):
+        loop = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 10, descr=arraydescr)
+        call(i2, descr=nonwritedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 10, descr=arraydescr)
+        call(i2, descr=nonwritedescr)
+        i3 = arraylen_gc(p1, descr=arraydescr) # Should be killed by backend
+        jump(p0, i2, p1)
+        """
+        self.optimize_bridge(loop, loop, expected)
+        bridge = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 15, descr=arraydescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 15, descr=arraydescr)
+        i3 = arraylen_gc(p1, descr=arraydescr) # Should be killed by backend        
+        i4 = getarrayitem_gc(p1, 10, descr=arraydescr)
+        jump(p0, i4, p1)
+        """        
+        self.optimize_bridge(loop, bridge, expected)
+        bridge = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 5, descr=arraydescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p1 = getfield_gc(p0, descr=nextdescr)
+        i2 = getarrayitem_gc(p1, 5, descr=arraydescr)
+        i3 = arraylen_gc(p1, descr=arraydescr) # Should be killed by backend
+        i4 = int_ge(i3, 11)
+        guard_true(i4) []
+        i5 = getarrayitem_gc(p1, 10, descr=arraydescr)
+        jump(p0, i5, p1)
+        """        
+        self.optimize_bridge(loop, bridge, expected)
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        p1 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p1) []
+        i3 = arraylen_gc(p1, descr=arraydescr) # Should be killed by backend
+        i4 = int_ge(i3, 11)
+        guard_true(i4) []
+        i5 = getarrayitem_gc(p1, 10, descr=arraydescr)
+        jump(p0, i5, p1)
+        """        
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+    def test_cached_setarrayitem_gc(self):
+        loop = """
+        [p0, p1]
+        p2 = getfield_gc(p0, descr=nextdescr)
+        pp = getarrayitem_gc(p2, 0, descr=arraydescr)
+        call(pp, descr=nonwritedescr)
+        p3 = getfield_gc(p1, descr=nextdescr)
+        setarrayitem_gc(p2, 0, p3, descr=arraydescr)
+        jump(p0, p3)
+        """
+        bridge = """
+        [p0, p1]
+        jump(p0, p1)
+        """
+        expected = """
+        [p0, p1]
+        guard_nonnull(p0) []
+        p2 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p2) []
+        i5 = arraylen_gc(p2, descr=arraydescr)
+        i6 = int_ge(i5, 1)
+        guard_true(i6) []
+        jump(p0, p1, p2)
+        """
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
+
+    def test_cache_constant_setfield(self):
+        loop = """
+        [p5]
+        i10 = getfield_gc(p5, descr=valuedescr)
+        call(i10, descr=nonwritedescr) 
+        setfield_gc(p5, 1, descr=valuedescr)
+        jump(p5)
+        """
+        bridge = """
+        [p0]
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        i10 = getfield_gc(p0, descr=valuedescr)
+        guard_value(i10, 1) []
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, expected, p0=self.myptr)        
+        bridge = """
+        [p0]
+        setfield_gc(p0, 7, descr=valuedescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        setfield_gc(p0, 7, descr=valuedescr)
+        jump(p0)
+        """
+        self.optimize_bridge(loop, bridge, expected, 'Preamble', p0=self.myptr)
+
+    def test_cached_equal_fields(self):
+        loop = """
+        [p5, p6]
+        i10 = getfield_gc(p5, descr=valuedescr)
+        i11 = getfield_gc(p6, descr=nextdescr)
+        call(i10, i11, descr=nonwritedescr)
+        setfield_gc(p6, i10, descr=nextdescr)        
+        jump(p5, p6)
+        """
+        bridge = """
+        [p5, p6]
+        jump(p5, p6)
+        """
+        expected = """
+        [p5, p6]
+        guard_nonnull(p5) []
+        guard_nonnull(p6) []
+        i10 = getfield_gc(p5, descr=valuedescr)
+        i11 = getfield_gc(p6, descr=nextdescr)
+        jump(p5, p6, i10, i11)
+        """
+        self.optimize_bridge(loop, bridge, expected, p5=self.myptr, p6=self.myptr2)
+
+class TestLLtypeGuards(BaseTestGenerateGuards, LLtypeMixin):
+    pass
+
+class TestLLtypeBridges(BaseTestBridges, LLtypeMixin):
+    pass
+
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -64,7 +64,7 @@
                     backendopt=False, trace_limit=sys.maxint,
                     inline=False, loop_longevity=0, retrace_limit=5,
                     function_threshold=4,
-                    enable_opts=ALL_OPTS_NAMES, **kwds):
+                    enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15, **kwds):
     from pypy.config.config import ConfigError
     translator = interp.typer.annotator.translator
     try:
@@ -88,6 +88,7 @@
         jd.warmstate.set_param_inlining(inline)
         jd.warmstate.set_param_loop_longevity(loop_longevity)
         jd.warmstate.set_param_retrace_limit(retrace_limit)
+        jd.warmstate.set_param_max_retrace_guards(max_retrace_guards)
         jd.warmstate.set_param_enable_opts(enable_opts)
     warmrunnerdesc.finish()
     res = interp.eval_graph(graph, args)
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
@@ -270,6 +270,11 @@
             if self.warmrunnerdesc.memory_manager:
                 self.warmrunnerdesc.memory_manager.retrace_limit = value
 
+    def set_param_max_retrace_guards(self, value):
+        if self.warmrunnerdesc:
+            if self.warmrunnerdesc.memory_manager:
+                self.warmrunnerdesc.memory_manager.max_retrace_guards = value
+
     def disable_noninlinable_function(self, greenkey):
         cell = self.jit_cell_at_key(greenkey)
         cell.dont_trace_here = True
diff --git a/pypy/jit/tl/pypyjit.py b/pypy/jit/tl/pypyjit.py
--- a/pypy/jit/tl/pypyjit.py
+++ b/pypy/jit/tl/pypyjit.py
@@ -37,13 +37,13 @@
 set_opt_level(config, level='jit')
 config.objspace.allworkingmodules = False
 config.objspace.usemodules.pypyjit = True
-config.objspace.usemodules.array = True
+config.objspace.usemodules.array = False
 config.objspace.usemodules._weakref = True
 config.objspace.usemodules._sre = False
 config.objspace.usemodules._lsprof = True
 #
 config.objspace.usemodules._ffi = True
-config.objspace.usemodules.micronumpy = True
+config.objspace.usemodules.micronumpy = False
 #
 set_pypy_opt_level(config, level='jit')
 
diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py
--- a/pypy/jit/tl/pypyjit_demo.py
+++ b/pypy/jit/tl/pypyjit_demo.py
@@ -1,9 +1,23 @@
+import pypyjit
+pypyjit.set_param(threshold=200)
+
+
+def main(a, b):
+    i = sa = 0
+    while i < 300:
+        if a > 0: # Specialises the loop
+            pass
+        if b < 2 and b > 0:
+            pass
+        if (a >> b) >= 0:
+            sa += 1
+        if (a << b) > 2:
+            sa += 10000
+        i += 1
+    return sa
 
 try:
-    import numpy
-    a = numpy.array(range(10))
-    b = a + a + a
-    print b[3]
+    print main(2, 1)
 
 except Exception, e:
     print "Exception: ", type(e)
diff --git a/pypy/jit/tool/findadrinlog.py b/pypy/jit/tool/findadrinlog.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/tool/findadrinlog.py
@@ -0,0 +1,48 @@
+import autopath
+import sys, re
+from pypy.tool import logparser
+
+# fflush(pypy_debug_file)
+
+if len(sys.argv) != 3:
+    print "Usage: %s <log file> <address>" % sys.argv[0]
+
+log = logparser.parse_log_file(sys.argv[1])
+text = logparser.extract_category(log, catprefix='jit-backend')
+address = int(sys.argv[2], 16)
+
+for l in text:
+    m = re.match('(Loop|Bridge)(.*?) \(.*has address (\w+) to (\w+)', l)
+    if m is not None:
+        trace = m.group(1) + m.group(2)
+        start = int(m.group(3), 16)
+        stop = int(m.group(4), 16)
+        if start <= address <= stop:
+            offset = address - start
+            print trace
+            print 'at offset ', offset
+            break
+else:
+    print "Not found"
+    exit(0)
+                                         
+if trace.startswith('Bridge'):
+    cat = 'jit-log-opt-bridge'
+else:
+    cat = 'jit-log-opt-loop'
+text = logparser.extract_category(log, catprefix=cat)
+
+print "..."
+s = trace.lower()
+s = re.subn('#', '', s)[0]
+s = '# ' + s + ' '
+for ll in text:
+    if ll.lower().startswith(s):
+        for l in ll.split('\n'):
+            m = re.match('\+(\d+):', l)
+            if m is not None:
+                if abs(int(m.group(1)) - offset) < 50:
+                    print l
+print "..."
+
+        
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
@@ -981,6 +981,86 @@
         assert a.x == 2
         raises(TypeError, descr.__delete__, a)
 
+    def test_partial_ordering(self):
+        class A:
+            def __lt__(self, other):
+                return self
+        a1 = A()
+        a2 = A()
+        assert (a1 < a2) is a1
+        assert (a1 > a2) is a2
+
+    def test_eq_order(self):
+        # this gives the ordering of equality-related functions on top of
+        # CPython **for old-style classes**.
+        class A:
+            def __eq__(self, other): return self.__class__.__name__+':A.eq'
+            def __ne__(self, other): return self.__class__.__name__+':A.ne'
+            def __lt__(self, other): return self.__class__.__name__+':A.lt'
+            def __le__(self, other): return self.__class__.__name__+':A.le'
+            def __gt__(self, other): return self.__class__.__name__+':A.gt'
+            def __ge__(self, other): return self.__class__.__name__+':A.ge'
+        class B:
+            def __eq__(self, other): return self.__class__.__name__+':B.eq'
+            def __ne__(self, other): return self.__class__.__name__+':B.ne'
+            def __lt__(self, other): return self.__class__.__name__+':B.lt'
+            def __le__(self, other): return self.__class__.__name__+':B.le'
+            def __gt__(self, other): return self.__class__.__name__+':B.gt'
+            def __ge__(self, other): return self.__class__.__name__+':B.ge'
+        #
+        assert (A() == B()) == 'A:A.eq'
+        assert (A() != B()) == 'A:A.ne'
+        assert (A() <  B()) == 'A:A.lt'
+        assert (A() <= B()) == 'A:A.le'
+        assert (A() >  B()) == 'A:A.gt'
+        assert (A() >= B()) == 'A:A.ge'
+        #
+        assert (B() == A()) == 'B:B.eq'
+        assert (B() != A()) == 'B:B.ne'
+        assert (B() <  A()) == 'B:B.lt'
+        assert (B() <= A()) == 'B:B.le'
+        assert (B() >  A()) == 'B:B.gt'
+        assert (B() >= A()) == 'B:B.ge'
+        #
+        class C(A):
+            def __eq__(self, other): return self.__class__.__name__+':C.eq'
+            def __ne__(self, other): return self.__class__.__name__+':C.ne'
+            def __lt__(self, other): return self.__class__.__name__+':C.lt'
+            def __le__(self, other): return self.__class__.__name__+':C.le'
+            def __gt__(self, other): return self.__class__.__name__+':C.gt'
+            def __ge__(self, other): return self.__class__.__name__+':C.ge'
+        #
+        assert (A() == C()) == 'A:A.eq'
+        assert (A() != C()) == 'A:A.ne'
+        assert (A() <  C()) == 'A:A.lt'
+        assert (A() <= C()) == 'A:A.le'
+        assert (A() >  C()) == 'A:A.gt'
+        assert (A() >= C()) == 'A:A.ge'
+        #
+        assert (C() == A()) == 'C:C.eq'
+        assert (C() != A()) == 'C:C.ne'
+        assert (C() <  A()) == 'C:C.lt'
+        assert (C() <= A()) == 'C:C.le'
+        assert (C() >  A()) == 'C:C.gt'
+        assert (C() >= A()) == 'C:C.ge'
+        #
+        class D(A):
+            pass
+        #
+        assert (A() == D()) == 'A:A.eq'
+        assert (A() != D()) == 'A:A.ne'
+        assert (A() <  D()) == 'A:A.lt'
+        assert (A() <= D()) == 'A:A.le'
+        assert (A() >  D()) == 'A:A.gt'
+        assert (A() >= D()) == 'A:A.ge'
+        #
+        assert (D() == A()) == 'D:A.eq'
+        assert (D() != A()) == 'D:A.ne'
+        assert (D() <  A()) == 'D:A.lt'
+        assert (D() <= A()) == 'D:A.le'
+        assert (D() >  A()) == 'D:A.gt'
+        assert (D() >= A()) == 'D:A.ge'
+
 
 class AppTestOldStyleClassStrDict(object):
     def setup_class(cls):
diff --git a/pypy/module/__pypy__/interp_builders.py b/pypy/module/__pypy__/interp_builders.py
--- a/pypy/module/__pypy__/interp_builders.py
+++ b/pypy/module/__pypy__/interp_builders.py
@@ -7,7 +7,7 @@
 
 class W_UnicodeBuilder(Wrappable):
     def __init__(self, space, size):
-        if size == -1:
+        if size < 0:
             self.builder = UnicodeBuilder()
         else:
             self.builder = UnicodeBuilder(size)
@@ -47,4 +47,4 @@
     append_slice = interp2app(W_UnicodeBuilder.descr_append_slice),
     build = interp2app(W_UnicodeBuilder.descr_build),
 )
-W_UnicodeBuilder.typedef.acceptable_as_base_class = False
\ No newline at end of file
+W_UnicodeBuilder.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -70,7 +70,7 @@
         ss = _ssl.sslwrap(s, 0)
         exc = raises(_socket.error, ss.do_handshake)
         if sys.platform == 'win32':
-            assert exc.value.errno == 2 # Cannot find file (=not a socket)
+            assert exc.value.errno == 10057 # WSAENOTCONN
         else:
             assert exc.value.errno == 32 # Broken pipe
         del exc, ss, s
diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py
--- a/pypy/module/cpyext/eval.py
+++ b/pypy/module/cpyext/eval.py
@@ -1,11 +1,16 @@
 from pypy.interpreter.error import OperationError
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
-    cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP)
+    cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP,
+    cpython_struct)
 from pypy.module.cpyext.pyobject import PyObject, borrow_from
 from pypy.module.cpyext.pyerrors import PyErr_SetFromErrno
 from pypy.module.__builtin__ import compiling
 
+PyCompilerFlags = cpython_struct(
+    "PyCompilerFlags", ())
+PyCompilerFlagsPtr = lltype.Ptr(PyCompilerFlags)
+
 @cpython_api([PyObject, PyObject, PyObject], PyObject)
 def PyEval_CallObjectWithKeywords(space, w_obj, w_arg, w_kwds):
     return space.call(w_obj, w_arg, w_kwds)
@@ -69,7 +74,7 @@
 Py_file_input = 257
 Py_eval_input = 258
 
-def run_string(space, source, filename, start, w_globals, w_locals):
+def compile_string(space, source, filename, start):
     w_source = space.wrap(source)
     start = rffi.cast(lltype.Signed, start)
     if start == Py_file_input:
@@ -80,8 +85,11 @@
         mode = 'single'
     else:
         raise OperationError(space.w_ValueError, space.wrap(
-            "invalid mode parameter for PyRun_String"))
-    w_code = compiling.compile(space, w_source, filename, mode)
+            "invalid mode parameter for compilation"))
+    return compiling.compile(space, w_source, filename, mode)
+
+def run_string(space, source, filename, start, w_globals, w_locals):
+    w_code = compile_string(space, source, filename, start)
     return compiling.eval(space, w_code, w_globals, w_locals)
 
 @cpython_api([CONST_STRING], rffi.INT_real, error=-1)
@@ -140,3 +148,19 @@
         pi[0] = space.getindex_w(w_obj, None)
     return 1
 
+ at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real, PyCompilerFlagsPtr],
+             PyObject)
+def Py_CompileStringFlags(space, source, filename, start, flags):
+    """Parse and compile the Python source code in str, returning the
+    resulting code object.  The start token is given by start; this
+    can be used to constrain the code which can be compiled and should
+    be Py_eval_input, Py_file_input, or Py_single_input.  The filename
+    specified by filename is used to construct the code object and may
+    appear in tracebacks or SyntaxError exception messages.  This
+    returns NULL if the code cannot be parsed or compiled."""
+    source = rffi.charp2str(source)
+    filename = rffi.charp2str(filename)
+    if flags:
+        raise OperationError(space.w_NotImplementedError, space.wrap(
+                "cpyext Py_CompileStringFlags does not accept flags"))
+    return compile_string(space, source, filename, start)
diff --git a/pypy/module/cpyext/import_.py b/pypy/module/cpyext/import_.py
--- a/pypy/module/cpyext/import_.py
+++ b/pypy/module/cpyext/import_.py
@@ -2,9 +2,11 @@
 from pypy.module.cpyext.api import (
     generic_cpy_call, cpython_api, PyObject, CONST_STRING)
 from pypy.module.cpyext.pyobject import borrow_from
-from pypy.rpython.lltypesystem import rffi
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.module import Module
+from pypy.interpreter.pycode import PyCode
+from pypy.module.imp import importing
 
 @cpython_api([PyObject], PyObject)
 def PyImport_Import(space, w_name):
@@ -80,3 +82,44 @@
     w_modulesDict = space.sys.get('modules')
     return borrow_from(None, w_modulesDict)
 
+ at cpython_api([rffi.CCHARP, PyObject], PyObject)
+def PyImport_ExecCodeModule(space, name, w_code):
+    """Given a module name (possibly of the form package.module) and a code
+    object read from a Python bytecode file or obtained from the built-in
+    function compile(), load the module.  Return a new reference to the module
+    object, or NULL with an exception set if an error occurred.  Before Python
+    2.4, the module could still be created in error cases.  Starting with Python
+    2.4, name is removed from sys.modules in error cases, and even if name was
+    already in sys.modules on entry to PyImport_ExecCodeModule().  Leaving
+    incompletely initialized modules in sys.modules is dangerous, as imports of
+    such modules have no way to know that the module object is an unknown (and
+    probably damaged with respect to the module author's intents) state.
+
+    The module's __file__ attribute will be set to the code object's
+    co_filename.
+
+    This function will reload the module if it was already imported.  See
+    PyImport_ReloadModule() for the intended way to reload a module.
+
+    If name points to a dotted name of the form package.module, any package
+    structures not already created will still not be created.
+
+    name is removed from sys.modules in error cases."""
+    return PyImport_ExecCodeModuleEx(space, name, w_code,
+                                     lltype.nullptr(rffi.CCHARP.TO))
+
+
+ at cpython_api([rffi.CCHARP, PyObject, rffi.CCHARP], PyObject)
+def PyImport_ExecCodeModuleEx(space, name, w_code, pathname):
+    """Like PyImport_ExecCodeModule(), but the __file__ attribute of
+    the module object is set to pathname if it is non-NULL."""
+    code = space.interp_w(PyCode, w_code)
+    w_name = space.wrap(rffi.charp2str(name))
+    if pathname:
+        pathname = rffi.charp2str(pathname)
+    else:
+        pathname = code.co_filename
+    w_mod = importing.add_module(space, w_name)
+    space.setattr(w_mod, space.wrap('__file__'), space.wrap(pathname))
+    importing.exec_code_module(space, w_mod, code)
+    return w_mod
diff --git a/pypy/module/cpyext/include/pythonrun.h b/pypy/module/cpyext/include/pythonrun.h
--- a/pypy/module/cpyext/include/pythonrun.h
+++ b/pypy/module/cpyext/include/pythonrun.h
@@ -13,6 +13,12 @@
 
 #define Py_FrozenFlag 0
 
+typedef struct {
+    int cf_flags;  /* bitmask of CO_xxx flags relevant to future */
+} PyCompilerFlags;
+
+#define Py_CompileString(str, filename, start) Py_CompileStringFlags(str, filename, start, NULL)
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/presetup.py b/pypy/module/cpyext/presetup.py
--- a/pypy/module/cpyext/presetup.py
+++ b/pypy/module/cpyext/presetup.py
@@ -42,4 +42,4 @@
 patch_distutils()
 
 del sys.argv[0]
-execfile(sys.argv[0])
+execfile(sys.argv[0], {'__file__': sys.argv[0]})
diff --git a/pypy/module/cpyext/setobject.py b/pypy/module/cpyext/setobject.py
--- a/pypy/module/cpyext/setobject.py
+++ b/pypy/module/cpyext/setobject.py
@@ -14,6 +14,11 @@
 
 @cpython_api([PyObject], PyObject)
 def PySet_New(space, w_iterable):
+    """Return a new set containing objects returned by the iterable.  The
+    iterable may be NULL to create a new empty set.  Return the new set on
+    success or NULL on failure.  Raise TypeError if iterable is not
+    actually iterable.  The constructor is also useful for copying a set
+    (c=set(s))."""
     if w_iterable is None:
         return space.call_function(space.w_set)
     else:
@@ -21,6 +26,15 @@
 
 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
 def PySet_Add(space, w_s, w_obj):
+    """Add key to a set instance.  Does not apply to frozenset
+    instances.  Return 0 on success or -1 on failure. Raise a TypeError if
+    the key is unhashable. Raise a MemoryError if there is no room to grow.
+    Raise a SystemError if set is an not an instance of set or its
+    subtype.
+
+    Now works with instances of frozenset or its subtypes.
+    Like PyTuple_SetItem() in that it can be used to fill-in the
+    values of brand new frozensets before they are exposed to other code."""
     if not PySet_Check(space, w_s):
         PyErr_BadInternalCall(space)
     space.call_method(w_s, 'add', w_obj)
@@ -28,6 +42,12 @@
 
 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
 def PySet_Discard(space, w_s, w_obj):
+    """Return 1 if found and removed, 0 if not found (no action taken), and -1 if an
+    error is encountered.  Does not raise KeyError for missing keys.  Raise a
+    TypeError if the key is unhashable.  Unlike the Python discard()
+    method, this function does not automatically convert unhashable sets into
+    temporary frozensets. Raise PyExc_SystemError if set is an not an
+    instance of set or its subtype."""
     if not PySet_Check(space, w_s):
         PyErr_BadInternalCall(space)
     space.call_method(w_s, 'discard', w_obj)
@@ -36,11 +56,25 @@
 
 @cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
 def PySet_GET_SIZE(space, w_s):
+    """Macro form of PySet_Size() without error checking."""
     return space.int_w(space.len(w_s))
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
 def PySet_Size(space, ref):
+    """Return the length of a set or frozenset object. Equivalent to
+    len(anyset).  Raises a PyExc_SystemError if anyset is not a set, frozenset,
+    or an instance of a subtype."""
     if not PySet_Check(space, ref):
         raise OperationError(space.w_TypeError,
                              space.wrap("expected set object"))
     return PySet_GET_SIZE(space, ref)
+
+ at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
+def PySet_Contains(space, w_obj, w_key):
+    """Return 1 if found, 0 if not found, and -1 if an error is encountered.  Unlike
+    the Python __contains__() method, this function does not automatically
+    convert unhashable sets into temporary frozensets.  Raise a TypeError if
+    the key is unhashable. Raise PyExc_SystemError if anyset is not a
+    set, frozenset, or an instance of a subtype."""
+    w_res = space.contains(w_obj, w_key)
+    return space.int_w(w_res)
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
@@ -1048,37 +1048,6 @@
     """
     raise NotImplementedError
 
- at cpython_api([rffi.CCHARP, PyObject], PyObject)
-def PyImport_ExecCodeModule(space, name, co):
-    """Given a module name (possibly of the form package.module) and a code
-    object read from a Python bytecode file or obtained from the built-in
-    function compile(), load the module.  Return a new reference to the module
-    object, or NULL with an exception set if an error occurred.  Before Python
-    2.4, the module could still be created in error cases.  Starting with Python
-    2.4, name is removed from sys.modules in error cases, and even if name was
-    already in sys.modules on entry to PyImport_ExecCodeModule().  Leaving
-    incompletely initialized modules in sys.modules is dangerous, as imports of
-    such modules have no way to know that the module object is an unknown (and
-    probably damaged with respect to the module author's intents) state.
-
-    The module's __file__ attribute will be set to the code object's
-    co_filename.
-
-    This function will reload the module if it was already imported.  See
-    PyImport_ReloadModule() for the intended way to reload a module.
-
-    If name points to a dotted name of the form package.module, any package
-    structures not already created will still not be created.
-
-    name is removed from sys.modules in error cases."""
-    raise NotImplementedError
-
- at cpython_api([rffi.CCHARP, PyObject, rffi.CCHARP], PyObject)
-def PyImport_ExecCodeModuleEx(space, name, co, pathname):
-    """Like PyImport_ExecCodeModule(), but the __file__ attribute of
-    the module object is set to pathname if it is non-NULL."""
-    raise NotImplementedError
-
 @cpython_api([], lltype.Signed, error=CANNOT_FAIL)
 def PyImport_GetMagicNumber(space):
     """Return the magic number for Python bytecode files (a.k.a. .pyc and
@@ -1958,12 +1927,6 @@
     raise NotImplementedError
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
-def PySet_Check(space, p):
-    """Return true if p is a set object or an instance of a subtype.
-    """
-    raise NotImplementedError
-
- at cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyFrozenSet_Check(space, p):
     """Return true if p is a frozenset object or an instance of a
     subtype.
@@ -1989,15 +1952,6 @@
     raise NotImplementedError
 
 @cpython_api([PyObject], PyObject)
-def PySet_New(space, iterable):
-    """Return a new set containing objects returned by the iterable.  The
-    iterable may be NULL to create a new empty set.  Return the new set on
-    success or NULL on failure.  Raise TypeError if iterable is not
-    actually iterable.  The constructor is also useful for copying a set
-    (c=set(s))."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], PyObject)
 def PyFrozenSet_New(space, iterable):
     """Return a new frozenset containing objects returned by the iterable.
     The iterable may be NULL to create a new empty frozenset.  Return the new
@@ -2009,53 +1963,6 @@
     building-up new frozensets with PySet_Add()."""
     raise NotImplementedError
 
- at cpython_api([PyObject], Py_ssize_t, error=-1)
-def PySet_Size(space, anyset):
-    """Return the length of a set or frozenset object. Equivalent to
-    len(anyset).  Raises a PyExc_SystemError if anyset is not a set, frozenset,
-    or an instance of a subtype.
-
-    This function returned an int. This might require changes in
-    your code for properly supporting 64-bit systems."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], Py_ssize_t, error=-1)
-def PySet_GET_SIZE(space, anyset):
-    """Macro form of PySet_Size() without error checking."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
-def PySet_Contains(space, anyset, key):
-    """Return 1 if found, 0 if not found, and -1 if an error is encountered.  Unlike
-    the Python __contains__() method, this function does not automatically
-    convert unhashable sets into temporary frozensets.  Raise a TypeError if
-    the key is unhashable. Raise PyExc_SystemError if anyset is not a
-    set, frozenset, or an instance of a subtype."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
-def PySet_Add(space, set, key):
-    """Add key to a set instance.  Does not apply to frozenset
-    instances.  Return 0 on success or -1 on failure. Raise a TypeError if
-    the key is unhashable. Raise a MemoryError if there is no room to grow.
-    Raise a SystemError if set is an not an instance of set or its
-    subtype.
-
-    Now works with instances of frozenset or its subtypes.
-    Like PyTuple_SetItem() in that it can be used to fill-in the
-    values of brand new frozensets before they are exposed to other code."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
-def PySet_Discard(space, set, key):
-    """Return 1 if found and removed, 0 if not found (no action taken), and -1 if an
-    error is encountered.  Does not raise KeyError for missing keys.  Raise a
-    TypeError if the key is unhashable.  Unlike the Python discard()
-    method, this function does not automatically convert unhashable sets into
-    temporary frozensets. Raise PyExc_SystemError if set is an not an
-    instance of set or its subtype."""
-    raise NotImplementedError
-
 @cpython_api([PyObject], PyObject)
 def PySet_Pop(space, set):
     """Return a new reference to an arbitrary object in the set, and removes the
@@ -2224,29 +2131,6 @@
     """Return 1 or 0 depending on whether ch is an alphabetic character."""
     raise NotImplementedError
 
- at cpython_api([Py_UNICODE], Py_UNICODE, error=CANNOT_FAIL)
-def Py_UNICODE_TOTITLE(space, ch):
-    """Return the character ch converted to title case."""
-    raise NotImplementedError
-
- at cpython_api([Py_UNICODE], rffi.INT_real, error=CANNOT_FAIL)
-def Py_UNICODE_TODECIMAL(space, ch):
-    """Return the character ch converted to a decimal positive integer.  Return
-    -1 if this is not possible.  This macro does not raise exceptions."""
-    raise NotImplementedError
-
- at cpython_api([Py_UNICODE], rffi.INT_real, error=CANNOT_FAIL)
-def Py_UNICODE_TODIGIT(space, ch):
-    """Return the character ch converted to a single digit integer. Return -1 if
-    this is not possible.  This macro does not raise exceptions."""
-    raise NotImplementedError
-
- at cpython_api([Py_UNICODE], rffi.DOUBLE, error=CANNOT_FAIL)
-def Py_UNICODE_TONUMERIC(space, ch):
-    """Return the character ch converted to a double. Return -1.0 if this is not
-    possible.  This macro does not raise exceptions."""
-    raise NotImplementedError
-
 @cpython_api([rffi.CCHARP], PyObject)
 def PyUnicode_FromFormat(space, format):
     """Take a C printf()-style format string and a variable number of
@@ -2732,12 +2616,6 @@
     use the default error handling."""
     raise NotImplementedError
 
- at cpython_api([PyObject, PyObject], PyObject)
-def PyUnicode_Join(space, separator, seq):
-    """Join a sequence of strings using the given separator and return the resulting
-    Unicode string."""
-    raise NotImplementedError
-
 @cpython_api([PyObject, PyObject, Py_ssize_t, Py_ssize_t, rffi.INT_real], rffi.INT_real, error=-1)
 def PyUnicode_Tailmatch(space, str, substr, start, end, direction):
     """Return 1 if substr matches str*[*start:end] at the given tail end
@@ -2800,12 +2678,6 @@
     Py_NE, Py_LT, and Py_LE."""
     raise NotImplementedError
 
- at cpython_api([PyObject, PyObject], PyObject)
-def PyUnicode_Format(space, format, args):
-    """Return a new string object from format and args; this is analogous to
-    format % args.  The args argument must be a tuple."""
-    raise NotImplementedError
-
 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
 def PyUnicode_Contains(space, container, element):
     """Check whether element is contained in container and return true or false
@@ -2992,23 +2864,6 @@
     returns."""
     raise NotImplementedError
 
- at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real], PyObject)
-def Py_CompileString(space, str, filename, start):
-    """This is a simplified interface to Py_CompileStringFlags() below, leaving
-    flags set to NULL."""
-    raise NotImplementedError
-
- at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real, PyCompilerFlags], PyObject)
-def Py_CompileStringFlags(space, str, filename, start, flags):
-    """Parse and compile the Python source code in str, returning the resulting code
-    object.  The start token is given by start; this can be used to constrain the
-    code which can be compiled and should be Py_eval_input,
-    Py_file_input, or Py_single_input.  The filename specified by
-    filename is used to construct the code object and may appear in tracebacks or
-    SyntaxError exception messages.  This returns NULL if the code cannot
-    be parsed or compiled."""
-    raise NotImplementedError
-
 @cpython_api([PyCodeObject, PyObject, PyObject], PyObject)
 def PyEval_EvalCode(space, co, globals, locals):
     """This is a simplified interface to PyEval_EvalCodeEx(), with just
@@ -3076,11 +2931,3 @@
     None, or NULL, this will return NULL and raise TypeError.
     """
     raise NotImplementedError
-
- at cpython_api([PyObject], PyObject)
-def PyWeakref_GET_OBJECT(space, ref):
-    """Similar to PyWeakref_GetObject(), but implemented as a macro that does no
-    error checking.
-    """
-    borrow_from()
-    raise NotImplementedError
diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -221,4 +221,38 @@
             return args
         assert module.call_func(f) == (None,)
         assert module.call_method("text") == 2
-        
+
+    def test_CompileString_and_Exec(self):
+        module = self.import_extension('foo', [
+            ("compile_string", "METH_NOARGS",
+             """
+                return Py_CompileString(
+                   "f = lambda x: x+5", "someFile", Py_file_input);
+             """),
+            ("exec_code", "METH_O",
+             """
+                return PyImport_ExecCodeModule("cpyext_test_modname", args);
+             """),
+            ("exec_code_ex", "METH_O",
+             """
+                return PyImport_ExecCodeModuleEx("cpyext_test_modname",
+                                                 args, "otherFile");
+             """),
+            ])
+        code = module.compile_string()
+        assert code.co_filename == "someFile"
+        assert code.co_name == "<module>"
+
+        mod = module.exec_code(code)
+        assert mod.__name__ == "cpyext_test_modname"
+        assert mod.__file__ == "someFile"
+        print dir(mod)
+        print mod.__dict__
+        assert mod.f(42) == 47
+
+        mod = module.exec_code_ex(code)
+        assert mod.__name__ == "cpyext_test_modname"
+        assert mod.__file__ == "otherFile"
+        print dir(mod)
+        print mod.__dict__
+        assert mod.f(42) == 47
diff --git a/pypy/module/cpyext/test/test_setobject.py b/pypy/module/cpyext/test/test_setobject.py
--- a/pypy/module/cpyext/test/test_setobject.py
+++ b/pypy/module/cpyext/test/test_setobject.py
@@ -27,3 +27,8 @@
         assert api.PySet_Size(w_set) == 5
         api.PySet_Discard(w_set, space.wrap(6))
         assert api.PySet_Size(w_set) == 4
+
+    def test_set_contains(self, space, api):
+        w_set = api.PySet_New(space.wrap([1,2,3,4]))
+        assert api.PySet_Contains(w_set, space.wrap(1))
+        assert not api.PySet_Contains(w_set, space.wrap(0))
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
@@ -219,6 +219,24 @@
         assert api.Py_UNICODE_TOUPPER(u'&#65533;') == u'&#65533;'
         assert api.Py_UNICODE_TOUPPER(u'&#65533;') == u'&#65533;'
 
+    def test_TOTITLE(self, space, api):
+        assert api.Py_UNICODE_TOTITLE(u'/') == u'/'
+        assert api.Py_UNICODE_TOTITLE(u'&#65533;') == u'&#65533;'
+        assert api.Py_UNICODE_TOTITLE(u'&#65533;') == u'&#65533;'
+
+    def test_TODECIMAL(self, space, api):
+        assert api.Py_UNICODE_TODECIMAL(u'6') == 6
+        assert api.Py_UNICODE_TODECIMAL(u'A') == -1
+
+    def test_TODIGIT(self, space, api):
+        assert api.Py_UNICODE_TODIGIT(u'6') == 6
+        assert api.Py_UNICODE_TODIGIT(u'A') == -1
+
+    def test_TONUMERIC(self, space, api):
+        assert api.Py_UNICODE_TONUMERIC(u'6') == 6.0
+        assert api.Py_UNICODE_TONUMERIC(u'A') == -1.0
+        assert api.Py_UNICODE_TONUMERIC(u'\N{VULGAR FRACTION ONE HALF}') == .5
+
     def test_fromobject(self, space, api):
         w_u = space.wrap(u'a')
         assert api.PyUnicode_FromObject(w_u) is w_u
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
@@ -122,6 +122,38 @@
     """Return the character ch converted to upper case."""
     return unichr(unicodedb.toupper(ord(ch)))
 
+ at cpython_api([Py_UNICODE], Py_UNICODE, error=CANNOT_FAIL)
+def Py_UNICODE_TOTITLE(space, ch):
+    """Return the character ch converted to title case."""
+    return unichr(unicodedb.totitle(ord(ch)))
+
+ at cpython_api([Py_UNICODE], rffi.INT_real, error=CANNOT_FAIL)
+def Py_UNICODE_TODECIMAL(space, ch):
+    """Return the character ch converted to a decimal positive integer.  Return
+    -1 if this is not possible.  This macro does not raise exceptions."""
+    try:
+        return unicodedb.decimal(ord(ch))
+    except KeyError:
+        return -1
+
+ at cpython_api([Py_UNICODE], rffi.INT_real, error=CANNOT_FAIL)
+def Py_UNICODE_TODIGIT(space, ch):
+    """Return the character ch converted to a single digit integer. Return -1 if
+    this is not possible.  This macro does not raise exceptions."""
+    try:
+        return unicodedb.digit(ord(ch))
+    except KeyError:
+        return -1
+
+ at cpython_api([Py_UNICODE], rffi.DOUBLE, error=CANNOT_FAIL)
+def Py_UNICODE_TONUMERIC(space, ch):
+    """Return the character ch converted to a double. Return -1.0 if this is not
+    possible.  This macro does not raise exceptions."""
+    try:
+        return unicodedb.numeric(ord(ch))
+    except KeyError:
+        return -1.0
+
 @cpython_api([PyObject], rffi.CCHARP, error=CANNOT_FAIL)
 def PyUnicode_AS_DATA(space, ref):
     """Return a pointer to the internal buffer of the object. o has to be a
@@ -526,8 +558,12 @@
 
 @cpython_api([PyObject, PyObject], PyObject)
 def PyUnicode_Format(space, w_format, w_args):
+    """Return a new string object from format and args; this is analogous to
+    format % args.  The args argument must be a tuple."""
     return space.mod(w_format, w_args)
 
 @cpython_api([PyObject, PyObject], PyObject)
 def PyUnicode_Join(space, w_sep, w_seq):
+    """Join a sequence of strings using the given separator and return the resulting
+    Unicode string."""
     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
@@ -25,6 +25,9 @@
 
 @cpython_api([PyObject], PyObject)
 def PyWeakref_GET_OBJECT(space, w_ref):
+    """Similar to PyWeakref_GetObject(), but implemented as a macro that does no
+    error checking.
+    """
     return borrow_from(w_ref, space.call_function(w_ref))
 
 @cpython_api([PyObject], PyObject)
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
@@ -540,6 +540,13 @@
     if pkgdir is not None:
         space.setattr(w_mod, w('__path__'), space.newlist([w(pkgdir)]))
 
+def add_module(space, w_name):
+    w_mod = check_sys_modules(space, w_name)
+    if w_mod is None:
+        w_mod = space.wrap(Module(space, w_name))
+        space.sys.setmodule(w_mod)
+    return w_mod
+
 def load_c_extension(space, filename, modulename):
     # the next line is mandatory to init cpyext
     space.getbuiltinmodule("cpyext")
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
@@ -176,6 +176,38 @@
     descr_argmax = _reduce_argmax_argmin_impl(maximum)
     descr_argmin = _reduce_argmax_argmin_impl(minimum)
 
+    def descr_sort(self, space):
+        size = self.find_size()
+        stack = [(0,size-1)]
+        first=0; last=size-1; splitpoint=first;
+        while (len(stack) > 0):
+            first, last = stack.pop()
+            while last>first:
+                #splitpoint = split(first,last)
+                x = self.eval(first)
+                splitpoint = first
+                unknown = first+1
+                while (unknown<=last):
+                    if (self.eval(unknown)<x):
+                        splitpoint = splitpoint + 1
+                        #interchange(splitpoint,unknown)
+                        temp = self.eval(splitpoint)
+                        self.storage[splitpoint] = self.eval(unknown)
+                        self.storage[unknown] = temp
+                    unknown = unknown + 1
+                #interchange(first,splitpoint)
+                temp = self.eval(splitpoint)
+                self.storage[splitpoint] = self.eval(first)
+                self.storage[first] = temp
+
+                if (last-splitpoint<splitpoint-first):
+                    stack.append((first,splitpoint-1));
+                    first = splitpoint + 1
+                else:
+                    stack.append((splitpoint+1,last));
+                    last = splitpoint - 1
+
+
     def descr_dot(self, space, w_other):
         if isinstance(w_other, BaseArray):
             w_res = self.descr_mul(space, w_other)
@@ -563,4 +595,5 @@
     all = interp2app(BaseArray.descr_all),
     any = interp2app(BaseArray.descr_any),
     dot = interp2app(BaseArray.descr_dot),
+    sort = interp2app(BaseArray.descr_sort),
 )
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
@@ -427,6 +427,24 @@
         b = array([])
         raises(ValueError, "b.argmin()")
 
+    def test_sort(self):
+        from numpy import array
+        a = [3.0,4.0,0.0,-1.0]
+        b = array(a)
+        a.sort()
+        b.sort()
+        assert(len(a)==len(b))
+        for i in range(len(a)):
+            assert(a[i]==b[i])
+        a = array(list(reversed(range(6))))
+        b = array(range(6))
+        a.sort()
+        assert(len(a)==len(b))
+        for i in range(len(a)):
+            assert(a[i]==b[i])
+
+
+
     def test_all(self):
         from numpy import array
         a = array(range(5))
diff --git a/pypy/module/oracle/interp_connect.py b/pypy/module/oracle/interp_connect.py
--- a/pypy/module/oracle/interp_connect.py
+++ b/pypy/module/oracle/interp_connect.py
@@ -82,6 +82,12 @@
         return space.wrap(self)
 
     def __del__(self):
+        self.enqueue_for_destruction(self.environment.space,
+                                     W_Connection.destructor,
+                                     '__del__ method of ')
+
+    def destructor(self):
+        assert isinstance(self, W_Connection)
         if self.release:
             roci.OCITransRollback(
                 self.handle, self.environment.errorHandle,
diff --git a/pypy/module/oracle/interp_object.py b/pypy/module/oracle/interp_object.py
--- a/pypy/module/oracle/interp_object.py
+++ b/pypy/module/oracle/interp_object.py
@@ -16,6 +16,12 @@
         self.initialize(connection, param)
 
     def __del__(self):
+        self.enqueue_for_destruction(self.environment.space,
+                                     W_ObjectType.destructor,
+                                     '__del__ method of ')
+
+    def destructor(self):
+        assert isinstance(self, W_ObjectType)
         if self.tdo:
             roci.OCIObjectUnpin(
                 self.environment.handle,
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
@@ -167,6 +167,12 @@
         self.initialize(self.environment.space, cursor)
 
     def __del__(self):
+        self.enqueue_for_destruction(self.environment.space,
+                                     W_Variable.destructor,
+                                     '__del__ method of ')
+
+    def destructor(self):
+        assert isinstance(self, W_Variable)
         self.finalize()
         lltype.free(self.actualElementsPtr, flavor='raw')
         if self.actualLength:
diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -271,8 +271,12 @@
         f.close()
 
         # Ensure that fcntl is not faked
-        import fcntl
-        assert fcntl.__file__.endswith('pypy/module/fcntl')
+        try:
+            import fcntl
+        except ImportError:
+            pass
+        else:
+            assert fcntl.__file__.endswith('pypy/module/fcntl')
         exc = raises(OSError, posix.fdopen, fd)
         assert exc.value.errno == errno.EBADF
 
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
@@ -9,6 +9,7 @@
 
 from pypy.rpython.tool import rffi_platform
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.translator.platform import platform
 
 import sys
 import py
@@ -19,7 +20,9 @@
     libname = 'expat'
 eci = ExternalCompilationInfo(
     libraries=[libname],
+    library_dirs=platform.preprocess_library_dirs([]),
     includes=['expat.h'],
+    include_dirs=platform.preprocess_include_dirs([]),
     )
 
 eci = rffi_platform.configure_external_library(
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__', 'cStringIO']:
+                       '__pypy__', 'cStringIO', '_collections']:
             return True
         return False
 
diff --git a/pypy/module/pypyjit/test/test_policy.py b/pypy/module/pypyjit/test/test_policy.py
--- a/pypy/module/pypyjit/test/test_policy.py
+++ b/pypy/module/pypyjit/test/test_policy.py
@@ -37,8 +37,10 @@
     assert pypypolicy.look_inside_function(Local.getdict.im_func)
 
 def test_pypy_module():
+    from pypy.module._collections.interp_deque import W_Deque
     from pypy.module._random.interp_random import W_Random
     assert not pypypolicy.look_inside_function(W_Random.random)
+    assert pypypolicy.look_inside_function(W_Deque.length)
     assert not pypypolicy.look_inside_pypy_module('select.interp_epoll')
     assert pypypolicy.look_inside_pypy_module('__builtin__.operation')
     assert pypypolicy.look_inside_pypy_module('__builtin__.abstractinst')
diff --git a/pypy/module/pypyjit/test_pypy_c/model.py b/pypy/module/pypyjit/test_pypy_c/model.py
--- a/pypy/module/pypyjit/test_pypy_c/model.py
+++ b/pypy/module/pypyjit/test_pypy_c/model.py
@@ -75,6 +75,10 @@
         Function.__init__(self, *args, **kwds)
         self.ids = {}
         self.code = self.chunks[0].getcode()
+        if not self.code and len(self.chunks)>1 and \
+               isinstance(self.chunks[1], TraceForOpcode):
+            # First chunk might be missing the debug_merge_point op
+            self.code = self.chunks[1].getcode()
         if self.code:
             self.compute_ids(self.ids)
 
@@ -132,8 +136,9 @@
     def _allops(self, include_debug_merge_points=False, opcode=None):
         opcode_name = opcode
         for chunk in self.flatten_chunks():
-            opcode = chunk.getopcode()                                                          
-            if opcode_name is None or opcode.__class__.__name__ == opcode_name:
+            opcode = chunk.getopcode()
+            if opcode_name is None or \
+                   (opcode and opcode.__class__.__name__ == opcode_name):
                 for op in self._ops_for_chunk(chunk, include_debug_merge_points):
                     yield op
 
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
@@ -192,6 +192,9 @@
             i20 = int_ge(i18, i8)
             guard_false(i20, descr=...)
             f21 = getarrayitem_raw(i13, i18, descr=...)
+            i14 = int_sub(i6, 1)
+            i15 = int_ge(i14, i8)
+            guard_false(i15, descr=...)
             f23 = getarrayitem_raw(i13, i14, descr=...)
             f24 = float_add(f21, f23)
             f26 = getarrayitem_raw(i13, i6, descr=...)
@@ -240,7 +243,10 @@
             ...
             i17 = int_and(i14, 255)
             f18 = getarrayitem_raw(i8, i17, descr=...)
-            f20 = getarrayitem_raw(i8, i9, descr=...)
+            i19s = int_sub_ovf(i6, 1)
+            guard_no_overflow(descr=...)
+            i22s = int_and(i19s, 255)
+            f20 = getarrayitem_raw(i8, i22s, descr=...)
             f21 = float_add(f18, f20)
             f23 = getarrayitem_raw(i8, i10, descr=...)
             f24 = float_add(f21, f23)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_boolrewrite.py b/pypy/module/pypyjit/test_pypy_c/test_boolrewrite.py
--- a/pypy/module/pypyjit/test_pypy_c/test_boolrewrite.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_boolrewrite.py
@@ -39,19 +39,19 @@
             #
             log = self.run(src, [], threshold=400)
             assert log.result == res
-            loop, = log.loops_by_filename(self.filepath)
-            le_ops = log.opnames(loop.ops_by_id('lt'))
-            ge_ops = log.opnames(loop.ops_by_id('ge'))
-            assert le_ops.count('int_lt') == 1
-            #
-            if opt_expected:
-                assert ge_ops.count('int_ge') == 0
-            else:
-                # if this assert fails it means that the optimization was
-                # applied even if we don't expect to. Check whether the
-                # optimization is valid, and either fix the code or fix the
-                # test :-)
-                assert ge_ops.count('int_ge') == 1
+            for loop in log.loops_by_filename(self.filepath):
+                le_ops = log.opnames(loop.ops_by_id('lt'))
+                ge_ops = log.opnames(loop.ops_by_id('ge'))
+                assert le_ops.count('int_lt') == 1
+                #
+                if opt_expected:
+                    assert ge_ops.count('int_ge') == 0
+                else:
+                    # if this assert fails it means that the optimization was
+                    # applied even if we don't expect to. Check whether the
+                    # optimization is valid, and either fix the code or fix the
+                    # test :-)
+                    assert ge_ops.count('int_ge') == 1
 
     def test_boolrewrite_reflex(self):
         """
@@ -87,19 +87,19 @@
             """ % (a, b)
             log = self.run(src, [], threshold=400)
             assert log.result == res
-            loop, = log.loops_by_filename(self.filepath)
-            le_ops = log.opnames(loop.ops_by_id('lt'))
-            gt_ops = log.opnames(loop.ops_by_id('gt'))
-            assert le_ops.count('int_lt') == 1
-            #
-            if opt_expected:
-                assert gt_ops.count('int_gt') == 0
-            else:
-                # if this assert fails it means that the optimization was
-                # applied even if we don't expect to. Check whether the
-                # optimization is valid, and either fix the code or fix the
-                # test :-)
-                assert gt_ops.count('int_gt') == 1
+            for loop in log.loops_by_filename(self.filepath):
+                le_ops = log.opnames(loop.ops_by_id('lt'))
+                gt_ops = log.opnames(loop.ops_by_id('gt'))
+                assert le_ops.count('int_lt') == 1
+                #
+                if opt_expected:
+                    assert gt_ops.count('int_gt') == 0
+                else:
+                    # if this assert fails it means that the optimization was
+                    # applied even if we don't expect to. Check whether the
+                    # optimization is valid, and either fix the code or fix the
+                    # test :-)
+                    assert gt_ops.count('int_gt') == 1
 
 
     def test_boolrewrite_allcases_inverse(self):
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
@@ -187,7 +187,7 @@
             guard_no_overflow(descr=...)
             i18 = force_token()
             --TICK--
-            jump(p0, p1, p2, p3, p4, i8, p7, i17, p8, i9, p10, p11, p12, descr=<Loop0>)
+            jump(p0, p1, p2, p3, p4, i8, p7, i17, p8, i9, i17, p10, p11, p12, descr=<Loop0>)
         """)
 
     def test_default_and_kw(self):
@@ -205,6 +205,7 @@
         assert log.result == 1000
         loop, = log.loops_by_id('call')
         assert loop.match_by_id('call', """
+            p14 = getarrayitem_gc_pure(p8, i9, descr=<GcPtrArrayDescr>)
             i14 = force_token()
             i16 = force_token()
         """)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_generators.py b/pypy/module/pypyjit/test_pypy_c/test_generators.py
--- a/pypy/module/pypyjit/test_pypy_c/test_generators.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_generators.py
@@ -20,6 +20,7 @@
             i16 = force_token()
             p45 = new_with_vtable(ConstClass(W_IntObject))
             setfield_gc(p45, i29, descr=<SignedFieldDescr .*>)
+            i47 = arraylen_gc(p8, descr=<GcPtrArrayDescr>) # Should be removed by backend
             setarrayitem_gc(p8, 0, p45, descr=<GcPtrArrayDescr>)
             jump(..., descr=...)
             """)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_import.py b/pypy/module/pypyjit/test_pypy_c/test_import.py
--- a/pypy/module/pypyjit/test_pypy_c/test_import.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_import.py
@@ -16,12 +16,6 @@
         loop, = log.loops_by_id('import')
         assert loop.match_by_id('import', """
             guard_not_invalidated(descr=...)
-            p11 = getfield_gc(ConstPtr(ptr10), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
-            guard_value(p11, ConstPtr(ptr12), descr=...)
-            p14 = getfield_gc(ConstPtr(ptr13), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
-            p16 = getfield_gc(ConstPtr(ptr15), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
-            guard_value(p14, ConstPtr(ptr17), descr=...)
-            guard_isnull(p16, descr=...)
         """)
 
     def test_import_fast_path(self, tmpdir):
diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py b/pypy/module/pypyjit/test_pypy_c/test_instance.py
--- a/pypy/module/pypyjit/test_pypy_c/test_instance.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py
@@ -115,16 +115,19 @@
         # ----------------------
         loop, = log.loops_by_filename(self.filepath)
         assert loop.match("""
+            i8 = getfield_gc_pure(p5, descr=...)
             i9 = int_lt(i8, i7)
             guard_true(i9, descr=.*)
             guard_not_invalidated(descr=.*)
-            i11 = int_add(i8, 1)
+            i82 = getfield_gc_pure(p8, descr=...)
+            i11 = int_add_ovf(i82, 1)
+            guard_no_overflow(descr=...)
             i12 = force_token()
             --TICK--
             p20 = new_with_vtable(ConstClass(W_IntObject))
             setfield_gc(p20, i11, descr=<SignedFieldDescr.*W_IntObject.inst_intval .*>)
             setfield_gc(ConstPtr(ptr21), p20, descr=<GcPtrFieldDescr .*TypeCell.inst_w_value .*>)
-            jump(p0, p1, p2, p3, p4, p20, p6, i11, i7, descr=<Loop.>)
+            jump(p0, p1, p2, p3, p4, p20, p6, i7, p20, descr=<Loop.>)
         """)
 
     def test_oldstyle_newstyle_mix(self):
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
@@ -145,6 +145,7 @@
             guard_no_overflow(descr=...)
             i14 = int_add_ovf(i7, 1)
             guard_no_overflow(descr=...)
+            i16s = int_sub(i8, 1)            
             i16 = int_add_ovf(i6, 1)
             guard_no_overflow(descr=...)
             i19 = int_add(i8, 1)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_math.py b/pypy/module/pypyjit/test_pypy_c/test_math.py
--- a/pypy/module/pypyjit/test_pypy_c/test_math.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_math.py
@@ -60,4 +60,32 @@
             i7 = int_add(i0, f1)
             --TICK--
             jump(..., descr=)
+        """)
+
+    def test_fmod(self):
+        def main(n):
+            import math
+
+            s = 0
+            while n > 0:
+                s += math.fmod(n, 2.0)
+                n -= 1
+            return s
+        log = self.run(main, [500])
+        assert log.result == main(500)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i1 = int_gt(i0, 0)
+            guard_true(i1, descr=...)
+            f1 = cast_int_to_float(i0)
+            i2 = float_eq(f1, inf)
+            i3 = float_eq(f1, -inf)
+            i4 = int_or(i2, i3)
+            i5 = int_is_true(i4)
+            guard_false(i5, descr=...)
+            f2 = call(ConstClass(fmod), f1, 2.0, descr=<FloatCallDescr>)
+            f3 = float_add(f0, f2)
+            i6 = int_sub(i0, 1)
+            --TICK--
+            jump(..., descr=)
         """)
\ No newline at end of file
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
@@ -115,6 +115,7 @@
             i21 = force_token()
             setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
             guard_not_invalidated(descr=...)
+            i26 = int_sub(i9, 1)
             i23 = int_lt(i18, 0)
             guard_false(i23, descr=...)
             i25 = int_ge(i18, i9)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -18,6 +18,8 @@
             i14 = int_lt(i6, i9)
             guard_true(i14, descr=...)
             guard_not_invalidated(descr=...)
+            i16 = int_eq(i6, -9223372036854775808)
+            guard_false(i16, descr=...)
             i15 = int_mod(i6, i10)
             i17 = int_rshift(i15, 63)
             i18 = int_and(i10, i17)
diff --git a/pypy/module/termios/interp_termios.py b/pypy/module/termios/interp_termios.py
--- a/pypy/module/termios/interp_termios.py
+++ b/pypy/module/termios/interp_termios.py
@@ -19,8 +19,9 @@
     w_exception_class = space.fromcache(Cache).w_error
     return wrap_oserror(space, error, w_exception_class=w_exception_class)
 
- at unwrap_spec(fd=int, when=int)
-def tcsetattr(space, fd, when, w_attributes):
+ at unwrap_spec(when=int)
+def tcsetattr(space, w_fd, when, w_attributes):
+    fd = space.c_filedescriptor_w(w_fd)
     w_iflag, w_oflag, w_cflag, w_lflag, w_ispeed, w_ospeed, w_cc = \
              space.unpackiterable(w_attributes, expected_length=7)
     w_builtin = space.getbuiltinmodule('__builtin__')
@@ -40,8 +41,8 @@
     except OSError, e:
         raise convert_error(space, e)
 
- at unwrap_spec(fd=int)
-def tcgetattr(space, fd):
+def tcgetattr(space, w_fd):
+    fd = space.c_filedescriptor_w(w_fd)
     try:
         tup = rtermios.tcgetattr(fd)
     except OSError, e:
@@ -57,29 +58,32 @@
     l_w.append(w_cc)
     return space.newlist(l_w)
 
- at unwrap_spec(fd=int, duration=int)
-def tcsendbreak(space, fd, duration):
+ at unwrap_spec(duration=int)
+def tcsendbreak(space, w_fd, duration):
+    fd = space.c_filedescriptor_w(w_fd)
     try:
         termios.tcsendbreak(fd, duration)
     except OSError, e:
         raise convert_error(space, e)
 
- at unwrap_spec(fd=int)
-def tcdrain(space, fd):
+def tcdrain(space, w_fd):
+    fd = space.c_filedescriptor_w(w_fd)
     try:
         termios.tcdrain(fd)
     except OSError, e:
         raise convert_error(space, e)
 
- at unwrap_spec(fd=int, queue=int)
-def tcflush(space, fd, queue):
+ at unwrap_spec(queue=int)
+def tcflush(space, w_fd, queue):
+    fd = space.c_filedescriptor_w(w_fd)
     try:
         termios.tcflush(fd, queue)
     except OSError, e:
         raise convert_error(space, e)
 
- at unwrap_spec(fd=int, action=int)
-def tcflow(space, fd, action):
+ at unwrap_spec(action=int)
+def tcflow(space, w_fd, action):
+    fd = space.c_filedescriptor_w(w_fd)
     try:
         termios.tcflow(fd, action)
     except OSError, e:
diff --git a/pypy/module/termios/test/test_termios.py b/pypy/module/termios/test/test_termios.py
--- a/pypy/module/termios/test/test_termios.py
+++ b/pypy/module/termios/test/test_termios.py
@@ -62,8 +62,9 @@
 
     def test_tcsetattr(self):
         source = py.code.Source("""
+        import sys
         import termios
-        termios.tcsetattr(0, 1, [16640, 4, 191, 2608, 15, 15, ['\x03', '\x1c', '\x7f', '\x15', '\x04', 0, 1, '\x00', '\x11', '\x13', '\x1a', '\x00', '\x12', '\x0f', '\x17', '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']])
+        termios.tcsetattr(sys.stdin, 1, [16640, 4, 191, 2608, 15, 15, ['\x03', '\x1c', '\x7f', '\x15', '\x04', 0, 1, '\x00', '\x11', '\x13', '\x1a', '\x00', '\x12', '\x0f', '\x17', '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']])
         print 'ok!'
         """)
         f = udir.join("test_tcsetattr.py")
diff --git a/pypy/module/thread/test/test_ll_thread.py b/pypy/module/thread/test/test_ll_thread.py
--- a/pypy/module/thread/test/test_ll_thread.py
+++ b/pypy/module/thread/test/test_ll_thread.py
@@ -34,6 +34,10 @@
     use_threads = True
 
     def test_start_new_thread(self):
+        py.test.skip("xxx ideally, investigate why it fails randomly")
+        # xxx but in practice start_new_thread() is also tested by the
+        # next test, and it's a mess to test start_new_thread() without
+        # the proper GIL to protect the GC
         import time
 
         class State:
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -724,13 +724,22 @@
         w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, left)
         w_first = w_obj1
         w_second = w_obj2
-
-        if _same_class_w(space, w_obj1, w_obj2, w_typ1, w_typ2):
+        #
+        if left == right and _same_class_w(space, w_obj1, w_obj2,
+                                           w_typ1, w_typ2):
+            # for __eq__ and __ne__, if the objects have the same
+            # (old-style or new-style) class, then don't try the
+            # opposite method, which is the same one.
             w_right_impl = None
         else:
-            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2, right)
-            # XXX see binop_impl
-            if space.is_true(space.issubtype(w_typ2, w_typ1)):
+            # in all other cases, try the opposite method.
+            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2,right)
+            if space.is_w(w_typ1, w_typ2):
+                # if the type is the same, *or* if both are old-style classes,
+                # then don't reverse: try left first, right next.
+                pass
+            elif space.is_true(space.issubtype(w_typ2, w_typ1)):
+                # for new-style classes, if typ2 is a subclass of typ1.
                 w_obj1, w_obj2 = w_obj2, w_obj1
                 w_left_impl, w_right_impl = w_right_impl, w_left_impl
 
diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py
--- a/pypy/objspace/test/test_descroperation.py
+++ b/pypy/objspace/test/test_descroperation.py
@@ -377,7 +377,26 @@
 
         setattr(P, "__weakref__", 0)
 
+    def test_subclass_addition(self):
+        # the __radd__ is never called (compare with the next test)
+        l = []
+        class A(object):
+            def __add__(self, other):
+                l.append(self.__class__)
+                l.append(other.__class__)
+                return 123
+            def __radd__(self, other):
+                # should never be called!
+                return 456
+        class B(A):
+            pass
+        res1 = A() + B()
+        res2 = B() + A()
+        assert res1 == res2 == 123
+        assert l == [A, B, B, A]
+
     def test_subclass_comparison(self):
+        # the __eq__ *is* called with reversed arguments
         l = []
         class A(object):
             def __eq__(self, other):
@@ -395,7 +414,27 @@
 
         A() == B()
         A() < B()
-        assert l == [B, A, A, B]
+        B() < A()
+        assert l == [B, A, A, B, B, A]
+
+    def test_subclass_comparison_more(self):
+        # similarly, __gt__(b,a) is called instead of __lt__(a,b)
+        l = []
+        class A(object):
+            def __lt__(self, other):
+                l.append(self.__class__)
+                l.append(other.__class__)
+                return '<'
+            def __gt__(self, other):
+                l.append(self.__class__)
+                l.append(other.__class__)
+                return '>'
+        class B(A):
+            pass
+        res1 = A() < B()
+        res2 = B() < A()
+        assert res1 == '>' and res2 == '<'
+        assert l == [B, A, B, A]
 
     def test_rich_comparison(self):
         # Old-style
@@ -434,6 +473,84 @@
         assert not(C(1) == D(2))
         assert not(D(1) == C(2))
 
+    def test_partial_ordering(self):
+        class A(object):
+            def __lt__(self, other):
+                return self
+        a1 = A()
+        a2 = A()
+        assert (a1 < a2) is a1
+        assert (a1 > a2) is a2
+
+    def test_eq_order(self):
+        class A(object):
+            def __eq__(self, other): return self.__class__.__name__+':A.eq'
+            def __ne__(self, other): return self.__class__.__name__+':A.ne'
+            def __lt__(self, other): return self.__class__.__name__+':A.lt'
+            def __le__(self, other): return self.__class__.__name__+':A.le'
+            def __gt__(self, other): return self.__class__.__name__+':A.gt'
+            def __ge__(self, other): return self.__class__.__name__+':A.ge'
+        class B(object):
+            def __eq__(self, other): return self.__class__.__name__+':B.eq'
+            def __ne__(self, other): return self.__class__.__name__+':B.ne'
+            def __lt__(self, other): return self.__class__.__name__+':B.lt'
+            def __le__(self, other): return self.__class__.__name__+':B.le'
+            def __gt__(self, other): return self.__class__.__name__+':B.gt'
+            def __ge__(self, other): return self.__class__.__name__+':B.ge'
+        #
+        assert (A() == B()) == 'A:A.eq'
+        assert (A() != B()) == 'A:A.ne'
+        assert (A() <  B()) == 'A:A.lt'
+        assert (A() <= B()) == 'A:A.le'
+        assert (A() >  B()) == 'A:A.gt'
+        assert (A() >= B()) == 'A:A.ge'
+        #
+        assert (B() == A()) == 'B:B.eq'
+        assert (B() != A()) == 'B:B.ne'
+        assert (B() <  A()) == 'B:B.lt'
+        assert (B() <= A()) == 'B:B.le'
+        assert (B() >  A()) == 'B:B.gt'
+        assert (B() >= A()) == 'B:B.ge'
+        #
+        class C(A):
+            def __eq__(self, other): return self.__class__.__name__+':C.eq'
+            def __ne__(self, other): return self.__class__.__name__+':C.ne'
+            def __lt__(self, other): return self.__class__.__name__+':C.lt'
+            def __le__(self, other): return self.__class__.__name__+':C.le'
+            def __gt__(self, other): return self.__class__.__name__+':C.gt'
+            def __ge__(self, other): return self.__class__.__name__+':C.ge'
+        #
+        assert (A() == C()) == 'C:C.eq'
+        assert (A() != C()) == 'C:C.ne'
+        assert (A() <  C()) == 'C:C.gt'
+        assert (A() <= C()) == 'C:C.ge'
+        assert (A() >  C()) == 'C:C.lt'
+        assert (A() >= C()) == 'C:C.le'
+        #
+        assert (C() == A()) == 'C:C.eq'
+        assert (C() != A()) == 'C:C.ne'
+        assert (C() <  A()) == 'C:C.lt'
+        assert (C() <= A()) == 'C:C.le'
+        assert (C() >  A()) == 'C:C.gt'
+        assert (C() >= A()) == 'C:C.ge'
+        #
+        class D(A):
+            pass
+        #
+        assert (A() == D()) == 'D:A.eq'
+        assert (A() != D()) == 'D:A.ne'
+        assert (A() <  D()) == 'D:A.gt'
+        assert (A() <= D()) == 'D:A.ge'
+        assert (A() >  D()) == 'D:A.lt'
+        assert (A() >= D()) == 'D:A.le'
+        #
+        assert (D() == A()) == 'D:A.eq'
+        assert (D() != A()) == 'D:A.ne'
+        assert (D() <  A()) == 'D:A.lt'
+        assert (D() <= A()) == 'D:A.le'
+        assert (D() >  A()) == 'D:A.gt'
+        assert (D() >= A()) == 'D:A.ge'
+
     def test_addition(self):
         # Old-style
         class A:
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -13,6 +13,8 @@
         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
+    (3) the function call can be moved around by optimizer,
+        but only so it'll be called earlier and not later.
 
     Most importantly it doesn't mean that an elidable function has no observable
     side effect, but those side effects are idempotent (ie caching).
@@ -296,6 +298,7 @@
               'inlining': 1,
               'loop_longevity': 1000,
               'retrace_limit': 5,
+              'max_retrace_guards': 15,
               'enable_opts': 'all',
               }
 unroll_parameters = unrolling_iterable(PARAMETERS.items())
diff --git a/pypy/rlib/parsing/makepackrat.py b/pypy/rlib/parsing/makepackrat.py
--- a/pypy/rlib/parsing/makepackrat.py
+++ b/pypy/rlib/parsing/makepackrat.py
@@ -251,9 +251,11 @@
         return "ErrorInformation(%s, %s)" % (self.pos, self.expected)
 
     def get_line_column(self, source):
-        uptoerror = source[:self.pos]
+        pos = self.pos
+        assert pos >= 0
+        uptoerror = source[:pos]
         lineno = uptoerror.count("\n")
-        columnno = self.pos - uptoerror.rfind("\n")
+        columnno = pos - uptoerror.rfind("\n")
         return lineno, columnno
 
     def nice_error_message(self, filename='<filename>', source=""):
diff --git a/pypy/rlib/streamio.py b/pypy/rlib/streamio.py
--- a/pypy/rlib/streamio.py
+++ b/pypy/rlib/streamio.py
@@ -496,29 +496,24 @@
         if bufsize == -1:     # Get default from the class
             bufsize = self.bufsize
         self.bufsize = bufsize  # buffer size (hint only)
-        self.lines = []         # ready-made lines (sans "\n")
-        self.buf = ""           # raw data (may contain "\n")
-        # Invariant: readahead == "\n".join(self.lines + [self.buf])
-        # self.lines contains no "\n"
-        # self.buf may contain "\n"
+        self.buf = ""           # raw data
+        self.pos = 0
 
     def flush_buffers(self):
-        if self.lines or self.buf:
+        if self.buf:
             try:
                 self.do_seek(self.tell(), 0)
             except MyNotImplementedError:
                 pass
             else:
-                self.lines = []
                 self.buf = ""
+                self.pos = 0
 
     def tell(self):
-        bytes = self.do_tell()  # This may fail
-        offset = len(self.buf)
-        for line in self.lines:
-            offset += len(line) + 1
-        assert bytes >= offset #, (locals(), self.__dict__)
-        return bytes - offset
+        tellpos = self.do_tell()  # This may fail
+        offset = len(self.buf) - self.pos
+        assert tellpos >= offset #, (locals(), self.__dict__)
+        return tellpos - offset
 
     def seek(self, offset, whence):
         # This may fail on the do_seek() or do_tell() call.
@@ -526,32 +521,25 @@
         # Nor on a seek to the very end.
         if whence == 0:
             self.do_seek(offset, 0)
-            self.lines = []
             self.buf = ""
+            self.pos = 0
             return
         if whence == 1:
+            currentsize = len(self.buf) - self.pos
             if offset < 0:
-                self.do_seek(self.tell() + offset, 0)
-                self.lines = []
-                self.buf = ""
+                if self.pos + offset >= 0:
+                    self.pos += offset
+                else:
+                    self.do_seek(self.tell() + offset, 0)
+                    self.pos = 0
+                    self.buf = ""
                 return
-            while self.lines:
-                line = self.lines[-1]
-                if offset <= len(line):
-                    intoffset = intmask(offset)
-                    assert intoffset >= 0
-                    self.lines[-1] = line[intoffset:]
-                    return
-                offset -= len(self.lines[-1]) - 1
-                self.lines.pop()
-            assert not self.lines
-            if offset <= len(self.buf):
-                intoffset = intmask(offset)
-                assert intoffset >= 0
-                self.buf = self.buf[intoffset:]
+            elif offset <= currentsize:
+                self.pos += offset
                 return
-            offset -= len(self.buf)
             self.buf = ""
+            self.pos = 0
+            offset -= currentsize
             try:
                 self.do_seek(offset, 1)
             except MyNotImplementedError:
@@ -564,18 +552,18 @@
             except MyNotImplementedError:
                 pass
             else:
-                self.lines = []
+                self.pos = 0
                 self.buf = ""
                 return
             # Skip relative to EOF by reading and saving only just as
             # much as needed
             intoffset = offset2int(offset)
-            self.lines.reverse()
-            data = "\n".join(self.lines + [self.buf])
-            total = len(data)
-            buffers = [data]
-            self.lines = []
+            pos = self.pos
+            assert pos >= 0
+            buffers = [self.buf[pos:]]
+            total = len(buffers[0])
             self.buf = ""
+            self.pos = 0
             while 1:
                 data = self.do_read(self.bufsize)
                 if not data:
@@ -589,157 +577,101 @@
             if cutoff < 0:
                 raise StreamError("cannot seek back")
             if buffers:
+                assert cutoff >= 0
                 buffers[0] = buffers[0][cutoff:]
             self.buf = "".join(buffers)
-            self.lines = []
             return
+
         raise StreamError("whence should be 0, 1 or 2")
 
     def readall(self):
-        self.lines.reverse()
-        self.lines.append(self.buf)
-        more = ["\n".join(self.lines)]
-        self.lines = []
+        pos = self.pos
+        assert pos >= 0
+        chunks = [self.buf[pos:]]
         self.buf = ""
+        self.pos = 0
         bufsize = self.bufsize
         while 1:
             data = self.do_read(bufsize)
             if not data:
                 break
-            more.append(data)
+            chunks.append(data)
             bufsize = min(bufsize*2, self.bigsize)
-        return "".join(more)
+        return "".join(chunks)
 
-    def read(self, n):
+    def read(self, n=-1):
         assert isinstance(n, int)
-        assert n >= 0
-        if self.lines:
-            # See if this can be satisfied from self.lines[0]
-            line = self.lines[-1]
-            if len(line) >= n:
-                self.lines[-1] = line[n:]
-                return line[:n]
-
-            # See if this can be satisfied *without exhausting* self.lines
-            k = 0
-            i = 0
-            lgt = len(self.lines)
-            for linenum in range(lgt-1,-1,-1):
-                line = self.lines[linenum]
-                k += len(line)
-                if k >= n:
-                    lines = self.lines[linenum + 1:]
-                    data = self.lines[linenum]
-                    cutoff = len(data) - (k-n)
-                    assert cutoff >= 0
-                    lines.reverse()
-                    lines.append(data[:cutoff])
-                    del self.lines[linenum:]
-                    self.lines.append(data[cutoff:])
-                    return "\n".join(lines)
-                k += 1
-
-            # See if this can be satisfied from self.lines plus self.buf
-            if k + len(self.buf) >= n:
-                lines = self.lines
-                lines.reverse()
-                self.lines = []
-                cutoff = n - k
-                assert cutoff >= 0
-                lines.append(self.buf[:cutoff])
-                self.buf = self.buf[cutoff:]
-                return "\n".join(lines)
-
+        if n < 0:
+            return self.readall()
+        currentsize = len(self.buf) - self.pos
+        start = self.pos
+        assert start >= 0
+        if n <= currentsize:
+            stop = start + n
+            assert stop >= 0
+            result = self.buf[start:stop]
+            self.pos += n
+            return result
         else:
-            # See if this can be satisfied from self.buf
-            data = self.buf
-            k = len(data)
-            if k >= n:
-                cutoff = len(data) - (k-n)
-                assert cutoff >= 0
-                assert len(data) >= cutoff
-                self.buf = data[cutoff:]
-                return data[:cutoff]
-
-        lines = self.lines
-        lines.reverse()
-        self.lines = []
-        lines.append(self.buf)
-        self.buf = ""
-        data = "\n".join(lines)
-        more = [data]
-        k = len(data)
-        while k < n:
-            data = self.do_read(max(self.bufsize, n-k))
-            k += len(data)
-            more.append(data)
-            if not data:
-                break
-        cutoff = len(data) - (k-n)
-        assert cutoff >= 0
-        if len(data) <= cutoff:
-            self.buf = ""
-        else:
-            self.buf = data[cutoff:]
-            more[-1] = data[:cutoff]
-        return "".join(more)
-
-    # read_next_bunch is generally this, version below is slightly faster
-    #def _read_next_bunch(self):
-    #    self.lines = self.buf.split("\n")
-    #    self.buf = self.lines.pop()
-    #    self.lines.reverse()
-
-    def _read_next_bunch(self):
-        numlines = self.buf.count("\n")
-        self.lines = [None] * numlines
-        last = -1
-        num = numlines - 1
-        while True:
-            start = last + 1
-            assert start >= 0
-            next = self.buf.find("\n", start)
-            if next == -1:
-                if last != -1:
-                    self.buf = self.buf[start:]
-                break
-            assert next >= 0
-            self.lines[num] = self.buf[start:next]
-            last = next
-            num -= 1
+            chunks = [self.buf[start:]]
+            while 1:
+                self.buf = self.do_read(self.bufsize)
+                if not self.buf:
+                    self.pos = 0
+                    break
+                currentsize += len(self.buf)
+                if currentsize >= n:
+                    self.pos = len(self.buf) - (currentsize - n)
+                    stop = self.pos
+                    assert stop >= 0
+                    chunks.append(self.buf[:stop])
+                    break
+                chunks.append(self.buf)
+            return ''.join(chunks)
 
     def readline(self):
-        if self.lines:
-            return self.lines.pop() + "\n"
-
-        # This block is needed because read() can leave self.buf
-        # containing newlines
-        self._read_next_bunch()
-        if self.lines:
-            return self.lines.pop() + "\n"
-
-        if self.buf:
-            buf = [self.buf]
-        else:
-            buf = []
+        pos = self.pos
+        assert pos >= 0
+        i = self.buf.find("\n", pos)
+        start = self.pos
+        assert start >= 0
+        if i >= 0: # new line found
+            i += 1
+            result = self.buf[start:i]
+            self.pos = i
+            return result
+        temp = self.buf[start:]
+        # read one buffer and most of the time a new line will be found
+        self.buf = self.do_read(self.bufsize)
+        i = self.buf.find("\n")
+        if i >= 0: # new line found
+            i += 1
+            result = temp + self.buf[:i]
+            self.pos = i
+            return result
+        if not self.buf:
+            self.pos = 0
+            return temp
+        # need to keep getting data until we find a new line
+        chunks = [temp, self.buf]
         while 1:
             self.buf = self.do_read(self.bufsize)
-            self._read_next_bunch()
-            if self.lines:
-                buf.append(self.lines.pop())
-                buf.append("\n")
+            if not self.buf:
+                self.pos = 0
                 break
-            if not self.buf:
+            i = self.buf.find("\n")
+            if i >= 0:
+                i += 1
+                chunks.append(self.buf[:i])
+                self.pos = i
                 break
-            buf.append(self.buf)
-
-        return "".join(buf)
+            chunks.append(self.buf)
+        return "".join(chunks)
 
     def peek(self):
-        if self.lines:
-            return self.lines[-1] + "\n"
-        else:
-            return self.buf
+        pos = self.pos
+        assert pos >= 0
+        return self.buf[pos:]
 
     write      = PassThrough("write",     flush_buffers=True)
     truncate   = PassThrough("truncate",  flush_buffers=True)
diff --git a/pypy/rpython/extfuncregistry.py b/pypy/rpython/extfuncregistry.py
--- a/pypy/rpython/extfuncregistry.py
+++ b/pypy/rpython/extfuncregistry.py
@@ -44,6 +44,13 @@
        ('log10', [float], float),
        ('sin', [float], float),
        ('cos', [float], float),
+       ('atan2', [float, float], float),
+       ('hypot', [float, float], float),
+       ('frexp', [float], (float, int)),
+       ('ldexp', [float, int], float),
+       ('modf', [float], (float, float)),
+       ('fmod', [float, float], float),
+       ('pow', [float, float], float),
     ]),
 ]
 for module, methods in _register:
@@ -54,23 +61,6 @@
                           sandboxsafe=True,
                           llimpl=getattr(ll_math, method_name))
 
-
-complex_math_functions = [
-    ('frexp', [float],        (float, int)),
-    ('ldexp', [float, int],   float),
-    ('modf',  [float],        (float, float)),
-    ] + [(name, [float, float], float)
-         for name in 'atan2', 'fmod', 'hypot', 'pow']
-
-for name, args, res in complex_math_functions:
-    func = getattr(math, name)
-    llimpl = getattr(ll_math, 'll_math_%s' % name, None)
-    oofake = getattr(oo_math, 'll_math_%s' % name, None)
-    register_external(func, args, res, 'll_math.ll_math_%s' % name,
-                      llimpl=llimpl, oofakeimpl=oofake,
-                      sandboxsafe=True)
-
-
 # ___________________________
 # os.path functions
 
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
@@ -223,22 +223,13 @@
 
 
 def ll_math_fmod(x, y):
-    if isinf(y):
-        if isinf(x):
-            raise ValueError("math domain error")
-        return x  # fmod(x, +/-Inf) returns x for finite x (or if x is a NaN).
+    if isinf(x) and not isnan(y):
+        raise ValueError("math domain error")
 
-    _error_reset()
-    r = math_fmod(x, y)
-    errno = rposix.get_errno()
-    if isnan(r):
-        if isnan(x) or isnan(y):
-            errno = 0
-        else:
-            errno = EDOM
-    if errno:
-        _likely_raise(errno, r)
-    return r
+    if y == 0:
+        raise ValueError("math domain error")
+
+    return math_fmod(x, y)
 
 
 def ll_math_hypot(x, y):
diff --git a/pypy/rpython/memory/gc/env.py b/pypy/rpython/memory/gc/env.py
--- a/pypy/rpython/memory/gc/env.py
+++ b/pypy/rpython/memory/gc/env.py
@@ -116,6 +116,10 @@
     def get_total_memory():
         return get_total_memory_darwin(get_darwin_sysctl_signed('hw.memsize'))
 
+elif sys.platform.startswith('freebsd'):
+    def get_total_memory():
+        return get_total_memory_darwin(get_darwin_sysctl_signed('hw.usermem'))
+
 else:
     def get_total_memory():
         return addressable_size       # XXX implement me for other platforms
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
@@ -174,6 +174,8 @@
         return self.code is not None
 
     def getopcode(self):
+        if self.code is None:
+            return None
         return self.code.map[self.bytecode_no]
 
     def getlineno(self):
diff --git a/pypy/tool/logparser.py b/pypy/tool/logparser.py
--- a/pypy/tool/logparser.py
+++ b/pypy/tool/logparser.py
@@ -86,9 +86,11 @@
     for entry in log:
         if entry[0] == 'debug_print':
             resulttext.append(entry[1])
-        else:
+        elif len(entry) == 4:
             got.extend(extract_category(
                 entry[3], catprefix, toplevel=entry[0].startswith(catprefix)))
+        else:
+            resulttext.append('... LOG TRUCATED ...')
     if toplevel:
         resulttext.append('')
         got.insert(0, '\n'.join(resulttext))
diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py
--- a/pypy/tool/release/package.py
+++ b/pypy/tool/release/package.py
@@ -133,6 +133,8 @@
     if copy_to_dir is not None:
         print "Copying %s to %s" % (archive, copy_to_dir)
         shutil.copy(archive, str(copy_to_dir))
+    else:
+        print "Ready in %s" % (builddir,)
     return builddir # for tests
 
 if __name__ == '__main__':
diff --git a/pypy/translator/sandbox/test/test_sandbox.py b/pypy/translator/sandbox/test/test_sandbox.py
--- a/pypy/translator/sandbox/test/test_sandbox.py
+++ b/pypy/translator/sandbox/test/test_sandbox.py
@@ -156,6 +156,60 @@
     rescode = pipe.wait()
     assert rescode == 0
 
+def test_segfault_1():
+    class A:
+        def __init__(self, m):
+            self.m = m
+    def g(m):
+        if m < 10:
+            return None
+        return A(m)
+    def entry_point(argv):
+        x = g(len(argv))
+        return int(x.m)
+
+    exe = compile(entry_point)
+    g, f, e = os.popen3(exe, "t", 0)
+    g.close()
+    tail = f.read()
+    f.close()
+    assert tail == ""
+    errors = e.read()
+    e.close()
+    assert 'Invalid RPython operation' in errors
+
+def test_segfault_2():
+    py.test.skip("hum, this is one example, but we need to be very careful")
+    class Base:
+        pass
+    class A(Base):
+        def __init__(self, m):
+            self.m = m
+        def getm(self):
+            return self.m
+    class B(Base):
+        def __init__(self, a):
+            self.a = a
+    def g(m):
+        a = A(m)
+        if m < 10:
+            a = B(a)
+        return a
+    def entry_point(argv):
+        x = g(len(argv))
+        os.write(2, str(x.getm()))
+        return 0
+
+    exe = compile(entry_point)
+    g, f, e = os.popen3(exe, "t", 0)
+    g.close()
+    tail = f.read(23)
+    f.close()
+    assert tail == ""    # and not ll_os.ll_os_write
+    errors = e.read()
+    e.close()
+    assert '...think what kind of errors to get...' in errors
+
 def test_safe_alloc():
     from pypy.rlib.rmmap import alloc, free
 


More information about the pypy-commit mailing list