[pypy-commit] pypy reflex-support: merge default into branch

wlav noreply at buildbot.pypy.org
Thu Aug 25 01:03:18 CEST 2011


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r46760:3f55cea6c97e
Date: 2011-08-18 21:11 -0700
http://bitbucket.org/pypy/pypy/changeset/3f55cea6c97e/

Log:	merge default into branch

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/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/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"''
@@ -75,14 +75,14 @@
 
     $ curl -O https://github.com/pypa/pip/raw/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/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -52,6 +52,9 @@
     escaped                  = False  # see mark_as_escaped()
 
     def __init__(self, space, code, w_globals, closure):
+        if not we_are_translated():
+            assert type(self) in (space.FrameClass, CPythonFrame), (
+                "use space.FrameClass(), not directly PyFrame()")
         self = hint(self, access_directly=True, fresh_virtualizable=True)
         assert isinstance(code, pycode.PyCode)
         self.pycode = code
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
@@ -74,10 +74,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[:]
@@ -225,12 +266,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:
@@ -333,7 +372,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)],
@@ -350,6 +389,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)
@@ -365,7 +405,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),
@@ -377,6 +417,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')
@@ -317,6 +320,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,325 +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()
-            if effectinfo is not None:
-                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 effectinfo is not None:
-                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
@@ -662,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
@@ -703,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,7 +798,7 @@
                 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
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
@@ -323,6 +323,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
@@ -63,7 +63,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:
@@ -87,6 +87,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_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/_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/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -57,7 +57,7 @@
     code = space.interp_w(PyCode, w_code)
     w_globals = from_ref(space, py_frame.c_f_globals)
 
-    frame = PyFrame(space, code, w_globals, closure=None)
+    frame = space.FrameClass(space, code, w_globals, closure=None)
     frame.f_lineno = py_frame.c_f_lineno
     w_obj = space.wrap(frame)
     track_reference(space, py_obj, w_obj)
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/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_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/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/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/pytest/test/test_pytestsupport.py b/pypy/tool/pytest/test/test_pytestsupport.py
--- a/pypy/tool/pytest/test/test_pytestsupport.py
+++ b/pypy/tool/pytest/test/test_pytestsupport.py
@@ -2,7 +2,6 @@
 from pypy.interpreter.gateway import app2interp_temp
 from pypy.interpreter.argument import Arguments
 from pypy.interpreter.pycode import PyCode
-from pypy.interpreter.pyframe import PyFrame
 from pypy.tool.pytest.appsupport import (AppFrame, build_pytest_assertion,
     AppExceptionInfo, interpret)
 import py
@@ -20,7 +19,7 @@
 def test_AppFrame(space):
     import sys
     co = PyCode._from_code(space, somefunc.func_code)
-    pyframe = PyFrame(space, co, space.newdict(), None)
+    pyframe = space.FrameClass(space, co, space.newdict(), None)
     runner = AppFrame(space, pyframe)
     interpret("f = lambda x: x+1", runner, should_fail=False)
     msg = interpret("assert isinstance(f(2), float)", runner)
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