[pypy-svn] pypy jitypes2: try again to merge the default branch

antocuni commits-noreply at bitbucket.org
Tue Jan 11 10:39:33 CET 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: jitypes2
Changeset: r40573:2023b81e9de4
Date: 2011-01-11 10:14 +0100
http://bitbucket.org/pypy/pypy/changeset/2023b81e9de4/

Log:	try again to merge the default branch

diff --git a/pypy/jit/metainterp/specnode.py b/pypy/jit/metainterp/specnode.py
deleted file mode 100644
--- a/pypy/jit/metainterp/specnode.py
+++ /dev/null
@@ -1,127 +0,0 @@
-from pypy.tool.pairtype import extendabletype
-from pypy.jit.metainterp.history import Const
-
-
-class SpecNode(object):
-    __metaclass__ = extendabletype     # extended in optimizefindnode.py
-    __slots__ = ()
-
-    def equals(self, other, ge):     # 'ge' stands for greater-or-equal;
-        raise NotImplementedError    # if false, the default is 'equal'.
- 
-    def extract_runtime_data(self, cpu, valuebox, resultlist):
-        raise NotImplementedError
-
-
-class NotSpecNode(SpecNode):
-    __slots__ = ()
-
-    def equals(self, other, ge):
-        return isinstance(other, NotSpecNode) or ge
-
-    def extract_runtime_data(self, cpu, valuebox, resultlist):
-        resultlist.append(valuebox)
-
-
-prebuiltNotSpecNode = NotSpecNode()
-
-
-class ConstantSpecNode(SpecNode):
-    def __init__(self, constbox):
-        assert isinstance(constbox, Const)
-        self.constbox = constbox
-
-    def equals(self, other, ge):
-        return isinstance(other, ConstantSpecNode) and \
-               self.constbox.same_constant(other.constbox)
-
-    def extract_runtime_data(self, cpu, valuebox, resultlist):
-        pass
-
-
-class AbstractVirtualStructSpecNode(SpecNode):
-    def __init__(self, fields):
-        self.fields = fields    # list: [(fieldofs, subspecnode)]
-
-    def equal_fields(self, other, ge):
-        if len(self.fields) != len(other.fields):
-            return False
-        for i in range(len(self.fields)):
-            o1, s1 = self.fields[i]
-            o2, s2 = other.fields[i]
-            if not (o1 is o2 and s1.equals(s2, ge)):
-                return False
-        return True
-
-    def extract_runtime_data(self, cpu, valuebox, resultlist):
-        from pypy.jit.metainterp import executor, history, resoperation
-        for ofs, subspecnode in self.fields:
-            assert isinstance(ofs, history.AbstractDescr)
-            fieldbox = executor.execute(cpu, None,
-                                        resoperation.rop.GETFIELD_GC,
-                                        ofs, valuebox)
-            subspecnode.extract_runtime_data(cpu, fieldbox, resultlist)
-
-
-class VirtualInstanceSpecNode(AbstractVirtualStructSpecNode):
-    def __init__(self, known_class, fields):
-        AbstractVirtualStructSpecNode.__init__(self, fields)
-        assert isinstance(known_class, Const)
-        self.known_class = known_class
-
-    def equals(self, other, ge):
-        if not (isinstance(other, VirtualInstanceSpecNode) and
-                self.known_class.same_constant(other.known_class)):
-            return False
-        return self.equal_fields(other, ge)
-
-
-class VirtualArraySpecNode(SpecNode):
-    def __init__(self, arraydescr, items):
-        self.arraydescr = arraydescr
-        self.items = items      # list of subspecnodes
-
-    def equals(self, other, ge):
-        if not (isinstance(other, VirtualArraySpecNode) and
-                len(self.items) == len(other.items)):
-            return False
-        assert self.arraydescr == other.arraydescr
-        for i in range(len(self.items)):
-            s1 = self.items[i]
-            s2 = other.items[i]
-            if not s1.equals(s2, ge):
-                return False
-        return True
-
-    def extract_runtime_data(self, cpu, valuebox, resultlist):
-        from pypy.jit.metainterp import executor, history, resoperation
-        for i in range(len(self.items)):
-            itembox = executor.execute(cpu, None,
-                                       resoperation.rop.GETARRAYITEM_GC,
-                                       self.arraydescr,
-                                       valuebox, history.ConstInt(i))
-            subspecnode = self.items[i]
-            subspecnode.extract_runtime_data(cpu, itembox, resultlist)
-
-
-class VirtualStructSpecNode(AbstractVirtualStructSpecNode):
-    def __init__(self, typedescr, fields):
-        AbstractVirtualStructSpecNode.__init__(self, fields)
-        self.typedescr = typedescr
-
-    def equals(self, other, ge):
-        if not isinstance(other, VirtualStructSpecNode):
-            return False
-        assert self.typedescr == other.typedescr
-        return self.equal_fields(other, ge)
-
-
-def equals_specnodes(specnodes1, specnodes2, ge=False):
-    assert len(specnodes1) == len(specnodes2)
-    for i in range(len(specnodes1)):
-        if not specnodes1[i].equals(specnodes2[i], ge):
-            return False
-    return True
-
-def more_general_specnodes(specnodes1, specnodes2):
-    return equals_specnodes(specnodes1, specnodes2, ge=True)


diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -105,14 +105,11 @@
         make_sure_not_resized(self.arguments_w)
         if w_stararg is not None:
             self._combine_starargs_wrapped(w_stararg)
-        if w_starstararg is not None:
-            self._combine_starstarargs_wrapped(w_starstararg)
-            # if we have a call where **args are used at the callsite
-            # we shouldn't let the JIT see the argument matching
-            self._dont_jit = True
-        else:
-            self._dont_jit = False
-        
+        # if we have a call where **args are used at the callsite
+        # we shouldn't let the JIT see the argument matching
+        self._dont_jit = (w_starstararg is not None and
+                          self._combine_starstarargs_wrapped(w_starstararg))
+
     def __repr__(self):
         """ NOT_RPYTHON """
         name = self.__class__.__name__
@@ -160,10 +157,20 @@
             raise OperationError(space.w_TypeError,
                                  space.wrap("argument after ** must be "
                                             "a dictionary"))
-        keywords_w = [None] * space.int_w(space.len(w_starstararg))
-        keywords = [None] * space.int_w(space.len(w_starstararg))
+        if space.is_true(w_starstararg):
+            self._do_combine_starstarargs_wrapped(w_starstararg)
+            return True
+        else:
+            return False    # empty dict; don't disable the JIT
+
+    def _do_combine_starstarargs_wrapped(self, w_starstararg):
+        space = self.space
+        keys_w = space.unpackiterable(w_starstararg)
+        length = len(keys_w)
+        keywords_w = [None] * length
+        keywords = [None] * length
         i = 0
-        for w_key in space.unpackiterable(w_starstararg):
+        for w_key in keys_w:
             try:
                 key = space.str_w(w_key)
             except OperationError, e:



diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -939,7 +939,9 @@
     if os.sep not in path:
         path = os.curdir + os.sep + path      # force a '/' in the path
     state = space.fromcache(State)
-    state.package_context = name
+    if state.find_extension(name, path) is not None:
+        return
+    state.package_context = name, path
     try:
         from pypy.rlib import rdynload
         try:
@@ -964,7 +966,8 @@
         generic_cpy_call(space, initfunc)
         state.check_and_raise_exception()
     finally:
-        state.package_context = None
+        state.package_context = None, None
+    state.fixup_extension(name, path)
 
 @specialize.ll()
 def generic_cpy_call(space, func, *args):


diff --git a/pypy/jit/tool/test/test_jitoutput.py b/pypy/jit/tool/test/test_jitoutput.py
--- a/pypy/jit/tool/test/test_jitoutput.py
+++ b/pypy/jit/tool/test/test_jitoutput.py
@@ -36,13 +36,13 @@
     assert info.tracing_no == 1
     assert info.asm_no == 1
     assert info.blackhole_no == 1
-    assert info.backend_no == 1
+    assert info.backend_no == 2
     assert info.ops.total == 2
     assert info.recorded_ops.total == 2
     assert info.recorded_ops.calls == 0
     assert info.guards == 1
-    assert info.opt_ops == 6
-    assert info.opt_guards == 1
+    assert info.opt_ops == 11
+    assert info.opt_guards == 2
     assert info.forcings == 0
 
 DATA = '''Tracing:         1       0.006992
@@ -60,6 +60,7 @@
 abort: trace too long:  10
 abort: compiling:       11
 abort: vable escape:    12
+abort: bad loop:        135
 nvirtuals:              13
 nvholes:                14
 nvreused:               15
@@ -87,6 +88,7 @@
     assert info.abort.trace_too_long == 10
     assert info.abort.compiling == 11
     assert info.abort.vable_escape == 12
+    assert info.abort.bad_loop == 135
     assert info.nvirtuals == 13
     assert info.nvholes == 14
     assert info.nvreused == 15

diff --git a/pypy/jit/metainterp/test/test_loop_dummy.py b/pypy/jit/metainterp/test/test_loop_dummy.py
deleted file mode 100644
--- a/pypy/jit/metainterp/test/test_loop_dummy.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# xxx mostly pointless
-
-from pypy.jit.metainterp.test import test_loop, test_send
-from pypy.jit.metainterp.warmspot import ll_meta_interp
-from pypy.rlib.jit import OPTIMIZER_SIMPLE
-from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
-
-class LoopDummyTest(test_send.SendTests):
-    def meta_interp(self, func, args, **kwds):
-        return ll_meta_interp(func, args, optimizer=OPTIMIZER_SIMPLE,
-                              CPUClass=self.CPUClass, 
-                              type_system=self.type_system,
-                              **kwds)
-
-    def check_loops(self, *args, **kwds):
-        pass
-
-    def check_loop_count(self, count):
-        pass
-
-    def check_jumps(self, maxcount):
-        pass
-
-class TestLLtype(LoopDummyTest, LLJitMixin):
-    pass
-
-class TestOOtype(LoopDummyTest, OOJitMixin):
-    pass

diff --git a/pypy/jit/metainterp/test/test_optimizeopt.py b/pypy/jit/metainterp/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/test/test_optimizeopt.py
@@ -1,26 +1,19 @@
 import py
 from pypy.rlib.objectmodel import instantiate
-from pypy.jit.metainterp.test.test_optimizefindnode import (LLtypeMixin,
-                                                            #OOtypeMixin,
-                                                            BaseTest)
-from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder
+from pypy.jit.metainterp.test.test_optimizeutil import (LLtypeMixin,
+                                                        #OOtypeMixin,
+                                                        BaseTest)
 import pypy.jit.metainterp.optimizeopt.optimizer as optimizeopt
 import pypy.jit.metainterp.optimizeopt.virtualize as virtualize
 from pypy.jit.metainterp.optimizeopt import optimize_loop_1
 from pypy.jit.metainterp.optimizeutil import InvalidLoop
 from pypy.jit.metainterp.history import AbstractDescr, ConstInt, BoxInt
+from pypy.jit.metainterp.history import TreeLoop, LoopToken
 from pypy.jit.metainterp.jitprof import EmptyProfiler
 from pypy.jit.metainterp import executor, compile, resume, history
 from pypy.jit.metainterp.resoperation import rop, opname, ResOperation
 from pypy.jit.tool.oparser import pure_parse
-
-##class FakeFrame(object):
-##    parent_resumedata_snapshot = None
-##    parent_resumedata_frame_info_list = None
-
-##    def __init__(self, code="", pc=0):
-##        self.jitcode = code
-##        self.pc = pc
+from pypy.jit.metainterp.test.test_optimizebasic import equaloplists
 
 class Fake(object):
     failargs_limit = 1000
@@ -129,90 +122,7 @@
 
     assert not optimizeutil.descrlist_eq([FakeDescr()], [FakeDescr()])
 
-
 # ____________________________________________________________
-
-def equaloplists(oplist1, oplist2, strict_fail_args=True, remap={}):
-    # try to use the full width of the terminal to display the list
-    # unfortunately, does not work with the default capture method of py.test
-    # (which is fd), you you need to use either -s or --capture=sys, else you
-    # get the standard 80 columns width
-    totwidth = py.io.get_terminal_width()
-    width = totwidth / 2 - 1
-    print ' Comparing lists '.center(totwidth, '-')
-    print '%s| %s' % ('optimized'.center(width), 'expected'.center(width))
-    for op1, op2 in zip(oplist1, oplist2):
-        txt1 = str(op1)
-        txt2 = str(op2)
-        while txt1 or txt2:
-            print '%s| %s' % (txt1[:width].ljust(width), txt2[:width])
-            txt1 = txt1[width:]
-            txt2 = txt2[width:]
-        assert op1.getopnum() == op2.getopnum()
-        assert op1.numargs() == op2.numargs()
-        for i in range(op1.numargs()):
-            x = op1.getarg(i)
-            y = op2.getarg(i)
-            assert x == remap.get(y, y)
-        if op2.result in remap:
-            assert op1.result == remap[op2.result]
-        else:
-            remap[op2.result] = op1.result
-        if op1.getopnum() != rop.JUMP:      # xxx obscure
-            assert op1.getdescr() == op2.getdescr()
-        if op1.getfailargs() or op2.getfailargs():
-            assert len(op1.getfailargs()) == len(op2.getfailargs())
-            if strict_fail_args:
-                for x, y in zip(op1.getfailargs(), op2.getfailargs()):
-                    assert x == remap.get(y, y)
-            else:
-                fail_args1 = set(op1.getfailargs())
-                fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()])
-                assert fail_args1 == fail_args2
-    assert len(oplist1) == len(oplist2)
-    print '-'*57
-    return True
-
-def test_equaloplists():
-    ops = """
-    [i0]
-    i1 = int_add(i0, 1)
-    i2 = int_add(i1, 1)
-    guard_true(i1) [i2]
-    jump(i1)
-    """
-    namespace = {}
-    loop1 = pure_parse(ops, namespace=namespace)
-    loop2 = pure_parse(ops, namespace=namespace)
-    loop3 = pure_parse(ops.replace("i2 = int_add", "i2 = int_sub"),
-                       namespace=namespace)
-    assert equaloplists(loop1.operations, loop2.operations)
-    py.test.raises(AssertionError,
-                   "equaloplists(loop1.operations, loop3.operations)")
-
-def test_equaloplists_fail_args():
-    ops = """
-    [i0]
-    i1 = int_add(i0, 1)
-    i2 = int_add(i1, 1)
-    guard_true(i1) [i2, i1]
-    jump(i1)
-    """
-    namespace = {}
-    loop1 = pure_parse(ops, namespace=namespace)
-    loop2 = pure_parse(ops.replace("[i2, i1]", "[i1, i2]"),
-                       namespace=namespace)
-    py.test.raises(AssertionError,
-                   "equaloplists(loop1.operations, loop2.operations)")
-    assert equaloplists(loop1.operations, loop2.operations,
-                        strict_fail_args=False)
-    loop3 = pure_parse(ops.replace("[i2, i1]", "[i2, i0]"),
-                       namespace=namespace)
-    py.test.raises(AssertionError,
-                   "equaloplists(loop1.operations, loop3.operations)")
-
-# ____________________________________________________________
-
 class Storage(compile.ResumeGuardDescr):
     "for tests."
     def __init__(self, metainterp_sd=None, original_greenkey=None):
@@ -222,6 +132,10 @@
         op.setfailargs(boxes)
     def __eq__(self, other):
         return type(self) is type(other)      # xxx obscure
+    def clone_if_mutable(self):
+        res = Storage(self.metainterp_sd, self.original_greenkey)
+        self.copy_all_attrbutes_into(res)
+        return res
 
 def _sortboxes(boxes):
     _kind2count = {history.INT: 1, history.REF: 2, history.FLOAT: 3}
@@ -238,31 +152,25 @@
         descr.rd_snapshot = resume.Snapshot(None, _sortboxes(fail_args))
         return descr
 
-    def assert_equal(self, optimized, expected):
+    def assert_equal(self, optimized, expected, text_right=None):
         assert len(optimized.inputargs) == len(expected.inputargs)
         remap = {}
         for box1, box2 in zip(optimized.inputargs, expected.inputargs):
             assert box1.__class__ == box2.__class__
             remap[box2] = box1
         assert equaloplists(optimized.operations,
-                            expected.operations, False, remap)
-
-    def optimize_loop(self, ops, spectext, optops, checkspecnodes=True):
+                            expected.operations, False, remap, text_right)
+
+    def optimize_loop(self, ops, optops, expected_preamble=None):
         loop = self.parse(ops)
-        #
-        if checkspecnodes:
-            # verify that 'spectext' is indeed what optimizefindnode would
-            # compute for this loop
-            cpu = self.cpu
-            perfect_specialization_finder = PerfectSpecializationFinder(cpu)
-            perfect_specialization_finder.find_nodes_loop(loop)
-            self.check_specnodes(loop.token.specnodes, spectext)
-        else:
-            # for cases where we want to see how optimizeopt behaves with
-            # combinations different from the one computed by optimizefindnode
-            loop.token.specnodes = self.unpack_specnodes(spectext)
+        expected = self.parse(optops)
+        if expected_preamble:
+            expected_preamble = self.parse(expected_preamble)
         #
         self.loop = loop
+        loop.preamble = TreeLoop('preamble')
+        loop.preamble.inputargs = loop.inputargs
+        loop.preamble.token = LoopToken()
         metainterp_sd = FakeMetaInterpStaticData(self.cpu, self.jit_ffi)
         if hasattr(self, 'vrefinfo'):
             metainterp_sd.virtualref_info = self.vrefinfo
@@ -270,28 +178,70 @@
             metainterp_sd.callinfocollection = self.callinfocollection
         optimize_loop_1(metainterp_sd, loop)
         #
-        expected = self.parse(optops)
+
+        print
+        print loop.preamble.inputargs
+        print '\n'.join([str(o) for o in loop.preamble.operations])
+        print 
+        print loop.inputargs
         print '\n'.join([str(o) for o in loop.operations])
+        print
+        
         self.assert_equal(loop, expected)
+        if expected_preamble:
+            self.assert_equal(loop.preamble, expected_preamble,
+                              text_right='expected preamble')
+
         return loop
 
-
 class OptimizeOptTest(BaseTestOptimizeOpt):
 
+    def setup_method(self, meth=None):
+        class FailDescr(compile.ResumeGuardDescr):
+            oparse = None
+            def _oparser_uses_descr_of_guard(self, oparse, fail_args):
+                # typically called 3 times: once when parsing 'ops',
+                # once when parsing 'preamble', once when parsing 'expected'.
+                self.oparse = oparse
+                self.rd_frame_info_list, self.rd_snapshot = snapshot(fail_args)
+            def _clone_if_mutable(self):
+                assert self is fdescr
+                return fdescr2
+            def __repr__(self):
+                if self is fdescr:
+                    return 'fdescr'
+                if self is fdescr2:
+                    return 'fdescr2'
+                return compile.ResumeGuardDescr.__repr__(self)
+        #
+        def snapshot(fail_args, got=[]):
+            if not got:    # only the first time, i.e. when parsing 'ops'
+                rd_frame_info_list = resume.FrameInfo(None, "code", 11)
+                rd_snapshot = resume.Snapshot(None, fail_args)
+                got.append(rd_frame_info_list)
+                got.append(rd_snapshot)
+            return got
+        #
+        fdescr = instantiate(FailDescr)
+        self.namespace['fdescr'] = fdescr
+        fdescr2 = instantiate(FailDescr)
+        self.namespace['fdescr2'] = fdescr2
+
+    def teardown_method(self, meth):
+        self.namespace.pop('fdescr', None)
+        self.namespace.pop('fdescr2', None)
+
+
     def test_simple(self):
         ops = """
-        [i]
-        i0 = int_sub(i, 1)
-        guard_value(i0, 0) [i0]
-        jump(i)
-        """
-        expected = """
-        [i]
-        i0 = int_sub(i, 1)
-        guard_value(i0, 0) [i0]
-        jump(1)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        []
+        f = escape()
+        f0 = float_sub(f, 1.0)
+        guard_value(f0, 0.0) [f0]
+        escape(f)
+        jump()
+        """
+        self.optimize_loop(ops, ops)
 
     def test_constant_propagate(self):
         ops = """
@@ -308,7 +258,7 @@
         []
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_constant_propagate_ovf(self):
         ops = """
@@ -326,7 +276,7 @@
         []
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_constfold_all(self):
         from pypy.jit.backend.llgraph.llimpl import TYPES     # xxx fish
@@ -360,7 +310,7 @@
             escape(%d)
             jump()
             """ % expected_value
-            self.optimize_loop(ops, '', expected)
+            self.optimize_loop(ops, expected)
 
     # ----------
 
@@ -371,12 +321,16 @@
         guard_class(p0, ConstClass(node_vtable)) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         guard_class(p0, ConstClass(node_vtable)) []
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, expected_preamble=preamble)
 
     def test_remove_guard_class_2(self):
         ops = """
@@ -392,7 +346,7 @@
         escape(p0)
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_remove_guard_class_constant(self):
         ops = """
@@ -405,7 +359,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_constant_boolrewrite_lt(self):
         ops = """
@@ -416,13 +370,17 @@
         guard_false(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, expected_preamble=preamble)
 
     def test_constant_boolrewrite_gt(self):
         ops = """
@@ -433,13 +391,17 @@
         guard_false(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_gt(i0, 0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, expected_preamble=preamble)
 
     def test_constant_boolrewrite_reflex(self):
         ops = """
@@ -450,13 +412,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_gt(i0, 0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, expected_preamble=preamble)
 
     def test_constant_boolrewrite_reflex_invers(self):
         ops = """
@@ -467,13 +433,17 @@
         guard_false(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_gt(i0, 0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, expected_preamble=preamble)
 
     def test_remove_consecutive_guard_value_constfold(self):
         ops = """
@@ -493,19 +463,19 @@
         escape(3)
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_remove_guard_value_if_constant(self):
         ops = """
         [p1]
         guard_value(p1, ConstPtr(myptr)) []
-        jump(ConstPtr(myptr))
+        jump(p1)
         """
         expected = """
         []
         jump()
         """
-        self.optimize_loop(ops, 'Constant(myptr)', expected)
+        self.optimize_loop(ops, expected)
 
     def test_ooisnull_oononnull_1(self):
         ops = """
@@ -514,12 +484,52 @@
         guard_nonnull(p0) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         guard_class(p0, ConstClass(node_vtable)) []
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_guard_nonnull_class_1(self):
+        ops = """
+        [p0]
+        guard_class(p0, ConstClass(node_vtable)) []
+        guard_nonnull(p0) []
+        guard_nonnull_class(p0, ConstClass(node_vtable)) []
+        jump(p0)
+        """
+        preamble = """
+        [p0]
+        guard_class(p0, ConstClass(node_vtable)) []
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_guard_nonnull_class_2(self):
+        ops = """
+        [p0]
+        guard_nonnull_class(p0, ConstClass(node_vtable)) []
+        jump(p0)
+        """
+        preamble = """
+        [p0]
+        guard_nonnull_class(p0, ConstClass(node_vtable)) []
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_int_is_true_1(self):
         ops = """
@@ -530,13 +540,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_is_true(i0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_int_is_true_is_zero(self):
         py.test.skip("XXX implement me")
@@ -554,7 +568,7 @@
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_ooisnull_oononnull_2(self):
         ops = """
@@ -563,12 +577,16 @@
         guard_nonnull(p0) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         guard_nonnull(p0) []
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_ooisnull_on_null_ptr_1(self):
         ops = """
@@ -584,7 +602,7 @@
         guard_isnull(p0) []
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_ooisnull_oononnull_via_virtual(self):
         ops = """
@@ -596,12 +614,16 @@
         guard_nonnull(p1) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         guard_nonnull(p0) []
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_oois_1(self):
         ops = """
@@ -617,12 +639,16 @@
         guard_false(i1) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         guard_class(p0, ConstClass(node_vtable)) []
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_nonnull_1(self):
         ops = """
@@ -644,7 +670,7 @@
         setfield_gc(p0, 5, descr=valuedescr)
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_const_guard_value(self):
         ops = """
@@ -657,7 +683,7 @@
         []
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_constptr_guard_value(self):
         ops = """
@@ -666,7 +692,7 @@
         guard_value(p1, ConstPtr(myptr)) []
         jump()
         """
-        self.optimize_loop(ops, '', ops)
+        self.optimize_loop(ops, ops)
 
     def test_guard_value_to_guard_true(self):
         ops = """
@@ -675,13 +701,17 @@
         guard_value(i1, 1) [i]
         jump(i)
         """
-        expected = """
+        preamble = """
         [i]
         i1 = int_lt(i, 3)
         guard_true(i1) [i]
         jump(i)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i]
+        jump(i)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_guard_value_to_guard_false(self):
         ops = """
@@ -690,13 +720,17 @@
         guard_value(i1, 0) [i]
         jump(i)
         """
-        expected = """
+        preamble = """
         [i]
         i1 = int_is_true(i)
         guard_false(i1) [i]
         jump(i)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i]
+        jump(i)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_guard_value_on_nonbool(self):
         ops = """
@@ -705,13 +739,17 @@
         guard_value(i1, 0) [i]
         jump(i)
         """
-        expected = """
+        preamble = """
         [i]
         i1 = int_add(i, 3)
         guard_value(i1, 0) [i]
-        jump(-3)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_int_is_true_of_bool(self):
         ops = """
@@ -722,13 +760,17 @@
         guard_value(i4, 0) [i0, i1]
         jump(i0, i1)
         """
-        expected = """
+        preamble = """
         [i0, i1]
         i2 = int_gt(i0, i1)
         guard_false(i2) [i0, i1]
         jump(i0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        expected = """
+        [i0, i1]
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
 
 
@@ -742,8 +784,22 @@
         setfield_gc(p1, i1, descr=valuedescr)
         jump(i1, p1, p2)
         """
+        preamble = """
+        [i1, p2, p3]
+        i3 = getfield_gc(p3, descr=valuedescr)
+        escape(i3)
+        jump(i1, p2)
+        """
+        expected = """
+        [i1, p2]
+        i3 = getfield_gc(p2, descr=valuedescr)
+        escape(i3)
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3, i1, descr=valuedescr)        
+        jump(i1, p3)
+        """
         # We cannot track virtuals that survive for more than two iterations.
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_p123_nested(self):
         ops = """
@@ -759,7 +815,24 @@
         """
         # The same as test_p123_simple, but with a virtual containing another
         # virtual.
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        preamble = """
+        [i1, p2, p3]
+        i3 = getfield_gc(p3, descr=valuedescr)
+        escape(i3)
+        jump(i1, p2)
+        """
+        expected = """
+        [i1, p2]
+        i3 = getfield_gc(p2, descr=valuedescr)
+        escape(i3)
+        p4 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p4, i1, descr=valuedescr)
+        p1sub = new_with_vtable(ConstClass(node_vtable2))
+        setfield_gc(p1sub, i1, descr=valuedescr)
+        setfield_gc(p4, p1sub, descr=nextdescr)
+        jump(i1, p4)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_p123_anti_nested(self):
         ops = """
@@ -775,7 +848,58 @@
         """
         # The same as test_p123_simple, but in the end the "old" p2 contains
         # a "young" virtual p2sub.  Make sure it is all forced.
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        preamble = """
+        [i1, p2, p3]
+        p3sub = getfield_gc(p3, descr=nextdescr)
+        i3 = getfield_gc(p3sub, descr=valuedescr)
+        escape(i3)
+        p2sub = new_with_vtable(ConstClass(node_vtable2))
+        setfield_gc(p2sub, i1, descr=valuedescr)
+        setfield_gc(p2, p2sub, descr=nextdescr)        
+        jump(i1, p2, p2sub)
+        """
+        expected = """
+        [i1, p2, p2sub]
+        i3 = getfield_gc(p2sub, descr=valuedescr)
+        escape(i3)
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p3sub = new_with_vtable(ConstClass(node_vtable2))
+        setfield_gc(p3sub, i1, descr=valuedescr)
+        setfield_gc(p1, p3sub, descr=nextdescr)
+        jump(i1, p1, p3sub)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_dont_delay_setfields(self):
+        ops = """
+        [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)
+        """
+        preamble = """
+        [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)
+        jump(p2, i2)
+        """
+        expected = """
+        [p2, i1]
+        i2 = int_sub(i1, 1)
+        i2b = int_is_true(i2)
+        guard_true(i2b) []
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3, i2, descr=nextdescr)
+        jump(p3, i2)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     # ----------
 
@@ -794,7 +918,7 @@
         # note that 'guard_no_exception' at the very start must be kept
         # around: bridges may start with one.  (In case of loops we could
         # remove it, but we probably don't care.)
-        expected = """
+        preamble = """
         [i]
         guard_no_exception() []
         i1 = int_add(i, 3)
@@ -803,7 +927,15 @@
         i3 = call(i2, descr=nonwritedescr)
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i]
+        i1 = int_add(i, 3)
+        i2 = call(i1, descr=nonwritedescr)
+        guard_no_exception() [i1, i2]
+        i3 = call(i2, descr=nonwritedescr)
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     # ----------
 
@@ -821,14 +953,18 @@
         guard_value(i4, 1) []
         jump(i1)
         """
-        expected = """
+        preamble = """
         [i1]
         i2 = call(1, i1, descr=nonwritedescr)
         guard_no_exception() []
         guard_value(i2, 1) []
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i1]
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
 
     # ----------
@@ -838,49 +974,44 @@
         [i, p0]
         i0 = getfield_gc(p0, descr=valuedescr)
         i1 = int_add(i0, i)
-        setfield_gc(p0, i1, descr=valuedescr)
-        jump(i, p0)
-        """
-        expected = """
-        [i, i2]
-        i1 = int_add(i2, i)
-        jump(i, i1)
-        """
-        self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)',
-                           expected, checkspecnodes=False)
-
-    def test_virtual_float(self):
-        ops = """
-        [f, p0]
-        f0 = getfield_gc(p0, descr=floatdescr)
-        f1 = float_add(f0, f)
-        setfield_gc(p0, f1, descr=floatdescr)
-        jump(f, p0)
-        """
-        expected = """
-        [f, f2]
-        f1 = float_add(f2, f)
-        jump(f, f1)
-        """
-        self.optimize_loop(ops, 'Not, Virtual(node_vtable, floatdescr=Not)',
-                           expected, checkspecnodes=False)
-
-    def test_virtual_2(self):
-        ops = """
-        [i, p0]
-        i0 = getfield_gc(p0, descr=valuedescr)
-        i1 = int_add(i0, i)
         p1 = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p1, i1, descr=valuedescr)
         jump(i, p1)
         """
+        preamble = """
+        [i, p0]
+        i0 = getfield_gc(p0, descr=valuedescr)        
+        i1 = int_add(i0, i)
+        jump(i, i1)
+        """
         expected = """
         [i, i2]
         i1 = int_add(i2, i)
         jump(i, i1)
         """
-        self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)',
-                           expected)
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_virtual_float(self):
+        ops = """
+        [f, p0]
+        f0 = getfield_gc(p0, descr=floatdescr)
+        f1 = float_add(f0, f)
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, f1, descr=floatdescr)
+        jump(f, p1)
+        """
+        preamble = """
+        [f, p0]
+        f2 = getfield_gc(p0, descr=floatdescr)
+        f1 = float_add(f2, f)
+        jump(f, f1)
+        """
+        expected = """
+        [f, f2]
+        f1 = float_add(f2, f)
+        jump(f, f1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_virtual_oois(self):
         ops = """
@@ -909,14 +1040,10 @@
         jump(p0, p1, p2)
         """
         expected = """
-        [p2]
+        [p0, p1, p2]
         # all constant-folded :-)
-        jump(p2)
-        """
-        self.optimize_loop(ops, '''Virtual(node_vtable),
-                                   Virtual(node_vtable),
-                                   Not''',
-                           expected, checkspecnodes=False)
+        jump(p0, p1, p2)
+        """
         #
         # to be complete, we also check the no-opt case where most comparisons
         # are not removed.  The exact set of comparisons removed depends on
@@ -932,7 +1059,7 @@
         guard_true(i11) []
         jump(p0, p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected2)
+        self.optimize_loop(ops, expected, expected2)
 
     def test_virtual_default_field(self):
         ops = """
@@ -943,15 +1070,17 @@
         # the field 'value' has its default value of 0
         jump(p1)
         """
-        expected = """
-        [i]
-        guard_value(i, 0) []
-        jump(0)
-        """
-        # the 'expected' is sub-optimal, but it should be done by another later
-        # optimization step.  See test_find_nodes_default_field() for why.
-        self.optimize_loop(ops, 'Virtual(node_vtable, valuedescr=Not)',
-                           expected)
+        preamble = """
+        [p0]
+        i0 = getfield_gc(p0, descr=valuedescr)
+        guard_value(i0, 0) []
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_virtual_3(self):
         ops = """
@@ -967,7 +1096,7 @@
         i1 = int_add(i, 1)
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_virtual_4(self):
         ops = """
@@ -980,14 +1109,21 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(i3, p1)
         """
+        preamble = """
+        [i0, p0]
+        guard_class(p0, ConstClass(node_vtable)) []
+        i1 = getfield_gc(p0, descr=valuedescr)
+        i2 = int_sub(i1, 1)
+        i3 = int_add(i0, i1)
+        jump(i3, i2)
+        """
         expected = """
         [i0, i1]
         i2 = int_sub(i1, 1)
         i3 = int_add(i0, i1)
         jump(i3, i2)
         """
-        self.optimize_loop(ops, 'Not, Virtual(node_vtable, valuedescr=Not)',
-                           expected)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_virtual_5(self):
         ops = """
@@ -1003,18 +1139,21 @@
         setfield_gc(p1, p2, descr=nextdescr)
         jump(i3, p1)
         """
+        preamble = """
+        [i0, p0]
+        guard_class(p0, ConstClass(node_vtable)) []
+        i1 = getfield_gc(p0, descr=valuedescr)
+        i2 = int_sub(i1, 1)
+        i3 = int_add(i0, i1)
+        jump(i3, i2, i1)
+        """
         expected = """
         [i0, i1, i1bis]
         i2 = int_sub(i1, 1)
         i3 = int_add(i0, i1)
         jump(i3, i2, i1)
         """
-        self.optimize_loop(ops,
-            '''Not, Virtual(node_vtable,
-                            valuedescr=Not,
-                            nextdescr=Virtual(node_vtable2,
-                                              valuedescr=Not))''',
-                           expected)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_virtual_constant_isnull(self):
         ops = """
@@ -1025,11 +1164,15 @@
         i1 = ptr_eq(p2, NULL)
         jump(i1)
         """
-        expected = """
+        preamble = """
         [i0]
-        jump(1)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
 
     def test_virtual_constant_isnonnull(self):
@@ -1041,11 +1184,15 @@
         i1 = ptr_eq(p2, NULL)
         jump(i1)
         """
-        expected = """
+        preamble = """
         [i0]
-        jump(0)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected)
 
     def test_nonvirtual_1(self):
         ops = """
@@ -1067,7 +1214,7 @@
         escape(p1)
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_nonvirtual_2(self):
         ops = """
@@ -1079,8 +1226,22 @@
         setfield_gc(p1, i1, descr=valuedescr)
         jump(i, p1)
         """
-        expected = ops
-        self.optimize_loop(ops, 'Not, Not', expected)
+        preamble = """
+        [i, p0]
+        i0 = getfield_gc(p0, descr=valuedescr)
+        escape(p0)
+        i1 = int_add(i0, i)
+        jump(i, i1)
+        """
+        expected = """
+        [i, i1]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, i1, descr=valuedescr)
+        escape(p1)
+        i2 = int_add(i1, i)
+        jump(i, i2)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_nonvirtual_later(self):
         ops = """
@@ -1102,7 +1263,7 @@
         i3 = int_add(i, i2)
         jump(i3)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_nonvirtual_dont_write_null_fields_on_force(self):
         ops = """
@@ -1122,7 +1283,7 @@
         i2 = getfield_gc(p1, descr=valuedescr)
         jump(i2)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_getfield_gc_pure_1(self):
         ops = """
@@ -1136,7 +1297,7 @@
         [i]
         jump(i)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_getfield_gc_pure_2(self):
         ops = """
@@ -1145,11 +1306,11 @@
         jump(i1)
         """
         expected = """
-        [i]
-        jump(5)
+        []
+        jump()
         """
         self.node.value = 5
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_getfield_gc_nonpure_2(self):
         ops = """
@@ -1157,8 +1318,12 @@
         i1 = getfield_gc(ConstPtr(myptr), descr=valuedescr)
         jump(i1)
         """
-        expected = ops
-        self.optimize_loop(ops, 'Not', expected)
+        preamble = ops
+        expected = """
+        [i]
+        jump(i)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_varray_1(self):
         ops = """
@@ -1175,7 +1340,7 @@
         [i1]
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_varray_alloc_and_set(self):
         ops = """
@@ -1185,11 +1350,15 @@
         i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
         jump(i2)
         """
-        expected = """
+        preamble = """
         [i1]
-        jump(0)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_varray_float(self):
         ops = """
@@ -1206,7 +1375,7 @@
         [f1]
         jump(f1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_array_non_optimized(self):
         ops = """
@@ -1222,7 +1391,7 @@
         p1 = new_array(i1, descr=arraydescr)
         jump(i1, p1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_nonvirtual_array_dont_write_null_fields_on_force(self):
         ops = """
@@ -1240,7 +1409,7 @@
         escape(p1)
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_varray_2(self):
         ops = """
@@ -1254,13 +1423,21 @@
         setarrayitem_gc(p2, 0, 20, descr=arraydescr)
         jump(i0, p2)
         """
-        expected = """
-        [i0, i1, i2]
+        preamble = """
+        [i0, p1]
+        i1 = getarrayitem_gc(p1, 0, descr=arraydescr)
+        i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
         i3 = int_sub(i1, i2)
         guard_value(i3, 15) []
-        jump(i0, 20, i0)
-        """
-        self.optimize_loop(ops, 'Not, VArray(arraydescr, Not, Not)', expected)
+        jump(i0)
+        """
+        expected = """
+        [i0]
+        i3 = int_sub(20, i0)
+        guard_value(i3, 15) []
+        jump(5)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_p123_array(self):
         ops = """
@@ -1271,8 +1448,22 @@
         setarrayitem_gc(p1, 0, i1, descr=arraydescr)
         jump(i1, p1, p2)
         """
+        preamble = """
+        [i1, p2, p3]
+        i3 = getarrayitem_gc(p3, 0, descr=arraydescr)
+        escape(i3)
+        jump(i1, p2)
+        """
+        expected = """
+        [i1, p2]
+        i3 = getarrayitem_gc(p2, 0, descr=arraydescr)
+        escape(i3)
+        p1 = new_array(1, descr=arraydescr)
+        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
+        jump(i1, p1)
+        """
         # We cannot track virtuals that survive for more than two iterations.
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_varray_forced_1(self):
         ops = """
@@ -1294,7 +1485,7 @@
         escape(i2)
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_vstruct_1(self):
         ops = """
@@ -1305,12 +1496,18 @@
         setfield_gc(p3, i1, descr=adescr)
         jump(i1, p3)
         """
-        expected = """
-        [i1, i2]
+        preamble = """
+        [i1, p2]
+        i2 = getfield_gc(p2, descr=adescr)
         escape(i2)
-        jump(i1, i1)
-        """
-        self.optimize_loop(ops, 'Not, VStruct(ssize, adescr=Not)', expected)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        escape(i1)
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_p123_vstruct(self):
         ops = """
@@ -1321,8 +1518,22 @@
         setfield_gc(p1, i1, descr=adescr)
         jump(i1, p1, p2)
         """
+        preamble = """
+        [i1, p2, p3]
+        i3 = getfield_gc(p3, descr=adescr)
+        escape(i3)
+        jump(i1, p2)
+        """
+        expected = """
+        [i1, p2]
+        i3 = getfield_gc(p2, descr=adescr)
+        escape(i3)
+        p1 = new(descr=ssize)
+        setfield_gc(p1, i1, descr=adescr)
+        jump(i1, p1)
+        """
         # We cannot track virtuals that survive for more than two iterations.
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_duplicate_getfield_1(self):
         ops = """
@@ -1347,7 +1558,7 @@
         escape(i2)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_getfield_after_setfield(self):
         ops = """
@@ -1363,7 +1574,7 @@
         escape(i1)
         jump(p1, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_setfield_of_different_type_does_not_clear(self):
         ops = """
@@ -1381,7 +1592,7 @@
         escape(i1)
         jump(p1, p2, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_setfield_of_same_type_clears(self):
         ops = """
@@ -1392,7 +1603,7 @@
         escape(i3)
         jump(p1, p2, i1, i3)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_getfield_mergepoint_has_no_side_effects(self):
         ops = """
@@ -1412,7 +1623,7 @@
         escape(i1)
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getfield_ovf_op_does_not_clear(self):
         ops = """
@@ -1434,7 +1645,7 @@
         escape(i1)
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getfield_setarrayitem_does_not_clear(self):
         ops = """
@@ -1454,7 +1665,7 @@
         escape(i1)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getfield_constant(self):
         ops = """
@@ -1472,7 +1683,7 @@
         escape(i1)
         jump()
         """
-        self.optimize_loop(ops, '', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getfield_guard_value_const(self):
         ops = """
@@ -1491,7 +1702,7 @@
         escape(i1)
         jump()
         """
-        self.optimize_loop(ops, 'Constant(myptr)', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getfield_sideeffects_1(self):
         ops = """
@@ -1503,7 +1714,7 @@
         escape(i2)
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_getfield_sideeffects_2(self):
         ops = """
@@ -1514,7 +1725,7 @@
         escape(i2)
         jump(p1, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_setfield_1(self):
         ops = """
@@ -1528,7 +1739,7 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_setfield_2(self):
         ops = """
@@ -1545,7 +1756,7 @@
         escape(i1)
         jump(p1, i1, i3)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_setfield_3(self):
         ops = """
@@ -1558,7 +1769,7 @@
         """
         # potential aliasing of p1 and p2 means that we cannot kill the
         # the setfield_gc
-        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_setfield_4(self):
         ops = """
@@ -1575,7 +1786,7 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2, p3)
         """
-        expected = """
+        preamble = """
         [p1, i1, i2, p3]
         #
         i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
@@ -1585,9 +1796,20 @@
         #
         setfield_gc(p1, i2, descr=valuedescr)
         setfield_gc(p1, i4, descr=nextdescr)
-        jump(p1, i1, i2, p3)
-        """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+        jump(p1, i1, i2, p3, i3)
+        """
+        expected = """
+        [p1, i1, i2, p3, i3]
+        #
+        i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
+        i5 = int_add(i3, i4)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
+        #
+        setfield_gc(p1, i2, descr=valuedescr)
+        setfield_gc(p1, i4, descr=nextdescr)
+        jump(p1, i1, i2, p3, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_duplicate_setfield_5(self):
         ops = """
@@ -1609,7 +1831,7 @@
         escape(i1)
         jump(p0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_setfield_sideeffects_1(self):
         ops = """
@@ -1619,7 +1841,7 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_setfield_residual_guard_1(self):
         ops = """
@@ -1630,7 +1852,22 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2, i4)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+        preamble = """
+        [p1, i1, i2, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        guard_true(i3) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, i4)
+        """
+        expected = """
+        [p1, i1, i2, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        guard_true(i4) []
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, 1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_duplicate_setfield_residual_guard_2(self):
         # the difference with the previous test is that the field value is
@@ -1644,14 +1881,20 @@
         setfield_gc(p1, NULL, descr=nextdescr)
         jump(p1, i2, i4)
         """
-        expected = """
+        preamble = """
         [p1, i2, i3]
         guard_true(i3) [p1]
         i4 = int_neg(i2)
         setfield_gc(p1, NULL, descr=nextdescr)
         jump(p1, i2, i4)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        expected = """
+        [p1, i2, i4]
+        guard_true(i4) [p1]
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, 1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_duplicate_setfield_residual_guard_3(self):
         ops = """
@@ -1664,14 +1907,20 @@
         setfield_gc(p1, NULL, descr=nextdescr)
         jump(p1, i2, i4)
         """
-        expected = """
+        preamble = """
         [p1, i2, i3]
         guard_true(i3) [i2, p1]
         i4 = int_neg(i2)
         setfield_gc(p1, NULL, descr=nextdescr)
         jump(p1, i2, i4)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        expected = """
+        [p1, i2, i4]
+        guard_true(i4) [i2, p1]
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, 1)
+        """
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_setfield_residual_guard_4(self):
         # test that the setfield_gc does not end up between int_eq and
@@ -1685,7 +1934,16 @@
         setfield_gc(p1, i2, descr=valuedescr)
         jump(p1, i1, i2, i4)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+        preamble = ops
+        expected = """
+        [p1, i1, i2, i4]
+        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)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_duplicate_setfield_aliasing(self):
         # a case where aliasing issues (and not enough cleverness) mean
@@ -1697,7 +1955,7 @@
         setfield_gc(p1, i3, descr=valuedescr)
         jump(p1, p2, i1, i2, i3)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_duplicate_setfield_guard_value_const(self):
         ops = """
@@ -1712,7 +1970,7 @@
         setfield_gc(ConstPtr(myptr), i2, descr=valuedescr)
         jump(i1, i2)
         """
-        self.optimize_loop(ops, 'Constant(myptr), Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_1(self):
         ops = """
@@ -1737,7 +1995,7 @@
         escape(p3)
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_1(self):
         ops = """
@@ -1753,7 +2011,7 @@
         escape(p2)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_2(self):
         ops = """
@@ -1775,7 +2033,7 @@
         escape(p3)
         jump(p1, p2, p3, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_3(self):
         ops = """
@@ -1802,7 +2060,7 @@
         escape(p4)
         jump(p1, p2, p3, p4, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_getarrayitem_pure_does_not_invalidate(self):
         ops = """
@@ -1823,7 +2081,7 @@
         escape(p3)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_duplicate_getarrayitem_after_setarrayitem_two_arrays(self):
         ops = """
@@ -1844,7 +2102,36 @@
         escape(p4)
         jump(p1, p2, p3, p4, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
+
+    def test_duplicate_setfield_virtual(self):
+        ops = """
+        [p1, i2, i3, p4]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, p4, descr=nextdescr)
+        setfield_gc(p1, p2, descr=nextdescr)
+        guard_true(i3) []
+        i4 = int_neg(i2)
+        jump(p1, i2, i4, p4)
+        """
+        preamble = """
+        [p1, i2, i3, p4]
+        guard_true(i3) [p1, p4]
+        i4 = int_neg(i2)
+        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]
+        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)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bug_1(self):
         ops = """
@@ -1866,8 +2153,7 @@
         p3 = escape()
         jump(i0, p3)
         """
-        self.optimize_loop(ops, 'Not, Virtual(node_vtable, nextdescr=Not)',
-                           expected)
+        self.optimize_loop(ops, expected)
 
     def test_bug_2(self):
         ops = """
@@ -1889,8 +2175,7 @@
         p3 = escape()
         jump(i0, p3)
         """
-        self.optimize_loop(ops, 'Not, VArray(arraydescr2, Not)',
-                           expected)
+        self.optimize_loop(ops, expected)
 
     def test_bug_3(self):
         ops = """
@@ -1912,17 +2197,34 @@
         setfield_gc(p1a, p3a, descr=otherdescr)
         jump(p1a)
         """
-        expected = """
-        [p2, p3]
+        preamble = """
+        [p1]
+        guard_nonnull_class(p1, ConstClass(node_vtable2)) []
+        p2 = getfield_gc(p1, descr=nextdescr)
         guard_class(p2, ConstClass(node_vtable)) []
+        p3 = getfield_gc(p1, descr=otherdescr)
         guard_class(p3, ConstClass(node_vtable)) []
         setfield_gc(p3, p2, descr=otherdescr)
         p3a = new_with_vtable(ConstClass(node_vtable))
         escape(p3a)
-        p2a = new_with_vtable(ConstClass(node_vtable))
-        jump(p2a, p3a)
-        """
-        self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected)
+        jump(p3a)
+        """
+        expected = """
+        [p3a]
+        # p1=p1a(next=p2a, other=p3a), p2()
+        # p2 = getfield_gc(p1, descr=nextdescr) # p2a
+        # p3 = getfield_gc(p1, descr=otherdescr)# p3a
+        # setfield_gc(p3, p2, descr=otherdescr) # p3a.other = p2a
+        # p1a = new_with_vtable(ConstClass(node_vtable2))
+        # p2a = new_with_vtable(ConstClass(node_vtable))
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3a, p2, descr=otherdescr) # p3a.other = p2a
+        p3anew = new_with_vtable(ConstClass(node_vtable))
+        escape(p3anew)
+        jump(p3anew)
+        """
+        #self.optimize_loop(ops, expected) # XXX Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bug_3bis(self):
         ops = """
@@ -1944,17 +2246,31 @@
         setfield_gc(p1a, p3a, descr=otherdescr)
         jump(p1a)
         """
+        preamble = """
+        [p1]
+        guard_nonnull_class(p1, ConstClass(node_vtable2)) []
+        p2 = getfield_gc(p1, descr=nextdescr)
+        guard_class(p2, ConstClass(node_vtable)) []
+        p3 = getfield_gc(p1, descr=otherdescr)
+        guard_class(p3, ConstClass(node_vtable)) []
+        # p1a = new_with_vtable(ConstClass(node_vtable2))
+        p2a = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3, p2a, descr=otherdescr)
+        p3a = new_with_vtable(ConstClass(node_vtable))
+        escape(p3a)
+        # setfield_gc(p1a, p2a, descr=nextdescr)
+        # setfield_gc(p1a, p3a, descr=otherdescr)
+        jump(p2a, p3a)
+        """
         expected = """
         [p2, p3]
-        guard_class(p2, ConstClass(node_vtable)) []
-        guard_class(p3, ConstClass(node_vtable)) []
         p2a = new_with_vtable(ConstClass(node_vtable))
         setfield_gc(p3, p2a, descr=otherdescr)
         p3a = new_with_vtable(ConstClass(node_vtable))
         escape(p3a)
         jump(p2a, p3a)
         """
-        self.optimize_loop(ops, 'Virtual(node_vtable2, nextdescr=Not, otherdescr=Not)', expected)
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bug_4(self):
         ops = """
@@ -1963,7 +2279,18 @@
         setfield_gc(ConstPtr(myptr), p9, descr=nextdescr)
         jump(p30)
         """
-        self.optimize_loop(ops, 'Not', ops)
+        preamble = """
+        [p9]
+        setfield_gc(ConstPtr(myptr), p9, descr=nextdescr)
+        jump()
+        """
+        expected = """
+        []
+        p30 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(ConstPtr(myptr), p30, descr=nextdescr)
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_invalid_loop_1(self):
         ops = """
@@ -1974,10 +2301,9 @@
         jump(p2)
         """
         py.test.raises(InvalidLoop, self.optimize_loop,
-                       ops, 'Virtual(node_vtable)', None)
+                       ops, ops)
 
     def test_invalid_loop_2(self):
-        py.test.skip("this would fail if we had Fixed again in the specnodes")
         ops = """
         [p1]
         guard_class(p1, ConstClass(node_vtable2)) []
@@ -1987,7 +2313,7 @@
         jump(p2)
         """
         py.test.raises(InvalidLoop, self.optimize_loop,
-                       ops, '...', None)
+                       ops, ops)
 
     def test_invalid_loop_3(self):
         ops = """
@@ -2000,9 +2326,8 @@
         setfield_gc(p3, p4, descr=nextdescr)
         jump(p3)
         """
-        py.test.raises(InvalidLoop, self.optimize_loop, ops,
-                       'Virtual(node_vtable, nextdescr=Virtual(node_vtable))',
-                       None)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
+
 
     def test_merge_guard_class_guard_value(self):
         ops = """
@@ -2012,16 +2337,21 @@
         guard_value(p1, ConstPtr(myptr)) [i1]
         jump(p2, i0, i1, i3, p2)
         """
-        expected = """
+        preamble = """
         [p1, i0, i1, i2, p2]
         guard_value(p1, ConstPtr(myptr)) [i0]
         i3 = int_add(i1, i2)
-        jump(p2, i0, i1, i3, p2)
-        """
-        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
+        jump(p2, i0, i1, i3)
+        """
+        expected = """
+        [p2, i0, i1, i2]
+        guard_value(p2, ConstPtr(myptr)) [i0]
+        i3 = int_add(i1, i2)
+        jump(ConstPtr(myptr), i0, i1, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_merge_guard_nonnull_guard_class(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -2029,17 +2359,22 @@
         guard_class(p1, ConstClass(node_vtable)) [i1]
         jump(p2, i0, i1, i3, p2)
         """
-        expected = """
+        preamble = """
         [p1, i0, i1, i2, p2]
         guard_nonnull_class(p1, ConstClass(node_vtable), descr=fdescr) [i0]
         i3 = int_add(i1, i2)
-        jump(p2, i0, i1, i3, p2)
-        """
-        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
-        self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS)
+        jump(p2, i0, i1, i3)
+        """
+        expected = """
+        [p2, i0, i1, i2]
+        guard_nonnull_class(p2, ConstClass(node_vtable), descr=fdescr2) [i0]
+        i3 = int_add(i1, i2)
+        jump(p2, i0, i1, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
+        #self.check_expanded_fail_descr("i0", rop.GUARD_NONNULL_CLASS)
 
     def test_merge_guard_nonnull_guard_value(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -2047,17 +2382,22 @@
         guard_value(p1, ConstPtr(myptr)) [i1]
         jump(p2, i0, i1, i3, p2)
         """
-        expected = """
+        preamble = """
         [p1, i0, i1, i2, p2]
         guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0]
         i3 = int_add(i1, i2)
-        jump(p2, i0, i1, i3, p2)
-        """
-        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
-        self.check_expanded_fail_descr("i0", rop.GUARD_VALUE)
+        jump(p2, i0, i1, i3)
+        """
+        expected = """
+        [p2, i0, i1, i2]
+        guard_value(p2, ConstPtr(myptr), descr=fdescr2) [i0]
+        i3 = int_add(i1, i2)
+        jump(ConstPtr(myptr), i0, i1, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
+        #self.check_expanded_fail_descr("i0", rop.GUARD_VALUE)
 
     def test_merge_guard_nonnull_guard_class_guard_value(self):
-        self.make_fail_descr()
         ops = """
         [p1, i0, i1, i2, p2]
         guard_nonnull(p1, descr=fdescr) [i0]
@@ -2067,15 +2407,22 @@
         guard_value(p1, ConstPtr(myptr)) [i1]
         jump(p2, i0, i1, i4, p2)
         """
-        expected = """
+        preamble = """
         [p1, i0, i1, i2, p2]
         guard_value(p1, ConstPtr(myptr), descr=fdescr) [i0]
         i3 = int_add(i1, i2)
         i4 = int_sub(i3, 1)
-        jump(p2, i0, i1, i4, p2)
-        """
-        self.optimize_loop(ops, "Not, Not, Not, Not, Not", expected)
-        self.check_expanded_fail_descr("i0", rop.GUARD_VALUE)
+        jump(p2, i0, i1, i4)
+        """
+        expected = """
+        [p2, i0, i1, i2]
+        guard_value(p2, ConstPtr(myptr), descr=fdescr2) [i0]
+        i3 = int_add(i1, i2)
+        i4 = int_sub(i3, 1)
+        jump(ConstPtr(myptr), i0, i1, i4)
+        """
+        self.optimize_loop(ops, expected, preamble)
+        #self.check_expanded_fail_descr("i0", rop.GUARD_VALUE)
 
     def test_guard_class_oois(self):
         ops = """
@@ -2085,12 +2432,16 @@
         guard_true(i) []
         jump(p1)
         """
-        expected = """
+        preamble = """
         [p1]
         guard_class(p1, ConstClass(node_vtable2)) []
         jump(p1)
         """
-        self.optimize_loop(ops, "Not", expected)
+        expected = """
+        [p1]
+        jump(p1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_oois_of_itself(self):
         ops = """
@@ -2103,12 +2454,16 @@
         guard_false(i2) []
         jump(p0)
         """
-        expected = """
+        preamble = """
         [p0]
         p1 = getfield_gc(p0, descr=nextdescr)
         jump(p0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p0]
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_remove_duplicate_pure_op(self):
         ops = """
@@ -2127,7 +2482,7 @@
         guard_true(i2) []
         jump(p1, p2)
         """
-        expected = """
+        preamble = """
         [p1, p2]
         i1 = ptr_eq(p1, p2)
         i3 = int_add(i1, 1)
@@ -2138,7 +2493,13 @@
         guard_true(i1) []
         jump(p1, p2)
         """
-        self.optimize_loop(ops, "Not, Not", expected)
+        expected = """
+        [p1, p2]
+        escape(2)
+        escape(2)
+        jump(p1, p2)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_remove_duplicate_pure_op_with_descr(self):
         ops = """
@@ -2151,14 +2512,18 @@
         guard_true(i3) []
         jump(p1)
         """
-        expected = """
+        preamble = """
         [p1]
         i0 = arraylen_gc(p1, descr=arraydescr)
         i1 = int_gt(i0, 0)
         guard_true(i1) []
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [p1]
+        jump(p1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_remove_duplicate_pure_op_ovf(self):
         ops = """
@@ -2175,7 +2540,7 @@
         escape(i4)
         jump(i1)
         """
-        expected = """
+        preamble = """
         [i1]
         i3 = int_add_ovf(i1, 1)
         guard_no_overflow() []
@@ -2183,9 +2548,15 @@
         guard_true(i3b) []
         escape(i3)
         escape(i3)
-        jump(i1)
-        """
-        self.optimize_loop(ops, "Not", expected)
+        jump(i1, i3)
+        """
+        expected = """
+        [i1, i3]
+        escape(i3)
+        escape(i3)
+        jump(i1, i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_int_and_or_with_zero(self):
         ops = """
@@ -2200,7 +2571,7 @@
         [i0, i1]
         jump(i1, i0)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_fold_partially_constant_ops(self):
         ops = """
@@ -2212,7 +2583,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
         ops = """
         [i0]
@@ -2223,7 +2594,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
         ops = """
         [i0]
@@ -2234,7 +2605,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_fold_partially_constant_ops_ovf(self):
         ops = """
@@ -2247,7 +2618,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
         ops = """
         [i0]
@@ -2259,7 +2630,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
         ops = """
         [i0]
@@ -2271,406 +2642,10 @@
         [i0]
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     # ----------
 
-    def make_fail_descr(self):
-        class FailDescr(compile.ResumeGuardDescr):
-            oparse = None
-            def _oparser_uses_descr_of_guard(self, oparse, fail_args):
-                # typically called twice, before and after optimization
-                if self.oparse is None:
-                    fdescr.rd_frame_info_list = resume.FrameInfo(None,
-                                                                 "code", 11)
-                    fdescr.rd_snapshot = resume.Snapshot(None, fail_args)
-                self.oparse = oparse
-        #
-        fdescr = instantiate(FailDescr)
-        self.namespace['fdescr'] = fdescr
-
-    def teardown_method(self, meth):
-        self.namespace.pop('fdescr', None)
-
-    def _verify_fail_args(self, boxes, oparse, text):
-        import re
-        r = re.compile(r"\bwhere\s+(\w+)\s+is a\s+(\w+)")
-        parts = list(r.finditer(text))
-        ends = [match.start() for match in parts] + [len(text)]
-        #
-        virtuals = {}
-        for match, end in zip(parts, ends[1:]):
-            pvar = match.group(1)
-            fieldstext = text[match.end():end]
-            if match.group(2) == 'varray':
-                arrayname, fieldstext = fieldstext.split(':', 1)
-                tag = ('varray', self.namespace[arrayname.strip()])
-            elif match.group(2) == 'vstruct':
-                if ',' in fieldstext:
-                    structname, fieldstext = fieldstext.split(',', 1)
-                else:
-                    structname, fieldstext = fieldstext, ''
-                tag = ('vstruct', self.namespace[structname.strip()])
-            else:
-                tag = ('virtual', self.namespace[match.group(2)])
-            virtuals[pvar] = (tag, None, fieldstext)
-        #
-        r2 = re.compile(r"([\w\d()]+)[.](\w+)\s*=\s*([\w\d()]+)")
-        pendingfields = []
-        for match in r2.finditer(text):
-            pvar = match.group(1)
-            pfieldname = match.group(2)
-            pfieldvar = match.group(3)
-            pendingfields.append((pvar, pfieldname, pfieldvar))
-        #
-        def _variables_equal(box, varname, strict):
-            if varname not in virtuals:
-                if strict:
-                    assert box == oparse.getvar(varname)
-                else:
-                    assert box.value == oparse.getvar(varname).value
-            else:
-                tag, resolved, fieldstext = virtuals[varname]
-                if tag[0] == 'virtual':
-                    assert self.get_class_of_box(box) == tag[1]
-                elif tag[0] == 'varray':
-                    pass    # xxx check arraydescr
-                elif tag[0] == 'vstruct':
-                    pass    # xxx check typedescr
-                else:
-                    assert 0
-                if resolved is not None:
-                    assert resolved.value == box.value
-                else:
-                    virtuals[varname] = tag, box, fieldstext
-        #
-        basetext = text.splitlines()[0]
-        varnames = [s.strip() for s in basetext.split(',')]
-        if varnames == ['']:
-            varnames = []
-        assert len(boxes) == len(varnames)
-        for box, varname in zip(boxes, varnames):
-            _variables_equal(box, varname, strict=True)
-        for pvar, pfieldname, pfieldvar in pendingfields:
-            box = oparse.getvar(pvar)
-            fielddescr = self.namespace[pfieldname.strip()]
-            fieldbox = executor.execute(self.cpu, None,
-                                        rop.GETFIELD_GC,
-                                        fielddescr,
-                                        box)
-            _variables_equal(fieldbox, pfieldvar, strict=True)
-        #
-        for match in parts:
-            pvar = match.group(1)
-            tag, resolved, fieldstext = virtuals[pvar]
-            assert resolved is not None
-            index = 0
-            for fieldtext in fieldstext.split(','):
-                fieldtext = fieldtext.strip()
-                if not fieldtext:
-                    continue
-                if tag[0] in ('virtual', 'vstruct'):
-                    fieldname, fieldvalue = fieldtext.split('=')
-                    fielddescr = self.namespace[fieldname.strip()]
-                    fieldbox = executor.execute(self.cpu, None,
-                                                rop.GETFIELD_GC,
-                                                fielddescr,
-                                                resolved)
-                elif tag[0] == 'varray':
-                    fieldvalue = fieldtext
-                    fieldbox = executor.execute(self.cpu, None,
-                                                rop.GETARRAYITEM_GC,
-                                                tag[1],
-                                                resolved, ConstInt(index))
-                else:
-                    assert 0
-                _variables_equal(fieldbox, fieldvalue.strip(), strict=False)
-                index += 1
-
-    def check_expanded_fail_descr(self, expectedtext, guard_opnum):
-        from pypy.jit.metainterp.test.test_resume import ResumeDataFakeReader
-        from pypy.jit.metainterp.test.test_resume import MyMetaInterp
-        guard_op, = [op for op in self.loop.operations if op.is_guard()]
-        fail_args = guard_op.getfailargs()
-        fdescr = guard_op.getdescr()
-        assert fdescr.guard_opnum == guard_opnum
-        reader = ResumeDataFakeReader(fdescr, fail_args,
-                                      MyMetaInterp(self.cpu))
-        boxes = reader.consume_boxes()
-        self._verify_fail_args(boxes, fdescr.oparse, expectedtext)
-
-    def test_expand_fail_1(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, i3]
-        # first rename i3 into i4
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i3, descr=valuedescr)
-        i4 = getfield_gc(p1, descr=valuedescr)
-        #
-        i2 = int_add(10, 5)
-        guard_true(i1, descr=fdescr) [i2, i4]
-        jump(i1, i4)
-        """
-        expected = """
-        [i1, i3]
-        guard_true(i1, descr=fdescr) [i3]
-        jump(1, i3)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-        self.check_expanded_fail_descr('15, i3', rop.GUARD_TRUE)
-
-    def test_expand_fail_2(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, i2]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i2, descr=valuedescr)
-        setfield_gc(p1, p1, descr=nextdescr)
-        guard_true(i1, descr=fdescr) [p1]
-        jump(i1, i2)
-        """
-        expected = """
-        [i1, i2]
-        guard_true(i1, descr=fdescr) [i2]
-        jump(1, i2)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-        self.check_expanded_fail_descr('''ptr
-            where ptr is a node_vtable, valuedescr=i2
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_3(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, i2, i3, p3]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, 1, descr=valuedescr)
-        setfield_gc(p1, p2, descr=nextdescr)
-        setfield_gc(p2, i2, descr=valuedescr)
-        setfield_gc(p2, p3, descr=nextdescr)
-        guard_true(i1, descr=fdescr) [i3, p1]
-        jump(i2, i1, i3, p3)
-        """
-        expected = """
-        [i1, i2, i3, p3]
-        guard_true(i1, descr=fdescr) [i3, i2, p3]
-        jump(i2, 1, i3, p3)
-        """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
-        self.check_expanded_fail_descr('''i3, p1
-            where p1 is a node_vtable, valuedescr=1, nextdescr=p2
-            where p2 is a node_vtable, valuedescr=i2, nextdescr=p3
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_4(self):
-        for arg in ['p1', 'i2,p1', 'p1,p2', 'p2,p1',
-                    'i2,p1,p2', 'i2,p2,p1']:
-            self.make_fail_descr()
-            ops = """
-            [i1, i2, i3]
-            p1 = new_with_vtable(ConstClass(node_vtable))
-            setfield_gc(p1, i3, descr=valuedescr)
-            i4 = getfield_gc(p1, descr=valuedescr)   # copy of i3
-            p2 = new_with_vtable(ConstClass(node_vtable))
-            setfield_gc(p1, i2, descr=valuedescr)
-            setfield_gc(p1, p2, descr=nextdescr)
-            setfield_gc(p2, i2, descr=valuedescr)
-            guard_true(i1, descr=fdescr) [i4, i3, %s]
-            jump(i1, i2, i3)
-            """
-            expected = """
-            [i1, i2, i3]
-            guard_true(i1, descr=fdescr) [i3, i2]
-            jump(1, i2, i3)
-            """
-            self.optimize_loop(ops % arg, 'Not, Not, Not', expected)
-            self.check_expanded_fail_descr('''i3, i3, %s
-                where p1 is a node_vtable, valuedescr=i2, nextdescr=p2
-                where p2 is a node_vtable, valuedescr=i2''' % arg,
-                                           rop.GUARD_TRUE)
-
-    def test_expand_fail_5(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, i2, i3, i4]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i4, descr=valuedescr)
-        setfield_gc(p1, p2, descr=nextdescr)
-        setfield_gc(p2, i2, descr=valuedescr)
-        setfield_gc(p2, p1, descr=nextdescr)      # a cycle
-        guard_true(i1, descr=fdescr) [i3, i4, p1, p2]
-        jump(i2, i1, i3, i4)
-        """
-        expected = """
-        [i1, i2, i3, i4]
-        guard_true(i1, descr=fdescr) [i3, i4, i2]
-        jump(i2, 1, i3, i4)
-        """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
-        self.check_expanded_fail_descr('''i3, i4, p1, p2
-            where p1 is a node_vtable, valuedescr=i4, nextdescr=p2
-            where p2 is a node_vtable, valuedescr=i2, nextdescr=p1
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_6(self):
-        self.make_fail_descr()
-        ops = """
-        [p0, i0, i1]
-        guard_true(i0, descr=fdescr) [p0]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        jump(p1, i1, i1)
-        """
-        expected = """
-        [i1b, i0, i1]
-        guard_true(i0, descr=fdescr) [i1b]
-        jump(i1, i1, i1)
-        """
-        self.optimize_loop(ops, '''Virtual(node_vtable, valuedescr=Not),
-                                   Not, Not''', expected)
-        self.check_expanded_fail_descr('''p0
-            where p0 is a node_vtable, valuedescr=i1b
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_varray(self):
-        self.make_fail_descr()
-        ops = """
-        [i1]
-        p1 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p1, 1, i1, descr=arraydescr)
-        setarrayitem_gc(p1, 0, 25, descr=arraydescr)
-        guard_true(i1, descr=fdescr) [p1]
-        i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
-        jump(i2)
-        """
-        expected = """
-        [i1]
-        guard_true(i1, descr=fdescr) [i1]
-        jump(1)
-        """
-        self.optimize_loop(ops, 'Not', expected)
-        self.check_expanded_fail_descr('''p1
-            where p1 is a varray arraydescr: 25, i1
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_vstruct(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, p1]
-        p2 = new(descr=ssize)
-        setfield_gc(p2, i1, descr=adescr)
-        setfield_gc(p2, p1, descr=bdescr)
-        guard_true(i1, descr=fdescr) [p2]
-        i3 = getfield_gc(p2, descr=adescr)
-        p3 = getfield_gc(p2, descr=bdescr)
-        jump(i3, p3)
-        """
-        expected = """
-        [i1, p1]
-        guard_true(i1, descr=fdescr) [i1, p1]
-        jump(1, p1)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-        self.check_expanded_fail_descr('''p2
-            where p2 is a vstruct ssize, adescr=i1, bdescr=p1
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_v_all_1(self):
-        self.make_fail_descr()
-        ops = """
-        [i1, p1a, i2]
-        p6s = getarrayitem_gc(p1a, 0, descr=arraydescr2)
-        p7v = getfield_gc(p6s, descr=bdescr)
-        p5s = new(descr=ssize)
-        setfield_gc(p5s, i2, descr=adescr)
-        setfield_gc(p5s, p7v, descr=bdescr)
-        setarrayitem_gc(p1a, 1, p5s, descr=arraydescr2)
-        guard_true(i1, descr=fdescr) [p1a]
-        p2s = new(descr=ssize)
-        p3v = new_with_vtable(ConstClass(node_vtable))
-        p4a = new_array(2, descr=arraydescr2)
-        setfield_gc(p2s, i1, descr=adescr)
-        setfield_gc(p2s, p3v, descr=bdescr)
-        setfield_gc(p3v, i2, descr=valuedescr)
-        setarrayitem_gc(p4a, 0, p2s, descr=arraydescr2)
-        jump(i1, p4a, i2)
-        """
-        expected = """
-        [i1, ia, iv, pnull, i2]
-        guard_true(i1, descr=fdescr) [ia, iv, i2]
-        jump(1, 1, i2, NULL, i2)
-        """
-        self.optimize_loop(ops, '''
-            Not,
-            VArray(arraydescr2,
-                   VStruct(ssize,
-                           adescr=Not,
-                           bdescr=Virtual(node_vtable,
-                                          valuedescr=Not)),
-                   Not),
-            Not''', expected)
-        self.check_expanded_fail_descr('''p1a
-            where p1a is a varray arraydescr2: p6s, p5s
-            where p6s is a vstruct ssize, adescr=ia, bdescr=p7v
-            where p5s is a vstruct ssize, adescr=i2, bdescr=p7v
-            where p7v is a node_vtable, valuedescr=iv
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_lazy_setfield_1(self):
-        self.make_fail_descr()
-        ops = """
-        [p1, i2, i3]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p2, i2, descr=valuedescr)
-        setfield_gc(p1, p2, descr=nextdescr)
-        guard_true(i3, descr=fdescr) []
-        i4 = int_neg(i2)
-        setfield_gc(p1, NULL, descr=nextdescr)
-        jump(p1, i2, i4)
-        """
-        expected = """
-        [p1, i2, i3]
-        guard_true(i3, descr=fdescr) [p1, i2]
-        i4 = int_neg(i2)
-        setfield_gc(p1, NULL, descr=nextdescr)
-        jump(p1, i2, i4)
-        """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
-        self.loop.inputargs[0].value = self.nodebox.value
-        self.check_expanded_fail_descr('''
-            p1.nextdescr = p2
-            where p2 is a node_vtable, valuedescr=i2
-            ''', rop.GUARD_TRUE)
-
-    def test_expand_fail_lazy_setfield_2(self):
-        self.make_fail_descr()
-        ops = """
-        [i2, i3]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p2, i2, descr=valuedescr)
-        setfield_gc(ConstPtr(myptr), p2, descr=nextdescr)
-        guard_true(i3, descr=fdescr) []
-        i4 = int_neg(i2)
-        setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
-        jump(i2, i4)
-        """
-        expected = """
-        [i2, i3]
-        guard_true(i3, descr=fdescr) [i2]
-        i4 = int_neg(i2)
-        setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
-        jump(i2, i4)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-        self.check_expanded_fail_descr('''
-            ConstPtr(myptr).nextdescr = p2
-            where p2 is a node_vtable, valuedescr=i2
-            ''', rop.GUARD_TRUE)
-
-
 class TestLLtype(OptimizeOptTest, LLtypeMixin):
 
     def test_residual_call_does_not_invalidate_caches(self):
@@ -2691,7 +2666,7 @@
         escape(i1)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidate_some_caches(self):
         ops = """
@@ -2719,7 +2694,7 @@
         escape(i2)
         jump(p1, p2)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidate_arrays(self):
         ops = """
@@ -2746,7 +2721,7 @@
         escape(p4)
         jump(p1, p2, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidate_some_arrays(self):
         ops = """
@@ -2781,7 +2756,7 @@
         escape(i4)
         jump(p1, p2, i1)
         """
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidates_some_read_caches_1(self):
         ops = """
@@ -2801,7 +2776,7 @@
         setfield_gc(p2, i3, descr=adescr)
         jump(p1, i1, p2, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidates_some_read_caches_2(self):
         ops = """
@@ -2821,7 +2796,7 @@
         setfield_gc(p2, i3, descr=adescr)
         jump(p1, i1, p2, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_residual_call_invalidates_some_read_caches_3(self):
         ops = """
@@ -2833,7 +2808,7 @@
         setfield_gc(p2, i3, descr=adescr)
         jump(p1, i1, p2, i2)
         """
-        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_call_assembler_invalidates_caches(self):
         ops = '''
@@ -2843,7 +2818,7 @@
         setfield_gc(p1, i3, descr=valuedescr)
         jump(p1, i3)
         '''
-        self.optimize_loop(ops, 'Not, Not', ops)
+        self.optimize_loop(ops, ops)
 
     def test_call_pure_invalidates_caches(self):
         # CALL_PURE should still force the setfield_gc() to occur before it
@@ -2861,7 +2836,7 @@
         setfield_gc(p1, i3, descr=valuedescr)
         jump(p1, i3)
         '''
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_call_pure_constant_folding(self):
         # CALL_PURE is not marked as is_always_pure(), because it is wrong
@@ -2869,6 +2844,7 @@
         # time.  Check that it is either constant-folded (and replaced by
         # the result of the call, recorded as the first arg), or turned into
         # a regular CALL.
+        # XXX can this test be improved with unrolling?
         ops = '''
         [i0, i1, i2]
         escape(i1)
@@ -2877,14 +2853,23 @@
         i4 = call_pure(43, 123456, 4, i0, 6, descr=plaincalldescr)
         jump(i0, i3, i4)
         '''
-        expected = '''
+        preamble = '''
         [i0, i1, i2]
         escape(i1)
         escape(i2)
         i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
-        jump(i0, 42, i4)
+        jump(i0, i4)
         '''
-        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        expected = '''
+        [i0, i2]
+        escape(42)
+        escape(i2)
+        i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+        jump(i0, i4)
+        '''
+        self.optimize_loop(ops, expected, preamble)
+
+    # ----------
 
     def test_vref_nonvirtual_nonescape(self):
         ops = """
@@ -2898,7 +2883,7 @@
         i0 = force_token()
         jump(p1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected, expected)
 
     def test_vref_nonvirtual_escape(self):
         ops = """
@@ -2921,7 +2906,7 @@
         """
         # XXX we should optimize a bit more the case of a nonvirtual.
         # in theory it is enough to just do 'p2 = p1'.
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected, expected)
 
     def test_vref_virtual_1(self):
         ops = """
@@ -2961,10 +2946,9 @@
         setfield_gc(p2, -3, descr=virtualtokendescr)
         jump(p0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected, expected)
 
     def test_vref_virtual_2(self):
-        self.make_fail_descr()
         ops = """
         [p0, i1]
         #
@@ -2991,7 +2975,7 @@
         setfield_gc(p0, p2, descr=nextdescr)
         #
         call_may_force(i1, descr=mayforcevirtdescr)
-        guard_not_forced(descr=fdescr) [p2, i1]
+        guard_not_forced(descr=fdescr2) [p2, i1]
         #
         setfield_gc(p0, NULL, descr=nextdescr)
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -3004,14 +2988,13 @@
         """
         # the point of this test is that 'i1' should show up in the fail_args
         # of 'guard_not_forced', because it was stored in the virtual 'p1b'.
-        self.optimize_loop(ops, 'Not, Not', expected)
-        self.check_expanded_fail_descr('''p2, p1
-            where p1 is a node_vtable, nextdescr=p1b
-            where p1b is a node_vtable, valuedescr=i1
-            ''', rop.GUARD_NOT_FORCED)
+        self.optimize_loop(ops, expected)
+        #self.check_expanded_fail_descr('''p2, p1
+        #    where p1 is a node_vtable, nextdescr=p1b
+        #    where p1b is a node_vtable, valuedescr=i1
+        #    ''', rop.GUARD_NOT_FORCED)
 
     def test_vref_virtual_and_lazy_setfield(self):
-        self.make_fail_descr()
         ops = """
         [p0, i1]
         #
@@ -3028,7 +3011,7 @@
         setfield_gc(p0, NULL, descr=refdescr)
         jump(p0, i1)
         """
-        expected = """
+        preamble = """
         [p0, i1]
         i3 = force_token()
         call(i1, descr=nonwritedescr)
@@ -3036,21 +3019,28 @@
         setfield_gc(p0, NULL, descr=refdescr)
         jump(p0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        expected = """
+        [p0, i1]
+        i3 = force_token()
+        call(i1, descr=nonwritedescr)
+        guard_no_exception(descr=fdescr2) [i3, i1, p0]
+        setfield_gc(p0, NULL, descr=refdescr)
+        jump(p0, i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
         # the fail_args contain [i3, i1, p0]:
         #  - i3 is from the virtual expansion of p2
         #  - i1 is from the virtual expansion of p1
         #  - p0 is from the extra pendingfields
-        self.loop.inputargs[0].value = self.nodeobjvalue
-        self.check_expanded_fail_descr('''p2, p1
-            p0.refdescr = p2
-            where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2
-            where p1 is a node_vtable, nextdescr=p1b
-            where p1b is a node_vtable, valuedescr=i1
-            ''', rop.GUARD_NO_EXCEPTION)
+        #self.loop.inputargs[0].value = self.nodeobjvalue
+        #self.check_expanded_fail_descr('''p2, p1
+        #    p0.refdescr = p2
+        #    where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2
+        #    where p1 is a node_vtable, nextdescr=p1b
+        #    where p1b is a node_vtable, valuedescr=i1
+        #    ''', rop.GUARD_NO_EXCEPTION)
 
     def test_vref_virtual_after_finish(self):
-        self.make_fail_descr()
         ops = """
         [i1]
         p1 = new_with_vtable(ConstClass(node_vtable))
@@ -3075,10 +3065,9 @@
         guard_not_forced() []
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected, expected)
 
     def test_vref_nonvirtual_and_lazy_setfield(self):
-        self.make_fail_descr()
         ops = """
         [i1, p1]
         p2 = virtual_ref(p1, 23)
@@ -3101,7 +3090,9 @@
         guard_not_forced() [i1]
         jump(i1, p1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected, expected)
+
+    # ----------
 
     def test_arraycopy_1(self):
         ops = '''
@@ -3115,10 +3106,10 @@
         jump(i2)
         '''
         expected = '''
-        [i0]
-        jump(1)
+        []
+        jump()
         '''
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_arraycopy_2(self):
         ops = '''
@@ -3132,28 +3123,30 @@
         jump(i2)
         '''
         expected = '''
-        [i0]
-        jump(3)
+        []
+        jump()
         '''
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_arraycopy_not_virtual(self):
         ops = '''
-        [p0]
+        []
         p1 = new_array(3, descr=arraydescr)
         p2 = new_array(3, descr=arraydescr)
         setarrayitem_gc(p1, 2, 10, descr=arraydescr)
         setarrayitem_gc(p2, 2, 13, descr=arraydescr)
         call(0, p1, p2, 0, 0, 3, descr=arraycopydescr)
-        jump(p2)
+        escape(p2)
+        jump()
         '''
         expected = '''
-        [p0]
+        []
         p2 = new_array(3, descr=arraydescr)
         setarrayitem_gc(p2, 2, 10, descr=arraydescr)
-        jump(p2)
+        escape(p2)
+        jump()
         '''
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_arraycopy_no_elem(self):
         """ this was actually observed in the wild
@@ -3168,7 +3161,7 @@
         [p1]
         jump(p1)
         '''
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_bound_lt(self):
         ops = """
@@ -3179,13 +3172,18 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_noguard(self):
         ops = """
@@ -3200,7 +3198,7 @@
         i2 = int_lt(i0, 5)
         jump(i2)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected, expected)
 
     def test_bound_lt_noopt(self):
         ops = """
@@ -3211,15 +3209,19 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_false(i1) []
         i2 = int_lt(i0, 5)
         guard_true(i2) []
-        jump(4)
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        jump()
+        """
+        expected = """
+        []
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_rev(self):
         ops = """
@@ -3230,13 +3232,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_false(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_tripple(self):
         ops = """
@@ -3249,13 +3255,17 @@
         guard_true(i3) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 0)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_add(self):
         ops = """
@@ -3267,14 +3277,18 @@
         guard_true(i3) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_true(i1) []
         i2 = int_add(i0, 10)
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_add_before(self):
         ops = """
@@ -3286,14 +3300,18 @@
         guard_true(i1) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i2 = int_add(i0, 10)
         i3 = int_lt(i2, 15)
         guard_true(i3) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_add_ovf(self):
         ops = """
@@ -3306,14 +3324,18 @@
         guard_true(i3) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_true(i1) []
         i2 = int_add(i0, 10)
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_add_ovf_before(self):
         ops = """
@@ -3326,7 +3348,7 @@
         guard_true(i1) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i2 = int_add_ovf(i0, 10)
         guard_no_overflow() []
@@ -3334,7 +3356,12 @@
         guard_true(i3) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        i2 = int_add(i0, 10)
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_sub(self):
         ops = """
@@ -3346,14 +3373,18 @@
         guard_true(i3) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_true(i1) []
         i2 = int_sub(i0, 10)
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lt_sub_before(self):
         ops = """
@@ -3365,14 +3396,18 @@
         guard_true(i1) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i2 = int_sub(i0, 10)
         i3 = int_lt(i2, -5)
         guard_true(i3) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_ltle(self):
         ops = """
@@ -3383,13 +3418,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_lt(i0, 4)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lelt(self):
         ops = """
@@ -3400,13 +3439,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_le(i0, 4)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_gt(self):
         ops = """
@@ -3417,13 +3460,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_gt(i0, 5)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_gtge(self):
         ops = """
@@ -3434,13 +3481,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_gt(i0, 5)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_gegt(self):
         ops = """
@@ -3451,13 +3502,17 @@
         guard_true(i2) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_ge(i0, 5)
         guard_true(i1) []
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_ovf(self):
         ops = """
@@ -3470,7 +3525,7 @@
         guard_no_overflow() []
         jump(i3)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_ge(i0, 0)
         guard_true(i1) []
@@ -3479,7 +3534,14 @@
         i3 = int_add(i0, 1)
         jump(i3)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        i2 = int_lt(i0, 10)
+        guard_true(i2) []
+        i3 = int_add(i0, 1)
+        jump(i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_arraylen(self):
         ops = """
@@ -3499,7 +3561,7 @@
         setarrayitem_gc(p0, 0, p1)
         jump(i0, p0)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_bound_strlen(self):
         ops = """
@@ -3515,7 +3577,7 @@
         i0 = strlen(p0)
         jump(p0)
         """
-        self.optimize_strunicode_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, expected, expected)
 
     def test_addsub_const(self):
         ops = """
@@ -3532,7 +3594,7 @@
         i4 = int_mul(i0, i1)
         jump(i4)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_addsub_int(self):
         ops = """
@@ -3549,7 +3611,7 @@
         i4 = int_add(i0, i1)
         jump(i4, i10)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_addsub_int2(self):
         ops = """
@@ -3566,7 +3628,7 @@
         i4 = int_add(i0, i1)
         jump(i4, i10)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_framestackdepth_overhead(self):
         ops = """
@@ -3587,17 +3649,144 @@
         jump(p0, i22)
         """
         expected = """
-        [p0, i22]
-        i1 = getfield_gc(p0, descr=valuedescr)
-        i2 = int_gt(i1, i22)
-        guard_false(i2) []
-        i3 = int_add(i1, 1)
+        [p0, i22, i1]
         i331 = force_token()
         setfield_gc(p0, i1, descr=valuedescr)
-        jump(p0, i22)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-
+        jump(p0, i22, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_setgetfield_raw(self):
+        ops = """
+        [p4, p7, i30]
+        p16 = getfield_gc(p4, descr=valuedescr)
+        p17 = getarrayitem_gc(p4, 1, descr=arraydescr)        
+        guard_value(p16, ConstPtr(myptr), descr=<Guard3>) []
+        i1 = getfield_raw(p7, descr=nextdescr)
+        i2 = int_add(i1, i30)
+        setfield_raw(p7, 7, descr=nextdescr)
+        setfield_raw(p7, i2, descr=nextdescr)
+        jump(p4, p7, i30)
+        """
+        expected = """
+        [p4, p7, i30]
+        i1 = getfield_raw(p7, descr=nextdescr)
+        i2 = int_add(i1, i30)
+        setfield_raw(p7, 7, descr=nextdescr)
+        setfield_raw(p7, i2, descr=nextdescr)
+        jump(p4, p7, i30)
+        """
+        self.optimize_loop(ops, expected, ops)
+
+    def test_setgetarrayitem_raw(self):
+        ops = """
+        [p4, p7, i30]
+        p16 = getfield_gc(p4, descr=valuedescr)
+        guard_value(p16, ConstPtr(myptr), descr=<Guard3>) []
+        p17 = getarrayitem_gc(p4, 1, descr=arraydescr)
+        i1 = getarrayitem_raw(p7, 1, descr=arraydescr)
+        i2 = int_add(i1, i30)
+        setarrayitem_raw(p7, 1, 7, descr=arraydescr)
+        setarrayitem_raw(p7, 1, i2, descr=arraydescr)
+        jump(p4, p7, i30)
+        """
+        expected = """
+        [p4, p7, i30]
+        i1 = getarrayitem_raw(p7, 1, descr=arraydescr)
+        i2 = int_add(i1, i30)
+        setarrayitem_raw(p7, 1, 7, descr=arraydescr)
+        setarrayitem_raw(p7, 1, i2, descr=arraydescr)
+        jump(p4, p7, i30)
+        """
+        self.optimize_loop(ops, expected, ops)
+
+    def test_pure(self):
+        ops = """
+        [p42]
+        p53 = getfield_gc(ConstPtr(myptr), descr=nextdescr)
+        p59 = getfield_gc_pure(p53, descr=valuedescr)
+        i61 = call(1, p59, descr=nonwritedescr)
+        jump(p42)
+        """
+        expected = """
+        [p42, p59]
+        i61 = call(1, p59, descr=nonwritedescr)
+        jump(p42, p59)
+
+        """
+        self.node.value = 5
+        self.optimize_loop(ops, expected)
+
+    def test_getfield_guard_const(self):
+        ops = """
+        [p0]
+        p20 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p20) []
+        guard_class(p20, ConstClass(node_vtable)) []
+        guard_class(p20, ConstClass(node_vtable)) []
+        p23 = getfield_gc(p20, descr=valuedescr)
+        guard_isnull(p23) []
+        guard_class(p20, ConstClass(node_vtable)) []
+        guard_value(p20, ConstPtr(myptr)) []
+
+        p37 = getfield_gc(p0, descr=nextdescr)
+        guard_nonnull(p37) []
+        guard_class(p37, ConstClass(node_vtable)) []
+        guard_class(p37, ConstClass(node_vtable)) []
+        p40 = getfield_gc(p37, descr=valuedescr)
+        guard_isnull(p40) []
+        guard_class(p37, ConstClass(node_vtable)) []
+        guard_value(p37, ConstPtr(myptr)) []
+
+        p64 = call_may_force(p23, p40, descr=plaincalldescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p20 = getfield_gc(p0, descr=nextdescr)
+        guard_value(p20, ConstPtr(myptr)) []
+        p23 = getfield_gc(p20, descr=valuedescr)
+        guard_isnull(p23) []
+        p64 = call_may_force(NULL, NULL, descr=plaincalldescr)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, expected)
+
+    def test_getfield_guard_const_preamble(self):
+        ops = """
+        [p0]
+        p01 = getfield_gc(p0, descr=nextdescr)
+        p02 = getfield_gc(p01, descr=valuedescr)
+        guard_value(p01, ConstPtr(myptr)) []
+        p11 = getfield_gc(p0, descr=nextdescr)
+        p12 = getfield_gc(p11, descr=valuedescr)
+        guard_value(p11, ConstPtr(myptr)) []
+        p64 = call_may_force(p02, p12, descr=plaincalldescr)
+
+        p21 = getfield_gc(p0, descr=nextdescr)
+        p22 = getfield_gc(p21, descr=valuedescr)
+        guard_value(p21, ConstPtr(myptr)) []
+        p31 = getfield_gc(p0, descr=nextdescr)
+        p32 = getfield_gc(p31, descr=valuedescr)
+        guard_value(p31, ConstPtr(myptr)) []
+        p65 = call_may_force(p22, p32, descr=plaincalldescr)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p01 = getfield_gc(p0, descr=nextdescr)
+        p02 = getfield_gc(p01, descr=valuedescr)
+        guard_value(p01, ConstPtr(myptr)) []
+        p64 = call_may_force(p02, p02, descr=plaincalldescr)
+
+        p21 = getfield_gc(p0, descr=nextdescr)
+        p22 = getfield_gc(p21, descr=valuedescr)
+        guard_value(p21, ConstPtr(myptr)) []
+        p65 = call_may_force(p22, p22, descr=plaincalldescr)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected, expected)
+        
     def test_addsub_ovf(self):
         ops = """
         [i0]
@@ -3614,7 +3803,7 @@
         i2 = int_sub(i1, 5)
         jump(i2)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_subadd_ovf(self):
         ops = """
@@ -3632,7 +3821,7 @@
         i2 = int_add(i1, 5)
         jump(i2)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_bound_and(self):
         ops = """
@@ -3677,7 +3866,176 @@
         guard_true(i15) []
         jump(i1)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
+
+    def test_bound_xor(self):
+        ops = """
+        [i0, i1, i2]
+        it1 = int_ge(i1, 0)
+        guard_true(it1) []
+        it2 = int_gt(i2, 0)
+        guard_true(it2) []
+        ix1 = int_xor(i0, i0)
+        ix1t = int_ge(ix1, 0)
+        guard_true(ix1t) []
+        ix2 = int_xor(i0, i1)
+        ix2t = int_ge(ix2, 0)
+        guard_true(ix2t) []
+        ix3 = int_xor(i1, i0)
+        ix3t = int_ge(ix3, 0)
+        guard_true(ix3t) []
+        ix4 = int_xor(i1, i2)
+        ix4t = int_ge(ix4, 0)
+        guard_true(ix4t) []
+        jump(i0, i1, i2)
+        """
+        preamble = """
+        [i0, i1, i2]
+        it1 = int_ge(i1, 0)
+        guard_true(it1) []
+        it2 = int_gt(i2, 0)
+        guard_true(it2) []
+        ix2 = int_xor(i0, i1)
+        ix2t = int_ge(ix2, 0)
+        guard_true(ix2t) []
+        ix3 = int_xor(i1, i0)
+        ix3t = int_ge(ix3, 0)
+        guard_true(ix3t) []
+        ix4 = int_xor(i1, i2)
+        jump(i0, i1, i2)
+        """
+        expected = """
+        [i0, i1, i2]
+        jump(i0, i1, i2)        
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_bound_floordiv(self):
+        ops = """
+        [i0, i1, i2]
+        it1 = int_ge(i1, 0)
+        guard_true(it1) []
+        it2 = int_gt(i2, 0)
+        guard_true(it2) []
+        ix2 = int_floordiv(i0, i1)
+        ix2t = int_ge(ix2, 0)
+        guard_true(ix2t) []
+        ix3 = int_floordiv(i1, i0)
+        ix3t = int_ge(ix3, 0)
+        guard_true(ix3t) []
+        ix4 = int_floordiv(i1, i2)
+        ix4t = int_ge(ix4, 0)
+        guard_true(ix4t) []
+        jump(i0, i1, i2)
+        """
+        preamble = """
+        [i0, i1, i2]
+        it1 = int_ge(i1, 0)
+        guard_true(it1) []
+        it2 = int_gt(i2, 0)
+        guard_true(it2) []
+        ix2 = int_floordiv(i0, i1)
+        ix2t = int_ge(ix2, 0)
+        guard_true(ix2t) []
+        ix3 = int_floordiv(i1, i0)
+        ix3t = int_ge(ix3, 0)
+        guard_true(ix3t) []
+        ix4 = int_floordiv(i1, i2)
+        jump(i0, i1, i2)
+        """
+        expected = """
+        [i0, i1, i2]
+        jump(i0, i1, i2)        
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_bound_int_is_zero(self):
+        ops = """
+        [i1, i2a, i2b, i2c]
+        i3 = int_is_zero(i1)
+        i4 = int_gt(i2a, 7)
+        guard_true(i4) []
+        i5 = int_is_zero(i2a)
+        guard_false(i5) []
+        i6 = int_le(i2b, -7)
+        guard_true(i6) []
+        i7 = int_is_zero(i2b)
+        guard_false(i7) []
+        i8 = int_gt(i2c, -7)
+        guard_true(i8) []
+        i9 = int_is_zero(i2c)        
+        jump(i1, i2a, i2b, i2c)
+        """
+        preamble = """
+        [i1, i2a, i2b, i2c]
+        i3 = int_is_zero(i1)
+        i4 = int_gt(i2a, 7)
+        guard_true(i4) []
+        i6 = int_le(i2b, -7)
+        guard_true(i6) []
+        i8 = int_gt(i2c, -7)
+        guard_true(i8) []
+        i9 = int_is_zero(i2c)        
+        jump(i1, i2a, i2b, i2c)
+        """
+        expected = """
+        [i0, i1, i2, i3]
+        jump(i0, i1, i2, i3)        
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_division(self):
+        ops = """
+        [i7, i6, i8]
+        it1 = int_gt(i7, 0)
+        guard_true(it1) []
+        it2 = int_gt(i6, 0)
+        guard_true(it2) []
+        i13 = int_is_zero(i6)
+        guard_false(i13) []
+        i15 = int_and(i8, i6)
+        i17 = int_eq(i15, -1)
+        guard_false(i17) []
+        i18 = int_floordiv(i7, i6)
+        i19 = int_xor(i7, i6)
+        i21 = int_lt(i19, 0)
+        i22 = int_mod(i7, i6)
+        i23 = int_is_true(i22)
+        i24 = int_and(i21, i23)
+        i25 = int_sub(i18, i24)
+        jump(i7, i25, i8)
+        """
+        preamble = """
+        [i7, i6, i8]
+        it1 = int_gt(i7, 0)
+        guard_true(it1) []
+        it2 = int_gt(i6, 0)
+        guard_true(it2) []
+        i15 = int_and(i8, i6)
+        i17 = int_eq(i15, -1)
+        guard_false(i17) []
+        i18 = int_floordiv(i7, i6)
+        i19 = int_xor(i7, i6)
+        i22 = int_mod(i7, i6)
+        i23 = int_is_true(i22)
+        jump(i7, i18, i8)
+        """
+        expected = """
+        [i7, i6, i8]
+        it2 = int_gt(i6, 0)
+        guard_true(it2) []
+        i15 = int_and(i8, i6)
+        i17 = int_eq(i15, -1)
+        guard_false(i17) []
+        i18 = int_floordiv(i7, i6)
+        i19 = int_xor(i7, i6)
+        i22 = int_mod(i7, i6)
+        i23 = int_is_true(i22)
+        jump(i7, i18, i8)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+
 
     def test_subsub_ovf(self):
         ops = """
@@ -3692,7 +4050,7 @@
         guard_true(i4) []
         jump(i0)
         """
-        expected = """
+        preamble = """
         [i0]
         i1 = int_sub_ovf(1, i0)
         guard_no_overflow() []
@@ -3701,45 +4059,56 @@
         i3 = int_sub(1, i0)
         jump(i0)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        expected = """
+        [i0]
+        jump(i0)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_eq(self):
         ops = """
-        [i0, i1]
+        []
+        i0 = escape()
+        i1 = escape()
         i2 = int_le(i0, 4)
         guard_true(i2) []
         i3 = int_eq(i0, i1)
         guard_true(i3) []
         i4 = int_lt(i1, 5)
         guard_true(i4) []
-        jump(i0, i1)
-        """
-        expected = """
-        [i0, i1]
+        jump()
+        """
+        expected = """
+        []
+        i0 = escape()
+        i1 = escape()
         i2 = int_le(i0, 4)
         guard_true(i2) []
         i3 = int_eq(i0, i1)
         guard_true(i3) []
-        jump(i0, i1)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        jump()
+        """
+        self.optimize_loop(ops, expected)
 
     def test_bound_eq_const(self):
         ops = """
-        [i0]
+        []
+        i0 = escape()
         i1 = int_eq(i0, 7)
         guard_true(i1) []
         i2 = int_add(i0, 3)
-        jump(i2)
-        """
-        expected = """
-        [i0]
+        escape(i2)
+        jump()
+        """
+        expected = """
+        []
+        i0 = escape()
         i1 = int_eq(i0, 7)
         guard_true(i1) []
-        jump(10)
-
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        escape(10)
+        jump()
+        """
+        self.optimize_loop(ops, expected)
 
     def test_bound_eq_const_not(self):
         ops = """
@@ -3757,7 +4126,7 @@
         jump(i2)
 
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_bound_ne_const(self):
         ops = """
@@ -3767,14 +4136,7 @@
         i2 = int_add(i0, 3)
         jump(i2)
         """
-        expected = """
-        [i0]
-        i1 = int_ne(i0, 7)
-        guard_false(i1) []
-        jump(10)
-
-        """
-        self.optimize_loop(ops, 'Not', expected)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_bound_ne_const_not(self):
         ops = """
@@ -3791,7 +4153,7 @@
         i2 = int_add(i0, 3)
         jump(i2)
         """
-        self.optimize_loop(ops, 'Not', expected)
+        self.optimize_loop(ops, expected)
 
     def test_bound_ltne(self):
         ops = """
@@ -3802,13 +4164,17 @@
         guard_true(i2) []
         jump(i0, i1)
         """
-        expected = """
+        preamble = """
         [i0, i1]
         i2 = int_lt(i0, 7)
         guard_true(i2) []
         jump(i0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        expected = """
+        [i0, i1]
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_bound_lege_const(self):
         ops = """
@@ -3820,17 +4186,150 @@
         i3 = int_add(i0, 3)
         jump(i3)
         """
-        expected = """
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
+
+    def test_bound_lshift(self):
+        ops = """
+        [i0, i1, i1b, i2, i3]
+        i4 = int_lt(i1, 7)
+        guard_true(i4) []
+        i4b = int_lt(i1b, 7)
+        guard_true(i4b) []
+        i4c = int_ge(i1b, 0)
+        guard_true(i4c) []
+        i5 = int_lt(i3, 2)
+        guard_true(i5) []
+        i6 = int_ge(i3, 0)
+        guard_true(i6) []
+        i7 = int_lshift(i1, i3)
+        i8 = int_le(i7, 14)
+        guard_true(i8) []
+        i8b = int_lshift(i1, i2)
+        i9 = int_le(i8b, 14)
+        guard_true(i9) []
+        i10 = int_lshift(i0, i3)
+        i11 = int_le(i10, 14)
+        guard_true(i11) []
+        i12 = int_lt(i0, 15)
+        guard_true(i12) []
+        i13 = int_lshift(i1b, i3)
+        i14 = int_le(i13, 14)
+        guard_true(i14) []
+        i15 = int_lshift(i1b, i2)
+        i16 = int_le(i15, 14)
+        guard_true(i16) []
+        jump(i0, i1, i1b, i2, i3)
+        """
+        preamble = """
+        [i0, i1, i1b, i2, i3]        
+        i4 = int_lt(i1, 7)
+        guard_true(i4) []
+        i4b = int_lt(i1b, 7)
+        guard_true(i4b) []
+        i4c = int_ge(i1b, 0)
+        guard_true(i4c) []
+        i5 = int_lt(i3, 2)
+        guard_true(i5) []
+        i6 = int_ge(i3, 0)
+        guard_true(i6) []
+        i7 = int_lshift(i1, i3)
+        i8 = int_le(i7, 14)
+        guard_true(i8) []
+        i8b = int_lshift(i1, i2)
+        i9 = int_le(i8b, 14)
+        guard_true(i9) []
+        i10 = int_lshift(i0, i3)
+        i11 = int_le(i10, 14)
+        guard_true(i11) []
+        i13 = int_lshift(i1b, i3)
+        i15 = int_lshift(i1b, i2)
+        i16 = int_le(i15, 14)
+        guard_true(i16) []
+        jump(i0, i1, i1b, i2, i3)
+        """
+        expected = """
+        [i0, i1, i1b, i2, i3]
+        jump(i0, i1, i1b, i2, i3)        
+        """
+        self.optimize_loop(ops, expected, preamble)        
+
+    def test_bound_rshift(self):
+        ops = """
+        [i0, i1, i1b, i2, i3]
+        i4 = int_lt(i1, 7)
+        guard_true(i4) []
+        i4b = int_lt(i1b, 7)
+        guard_true(i4b) []
+        i4c = int_ge(i1b, 0)
+        guard_true(i4c) []
+        i5 = int_lt(i3, 2)
+        guard_true(i5) []
+        i6 = int_ge(i3, 0)
+        guard_true(i6) []
+        i7 = int_rshift(i1, i3)
+        i8 = int_le(i7, 14)
+        guard_true(i8) []
+        i8b = int_rshift(i1, i2)
+        i9 = int_le(i8b, 14)
+        guard_true(i9) []
+        i10 = int_rshift(i0, i3)
+        i11 = int_le(i10, 14)
+        guard_true(i11) []
+        i12 = int_lt(i0, 25)
+        guard_true(i12) []
+        i13 = int_rshift(i1b, i3)
+        i14 = int_le(i13, 14)
+        guard_true(i14) []
+        i15 = int_rshift(i1b, i2)
+        i16 = int_le(i15, 14)
+        guard_true(i16) []
+        jump(i0, i1, i1b, i2, i3)
+        """
+        preamble = """
+        [i0, i1, i1b, i2, i3]        
+        i4 = int_lt(i1, 7)
+        guard_true(i4) []
+        i4b = int_lt(i1b, 7)
+        guard_true(i4b) []
+        i4c = int_ge(i1b, 0)
+        guard_true(i4c) []
+        i5 = int_lt(i3, 2)
+        guard_true(i5) []
+        i6 = int_ge(i3, 0)
+        guard_true(i6) []
+        i7 = int_rshift(i1, i3)
+        i8b = int_rshift(i1, i2)
+        i9 = int_le(i8b, 14)
+        guard_true(i9) []
+        i10 = int_rshift(i0, i3)
+        i11 = int_le(i10, 14)
+        guard_true(i11) []
+        i12 = int_lt(i0, 25)
+        guard_true(i12) []
+        i13 = int_rshift(i1b, i3)
+        i15 = int_rshift(i1b, i2)
+        i16 = int_le(i15, 14)
+        guard_true(i16) []
+        jump(i0, i1, i1b, i2, i3)
+        """
+        expected = """
+        [i0, i1, i1b, i2, i3]
+        jump(i0, i1, i1b, i2, i3)        
+        """
+        self.optimize_loop(ops, expected, preamble)        
+
+    def test_bound_dont_backpropagate_rshift(self):
+        ops = """
         [i0]
-        i1 = int_ge(i0, 7)
-        guard_true(i1) []
-        i2 = int_le(i0, 7)
-        guard_true(i2) []
-        jump(10)
-
-        """
-        self.optimize_loop(ops, 'Not', expected)
-
+        i3 = int_rshift(i0, 1)
+        i5 = int_eq(i3, 1)
+        guard_true(i5) []
+        i11 = int_add(i0, 1)
+        jump(i11)
+        """
+        self.optimize_loop(ops, ops, ops)
+
+        
     def test_mul_ovf(self):
         ops = """
         [i0, i1]
@@ -3849,7 +4348,7 @@
         guard_true(i8) []
         jump(i0, i1)
         """
-        expected = """
+        preamble = """
         [i0, i1]
         i2 = int_and(i0, 255)
         i3 = int_lt(i1, 5)
@@ -3861,7 +4360,11 @@
         guard_true(i8) []
         jump(i0, i1)
         """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        expected = """
+        [i0, i1]
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_mul_ovf_before(self):
         ops = """
@@ -3878,7 +4381,7 @@
         guard_false(i6) []
         jump(i0, i1)
         """
-        expected = """
+        preamble = """
         [i0, i1]
         i2 = int_and(i0, 255)
         i22 = int_add(i2, 1)
@@ -3888,9 +4391,18 @@
         guard_true(i4) []
         i5 = int_gt(i3, 2)
         guard_true(i5) []
-        jump(i0, i1)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
+        jump(i0, i1, i22)
+        """
+        expected = """
+        [i0, i1, i22]
+        i3 = int_mul(i22, i1)
+        i4 = int_lt(i3, 10)
+        guard_true(i4) []
+        i5 = int_gt(i3, 2)
+        guard_true(i5) []
+        jump(i0, i1, i22)
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_sub_ovf_before(self):
         ops = """
@@ -3908,7 +4420,7 @@
         guard_false(i7) []
         jump(i0, i1)
         """
-        expected = """
+        preamble = """
         [i0, i1]
         i2 = int_and(i0, 255)
         i3 = int_sub_ovf(i2, i1)
@@ -3917,18 +4429,101 @@
         guard_true(i4) []
         i5 = int_ge(i3, 2)
         guard_true(i5) []
-        jump(i0, i1)
-        """
-        self.optimize_loop(ops, 'Not, Not', expected)
-
+        jump(i0, i1, i2)
+        """
+        expected = """
+        [i0, i1, i2]
+        i3 = int_sub(i2, i1)
+        i4 = int_le(i3, 10)
+        guard_true(i4) []
+        i5 = int_ge(i3, 2)
+        guard_true(i5) []
+        jump(i0, i1, i2)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_value_proven_to_be_constant_after_two_iterations(self):
+        class FakeDescr(AbstractDescr):
+            def __init__(self, name):
+                self.name = name
+            def sort_key(self):
+                return id(self)
+
+                
+        for n in ('inst_w_seq', 'inst_index', 'inst_w_list', 'inst_length',
+                  'inst_start', 'inst_step'):
+            self.namespace[n] = FakeDescr(n)
+        ops = """
+        [p0, p1, p2, p3, i4, p5, i6, p7, p8, p9, p14]
+        guard_value(i4, 3) []
+        guard_class(p9, 17278984) []
+        guard_class(p9, 17278984) []
+        p22 = getfield_gc(p9, descr=inst_w_seq)
+        guard_nonnull(p22) []
+        i23 = getfield_gc(p9, descr=inst_index)
+        p24 = getfield_gc(p22, descr=inst_w_list)
+        guard_isnull(p24) []
+        i25 = getfield_gc(p22, descr=inst_length)
+        i26 = int_ge(i23, i25)
+        guard_true(i26) []
+        setfield_gc(p9, ConstPtr(myptr), descr=inst_w_seq)
+
+        guard_nonnull(p14)  []
+        guard_class(p14, 17273920) []
+        guard_class(p14, 17273920) []
+
+        p75 = new_with_vtable(17278984)
+        setfield_gc(p75, p14, descr=inst_w_seq)
+        setfield_gc(p75, 0, descr=inst_index)
+        guard_class(p75, 17278984) []
+        guard_class(p75, 17278984) []
+        p79 = getfield_gc(p75, descr=inst_w_seq)
+        guard_nonnull(p79) []
+        i80 = getfield_gc(p75, descr=inst_index)
+        p81 = getfield_gc(p79, descr=inst_w_list)
+        guard_isnull(p81) []
+        i82 = getfield_gc(p79, descr=inst_length)
+        i83 = int_ge(i80, i82)
+        guard_false(i83) []
+        i84 = getfield_gc(p79, descr=inst_start)
+        i85 = getfield_gc(p79, descr=inst_step)
+        i86 = int_mul(i80, i85)
+        i87 = int_add(i84, i86)
+        i91 = int_add(i80, 1)
+        setfield_gc(p75, i91, descr=inst_index)
+        
+        p110 = same_as(ConstPtr(myptr))
+        i112 = same_as(3)
+        i114 = same_as(39)
+        jump(p0, p1, p110, p3, i112, p5, i114, p7, p8, p75, p14)
+        """
+        expected = """
+        [p0, p1, p3, p5, p7, p8, p14, i82]
+        i115 = int_ge(1, i82)
+        guard_true(i115) []
+        jump(p0, p1, p3, p5, p7, p8, p14, 1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_inputargs_added_by_forcing_jumpargs(self):
+        # FXIME: Can this occur?
+        ops = """
+        [p0, p1, pinv]
+        i1 = getfield_gc(pinv, descr=valuedescr)
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, i1, descr=nextdescr)
+        """
+        
     # ----------
-    def optimize_strunicode_loop(self, ops, spectext, optops):
+    def optimize_strunicode_loop(self, ops, optops, preamble=None):
+        if not preamble:
+            preamble = ops # FIXME: Force proper testing of preamble
         # check with the arguments passed in
-        self.optimize_loop(ops, spectext, optops)
+        self.optimize_loop(ops, optops, preamble)
         # check with replacing 'str' with 'unicode' everywhere
-        self.optimize_loop(ops.replace('str','unicode').replace('s"', 'u"'),
-                           spectext,
-                           optops.replace('str','unicode').replace('s"', 'u"'))
+        def r(s):
+            return s.replace('str','unicode').replace('s"', 'u"')
+        self.optimize_loop(r(ops), r(optops), r(preamble))
 
     def test_newstr_1(self):
         ops = """
@@ -3942,7 +4537,7 @@
         [i0]
         jump(i0)
         """
-        self.optimize_strunicode_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_newstr_2(self):
         ops = """
@@ -3958,7 +4553,7 @@
         [i0, i1]
         jump(i1, i0)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_1(self):
         ops = """
@@ -3979,7 +4574,7 @@
         copystrcontent(p2, p3, 0, i4, i5)
         jump(p2, p3)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_vstr2_str(self):
         ops = """
@@ -4002,7 +4597,7 @@
         copystrcontent(p2, p3, 0, 2, i4)
         jump(i1, i0, p3)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_str_vstr2(self):
         ops = """
@@ -4026,7 +4621,7 @@
         i6 = int_add(i5, 1)      # will be killed by the backend
         jump(i1, i0, p3)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_str_str_str(self):
         ops = """
@@ -4053,7 +4648,7 @@
         copystrcontent(p3, p5, 0, i12b, i3b)
         jump(p2, p3, p5)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_str_cstr1(self):
         ops = """
@@ -4072,7 +4667,7 @@
         i5 = int_add(i4, 1)      # will be killed by the backend
         jump(p3)
         """
-        self.optimize_strunicode_loop(ops, 'Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_concat_consts(self):
         ops = """
@@ -4083,12 +4678,18 @@
         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)
+        self.optimize_strunicode_loop(ops, expected, preamble)
 
     def test_str_slice_1(self):
         ops = """
@@ -4103,7 +4704,7 @@
         copystrcontent(p1, p2, i1, 0, i3)
         jump(p2, i1, i2)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_slice_2(self):
         ops = """
@@ -4117,7 +4718,7 @@
         copystrcontent(p1, p2, 0, 0, i2)
         jump(p2, i2)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_slice_3(self):
         ops = """
@@ -4135,7 +4736,7 @@
         copystrcontent(p1, p3, i6, 0, i5)
         jump(p3, i1, i2, i3, i4)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_slice_getitem1(self):
         ops = """
@@ -4153,7 +4754,7 @@
         escape(i4)
         jump(p1, i1, i2, i3)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_slice_plain(self):
         ops = """
@@ -4171,7 +4772,7 @@
         escape(i4)
         jump(i3, i4)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     def test_str_slice_concat(self):
         ops = """
@@ -4192,10 +4793,10 @@
         copystrcontent(p2, p4, 0, i3, i4b)
         jump(p4, i1, i2, p2)
         """
-        self.optimize_strunicode_loop(ops, 'Not, Not, Not, Not', expected)
+        self.optimize_strunicode_loop(ops, expected)
 
     # ----------
-    def optimize_strunicode_loop_extradescrs(self, ops, spectext, optops):
+    def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None):
         from pypy.jit.metainterp.optimizeopt import string
         class FakeCallInfoCollection:
             def callinfo_for_oopspec(self, oopspecindex):
@@ -4210,7 +4811,7 @@
                                      oopspecindex)
         #
         self.callinfocollection = FakeCallInfoCollection()
-        self.optimize_strunicode_loop(ops, spectext, optops)
+        self.optimize_strunicode_loop(ops, optops, preamble)
 
     def test_str_equal_noop1(self):
         ops = """
@@ -4219,7 +4820,7 @@
         escape(i0)
         jump(p1, p2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', ops)
+        self.optimize_strunicode_loop_extradescrs(ops, ops)
 
     def test_str_equal_noop2(self):
         ops = """
@@ -4244,7 +4845,7 @@
         escape(i0)
         jump(p1, p2, p3)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not',
+        self.optimize_strunicode_loop_extradescrs(ops,
                                                   expected)
 
     def test_str_equal_slice1(self):
@@ -4262,7 +4863,7 @@
         escape(i0)
         jump(p1, i1, i2, p3)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not',
+        self.optimize_strunicode_loop_extradescrs(ops,
                                                   expected)
 
     def test_str_equal_slice2(self):
@@ -4280,7 +4881,7 @@
         escape(i0)
         jump(p1, i1, i2, p3)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not',
+        self.optimize_strunicode_loop_extradescrs(ops,
                                                   expected)
 
     def test_str_equal_slice3(self):
@@ -4294,14 +4895,13 @@
         """
         expected = """
         [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)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not',
-                                                  expected)
+        self.optimize_strunicode_loop_extradescrs(ops,
+                                                  expected, ops)
 
     def test_str_equal_slice4(self):
         ops = """
@@ -4318,7 +4918,7 @@
         escape(i0)
         jump(p1, i1, i2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not',
+        self.optimize_strunicode_loop_extradescrs(ops,
                                                   expected)
 
     def test_str_equal_slice5(self):
@@ -4338,7 +4938,7 @@
         escape(i0)
         jump(p1, i1, i2, i3)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not, Not, Not',
+        self.optimize_strunicode_loop_extradescrs(ops,
                                                   expected)
 
     def test_str_equal_none1(self):
@@ -4354,7 +4954,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_none2(self):
         ops = """
@@ -4369,7 +4969,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_nonnull1(self):
         ops = """
@@ -4381,12 +4981,11 @@
         """
         expected = """
         [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, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_nonnull2(self):
         ops = """
@@ -4398,13 +4997,12 @@
         """
         expected = """
         [p1]
-        guard_nonnull(p1) []
         i1 = strlen(p1)
         i0 = int_eq(i1, 0)
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_nonnull3(self):
         ops = """
@@ -4416,12 +5014,11 @@
         """
         expected = """
         [p1]
-        guard_nonnull(p1) []
         i0 = call(0, p1, 120, descr=streq_nonnull_char_descr)
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_nonnull4(self):
         ops = """
@@ -4446,7 +5043,7 @@
         escape(i0)
         jump(p1, p2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_chars0(self):
         ops = """
@@ -4461,7 +5058,7 @@
         escape(1)
         jump(i1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_chars1(self):
         ops = """
@@ -4478,7 +5075,7 @@
         escape(i0)
         jump(i1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_chars2(self):
         ops = """
@@ -4499,7 +5096,7 @@
         escape(i0)
         jump(i1, i2)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not, Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_chars3(self):
         ops = """
@@ -4514,7 +5111,7 @@
         escape(i0)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str_equal_lengthmismatch1(self):
         ops = """
@@ -4530,7 +5127,7 @@
         escape(0)
         jump(i1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str2unicode_constant(self):
         ops = """
@@ -4544,7 +5141,7 @@
         escape(u"xy")
         jump()
         """
-        self.optimize_strunicode_loop_extradescrs(ops, '', expected)
+        self.optimize_strunicode_loop_extradescrs(ops, expected)
 
     def test_str2unicode_nonconstant(self):
         ops = """
@@ -4553,12 +5150,13 @@
         escape(p1)
         jump(p1)
         """
-        self.optimize_strunicode_loop_extradescrs(ops, 'Not', ops)
+        self.optimize_strunicode_loop_extradescrs(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()
 
 
+
 ##class TestOOtype(OptimizeOptTest, OOtypeMixin):
 
 ##    def test_instanceof(self):
@@ -4572,7 +5170,7 @@
 ##        [i0]
 ##        jump(1)
 ##        """
-##        self.optimize_loop(ops, 'Not', expected)
+##        self.optimize_loop(ops, expected)
 
 ##    def test_instanceof_guard_class(self):
 ##        ops = """
@@ -4586,4 +5184,4 @@
 ##        guard_class(p0, ConstClass(node_vtable)) []
 ##        jump(1, p0)
 ##        """
-##        self.optimize_loop(ops, 'Not, Not', expected)
+##        self.optimize_loop(ops, expected)


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
@@ -14,12 +14,14 @@
 from pypy.jit.metainterp.logger import Logger
 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
+from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE, \
+                                        ABORT_BAD_LOOP
 from pypy.jit.metainterp.jitexc import JitException, get_llexception
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib.objectmodel import specialize
 from pypy.jit.codewriter.jitcode import JitCode, SwitchDictDescr
 from pypy.jit.codewriter import heaptracker
+from pypy.jit.metainterp.optimizeutil import RetraceLoop
 
 # ____________________________________________________________
 
@@ -1384,6 +1386,11 @@
 
 # ____________________________________________________________
 
+class RetraceState(object):
+    def __init__(self, metainterp, live_arg_boxes):
+        self.merge_point = len(metainterp.current_merge_points) - 1
+        self.live_arg_boxes = live_arg_boxes
+
 class MetaInterp(object):
     in_recursion = 0
 
@@ -1397,6 +1404,7 @@
         self.portal_trace_positions = []
         self.free_frames_list = []
         self.last_exc_value_box = None
+        self.retracing_loop_from = None
 
     def perform_call(self, jitcode, boxes, greenkey=None):
         # causes the metainterp to enter the given subfunction
@@ -1747,7 +1755,15 @@
         #   that failed;
         # - if self.resumekey is a ResumeFromInterpDescr, it starts directly
         #   from the interpreter.
-        self.compile_bridge(live_arg_boxes)
+        if not self.retracing_loop_from:
+            try:
+                self.compile_bridge(live_arg_boxes)
+            except RetraceLoop:
+                start = len(self.history.operations)
+                self.current_merge_points.append((live_arg_boxes, start))
+                self.retracing_loop_from = RetraceState(self, live_arg_boxes)
+                return
+
         # raises in case it works -- which is the common case, hopefully,
         # at least for bridges starting from a guard.
 
@@ -1768,9 +1784,18 @@
             else:
                 # Found!  Compile it as a loop.
                 # raises in case it works -- which is the common case
-                self.compile(original_boxes, live_arg_boxes, start)
+                if self.retracing_loop_from and \
+                   self.retracing_loop_from.merge_point == j:
+                    bridge_arg_boxes = self.retracing_loop_from.live_arg_boxes
+                    self.compile_bridge_and_loop(original_boxes, \
+                                                 live_arg_boxes, start,
+                                                 bridge_arg_boxes)
+                else:
+                    self.compile(original_boxes, live_arg_boxes, start)
                 # creation of the loop was cancelled!
-                self.staticdata.log('cancelled, going on...')
+                #self.staticdata.log('cancelled, tracing more...')
+                self.staticdata.log('cancelled, stopping tracing')
+                raise SwitchToBlackhole(ABORT_BAD_LOOP)
 
         # Otherwise, no loop found so far, so continue tracing.
         start = len(self.history.operations)
@@ -1779,8 +1804,7 @@
     def designate_target_loop(self, gmp):
         loop_token = gmp.target_loop_token
         num_green_args = self.jitdriver_sd.num_green_args
-        residual_args = self.get_residual_args(loop_token.specnodes,
-                                               gmp.argboxes[num_green_args:])
+        residual_args = gmp.argboxes[num_green_args:]
         history.set_future_values(self.cpu, residual_args)
         return loop_token
 
@@ -1836,11 +1860,13 @@
         greenkey = original_boxes[:num_green_args]
         old_loop_tokens = self.get_compiled_merge_points(greenkey)
         self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None)
-        loop_token = compile.compile_new_loop(self, old_loop_tokens, start)
+        loop_token = compile.compile_new_loop(self, old_loop_tokens,
+                                              greenkey, start)
         if loop_token is not None: # raise if it *worked* correctly
             self.set_compiled_merge_points(greenkey, old_loop_tokens)
             raise GenerateMergePoint(live_arg_boxes, loop_token)
         self.history.operations.pop()     # remove the JUMP
+        # FIXME: Why is self.history.inputargs not restored?
 
     def compile_bridge(self, live_arg_boxes):
         num_green_args = self.jitdriver_sd.num_green_args
@@ -1848,12 +1874,51 @@
         old_loop_tokens = self.get_compiled_merge_points(greenkey)
         if len(old_loop_tokens) == 0:
             return
+        #if self.resumekey.guard_opnum == rop.GUARD_CLASS:
+        #    return # Kepp tracing for another iteration
         self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None)
-        target_loop_token = compile.compile_new_bridge(self, old_loop_tokens,
-                                                       self.resumekey)
-        if target_loop_token is not None:   # raise if it *worked* correctly
-            raise GenerateMergePoint(live_arg_boxes, target_loop_token)
+        try:
+            target_loop_token = compile.compile_new_bridge(self,
+                                                           old_loop_tokens,
+                                                           self.resumekey)
+            if target_loop_token is not None: # raise if it *worked* correctly
+                raise GenerateMergePoint(live_arg_boxes, target_loop_token)
+        finally:
+            self.history.operations.pop()     # remove the JUMP
+
+    def compile_bridge_and_loop(self, original_boxes, live_arg_boxes, start,
+                                bridge_arg_boxes):
+        num_green_args = self.jitdriver_sd.num_green_args
+        original_inputargs = self.history.inputargs
+        greenkey = original_boxes[:num_green_args]
+        old_loop_tokens = self.get_compiled_merge_points(greenkey)
+        original_operations = self.history.operations
+        self.history.inputargs = original_boxes[num_green_args:]
+        greenkey = original_boxes[:num_green_args]
+        self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None)
+        loop_token = compile.compile_new_loop(self, [], greenkey, start)
         self.history.operations.pop()     # remove the JUMP
+        if loop_token is None:
+            return
+
+        if loop_token.short_preamble:
+            old_loop_tokens[0].short_preamble.extend(loop_token.short_preamble)
+
+        self.history.inputargs = original_inputargs
+        self.history.operations = self.history.operations[:start]
+        live_arg_boxes = bridge_arg_boxes
+        
+        self.history.record(rop.JUMP, live_arg_boxes[num_green_args:], None)
+        try:
+            target_loop_token = compile.compile_new_bridge(self,
+                                                           [loop_token],
+                                                           self.resumekey)
+        except RetraceLoop:
+            assert False
+        assert target_loop_token is not None
+
+        self.history.operations = original_operations
+        raise GenerateMergePoint(live_arg_boxes, old_loop_tokens[0])
 
     def compile_done_with_this_frame(self, exitbox):
         self.gen_store_back_in_virtualizable()
@@ -1892,16 +1957,6 @@
         if target_loop_token is not loop_tokens[0]:
             compile.giveup()
 
-    def get_residual_args(self, specnodes, args):
-        if specnodes is None:     # it is None only for tests
-            return args
-        assert len(specnodes) == len(args)
-        expanded_args = []
-        for i in range(len(specnodes)):
-            specnode = specnodes[i]
-            specnode.extract_runtime_data(self.cpu, args[i], expanded_args)
-        return expanded_args
-
     @specialize.arg(1)
     def initialize_original_boxes(self, jitdriver_sd, *args):
         original_boxes = []


diff --git a/pypy/jit/metainterp/test/test_basic.py b/pypy/jit/metainterp/test/test_basic.py
--- a/pypy/jit/metainterp/test/test_basic.py
+++ b/pypy/jit/metainterp/test/test_basic.py
@@ -309,6 +309,123 @@
                     found += 1
             assert found == 1
 
+    def test_loop_invariant_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
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 7])
+        assert res == 252
+        self.check_loop_count(1)
+        self.check_loops({'guard_true': 1,
+                          'int_add': 1, 'int_sub': 1, 'int_gt': 1,
+                          'jump': 1})
+
+    def test_loop_invariant_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)
+                b = y * 2
+                res += ovfcheck(x * x) + b
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 7])
+        assert res == 308
+        self.check_loop_count(1)
+        self.check_loops({'guard_true': 1,
+                          'int_add': 2, 'int_sub': 1, 'int_gt': 1,
+                          'int_mul': 1,
+                          'jump': 1})
+
+    def test_loop_invariant_mul_bridge1(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
+                if y<16:
+                    x += 1
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32])
+        assert res == 3427
+        self.check_loop_count(3)
+
+    def test_loop_invariant_mul_bridge_maintaining1(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
+                if y<16:
+                    res += 1
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32])
+        assert res == 1167
+        self.check_loop_count(3)
+        self.check_loops({'int_add': 2, 'int_lt': 1,
+                          'int_sub': 2, 'guard_false': 1,
+                          'jump': 2,
+                          'int_gt': 1, 'guard_true': 1, 'int_mul': 1})
+
+
+    def test_loop_invariant_mul_bridge_maintaining2(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)
+                z = x * x
+                res += z
+                if y<16:
+                    res += z
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32])
+        assert res == 1692
+        self.check_loop_count(3)
+        self.check_loops({'int_add': 2, 'int_lt': 1,
+                          'int_sub': 2, 'guard_false': 1,
+                          'jump': 2,
+                          'int_gt': 1, 'guard_true': 1, 'int_mul': 1})
+
+    def test_loop_invariant_intbox(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
+        class I:
+            __slots__ = 'intval'
+            _immutable_ = True
+            def __init__(self, intval):
+                self.intval = intval
+        def f(i, y):
+            res = 0
+            x = I(i)
+            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.intval * x.intval
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 7])
+        assert res == 252
+        self.check_loop_count(1)
+        self.check_loops({'guard_true': 1,
+                          'int_add': 1, 'int_sub': 1, 'int_gt': 1,
+                          'jump': 1})
+
     def test_loops_are_transient(self):
         import gc, weakref
         myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'res'])
@@ -336,7 +453,9 @@
         assert res == f(6, 15)
         gc.collect()
 
-        assert not [wr for wr in wr_loops if wr()]
+        #assert not [wr for wr in wr_loops if wr()]
+        for loop in [wr for wr in wr_loops if wr()]:
+            assert loop().name == 'short preamble'
 
     def test_string(self):
         def f(n):
@@ -484,7 +603,7 @@
         res = self.meta_interp(f, [21, 5])
         assert res == -1
         # the CALL_PURE is constant-folded away by optimizeopt.py
-        self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=1)
+        self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=0)
 
     def test_constant_across_mp(self):
         myjitdriver = JitDriver(greens = [], reds = ['n'])
@@ -857,10 +976,9 @@
         self.meta_interp(f, [20], repeat=7)
         self.check_tree_loop_count(2)      # the loop and the entry path
         # we get:
-        #    ENTER             - compile the new loop
-        #    ENTER             - compile the entry bridge
+        #    ENTER             - compile the new loop and the entry bridge
         #    ENTER             - compile the leaving path
-        self.check_enter_count(3)
+        self.check_enter_count(2)
 
     def test_bridge_from_interpreter_2(self):
         # one case for backend - computing of framesize on guard failure
@@ -1238,7 +1356,7 @@
 
         res = self.meta_interp(f, [10, 3])
         assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
-        self.check_tree_loop_count(1)
+        self.check_tree_loop_count(2)
 
         res = self.meta_interp(f, [10, 13])
         assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
@@ -1343,7 +1461,8 @@
             return x
         res = self.meta_interp(f, [299], listops=True)
         assert res == f(299)
-        self.check_loops(guard_class=0, guard_value=3)
+        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
@@ -1373,6 +1492,9 @@
         assert res == f(299)
         self.check_loops(guard_class=0, guard_nonnull=0,
                          guard_nonnull_class=2, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=0,
+                         guard_nonnull_class=4, guard_isnull=2,
+                         everywhere=True)
 
     def test_merge_guardnonnull_guardvalue(self):
         from pypy.rlib.objectmodel import instantiate
@@ -1401,6 +1523,9 @@
         assert res == f(299)
         self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
                          guard_nonnull_class=0, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=4,
+                         guard_nonnull_class=0, guard_isnull=2,
+                         everywhere=True)
 
     def test_merge_guardnonnull_guardvalue_2(self):
         from pypy.rlib.objectmodel import instantiate
@@ -1429,6 +1554,9 @@
         assert res == f(299)
         self.check_loops(guard_class=0, guard_nonnull=0, guard_value=2,
                          guard_nonnull_class=0, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=4,
+                         guard_nonnull_class=0, guard_isnull=2,
+                         everywhere=True)
 
     def test_merge_guardnonnull_guardclass_guardvalue(self):
         from pypy.rlib.objectmodel import instantiate
@@ -1460,6 +1588,9 @@
         assert res == f(399)
         self.check_loops(guard_class=0, guard_nonnull=0, guard_value=3,
                          guard_nonnull_class=0, guard_isnull=1)
+        self.check_loops(guard_class=0, guard_nonnull=0, guard_value=6,
+                         guard_nonnull_class=0, guard_isnull=2,
+                         everywhere=True)
 
     def test_residual_call_doesnt_lose_info(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l'])
@@ -1485,7 +1616,8 @@
                 y.v = g(y.v) - y.v/y.v + lc/l[0] - 1
             return y.v
         res = self.meta_interp(f, [20], listops=True)
-        self.check_loops(getfield_gc=1, getarrayitem_gc=0)
+        self.check_loops(getfield_gc=0, getarrayitem_gc=0)
+        self.check_loops(getfield_gc=1, getarrayitem_gc=0, everywhere=True)
 
     def test_guard_isnull_nonnull(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'res'])
@@ -1515,7 +1647,7 @@
         assert res == 42
         self.check_loops(guard_nonnull=1, guard_isnull=1)
 
-    def test_loop_invariant(self):
+    def test_loop_invariant1(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'res'])
         class A(object):
             pass
@@ -1540,7 +1672,8 @@
             return res
         res = self.meta_interp(g, [21])
         assert res == 3 * 21
-        self.check_loops(call=1)
+        self.check_loops(call=0)
+        self.check_loops(call=1, everywhere=True)
 
     def test_bug_optimizeopt_mutates_ops(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'res', 'const', 'a'])
@@ -1676,6 +1809,171 @@
         assert res == 8
         py.test.raises(AssertGreenFailed, self.interp_operations, f, [8, 0])
 
+    def test_multiple_specialied_versions1(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res'])
+        class Base:
+            def __init__(self, val):
+                self.val = val
+        class A(Base):
+            def binop(self, other):
+                return A(self.val + other.val)
+        class B(Base):
+            def binop(self, other):
+                return B(self.val * other.val)
+        def f(x, y):
+            res = x
+            while y > 0:
+                myjitdriver.can_enter_jit(y=y, x=x, res=res)
+                myjitdriver.jit_merge_point(y=y, x=x, res=res)
+                res = res.binop(x)
+                y -= 1
+            return res
+        def g(x, y):
+            a1 = f(A(x), y)
+            a2 = f(A(x), y)
+            b1 = f(B(x), y)
+            b2 = f(B(x), y)
+            assert a1.val == a2.val
+            assert b1.val == b2.val
+            return a1.val + b1.val
+        res = self.meta_interp(g, [6, 7])
+        assert res == 6*8 + 6**8
+        self.check_loop_count(5)
+        self.check_loops({'guard_true': 2,
+                          'int_add': 1, 'int_mul': 1, 'int_sub': 2,
+                          'int_gt': 2, 'jump': 2})
+
+    def test_multiple_specialied_versions_bridge(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'z', 'res'])
+        class Base:
+            def __init__(self, val):
+                self.val = val
+            def getval(self):
+                return self.val
+        class A(Base):
+            def binop(self, other):
+                return A(self.getval() + other.getval())
+        class B(Base):
+            def binop(self, other):
+                return B(self.getval() * other.getval())
+        def f(x, y, z):
+            res = x
+            while y > 0:
+                myjitdriver.can_enter_jit(y=y, x=x, z=z, res=res)
+                myjitdriver.jit_merge_point(y=y, x=x, z=z, res=res)
+                res = res.binop(x)
+                y -= 1
+                if y < 7:
+                    x = z
+            return res
+        def g(x, y):
+            a1 = f(A(x), y, A(x))
+            a2 = f(A(x), y, A(x))
+            b1 = f(B(x), y, B(x))
+            b2 = f(B(x), y, B(x))
+            c1 = f(B(x), y, A(x))
+            c2 = f(B(x), y, A(x))
+            d1 = f(A(x), y, B(x))
+            d2 = f(A(x), y, B(x))
+            assert a1.val == a2.val
+            assert b1.val == b2.val
+            assert c1.val == c2.val
+            assert d1.val == d2.val
+            return a1.val + b1.val + c1.val + d1.val
+        res = self.meta_interp(g, [3, 14])
+        assert res == g(3, 14)
+
+    def test_specialied_bridge(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res'])
+        class A:
+            def __init__(self, val):
+                self.val = val
+            def binop(self, other):
+                return A(self.val + other.val)
+        def f(x, y):
+            res = A(0)
+            while y > 0:
+                myjitdriver.can_enter_jit(y=y, x=x, res=res)
+                myjitdriver.jit_merge_point(y=y, x=x, res=res)
+                res = res.binop(A(y))
+                if y<7:
+                    res = x
+                y -= 1
+            return res
+        def g(x, y):
+            a1 = f(A(x), y)
+            a2 = f(A(x), y)
+            assert a1.val == a2.val
+            return a1.val
+        res = self.meta_interp(g, [6, 14])
+        assert res == g(6, 14)
+
+    def test_specialied_bridge_const(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'const', 'x', 'res'])
+        class A:
+            def __init__(self, val):
+                self.val = val
+            def binop(self, other):
+                return A(self.val + other.val)
+        def f(x, y):
+            res = A(0)
+            const = 7
+            while y > 0:
+                myjitdriver.can_enter_jit(y=y, x=x, res=res, const=const)
+                myjitdriver.jit_merge_point(y=y, x=x, res=res, const=const)
+                const = hint(const, promote=True)
+                res = res.binop(A(const))
+                if y<7:
+                    res = x
+                y -= 1
+            return res
+        def g(x, y):
+            a1 = f(A(x), y)
+            a2 = f(A(x), y)
+            assert a1.val == a2.val
+            return a1.val
+        res = self.meta_interp(g, [6, 14])
+        assert res == g(6, 14)
+
+    def test_multiple_specialied_zigzag(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res'])
+        class Base:
+            def __init__(self, val):
+                self.val = val
+        class A(Base):
+            def binop(self, other):
+                return A(self.val + other.val)
+            def switch(self):
+                return B(self.val)
+        class B(Base):
+            def binop(self, other):
+                return B(self.val * other.val)
+            def switch(self):
+                return A(self.val)
+        def f(x, y):
+            res = x
+            while y > 0:
+                myjitdriver.can_enter_jit(y=y, x=x, res=res)
+                myjitdriver.jit_merge_point(y=y, x=x, res=res)
+                if y % 4 == 0:
+                    res = res.switch()
+                res = res.binop(x)
+                y -= 1
+            return res
+        def g(x, y):
+            a1 = f(A(x), y)
+            a2 = f(A(x), y)
+            b1 = f(B(x), y)
+            b2 = f(B(x), y)
+            assert a1.val == a2.val
+            assert b1.val == b2.val
+            return a1.val + b1.val
+        res = self.meta_interp(g, [3, 23])
+        assert res == 7068153
+        self.check_loop_count(6)
+        self.check_loops(guard_true=4, guard_class=0, int_add=2, int_mul=2,
+                         guard_false=2)
+
     def test_current_trace_length(self):
         myjitdriver = JitDriver(greens = ['g'], reds = ['x'])
         @dont_look_inside

diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -489,6 +489,9 @@
     def _get_str(self):    # for debugging only
         return self.constbox()._get_str()
 
+    def forget_value(self):
+        raise NotImplementedError
+
 class BoxInt(Box):
     type = INT
     _attrs_ = ('value',)
@@ -501,6 +504,9 @@
                 assert isinstance(value, Symbolic)
         self.value = value
 
+    def forget_value(self):
+        self.value = 0
+        
     def clonebox(self):
         return BoxInt(self.value)
 
@@ -536,6 +542,9 @@
         assert isinstance(floatval, float)
         self.value = floatval
 
+    def forget_value(self):
+        self.value = 0.0
+
     def clonebox(self):
         return BoxFloat(self.value)
 
@@ -568,6 +577,9 @@
         assert lltype.typeOf(value) == llmemory.GCREF
         self.value = value
 
+    def forget_value(self):
+        self.value = lltype.nullptr(llmemory.GCREF.TO)
+
     def clonebox(self):
         return BoxPtr(self.value)
 
@@ -612,6 +624,9 @@
         assert ootype.typeOf(value) is ootype.Object
         self.value = value
 
+    def forget_value(self):
+        self.value = ootype.NULL
+
     def clonebox(self):
         return BoxObj(self.value)
 
@@ -729,9 +744,9 @@
     was compiled; but the LoopDescr remains alive and points to the
     generated assembler.
     """
+    short_preamble = None
     terminating = False # see TerminatingLoopToken in compile.py
     outermost_jitdriver_sd = None
-    # specnodes = ...
     # and more data specified by the backend when the loop is compiled
     number = -1
     generation = r_int64(0)

diff --git a/pypy/jit/metainterp/test/test_optimizefindnode.py b/pypy/jit/metainterp/test/test_optimizefindnode.py
deleted file mode 100644
--- a/pypy/jit/metainterp/test/test_optimizefindnode.py
+++ /dev/null
@@ -1,1199 +0,0 @@
-import py, random
-
-from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr
-from pypy.rpython.ootypesystem import ootype
-from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
-
-from pypy.jit.backend.llgraph import runner
-from pypy.jit.metainterp.history import (BoxInt, BoxPtr, ConstInt, ConstPtr,
-                                         Const, TreeLoop, BoxObj,
-                                         ConstObj, AbstractDescr)
-from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder
-from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder
-from pypy.jit.metainterp.optimizeutil import sort_descrs, InvalidLoop
-from pypy.jit.metainterp.specnode import NotSpecNode, prebuiltNotSpecNode
-from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode
-from pypy.jit.metainterp.specnode import VirtualArraySpecNode
-from pypy.jit.metainterp.specnode import VirtualStructSpecNode
-from pypy.jit.metainterp.specnode import ConstantSpecNode
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.codewriter.heaptracker import register_known_gctype, adr2int
-from pypy.jit.tool.oparser import parse
-
-def test_sort_descrs():
-    class PseudoDescr(AbstractDescr):
-        def __init__(self, n):
-            self.n = n
-        def sort_key(self):
-            return self.n
-    for i in range(17):
-        lst = [PseudoDescr(j) for j in range(i)]
-        lst2 = lst[:]
-        random.shuffle(lst2)
-        sort_descrs(lst2)
-        assert lst2 == lst
-
-# ____________________________________________________________
-
-class LLtypeMixin(object):
-    type_system = 'lltype'
-
-    def get_class_of_box(self, box):
-        return box.getref(rclass.OBJECTPTR).typeptr
-
-    node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
-    node_vtable.name = rclass.alloc_array_name('node')
-    node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable)
-    node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True)
-    node_vtable2.name = rclass.alloc_array_name('node2')
-    node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2)
-    cpu = runner.LLtypeCPU(None)
-
-    NODE = lltype.GcForwardReference()
-    NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT),
-                                        ('value', lltype.Signed),
-                                        ('floatval', lltype.Float),
-                                        ('next', lltype.Ptr(NODE))))
-    NODE2 = lltype.GcStruct('NODE2', ('parent', NODE),
-                                     ('other', lltype.Ptr(NODE)))
-    node = lltype.malloc(NODE)
-    node.parent.typeptr = node_vtable
-    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))
-    nodesize = cpu.sizeof(NODE)
-    nodesize2 = cpu.sizeof(NODE2)
-    valuedescr = cpu.fielddescrof(NODE, 'value')
-    floatdescr = cpu.fielddescrof(NODE, 'floatval')
-    nextdescr = cpu.fielddescrof(NODE, 'next')
-    otherdescr = cpu.fielddescrof(NODE2, 'other')
-
-    NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT),
-                                         ('ref', lltype.Ptr(OBJECT)))
-    nodeobj = lltype.malloc(NODEOBJ)
-    nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj)
-    refdescr = cpu.fielddescrof(NODEOBJ, 'ref')
-
-    arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed))
-    floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float))
-
-    # a GcStruct not inheriting from OBJECT
-    S = lltype.GcStruct('TUPLE', ('a', lltype.Signed), ('b', lltype.Ptr(NODE)))
-    ssize = cpu.sizeof(S)
-    adescr = cpu.fielddescrof(S, 'a')
-    bdescr = cpu.fielddescrof(S, 'b')
-    sbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S)))
-    arraydescr2 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(S)))
-
-    T = lltype.GcStruct('TUPLE',
-                        ('c', lltype.Signed),
-                        ('d', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE)))))
-    tsize = cpu.sizeof(T)
-    cdescr = cpu.fielddescrof(T, 'c')
-    ddescr = cpu.fielddescrof(T, 'd')
-    arraydescr3 = cpu.arraydescrof(lltype.GcArray(lltype.Ptr(NODE)))
-
-    U = lltype.GcStruct('U',
-                        ('parent', OBJECT),
-                        ('one', lltype.Ptr(lltype.GcArray(lltype.Ptr(NODE)))))
-    u_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
-    u_vtable_adr = llmemory.cast_ptr_to_adr(u_vtable)
-    usize = cpu.sizeof(U)
-    onedescr = cpu.fielddescrof(U, 'one')
-
-    FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
-    plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
-    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                    EffectInfo([], [], []))
-    writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                  EffectInfo([], [adescr], []))
-    writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                      EffectInfo([], [adescr], [arraydescr]))
-    readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                 EffectInfo([adescr], [], []))
-    mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([nextdescr], [], [],
-                            EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE))
-    arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
-
-    for _name, _os in [
-        ('strconcatdescr',               'OS_STR_CONCAT'),
-        ('strslicedescr',                'OS_STR_SLICE'),
-        ('strequaldescr',                'OS_STR_EQUAL'),
-        ('streq_slice_checknull_descr',  'OS_STREQ_SLICE_CHECKNULL'),
-        ('streq_slice_nonnull_descr',    'OS_STREQ_SLICE_NONNULL'),
-        ('streq_slice_char_descr',       'OS_STREQ_SLICE_CHAR'),
-        ('streq_nonnull_descr',          'OS_STREQ_NONNULL'),
-        ('streq_nonnull_char_descr',     'OS_STREQ_NONNULL_CHAR'),
-        ('streq_checknull_char_descr',   'OS_STREQ_CHECKNULL_CHAR'),
-        ('streq_lengthok_descr',         'OS_STREQ_LENGTHOK'),
-        ]:
-        _oopspecindex = getattr(EffectInfo, _os)
-        locals()[_name] = \
-            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=_oopspecindex))
-        #
-        _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
-        locals()[_name.replace('str', 'unicode')] = \
-            cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=_oopspecindex))
-
-    s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
-    #
-
-    class LoopToken(AbstractDescr):
-        pass
-    asmdescr = LoopToken() # it can be whatever, it's not a descr though
-
-    from pypy.jit.metainterp.virtualref import VirtualRefInfo
-    class FakeWarmRunnerDesc:
-        pass
-    FakeWarmRunnerDesc.cpu = cpu
-    vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc)
-    virtualtokendescr = vrefinfo.descr_virtual_token
-    virtualrefindexdescr = vrefinfo.descr_virtualref_index
-    virtualforceddescr = vrefinfo.descr_forced
-    jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable
-    jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable)
-
-    register_known_gctype(cpu, node_vtable,  NODE)
-    register_known_gctype(cpu, node_vtable2, NODE2)
-    register_known_gctype(cpu, u_vtable,     U)
-    register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF)
-
-    namespace = locals()
-
-class OOtypeMixin_xxx_disabled(object):
-    type_system = 'ootype'
-
-##    def get_class_of_box(self, box):
-##        root = box.getref(ootype.ROOT)
-##        return ootype.classof(root)
-    
-##    cpu = runner.OOtypeCPU(None)
-##    NODE = ootype.Instance('NODE', ootype.ROOT, {})
-##    NODE._add_fields({'value': ootype.Signed,
-##                      'floatval' : ootype.Float,
-##                      'next': NODE})
-##    NODE2 = ootype.Instance('NODE2', NODE, {'other': NODE})
-
-##    node_vtable = ootype.runtimeClass(NODE)
-##    node_vtable_adr = ootype.cast_to_object(node_vtable)
-##    node_vtable2 = ootype.runtimeClass(NODE2)
-##    node_vtable_adr2 = ootype.cast_to_object(node_vtable2)
-
-##    node = ootype.new(NODE)
-##    nodebox = BoxObj(ootype.cast_to_object(node))
-##    myptr = nodebox.value
-##    myptr2 = ootype.cast_to_object(ootype.new(NODE))
-##    nodebox2 = BoxObj(ootype.cast_to_object(node))
-##    valuedescr = cpu.fielddescrof(NODE, 'value')
-##    floatdescr = cpu.fielddescrof(NODE, 'floatval')
-##    nextdescr = cpu.fielddescrof(NODE, 'next')
-##    otherdescr = cpu.fielddescrof(NODE2, 'other')
-##    nodesize = cpu.typedescrof(NODE)
-##    nodesize2 = cpu.typedescrof(NODE2)
-
-##    arraydescr = cpu.arraydescrof(ootype.Array(ootype.Signed))
-##    floatarraydescr = cpu.arraydescrof(ootype.Array(ootype.Float))
-
-##    # a plain Record
-##    S = ootype.Record({'a': ootype.Signed, 'b': NODE})
-##    ssize = cpu.typedescrof(S)
-##    adescr = cpu.fielddescrof(S, 'a')
-##    bdescr = cpu.fielddescrof(S, 'b')
-##    sbox = BoxObj(ootype.cast_to_object(ootype.new(S)))
-##    arraydescr2 = cpu.arraydescrof(ootype.Array(S))
-
-##    T = ootype.Record({'c': ootype.Signed,
-##                       'd': ootype.Array(NODE)})
-##    tsize = cpu.typedescrof(T)
-##    cdescr = cpu.fielddescrof(T, 'c')
-##    ddescr = cpu.fielddescrof(T, 'd')
-##    arraydescr3 = cpu.arraydescrof(ootype.Array(NODE))
-
-##    U = ootype.Instance('U', ootype.ROOT, {'one': ootype.Array(NODE)})
-##    usize = cpu.typedescrof(U)
-##    onedescr = cpu.fielddescrof(U, 'one')
-##    u_vtable = ootype.runtimeClass(U)
-##    u_vtable_adr = ootype.cast_to_object(u_vtable)
-
-##    # force a consistent order
-##    valuedescr.sort_key()
-##    nextdescr.sort_key()
-##    adescr.sort_key()
-##    bdescr.sort_key()
-
-##    FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
-##    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) # XXX fix ootype
-
-##    cpu.class_sizes = {node_vtable_adr: cpu.typedescrof(NODE),
-##                       node_vtable_adr2: cpu.typedescrof(NODE2),
-##                       u_vtable_adr: cpu.typedescrof(U)}
-##    namespace = locals()
-
-class BaseTest(object):
-    invent_fail_descr = None
-
-    def parse(self, s, boxkinds=None):
-        return parse(s, self.cpu, self.namespace,
-                     type_system=self.type_system,
-                     boxkinds=boxkinds,
-                     invent_fail_descr=self.invent_fail_descr)
-
-    def unpack_specnodes(self, text):
-        #
-        def constclass(cls_vtable):
-            if self.type_system == 'lltype':
-                return ConstInt(adr2int(llmemory.cast_ptr_to_adr(cls_vtable)))
-            else:
-                return ConstObj(ootype.cast_to_object(cls_vtable))
-        def constant(value):
-            if isinstance(lltype.typeOf(value), lltype.Ptr):
-                return ConstPtr(value)
-            elif isinstance(ootype.typeOf(value), ootype.OOType):
-                return ConstObj(ootype.cast_to_object(value))
-            else:
-                return ConstInt(value)
-
-        def parsefields(kwds_fields):
-            fields = []
-            for key, value in kwds_fields.items():
-                fields.append((self.namespace[key], value))
-            fields.sort(key = lambda (x, _): x.sort_key())
-            return fields
-        def makeConstant(value):
-            return ConstantSpecNode(constant(value))
-        def makeVirtual(cls_vtable, **kwds_fields):
-            fields = parsefields(kwds_fields)
-            return VirtualInstanceSpecNode(constclass(cls_vtable), fields)
-        def makeVirtualArray(arraydescr, *items):
-            return VirtualArraySpecNode(arraydescr, items)
-        def makeVirtualStruct(typedescr, **kwds_fields):
-            fields = parsefields(kwds_fields)
-            return VirtualStructSpecNode(typedescr, fields)
-        #
-        context = {'Not': prebuiltNotSpecNode,
-                   'Constant': makeConstant,
-                   'Virtual': makeVirtual,
-                   'VArray': makeVirtualArray,
-                   'VStruct': makeVirtualStruct}
-        lst = eval('[' + text + ']', self.namespace, context)
-        return lst
-
-    def check_specnodes(self, specnodes, text):
-        lst = self.unpack_specnodes(text)
-        assert len(specnodes) == len(lst)
-        for x, y in zip(specnodes, lst):
-            assert x.equals(y, ge=False)
-        return True
-
-# ____________________________________________________________
-
-class BaseTestOptimizeFindNode(BaseTest):
-
-    def find_nodes(self, ops, spectext, boxkinds=None):
-        assert boxkinds is None or isinstance(boxkinds, dict)
-        loop = self.parse(ops, boxkinds=boxkinds)
-        perfect_specialization_finder = PerfectSpecializationFinder(self.cpu)
-        perfect_specialization_finder.find_nodes_loop(loop)
-        self.check_specnodes(loop.token.specnodes, spectext)
-        return (loop.getboxes(), perfect_specialization_finder.getnode)
-
-    def test_find_nodes_simple(self):
-        ops = """
-        [i]
-        i0 = int_sub(i, 1)
-        guard_value(i0, 0) [i0]
-        jump(i0)
-        """
-        boxes, getnode = self.find_nodes(ops, 'Not')
-        assert getnode(boxes.i).fromstart
-        assert not getnode(boxes.i0).fromstart
-
-    def test_find_nodes_non_escape(self):
-        ops = """
-        [p0]
-        p1 = getfield_gc(p0, descr=nextdescr)
-        i0 = getfield_gc(p1, descr=valuedescr)
-        i1 = int_sub(i0, 1)
-        p2 = getfield_gc(p0, descr=nextdescr)
-        setfield_gc(p2, i1, descr=valuedescr)
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        jump(p3)
-        """
-        boxes, getnode = self.find_nodes(ops,
-                                         'Virtual(node_vtable, nextdescr=Not)')
-        assert not getnode(boxes.p0).escaped
-        assert getnode(boxes.p1).escaped
-        assert getnode(boxes.p2).escaped
-        assert getnode(boxes.p0).fromstart
-        assert getnode(boxes.p1).fromstart
-        assert getnode(boxes.p2).fromstart
-
-    def test_find_nodes_escape(self):
-        ops = """
-        [p0]
-        p1 = getfield_gc(p0, descr=nextdescr)
-        p2 = getfield_gc(p1, descr=nextdescr)
-        i0 = getfield_gc(p2, descr=valuedescr)
-        i1 = int_sub(i0, 1)
-        escape(p1)
-        p3 = getfield_gc(p0, descr=nextdescr)
-        setfield_gc(p3, i1, descr=valuedescr)
-        p4 = getfield_gc(p1, descr=nextdescr)
-        setfield_gc(p4, i1, descr=valuedescr)
-        p5 = new_with_vtable(ConstClass(node_vtable))
-        jump(p5)
-        """
-        boxes, getnode = self.find_nodes(ops,
-                                         'Virtual(node_vtable, nextdescr=Not)')
-        assert not getnode(boxes.p0).escaped
-        assert getnode(boxes.p1).escaped
-        assert getnode(boxes.p2).escaped    # forced by p1
-        assert getnode(boxes.p3).escaped    # forced because p3 == p1
-        assert getnode(boxes.p4).escaped    # forced by p1
-        assert getnode(boxes.p0).fromstart
-        assert getnode(boxes.p1).fromstart
-        assert getnode(boxes.p2).fromstart
-        assert getnode(boxes.p3).fromstart
-        assert not getnode(boxes.p4).fromstart
-
-    def test_find_nodes_new_1(self):
-        ops = """
-        [p1]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        jump(p2)
-        """
-        boxes, getnode = self.find_nodes(ops, 'Virtual(node_vtable)')
-
-        boxp1 = getnode(boxes.p1)
-        boxp2 = getnode(boxes.p2)
-        assert not boxp1.escaped
-        assert not boxp2.escaped
-
-        assert not boxp1.origfields
-        assert not boxp1.curfields
-        assert not boxp2.origfields
-        assert not boxp2.curfields
-
-        assert boxp1.fromstart
-        assert not boxp2.fromstart
-
-        assert boxp1.knownclsbox is None
-        assert boxp2.knownclsbox.getaddr() == self.node_vtable_adr
-
-    def test_find_nodes_new_2(self):
-        ops = """
-        [i1, p1]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        p3 = new_with_vtable(ConstClass(node_vtable2))
-        setfield_gc(p2, p3, descr=nextdescr)
-        setfield_gc(p3, i1, descr=valuedescr)
-        jump(i1, p2)
-        """
-        self.find_nodes(ops,
-            '''Not,
-               Virtual(node_vtable,
-                       nextdescr=Virtual(node_vtable2,
-                                         valuedescr=Not))''')
-
-    def test_find_nodes_new_3(self):
-        ops = """
-        [sum, p1]
-        guard_class(p1, ConstClass(node_vtable)) []
-        i1 = getfield_gc(p1, descr=valuedescr)
-        i2 = int_sub(i1, 1)
-        sum2 = int_add(sum, i1)
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p2, i2, descr=valuedescr)
-        p3 = new_with_vtable(ConstClass(node_vtable2))
-        setfield_gc(p2, p3, descr=nextdescr)
-        jump(sum2, p2)
-        """
-        boxes, getnode = self.find_nodes(
-            ops,
-            '''Not,
-               Virtual(node_vtable,
-                       valuedescr=Not,
-                       nextdescr=Virtual(node_vtable2))''',
-            boxkinds={'sum': BoxInt, 'sum2': BoxInt})
-        assert getnode(boxes.sum) is not getnode(boxes.sum2)
-        assert getnode(boxes.p1) is not getnode(boxes.p2)
-
-        boxp1 = getnode(boxes.p1)
-        boxp2 = getnode(boxes.p2)
-        boxp3 = getnode(boxes.p3)
-        assert not boxp1.escaped
-        assert not boxp2.escaped
-        assert not boxp3.escaped
-
-        assert not boxp1.curfields
-        assert boxp1.origfields[self.valuedescr] is getnode(boxes.i1)
-        assert not boxp2.origfields
-        assert boxp2.curfields[self.nextdescr] is boxp3
-
-        assert boxp1.fromstart
-        assert not boxp2.fromstart
-        assert not boxp3.fromstart
-
-        assert boxp2.knownclsbox.getaddr() == self.node_vtable_adr
-        assert boxp3.knownclsbox.getaddr() == self.node_vtable_adr2
-
-    def test_find_nodes_new_aliasing_0(self):
-        ops = """
-        [p1, p2]
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        jump(p3, p3)
-        """
-        # both p1 and p2 must be NotSpecNodes; it's not possible to pass
-        # the same Virtual both in p1 and p2 (at least so far).
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_new_aliasing_1(self):
-        ops = """
-        [sum, p1]
-        guard_class(p1, ConstClass(node_vtable)) []
-        p3 = getfield_gc(p1, descr=nextdescr)
-        guard_class(p3, ConstClass(node_vtable)) []
-        i1 = getfield_gc(p1, descr=valuedescr)
-        i2 = int_sub(i1, 1)
-        sum2 = int_add(sum, i1)
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p2, i2, descr=valuedescr)
-        setfield_gc(p2, p2, descr=nextdescr)
-        jump(sum2, p2)
-        """
-        # the issue is the cycle "p2->p2", which cannot be represented
-        # with SpecNodes so far
-        self.find_nodes(ops, 'Not, Not',
-                        boxkinds={'sum': BoxInt, 'sum2': BoxInt})
-
-    def test_find_nodes_new_aliasing_2(self):
-        ops = """
-        [p1, p2]
-        escape(p2)
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        jump(p3, p3)
-        """
-        # both p1 and p2 must be NotSpecNodes; it's not possible to pass
-        # in p1 a Virtual and not in p2, as they both come from the same p3.
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_new_mismatch(self):
-        ops = """
-        [p1]
-        guard_class(p1, ConstClass(node_vtable)) []
-        p2 = new_with_vtable(ConstClass(node_vtable2))
-        jump(p2)
-        """
-        # this is not a valid loop at all, because of the mismatch
-        # between the produced and the consumed class.
-        py.test.raises(InvalidLoop, self.find_nodes, ops, None)
-
-    def test_find_nodes_new_aliasing_mismatch(self):
-        ops = """
-        [p0, p1]
-        guard_class(p0, ConstClass(node_vtable)) []
-        guard_class(p1, ConstClass(node_vtable2)) []
-        p2 = new_with_vtable(ConstClass(node_vtable2))
-        jump(p2, p2)
-        """
-        # this is also not really a valid loop, but it's not detected
-        # because p2 is passed more than once in the jump().
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_new_escapes(self):
-        ops = """
-        [p0]
-        escape(p0)
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        jump(p1)
-        """
-        self.find_nodes(ops, 'Not')
-
-    def test_find_nodes_new_unused(self):
-        ops = """
-        [p0]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        setfield_gc(p2, p3, descr=nextdescr)
-        jump(p1)
-        """
-        self.find_nodes(ops, '''
-            Virtual(node_vtable,
-                    nextdescr=Virtual(node_vtable,
-                                      nextdescr=Virtual(node_vtable)))''')
-
-    def test_find_nodes_ptr_eq(self):
-        ops = """
-        [p3, p4, p2]
-        p0 = new_with_vtable(ConstClass(node_vtable))
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        guard_nonnull(p0) []
-        i3 = ptr_ne(p0, NULL)
-        guard_true(i3) []
-        i4 = ptr_eq(p0, NULL)
-        guard_false(i4) []
-        i5 = ptr_ne(NULL, p0)
-        guard_true(i5) []
-        i6 = ptr_eq(NULL, p0)
-        guard_false(i6) []
-        i7 = ptr_ne(p0, p1)
-        guard_true(i7) []
-        i8 = ptr_eq(p0, p1)
-        guard_false(i8) []
-        i9 = ptr_ne(p0, p2)
-        guard_true(i9) []
-        i10 = ptr_eq(p0, p2)
-        guard_false(i10) []
-        i11 = ptr_ne(p2, p1)
-        guard_true(i11) []
-        i12 = ptr_eq(p2, p1)
-        guard_false(i12) []
-        jump(p0, p1, p2)
-        """
-        self.find_nodes(ops, '''Virtual(node_vtable),
-                                Virtual(node_vtable),
-                                Not''')
-
-    def test_find_nodes_call(self):
-        ops = """
-        [i0, p2]
-        p0 = new_with_vtable(ConstClass(node_vtable))
-        i1 = call_pure(i0, p0)     # forces p0 to not be virtual
-        jump(i1, p0)
-        """
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_default_field(self):
-        ops = """
-        [p0]
-        i0 = getfield_gc(p0, descr=valuedescr)
-        guard_value(i0, 5) []
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        # the field 'value' has its default value of 0
-        jump(p1)
-        """
-        # The answer must contain the 'value' field, because otherwise
-        # we might get incorrect results: when tracing, i0 was 5.
-        self.find_nodes(ops, 'Virtual(node_vtable, valuedescr=Not)')
-
-    def test_find_nodes_nonvirtual_guard_class(self):
-        ops = """
-        [p1]
-        guard_class(p1, ConstClass(node_vtable)) [p1]
-        jump(p1)
-        """
-        self.find_nodes(ops, 'Not')
-
-    def test_find_nodes_p12_simple(self):
-        ops = """
-        [p1]
-        i3 = getfield_gc(p1, descr=valuedescr)
-        escape(i3)
-        jump(p1)
-        """
-        self.find_nodes(ops, 'Not')
-
-    def test_find_nodes_p123_simple(self):
-        ops = """
-        [i1, p2, p3]
-        i3 = getfield_gc(p3, descr=valuedescr)
-        escape(i3)
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        jump(i1, p1, p2)
-        """
-        # We cannot track virtuals that survive for more than two iterations.
-        self.find_nodes(ops, 'Not, Not, Not')
-
-    def test_find_nodes_p1234_simple(self):
-        ops = """
-        [i1, p2, p3, p4]
-        i4 = getfield_gc(p4, descr=valuedescr)
-        escape(i4)
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        jump(i1, p1, p2, p3)
-        """
-        # We cannot track virtuals that survive for more than two iterations.
-        self.find_nodes(ops, 'Not, Not, Not, Not')
-
-    def test_find_nodes_p123_guard_class(self):
-        ops = """
-        [i1, p2, p3]
-        guard_class(p3, ConstClass(node_vtable)) [i1, p2, p3]
-        i3 = getfield_gc(p3, descr=valuedescr)
-        escape(i3)
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        jump(i1, p1, p2)
-        """
-        # We cannot track virtuals that survive for more than two iterations.
-        self.find_nodes(ops, 'Not, Not, Not')
-
-    def test_find_nodes_p123_rec(self):
-        ops = """
-        [i1, p2, p0d]
-        p3 = getfield_gc(p0d, descr=nextdescr)
-        i3 = getfield_gc(p3, descr=valuedescr)
-        escape(i3)
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        p0c = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p0c, p2, descr=nextdescr)
-        jump(i1, p1, p0c)
-        """
-        # We cannot track virtuals that survive for more than two iterations.
-        self.find_nodes(ops, '''Not,
-                                Not,
-                                Virtual(node_vtable, nextdescr=Not)''')
-
-    def test_find_nodes_setfield_bug(self):
-        ops = """
-        [p1, p2]
-        escape(p1)
-        setfield_gc(p1, p2, descr=nextdescr)
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        jump(p1, p3)
-        """
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_array_virtual_1(self):
-        ops = """
-        [i1, p2]
-        i2 = getarrayitem_gc(p2, 1, descr=arraydescr)
-        escape(i2)
-        p3 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p3, 1, i1, descr=arraydescr)
-        jump(i1, p3)
-        """
-        self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)')
-
-    def test_find_nodes_array_virtual_2(self):
-        ops = """
-        [i1, p2]
-        i2 = arraylen_gc(p2, descr=arraydescr)
-        escape(i2)
-        p3 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p3, 1, i1, descr=arraydescr)
-        jump(i1, p3)
-        """
-        self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)')
-
-    def test_find_nodes_array_virtual_3(self):
-        ops = """
-        [pvalue1, p2]
-        pvalue2 = new_with_vtable(ConstClass(node_vtable2))
-        ps2 = getarrayitem_gc(p2, 1, descr=arraydescr)
-        setfield_gc(ps2, pvalue2, descr=nextdescr)
-        ps3 = getarrayitem_gc(p2, 1, descr=arraydescr)
-        pvalue3 = getfield_gc(ps3, descr=nextdescr)
-        ps1 = new_with_vtable(ConstClass(node_vtable))
-        p3 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p3, 1, ps1, descr=arraydescr)
-        jump(pvalue3, p3)
-        """
-        self.find_nodes(ops, 'Virtual(node_vtable2), VArray(arraydescr, Not, Virtual(node_vtable), Not)')
-
-    def test_find_nodes_array_virtual_empty(self):
-        ops = """
-        [i1, p2]
-        p3 = new_array(3, descr=arraydescr)
-        jump(i1, p3)
-        """
-        self.find_nodes(ops, 'Not, VArray(arraydescr, Not, Not, Not)')
-
-    def test_find_nodes_array_nonvirtual_1(self):
-        ops = """
-        [i1, p2]
-        i2 = getarrayitem_gc(p2, i1, descr=arraydescr)
-        escape(i2)
-        p3 = new_array(4, descr=arraydescr)
-        setarrayitem_gc(p3, i1, i2, descr=arraydescr)
-        jump(i1, p3)
-        """
-        # Does not work because of the variable index, 'i1'.
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_array_forced_1(self):
-        ops = """
-        [p1, i1]
-        p2 = new_array(1, descr=arraydescr)
-        setarrayitem_gc(p2, 0, p1, descr=arraydescr)
-        p3 = getarrayitem_gc(p2, i1, descr=arraydescr)
-        p4 = new_with_vtable(ConstClass(node_vtable))
-        jump(p4, i1)
-        """
-        # escapes because getarrayitem_gc uses a non-constant index
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_arrayitem_forced(self):
-        ops = """
-        [p1]
-        p2 = new_array(1, descr=arraydescr)
-        escape(p2)
-        p4 = new_with_vtable(ConstClass(node_vtable))
-        setarrayitem_gc(p2, 0, p4, descr=arraydescr)
-        jump(p4)
-        """
-        self.find_nodes(ops, 'Not')
-
-    def test_find_nodes_struct_virtual_1(self):
-        ops = """
-        [i1, p2]
-        i2 = getfield_gc(p2, descr=adescr)
-        escape(i2)
-        p3 = new(descr=ssize)
-        setfield_gc(p3, i1, descr=adescr)
-        jump(i1, p3)
-        """
-        self.find_nodes(ops, 'Not, VStruct(ssize, adescr=Not)')
-
-    def test_find_nodes_struct_nonvirtual_1(self):
-        ops = """
-        [i1, p2]
-        i2 = getfield_gc(p2, descr=adescr)
-        escape(p2)
-        p3 = new(descr=ssize)
-        setfield_gc(p3, i1, descr=adescr)
-        jump(i1, p3)
-        """
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_guard_value_constant(self):
-        ops = """
-        [p1]
-        guard_value(p1, ConstPtr(myptr)) []
-        jump(ConstPtr(myptr))
-        """
-        self.find_nodes(ops, 'Constant(myptr)')
-
-    def test_find_nodes_guard_value_constant_mismatch(self):
-        ops = """
-        [p1]
-        guard_value(p1, ConstPtr(myptr2)) []
-        jump(ConstPtr(myptr))
-        """
-        py.test.raises(InvalidLoop, self.find_nodes, ops, None)
-
-    def test_find_nodes_guard_value_escaping_constant(self):
-        ops = """
-        [p1]
-        escape(p1)
-        guard_value(p1, ConstPtr(myptr)) []
-        jump(ConstPtr(myptr))
-        """
-        self.find_nodes(ops, 'Constant(myptr)')
-
-    def test_find_nodes_guard_value_same_as_constant(self):
-        ops = """
-        [p1]
-        guard_value(p1, ConstPtr(myptr)) []
-        p2 = same_as(ConstPtr(myptr))
-        jump(p2)
-        """
-        self.find_nodes(ops, 'Constant(myptr)')
-
-    def test_find_nodes_store_into_loop_constant_1(self):
-        ops = """
-        [i0, p1, p4]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        jump(i0, p1, p2)
-        """
-        self.find_nodes(ops, 'Not, Not, Not')
-
-    def test_find_nodes_store_into_loop_constant_2(self):
-        ops = """
-        [i0, p4, p1]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        jump(i0, p2, p1)
-        """
-        self.find_nodes(ops, 'Not, Not, Not')
-
-    def test_find_nodes_store_into_loop_constant_3(self):
-        ops = """
-        [i0, p1]
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        call(i0)
-        jump(i0, p1)
-        """
-        self.find_nodes(ops, 'Not, Not')
-
-    def test_find_nodes_arithmetic_propagation_bug_0(self):
-        ops = """
-        [p1]
-        i1 = getarrayitem_gc(p1, 0, descr=arraydescr)
-        escape(i1)
-        i2 = int_add(0, 1)
-        p2 = new_array(i2, descr=arraydescr)
-        i3 = escape()
-        setarrayitem_gc(p2, 0, i3, descr=arraydescr)
-        jump(p2)
-        """
-        self.find_nodes(ops, 'VArray(arraydescr, Not)')
-
-    def test_find_nodes_arithmetic_propagation_bug_1(self):
-        ops = """
-        [p1]
-        i1 = getarrayitem_gc(p1, 0, descr=arraydescr)
-        escape(i1)
-        i2 = same_as(1)
-        p2 = new_array(i2, descr=arraydescr)
-        setarrayitem_gc(p2, 0, 5)
-        jump(p2)
-        """
-        self.find_nodes(ops, 'VArray(arraydescr, Not)')
-
-    def test_find_nodes_arithmetic_propagation_bug_2(self):
-        ops = """
-        [p1]
-        i0 = int_sub(17, 17)
-        i1 = getarrayitem_gc(p1, i0, descr=arraydescr)
-        escape(i1)
-        i2 = int_add(0, 1)
-        p2 = new_array(i2, descr=arraydescr)
-        i3 = escape()
-        setarrayitem_gc(p2, i0, i3, descr=arraydescr)
-        jump(p2)
-        """
-        self.find_nodes(ops, 'VArray(arraydescr, Not)')
-
-    def test_find_nodes_arithmetic_propagation_bug_3(self):
-        ops = """
-        [p1]
-        i1 = getarrayitem_gc(p1, 0, descr=arraydescr)
-        escape(i1)
-        p3 = new_array(1, descr=arraydescr)
-        i2 = arraylen_gc(p3, descr=arraydescr)
-        p2 = new_array(i2, descr=arraydescr)
-        i3 = escape()
-        setarrayitem_gc(p2, 0, i3, descr=arraydescr)
-        jump(p2)
-        """
-        self.find_nodes(ops, 'VArray(arraydescr, Not)')
-
-    def test_find_nodes_bug_1(self):
-        ops = """
-        [p12]
-        guard_nonnull(p12) []
-        guard_class(p12, ConstClass(node_vtable)) []
-        guard_class(p12, ConstClass(node_vtable)) []
-        i22 = getfield_gc_pure(p12, descr=valuedescr)
-        escape(i22)
-        guard_nonnull(p12) []
-        guard_class(p12, ConstClass(node_vtable)) []
-        guard_class(p12, ConstClass(node_vtable)) []
-        i29 = getfield_gc_pure(p12, descr=valuedescr)
-        i31 = int_add_ovf(i29, 1)
-        guard_no_overflow() []
-        p33 = new_with_vtable(ConstClass(node_vtable))      # NODE
-        setfield_gc(p33, i31, descr=valuedescr)
-        #
-        p35 = new_array(1, descr=arraydescr3)               # Array(NODE)
-        setarrayitem_gc(p35, 0, p33, descr=arraydescr3)
-        p38 = new_with_vtable(ConstClass(u_vtable))         # U
-        setfield_gc(p38, p35, descr=onedescr)
-        guard_nonnull(p38) []
-        guard_nonnull(p38) []
-        guard_class(p38, ConstClass(u_vtable)) []
-        p42 = getfield_gc(p38, descr=onedescr)              # Array(NODE)
-        i43 = arraylen_gc(p42, descr=arraydescr3)
-        i45 = int_sub(i43, 0)
-        p46 = new(descr=tsize)                              # T
-        setfield_gc(p46, i45, descr=cdescr)
-        p47 = new_array(i45, descr=arraydescr3)             # Array(NODE)
-        setfield_gc(p46, p47, descr=ddescr)
-        i48 = int_lt(0, i43)
-        guard_true(i48) []
-        p49 = getarrayitem_gc(p42, 0, descr=arraydescr3)    # NODE
-        p50 = getfield_gc(p46, descr=ddescr)                # Array(NODE)
-        setarrayitem_gc(p50, 0, p49, descr=arraydescr3)
-        i52 = int_lt(1, i43)
-        guard_false(i52) []
-        i53 = getfield_gc(p46, descr=cdescr)
-        i55 = int_ne(i53, 1)
-        guard_false(i55) []
-        p56 = getfield_gc(p46, descr=ddescr)                # Array(NODE)
-        p58 = getarrayitem_gc(p56, 0, descr=arraydescr3)    # NODE
-        guard_nonnull(p38) []
-        jump(p58)
-        """
-        self.find_nodes(ops, 'Virtual(node_vtable, valuedescr=Not)')
-
-    # ------------------------------
-    # Bridge tests
-
-    def find_bridge(self, ops, inputspectext, outputspectext, boxkinds=None,
-                    mismatch=False):
-        assert boxkinds is None or isinstance(boxkinds, dict)
-        inputspecnodes = self.unpack_specnodes(inputspectext)
-        outputspecnodes = self.unpack_specnodes(outputspectext)
-        bridge = self.parse(ops, boxkinds=boxkinds)
-        bridge_specialization_finder = BridgeSpecializationFinder(self.cpu)
-        bridge_specialization_finder.find_nodes_bridge(bridge, inputspecnodes)
-        matches = bridge_specialization_finder.bridge_matches(outputspecnodes)
-        if mismatch:
-            assert not matches
-        else:
-            assert matches
-
-    def test_bridge_simple(self):
-        ops = """
-        [i0]
-        i1 = int_add(i0, 1)
-        jump(i1)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable)', mismatch=True)
-
-    def test_bridge_simple_known_class(self):
-        ops = """
-        [p0]
-        setfield_gc(p0, 123, descr=valuedescr)
-        jump(p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-
-    def test_bridge_simple_constant(self):
-        ops = """
-        []
-        jump(ConstPtr(myptr))
-        """
-        self.find_bridge(ops, '', 'Not')
-        self.find_bridge(ops, '', 'Constant(myptr)')
-        self.find_bridge(ops, '', 'Constant(myptr2)', mismatch=True)
-
-    def test_bridge_simple_constant_mismatch(self):
-        ops = """
-        [p0]
-        jump(p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'Constant(myptr)', mismatch=True)
-
-    def test_bridge_simple_virtual_1(self):
-        ops = """
-        [i0]
-        p0 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p0, i0, descr=valuedescr)
-        jump(p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable, valuedescr=Not)')
-        self.find_bridge(ops, 'Not',
-                         '''Virtual(node_vtable,
-                                    valuedescr=Not,
-                                    nextdescr=Not)''')
-        #
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable)',
-                         mismatch=True)   # missing valuedescr
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable, nextdescr=Not)',
-                         mismatch=True)   # missing valuedescr
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable2, valuedescr=Not)',
-                         mismatch=True)   # bad class
-
-    def test_bridge_simple_virtual_struct(self):
-        ops = """
-        [i0]
-        p0 = new(descr=ssize)
-        setfield_gc(p0, i0, descr=adescr)
-        jump(p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'VStruct(ssize, adescr=Not)')
-
-    def test_bridge_simple_virtual_struct_non_unique(self):
-        ops = """
-        [i0]
-        p0 = new(descr=ssize)
-        setfield_gc(p0, i0, descr=adescr)
-        jump(p0, p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not, Not')
-        self.find_bridge(ops, 'Not', 'VStruct(ssize), VStruct(ssize)',
-                         mismatch=True)
-
-
-    def test_bridge_simple_virtual_2(self):
-        ops = """
-        [p0]
-        setfield_gc(p0, 123, descr=valuedescr)
-        jump(p0)
-        """
-        self.find_bridge(ops, 'Virtual(node_vtable)', 'Not')
-        self.find_bridge(ops, 'Virtual(node_vtable)',
-                              'Virtual(node_vtable, valuedescr=Not)')
-        self.find_bridge(ops, 'Virtual(node_vtable, valuedescr=Not)',
-                              'Virtual(node_vtable, valuedescr=Not)')
-        self.find_bridge(ops, 'Virtual(node_vtable, valuedescr=Not)',
-                            '''Virtual(node_vtable,
-                                       valuedescr=Not,
-                                       nextdescr=Not)''')
-        self.find_bridge(ops, '''Virtual(node_vtable,
-                                         valuedescr=Not,
-                                         nextdescr=Not)''',
-                              '''Virtual(node_vtable,
-                                         valuedescr=Not,
-                                         nextdescr=Not)''')
-        #
-        self.find_bridge(ops, 'Virtual(node_vtable)', 'Virtual(node_vtable)',
-                         mismatch=True)    # because of missing valuedescr
-        self.find_bridge(ops, 'Virtual(node_vtable)',
-                         'Virtual(node_vtable2, valuedescr=Not)',
-                         mismatch=True)    # bad class
-
-    def test_bridge_virtual_mismatch_1(self):
-        ops = """
-        [i0]
-        p0 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p0, i0, descr=valuedescr)
-        jump(p0, p0)
-        """
-        self.find_bridge(ops, 'Not', 'Not, Not')
-        #
-        self.find_bridge(ops, 'Not',
-                         '''Virtual(node_vtable, valuedescr=Not),
-                            Virtual(node_vtable, valuedescr=Not)''',
-                         mismatch=True)    # duplicate p0
-
-    def test_bridge_guard_class(self):
-        ops = """
-        [p1]
-        p2 = getfield_gc(p1, descr=nextdescr)
-        guard_class(p2, ConstClass(node_vtable)) []
-        jump(p2)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Virtual(node_vtable2, nextdescr=Not)', 'Not')
-        self.find_bridge(ops,
-            '''Virtual(node_vtable,
-                       nextdescr=Virtual(node_vtable,
-                                         nextdescr=Not))''',
-            '''Virtual(node_vtable,
-                       nextdescr=Not)''')
-        #
-        self.find_bridge(ops, 'Not', 'Virtual(node_vtable)',
-                         mismatch=True)
-
-    def test_bridge_unused(self):
-        ops = """
-        []
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        p3 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        setfield_gc(p2, p3, descr=nextdescr)
-        jump(p1)
-        """
-        self.find_bridge(ops, '',
-            '''Not''')
-        self.find_bridge(ops, '',
-            '''Virtual(node_vtable,
-                       nextdescr=Not)''')
-        self.find_bridge(ops, '',
-            '''Virtual(node_vtable,
-                       nextdescr=Virtual(node_vtable,
-                                         nextdescr=Not))''')
-        self.find_bridge(ops, '',
-            '''Virtual(node_vtable,
-                       nextdescr=Virtual(node_vtable,
-                                         nextdescr=Virtual(node_vtable)))''')
-        self.find_bridge(ops, '',
-            '''Virtual(node_vtable,
-                       nextdescr=Virtual(node_vtable,
-                                         nextdescr=Virtual(node_vtable,
-                                                           nextdescr=Not)))''')
-
-    def test_bridge_to_finish(self):
-        ops = """
-        [i1]
-        i2 = int_add(i1, 5)
-        finish(i2)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-
-    def test_bridge_virtual_to_finish(self):
-        ops = """
-        [i1]
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, i1, descr=valuedescr)
-        finish(p1)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not',
-                         'Virtual(node_vtable, valuedescr=Not)',
-                         mismatch=True)
-
-    def test_bridge_array_virtual_1(self):
-        ops = """
-        [i1]
-        p1 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
-        jump(p1)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)')
-
-    def test_bridge_array_virtual_size_mismatch(self):
-        ops = """
-        [i1]
-        p1 = new_array(5, descr=arraydescr)
-        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
-        jump(p1)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)',
-                         mismatch=True)
-
-    def test_bridge_array_virtual_2(self):
-        ops = """
-        [i1]
-        p1 = new_array(3, descr=arraydescr)
-        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
-        escape(p1)
-        jump(p1)
-        """
-        self.find_bridge(ops, 'Not', 'Not')
-        self.find_bridge(ops, 'Not', 'VArray(arraydescr, Not, Not, Not)',
-                         mismatch=True)
-
-    def test_bridge_nested_structs(self):
-        ops = """
-        []
-        p1 = new_with_vtable(ConstClass(node_vtable))
-        p2 = new_with_vtable(ConstClass(node_vtable))
-        setfield_gc(p1, p2, descr=nextdescr)
-        jump(p1)
-        """
-        self.find_bridge(ops, '', 'Not')
-        self.find_bridge(ops, '', 'Virtual(node_vtable, nextdescr=Not)')
-        self.find_bridge(ops, '',
-                   'Virtual(node_vtable, nextdescr=Virtual(node_vtable))')
-        self.find_bridge(ops, '',
-                   'Virtual(node_vtable, nextdescr=Virtual(node_vtable2))',
-                   mismatch=True)
-
-
-class TestLLtype(BaseTestOptimizeFindNode, LLtypeMixin):
-    pass
-
-##class TestOOtype(BaseTestOptimizeFindNode, OOtypeMixin):
-##    def test_find_nodes_instanceof(self):
-##        ops = """
-##        [i0]
-##        p0 = new_with_vtable(ConstClass(node_vtable))
-##        i1 = instanceof(p0, descr=nodesize)
-##        jump(i1)
-##        """
-##        boxes, getnode = self.find_nodes(ops, 'Not')
-##        assert not getnode(boxes.p0).escaped





diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -84,7 +84,7 @@
         descr = self.getdescr()
         if descr is not None:
             descr = descr.clone_if_mutable()
-        op = ResOperation(self.getopnum(), args, self.result, descr)
+        op = ResOperation(self.getopnum(), args[:], self.result, descr)
         if not we_are_translated():
             op.name = self.name
             op.pc = self.pc
@@ -198,6 +198,7 @@
 class GuardResOp(ResOpWithDescr):
 
     _fail_args = None
+    _jump_target = None
 
     def getfailargs(self):
         return self._fail_args
@@ -205,14 +206,22 @@
     def setfailargs(self, fail_args):
         self._fail_args = fail_args
 
+    def getjumptarget(self):
+        return self._jump_target
+
+    def setjumptarget(self, jump_target):
+        self._jump_target = jump_target
+
     def copy_and_change(self, opnum, args=None, result=None, descr=None):
         newop = AbstractResOp.copy_and_change(self, opnum, args, result, descr)
         newop.setfailargs(self.getfailargs())
+        newop.setjumptarget(self.getjumptarget())
         return newop
 
     def clone(self):
         newop = AbstractResOp.clone(self)
         newop.setfailargs(self.getfailargs())
+        newop.setjumptarget(self.getjumptarget())        
         return newop
 
 

diff --git a/pypy/jit/metainterp/test/test_loop_nopspec.py b/pypy/jit/metainterp/test/test_loop_nopspec.py
deleted file mode 100644
--- a/pypy/jit/metainterp/test/test_loop_nopspec.py
+++ /dev/null
@@ -1,27 +0,0 @@
-
-from pypy.jit.metainterp.test import test_loop, test_send
-from pypy.jit.metainterp.warmspot import ll_meta_interp
-from pypy.rlib.jit import OPTIMIZER_NO_PERFECTSPEC
-from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
-
-class LoopNoPSpecTest(test_send.SendTests):
-    def meta_interp(self, func, args, **kwds):
-        return ll_meta_interp(func, args, optimizer=OPTIMIZER_NO_PERFECTSPEC,
-                              CPUClass=self.CPUClass, 
-                              type_system=self.type_system,
-                              **kwds)
-
-    def check_loops(self, *args, **kwds):
-        pass
-
-    def check_loop_count(self, count):
-        pass
-
-    def check_jumps(self, maxcount):
-        pass
-
-class TestLLtype(LoopNoPSpecTest, LLJitMixin):
-    pass
-
-class TestOOtype(LoopNoPSpecTest, OOJitMixin):
-    pass


diff --git a/pypy/jit/metainterp/viewnode.py b/pypy/jit/metainterp/viewnode.py
deleted file mode 100644
--- a/pypy/jit/metainterp/viewnode.py
+++ /dev/null
@@ -1,124 +0,0 @@
-import py
-from pypy.jit.metainterp import specnode, optimizefindnode
-from pypy.tool.pairtype import extendabletype
-
-class __extend__(specnode.NotSpecNode):
-    def _dot(self, seen):
-        if self in seen:
-            return
-        seen.add(self)
-        yield '%s [label="<Not>"]' % (id(self), )
-
-class __extend__(specnode.ConstantSpecNode):
-    def _dot(self, seen):
-        if self in seen:
-            return
-        seen.add(self)
-        yield '%s [label="<Const: %s>"]' % (id(self), self.constbox)
-
-class __extend__(specnode.AbstractVirtualStructSpecNode):
-    def _dot(self, seen):
-        if self in seen:
-            return
-        seen.add(self)
-        yield '%s [label="<%s>"]' % (
-                id(self),
-                self.__class__.__name__[:-len("SpecNode")])
-        for label, node in self.fields:
-            yield '%s -> %s [label="%s"]' % (id(self), id(node), label.name)
-            for line in node._dot(seen):
-                yield line
-
-class __extend__(specnode.VirtualArraySpecNode):
-    def _dot(self, seen):
-        if self in seen:
-            return
-        seen.add(self)
-        yield '%s [label="<Array: %s>"]' % (
-                id(self),
-                len(self.items))
-        for i, node in enumerate(self.items):
-            yield '%s -> %s [label="%s"]' % (id(self), id(node), i)
-            for line in node._dot(seen):
-                yield line
-
-
-class __extend__(optimizefindnode.InstanceNode):
-    __metaclass__ = extendabletype # evil
-
-    def _dot(self, seen):
-        if self in seen:
-            return
-        seen.add(self)
-        if self.knownclsbox:
-            name = "Virtual "
-            if isinstance(self.knownclsbox.value, int):
-                name += str(self.knownclsbox.value)
-            else:
-                name += str(self.knownclsbox.value.adr.ptr).rpartition("_vtable")[0].rpartition('.')[2]
-        elif self.structdescr:
-            name = "Struct " + str(self.structdescr)
-        elif self.arraydescr:
-            name = "Array"
-        else:
-            name = "Not"
-        if self.escaped:
-            name = "ESC " + name
-        if self.fromstart:
-            name = "START " + name
-        if self.unique == optimizefindnode.UNIQUE_NO:
-            color = "blue"
-        else:
-            color = "black"
-
-        yield 'orig%s [label="in: [%s]", shape=box, color=%s]' % (
-                id(self), name, color)
-        yield '%s [label="out: [%s]", shape=box, color=%s]' % (
-                id(self), name, color)
-        yield 'orig%s -> %s [color=red]' % (id(self), id(self))
-        if self.origfields:
-            for descr, node in self.origfields.iteritems():
-                yield 'orig%s -> orig%s [label="%s"]' % (id(self), id(node), descr.name)
-                for line in node._dot(seen):
-                    yield line
-        if self.curfields:
-            for descr, node in self.curfields.iteritems():
-                yield '%s -> %s [label="%s"]' % (id(self), id(node), descr.name)
-                for line in node._dot(seen):
-                    yield line
-        if self.origitems:
-            for i, node in sorted(self.origitems.iteritems()):
-                yield 'orig%s -> orig%s [label="%s"]' % (id(self), id(node), i)
-                for line in node._dot(seen):
-                    yield line
-        if self.curitems:
-            for i, node in sorted(self.curitems.iteritems()):
-                yield '%s -> %s [label="%s"]' % (id(self), id(node), i)
-                for line in node._dot(seen):
-                    yield line
-
-
-def view(*objects):
-    from dotviewer import graphclient
-    content = ["digraph G{"]
-    seen = set()
-    for obj in objects:
-        content.extend(obj._dot(seen))
-    content.append("}")
-    p = py.test.ensuretemp("specnodes").join("temp.dot")
-    p.write("\n".join(content))
-    graphclient.display_dot_file(str(p))
-
-def viewnodes(l1, l2):
-    from dotviewer import graphclient
-    content = ["digraph G{"]
-    seen = set()
-    for obj in l1 + l2:
-        content.extend(obj._dot(seen))
-    for i, (o1, o2) in enumerate(zip(l1, l2)):
-        content.append("%s -> %s [color=green]" % (id(o1), i))
-        content.append("%s -> orig%s [color=green]" % (i, id(o2)))
-    content.append("}")
-    p = py.test.ensuretemp("specnodes").join("temp.dot")
-    p.write("\n".join(content))
-    graphclient.display_dot_file(str(p))

diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -373,6 +373,13 @@
     else:
         log.info("compiling new bridge")
 
+def compile_add_guard_jump_target(loop, loop_target):
+    loop = _from_opaque(loop)
+    loop_target = _from_opaque(loop_target)
+    op = loop.operations[-1]
+    assert op.is_guard()
+    op.jump_target = loop_target
+
 def compile_add_fail(loop, fail_index):
     loop = _from_opaque(loop)
     index = len(loop.operations)-1
@@ -1634,6 +1641,7 @@
 setannotation(compile_add_ref_result, annmodel.SomeInteger())
 setannotation(compile_add_float_result, annmodel.SomeInteger())
 setannotation(compile_add_jump_target, annmodel.s_None)
+setannotation(compile_add_guard_jump_target, annmodel.s_None)
 setannotation(compile_add_fail, annmodel.SomeInteger())
 setannotation(compile_add_fail_arg, annmodel.s_None)
 setannotation(compile_redirect_fail, annmodel.s_None)

diff --git a/pypy/jit/metainterp/optimizefindnode.py b/pypy/jit/metainterp/optimizefindnode.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizefindnode.py
+++ /dev/null
@@ -1,576 +0,0 @@
-from pypy.jit.metainterp.specnode import SpecNode
-from pypy.jit.metainterp.specnode import NotSpecNode, prebuiltNotSpecNode
-from pypy.jit.metainterp.specnode import ConstantSpecNode
-from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode
-from pypy.jit.metainterp.specnode import VirtualArraySpecNode
-from pypy.jit.metainterp.specnode import VirtualStructSpecNode
-from pypy.jit.metainterp.history import AbstractValue, ConstInt, Const
-from pypy.jit.metainterp.resoperation import rop
-from pypy.jit.metainterp.executor import execute_nonspec
-from pypy.jit.metainterp.optimizeutil import _findall, sort_descrs
-from pypy.jit.metainterp.optimizeutil import InvalidLoop
-
-# ____________________________________________________________
-
-UNIQUE_UNKNOWN = '\x00'
-UNIQUE_NO      = '\x01'
-UNIQUE_INST    = '\x02'
-UNIQUE_ARRAY   = '\x03'
-UNIQUE_STRUCT  = '\x04'
-
-class InstanceNode(object):
-    """An instance of this class is used to match the start and
-    the end of the loop, so it contains both 'origfields' that represents
-    the field's status at the start and 'curfields' that represents it
-    at the current point (== the end when optimizefindnode is complete).
-    """
-    escaped = False     # if True, then all the rest of the info is pointless
-    unique = UNIQUE_UNKNOWN   # for find_unique_nodes()
-
-    # fields used to store the shape of the potential VirtualInstance
-    knownclsbox = None  # set only on freshly-allocated or fromstart structures
-    origfields = None   # optimization; equivalent to an empty dict
-    curfields = None    # optimization; equivalent to an empty dict
-
-    knownvaluebox = None # a Const with the value of this box, if constant
-
-    # fields used to store the shape of the potential VirtualList
-    arraydescr = None   # set only on freshly-allocated or fromstart arrays
-    #arraysize = ..     # valid if and only if arraydescr is not None
-    origitems = None    # optimization; equivalent to an empty dict
-    curitems = None     # optimization; equivalent to an empty dict
-
-    # fields used to store the shape of the potential VirtualStruct
-    structdescr = None  # set only on freshly-allocated or fromstart structs
-    #origfields = ..    # same as above
-    #curfields = ..     # same as above
-
-    dependencies = None
-
-    def __init__(self, fromstart=False):
-        self.fromstart = fromstart    # for loops only: present since the start
-
-    def is_constant(self):
-        return self.knownvaluebox is not None
-
-    def add_escape_dependency(self, other):
-        assert not self.escaped
-        if self.dependencies is None:
-            self.dependencies = []
-        self.dependencies.append(other)
-
-    def mark_escaped(self):
-        # invariant: if escaped=True, then dependencies is None
-        if not self.escaped:
-            self.escaped = True
-            self.unique = UNIQUE_NO
-            # ^^^ always set unique to UNIQUE_NO when we set escaped to True.
-            # See for example test_find_nodes_store_into_loop_constant_2.
-            if self.dependencies is not None:
-                deps = self.dependencies
-                self.dependencies = None
-                for box in deps:
-                    box.mark_escaped()
-
-    def set_unique_nodes(self):
-        if self.fromstart:
-            self.mark_escaped()
-        if self.escaped or self.unique != UNIQUE_UNKNOWN:
-            # this node is not suitable for being a virtual, or we
-            # encounter it more than once when doing the recursion
-            self.unique = UNIQUE_NO
-        elif self.knownclsbox is not None:
-            self.unique = UNIQUE_INST
-            if self.curfields is not None:
-                for subnode in self.curfields.itervalues():
-                    subnode.set_unique_nodes()
-        elif self.arraydescr is not None:
-            self.unique = UNIQUE_ARRAY
-            if self.curitems is not None:
-                for subnode in self.curitems.itervalues():
-                    subnode.set_unique_nodes()
-        elif self.structdescr is not None:
-            self.unique = UNIQUE_STRUCT
-            if self.curfields is not None:
-                for subnode in self.curfields.itervalues():
-                    subnode.set_unique_nodes()
-        else:
-            assert 0, "most probably unreachable"
-
-    def __repr__(self):
-        flags = ''
-        if self.escaped:     flags += 'e'
-        if self.fromstart:   flags += 's'
-        if self.knownclsbox: flags += 'c'
-        if self.arraydescr:  flags += str(self.arraysize)
-        if self.structdescr: flags += 'S'
-        return "<InstanceNode (%s)>" % (flags,)
-
-# ____________________________________________________________
-# General find_nodes_xxx() interface, for both loops and bridges
-
-class NodeFinder(object):
-    """Abstract base class."""
-    node_escaped = InstanceNode()
-    node_escaped.unique = UNIQUE_NO
-    node_escaped.escaped = True
-
-    def __init__(self, cpu):
-        self.cpu = cpu
-        self.nodes = {}     # Box -> InstanceNode
-
-    def getnode(self, box):
-        if isinstance(box, Const):
-            return self.set_constant_node(box, box)
-        return self.nodes.get(box, self.node_escaped)
-
-    def set_constant_node(self, box, constbox):
-        assert isinstance(constbox, Const)
-        node = InstanceNode()
-        node.unique = UNIQUE_NO
-        node.escaped = True
-        node.knownvaluebox = constbox
-        self.nodes[box] = node
-        return node
-
-    def get_constant_box(self, box):
-        if isinstance(box, Const):
-            return box
-        try:
-            node = self.nodes[box]
-        except KeyError:
-            return None
-        else:
-            return node.knownvaluebox
-
-    def find_nodes(self, operations):
-        for op in operations:
-            opnum = op.getopnum()
-            for value, func in find_nodes_ops:
-                if opnum == value:
-                    func(self, op)
-                    break
-            else:
-                self.find_nodes_default(op)
-
-    def find_nodes_default(self, op):
-        if op.is_always_pure():
-            for i in range(op.numargs()):
-                arg = op.getarg(i)
-                if self.get_constant_box(arg) is None:
-                    break
-            else:
-                # all constant arguments: we can constant-fold
-                argboxes = [self.get_constant_box(op.getarg(i))
-                            for i in range(op.numargs())]
-                resbox = execute_nonspec(self.cpu, None,
-                                         op.getopnum(), argboxes, op.getdescr())
-                self.set_constant_node(op.result, resbox.constbox())
-        # default case: mark the arguments as escaping
-        for i in range(op.numargs()):
-            self.getnode(op.getarg(i)).mark_escaped()
-
-    def find_nodes_no_escape(self, op):
-        pass    # for operations that don't escape their arguments
-
-    find_nodes_PTR_EQ        = find_nodes_no_escape
-    find_nodes_PTR_NE        = find_nodes_no_escape
-    ##find_nodes_INSTANCEOF    = find_nodes_no_escape
-    find_nodes_GUARD_NONNULL = find_nodes_no_escape
-    find_nodes_GUARD_ISNULL  = find_nodes_no_escape
-
-    def find_nodes_NEW_WITH_VTABLE(self, op):
-        instnode = InstanceNode()
-        box = op.getarg(0)
-        assert isinstance(box, Const)
-        instnode.knownclsbox = box
-        self.nodes[op.result] = instnode
-
-    def find_nodes_NEW(self, op):
-        instnode = InstanceNode()
-        instnode.structdescr = op.getdescr()
-        self.nodes[op.result] = instnode
-
-    def find_nodes_NEW_ARRAY(self, op):
-        lengthbox = op.getarg(0)
-        lengthbox = self.get_constant_box(lengthbox)
-        if lengthbox is None:
-            return     # var-sized arrays are not virtual
-        arraynode = InstanceNode()
-        arraynode.arraysize = lengthbox.getint()
-        arraynode.arraydescr = op.getdescr()
-        self.nodes[op.result] = arraynode
-
-    def find_nodes_ARRAYLEN_GC(self, op):
-        arraynode = self.getnode(op.getarg(0))
-        if arraynode.arraydescr is not None:
-            resbox = ConstInt(arraynode.arraysize)
-            self.set_constant_node(op.result, resbox)
-
-    def find_nodes_GUARD_CLASS(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.fromstart:    # only useful (and safe) in this case
-            box = op.getarg(1)
-            assert isinstance(box, Const)
-            instnode.knownclsbox = box
-
-    def find_nodes_GUARD_VALUE(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.fromstart:    # only useful (and safe) in this case
-            box = op.getarg(1)
-            assert isinstance(box, Const)
-            instnode.knownvaluebox = box
-
-    def find_nodes_SETFIELD_GC(self, op):
-        instnode = self.getnode(op.getarg(0))
-        fieldnode = self.getnode(op.getarg(1))
-        if instnode.escaped:
-            fieldnode.mark_escaped()
-            return     # nothing to be gained from tracking the field
-        field = op.getdescr()
-        assert isinstance(field, AbstractValue)
-        if instnode.curfields is None:
-            instnode.curfields = {}
-        instnode.curfields[field] = fieldnode
-        instnode.add_escape_dependency(fieldnode)
-
-    def find_nodes_GETFIELD_GC(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.escaped:
-            return     # nothing to be gained from tracking the field
-        field = op.getdescr()
-        assert isinstance(field, AbstractValue)
-        if instnode.curfields is not None and field in instnode.curfields:
-            fieldnode = instnode.curfields[field]
-        elif instnode.origfields is not None and field in instnode.origfields:
-            fieldnode = instnode.origfields[field]
-        elif instnode.fromstart:
-            fieldnode = InstanceNode(fromstart=True)
-            instnode.add_escape_dependency(fieldnode)
-            if instnode.origfields is None:
-                instnode.origfields = {}
-            instnode.origfields[field] = fieldnode
-        else:
-            return    # nothing to be gained from tracking the field
-        self.nodes[op.result] = fieldnode
-
-    find_nodes_GETFIELD_GC_PURE = find_nodes_GETFIELD_GC
-
-    def find_nodes_SETARRAYITEM_GC(self, op):
-        indexbox = op.getarg(1)
-        indexbox = self.get_constant_box(indexbox)
-        if indexbox is None:
-            self.find_nodes_default(op)            # not a Const index
-            return
-        arraynode = self.getnode(op.getarg(0))
-        itemnode = self.getnode(op.getarg(2))
-        if arraynode.escaped:
-            itemnode.mark_escaped()
-            return     # nothing to be gained from tracking the item
-        if arraynode.curitems is None:
-            arraynode.curitems = {}
-        arraynode.curitems[indexbox.getint()] = itemnode
-        arraynode.add_escape_dependency(itemnode)
-
-    def find_nodes_GETARRAYITEM_GC(self, op):
-        indexbox = op.getarg(1)
-        indexbox = self.get_constant_box(indexbox)
-        if indexbox is None:
-            self.find_nodes_default(op)            # not a Const index
-            return
-        arraynode = self.getnode(op.getarg(0))
-        if arraynode.escaped:
-            return     # nothing to be gained from tracking the item
-        index = indexbox.getint()
-        if arraynode.curitems is not None and index in arraynode.curitems:
-            itemnode = arraynode.curitems[index]
-        elif arraynode.origitems is not None and index in arraynode.origitems:
-            itemnode = arraynode.origitems[index]
-        elif arraynode.fromstart:
-            itemnode = InstanceNode(fromstart=True)
-            arraynode.add_escape_dependency(itemnode)
-            if arraynode.origitems is None:
-                arraynode.origitems = {}
-            arraynode.origitems[index] = itemnode
-        else:
-            return    # nothing to be gained from tracking the item
-        self.nodes[op.result] = itemnode
-
-    find_nodes_GETARRAYITEM_GC_PURE = find_nodes_GETARRAYITEM_GC
-
-    def find_nodes_JUMP(self, op):
-        # only set up the 'unique' field of the InstanceNodes;
-        # real handling comes later (build_result_specnodes() for loops).
-        for i in range(op.numargs()):
-            box = op.getarg(i)
-            self.getnode(box).set_unique_nodes()
-
-    def find_nodes_FINISH(self, op):
-        # only for bridges, and only for the ones that end in a 'return'
-        # or 'raise'; all other cases end with a JUMP.
-        for i in range(op.numargs()):
-            box = op.getarg(i)
-            self.getnode(box).unique = UNIQUE_NO
-
-find_nodes_ops = _findall(NodeFinder, 'find_nodes_')
-
-# ____________________________________________________________
-# Perfect specialization -- for loops only
-
-class PerfectSpecializationFinder(NodeFinder):
-    node_fromstart = InstanceNode(fromstart=True)
-
-    def find_nodes_loop(self, loop, build_specnodes=True):
-        self._loop = loop
-        self.setup_input_nodes(loop.inputargs)
-        self.find_nodes(loop.operations)
-        if build_specnodes:
-            self.build_result_specnodes(loop)
-
-    def show(self):
-        from pypy.jit.metainterp.viewnode import viewnodes, view
-        op = self._loop.operations[-1]
-        assert op.getopnum() == rop.JUMP
-        exitnodes = [self.getnode(arg) for arg in op.args]
-        viewnodes(self.inputnodes, exitnodes)
-        if hasattr(self._loop.token, "specnodes"):
-            view(*self._loop.token.specnodes)
-
-
-    def setup_input_nodes(self, inputargs):
-        inputnodes = []
-        for box in inputargs:
-            instnode = InstanceNode(fromstart=True)
-            inputnodes.append(instnode)
-            self.nodes[box] = instnode
-        self.inputnodes = inputnodes
-
-    def build_result_specnodes(self, loop):
-        # Build the list of specnodes based on the result
-        # computed by NodeFinder.find_nodes().
-        op = loop.operations[-1]
-        assert op.getopnum() == rop.JUMP
-        assert len(self.inputnodes) == op.numargs()
-        while True:
-            self.restart_needed = False
-            specnodes = []
-            for i in range(op.numargs()):
-                inputnode = self.inputnodes[i]
-                exitnode = self.getnode(op.getarg(i))
-                specnodes.append(self.intersect(inputnode, exitnode))
-            if not self.restart_needed:
-                break
-        loop.token.specnodes = specnodes
-
-    def intersect(self, inputnode, exitnode):
-        assert inputnode.fromstart
-        if inputnode.is_constant() and \
-           exitnode.is_constant():
-            if inputnode.knownvaluebox.same_constant(exitnode.knownvaluebox):
-                return ConstantSpecNode(inputnode.knownvaluebox)
-            else:
-                raise InvalidLoop
-        if inputnode.escaped:
-            return prebuiltNotSpecNode
-        unique = exitnode.unique
-        if unique == UNIQUE_NO:
-            if inputnode is not self.node_fromstart:
-                # Mark the input node as escaped, and schedule a complete
-                # restart of intersect().  This is needed because there is
-                # an order dependency: calling inputnode.mark_escaped()
-                # might set the field exitnode.unique to UNIQUE_NO in some
-                # other node.  If inputnode is node_fromstart, there is no
-                # problem (and it must not be mutated by mark_escaped() then).
-                inputnode.mark_escaped()
-                self.restart_needed = True
-            return prebuiltNotSpecNode
-        if unique == UNIQUE_INST:
-            return self.intersect_instance(inputnode, exitnode)
-        if unique == UNIQUE_ARRAY:
-            return self.intersect_array(inputnode, exitnode)
-        if unique == UNIQUE_STRUCT:
-            return self.intersect_struct(inputnode, exitnode)
-        assert 0, "unknown value for exitnode.unique: %d" % ord(unique)
-
-    def compute_common_fields(self, orig, d):
-        fields = []
-        if orig is not None:
-            if d is not None:
-                d = d.copy()
-            else:
-                d = {}
-            for ofs in orig:
-                d.setdefault(ofs, self.node_escaped)
-        if d is not None:
-            lst = d.keys()
-            # we always use the "standardized" order of fields
-            sort_descrs(lst)
-            for ofs in lst:
-                try:
-                    if orig is None:
-                        raise KeyError
-                    node = orig[ofs]
-                except KeyError:
-                    # field stored at exit, but not read at input.  Must
-                    # still be allocated, otherwise it will be incorrectly
-                    # uninitialized after a guard failure.
-                    node = self.node_fromstart
-                specnode = self.intersect(node, d[ofs])
-                fields.append((ofs, specnode))
-        return fields
-
-    def intersect_instance(self, inputnode, exitnode):
-        if (inputnode.knownclsbox is not None and
-            not inputnode.knownclsbox.same_constant(exitnode.knownclsbox)):
-            # unique match, but the class is known to be a mismatch
-            raise InvalidLoop
-        #
-        fields = self.compute_common_fields(inputnode.origfields,
-                                            exitnode.curfields)
-        return VirtualInstanceSpecNode(exitnode.knownclsbox, fields)
-
-    def intersect_array(self, inputnode, exitnode):
-        assert inputnode.arraydescr is None
-        #
-        items = []
-        for i in range(exitnode.arraysize):
-            if exitnode.curitems is None:
-                exitsubnode = self.node_escaped
-            else:
-                exitsubnode = exitnode.curitems.get(i, self.node_escaped)
-            if inputnode.origitems is None:
-                node = self.node_fromstart
-            else:
-                node = inputnode.origitems.get(i, self.node_fromstart)
-            specnode = self.intersect(node, exitsubnode)
-            items.append(specnode)
-        return VirtualArraySpecNode(exitnode.arraydescr, items)
-
-    def intersect_struct(self, inputnode, exitnode):
-        assert inputnode.structdescr is None
-        #
-        fields = self.compute_common_fields(inputnode.origfields,
-                                            exitnode.curfields)
-        return VirtualStructSpecNode(exitnode.structdescr, fields)
-
-# ____________________________________________________________
-# A subclass of NodeFinder for bridges only
-
-class __extend__(SpecNode):
-    def make_instance_node(self):
-        raise NotImplementedError
-    def matches_instance_node(self, exitnode):
-        raise NotImplementedError
-
-class __extend__(NotSpecNode):
-    def make_instance_node(self):
-        return NodeFinder.node_escaped
-    def matches_instance_node(self, exitnode):
-        return True
-
-class __extend__(ConstantSpecNode):
-    def make_instance_node(self):
-        raise AssertionError, "not implemented (but not used actually)"
-    def matches_instance_node(self, exitnode):
-        if exitnode.knownvaluebox is None:
-            return False
-        return self.constbox.same_constant(exitnode.knownvaluebox)
-
-class __extend__(VirtualInstanceSpecNode):
-    def make_instance_node(self):
-        instnode = InstanceNode()
-        instnode.knownclsbox = self.known_class
-        instnode.curfields = {}
-        for ofs, subspecnode in self.fields:
-            instnode.curfields[ofs] = subspecnode.make_instance_node()
-        return instnode
-
-    def matches_instance_node(self, exitnode):
-        if exitnode.unique == UNIQUE_NO:
-            return False
-        #
-        assert exitnode.unique == UNIQUE_INST
-        if not self.known_class.same_constant(exitnode.knownclsbox):
-            # unique match, but the class is known to be a mismatch
-            return False
-        #
-        return matches_fields(self.fields, exitnode.curfields)
-
-def matches_fields(fields, d):
-    seen = 0
-    for ofs, subspecnode in fields:
-        try:
-            if d is None:
-                raise KeyError
-            instnode = d[ofs]
-            seen += 1
-        except KeyError:
-            instnode = NodeFinder.node_escaped
-        if not subspecnode.matches_instance_node(instnode):
-            return False
-    if d is not None and len(d) > seen:
-        return False          # some key is in d but not in fields
-    return True
-
-class __extend__(VirtualArraySpecNode):
-    def make_instance_node(self):
-        raise AssertionError, "not implemented (but not used actually)"
-    def matches_instance_node(self, exitnode):
-        if exitnode.unique == UNIQUE_NO:
-            return False
-        #
-        assert exitnode.unique == UNIQUE_ARRAY
-        assert self.arraydescr == exitnode.arraydescr
-        if len(self.items) != exitnode.arraysize:
-            # the size is known to be a mismatch
-            return False
-        #
-        d = exitnode.curitems
-        for i in range(exitnode.arraysize):
-            try:
-                if d is None:
-                    raise KeyError
-                itemnode = d[i]
-            except KeyError:
-                itemnode = NodeFinder.node_escaped
-            subspecnode = self.items[i]
-            if not subspecnode.matches_instance_node(itemnode):
-                return False
-        return True
-
-class __extend__(VirtualStructSpecNode):
-    def make_instance_node(self):
-        raise AssertionError, "not implemented (but not used actually)"
-    def matches_instance_node(self, exitnode):
-        if exitnode.unique == UNIQUE_NO:
-            return False
-        #
-        assert exitnode.unique == UNIQUE_STRUCT
-        assert self.typedescr == exitnode.structdescr
-        #
-        return matches_fields(self.fields, exitnode.curfields)
-
-
-class BridgeSpecializationFinder(NodeFinder):
-
-    def find_nodes_bridge(self, bridge, specnodes=None):
-        if specnodes is not None:      # not used actually
-            self.setup_bridge_input_nodes(specnodes, bridge.inputargs)
-        self.find_nodes(bridge.operations)
-        self.jump_op = bridge.operations[-1]
-
-    def setup_bridge_input_nodes(self, specnodes, inputargs):
-        assert len(specnodes) == len(inputargs)
-        for i in range(len(inputargs)):
-            instnode = specnodes[i].make_instance_node()
-            box = inputargs[i]
-            self.nodes[box] = instnode
-
-    def bridge_matches(self, nextloop_specnodes):
-        jump_op = self.jump_op
-        assert jump_op.numargs() == len(nextloop_specnodes)
-        for i in range(len(nextloop_specnodes)):
-            exitnode = self.getnode(jump_op.getarg(i))
-            if not nextloop_specnodes[i].matches_instance_node(exitnode):
-                return False
-        return True


diff --git a/pypy/jit/metainterp/test/test_specnode.py b/pypy/jit/metainterp/test/test_specnode.py
deleted file mode 100644
--- a/pypy/jit/metainterp/test/test_specnode.py
+++ /dev/null
@@ -1,132 +0,0 @@
-from pypy.rpython.lltypesystem import lltype, llmemory
-from pypy.jit.metainterp.history import AbstractDescr, BoxPtr, ConstInt, ConstPtr
-from pypy.jit.metainterp.specnode import prebuiltNotSpecNode
-from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode
-from pypy.jit.metainterp.specnode import VirtualArraySpecNode
-from pypy.jit.metainterp.specnode import VirtualStructSpecNode
-from pypy.jit.metainterp.specnode import ConstantSpecNode
-from pypy.jit.metainterp.specnode import equals_specnodes
-from pypy.jit.metainterp.specnode import more_general_specnodes
-from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin
-
-def _get_vspecnode(classnum=123):
-    return VirtualInstanceSpecNode(ConstInt(classnum),
-                         [(LLtypeMixin.valuedescr, prebuiltNotSpecNode),
-                          (LLtypeMixin.nextdescr,  prebuiltNotSpecNode)])
-
-def _get_aspecnode(length=2):
-    return VirtualArraySpecNode(LLtypeMixin.arraydescr,
-                                [prebuiltNotSpecNode] * length)
-
-def _get_sspecnode():
-    return VirtualStructSpecNode(LLtypeMixin.ssize,
-                                 [(LLtypeMixin.adescr, prebuiltNotSpecNode),
-                                  (LLtypeMixin.bdescr, prebuiltNotSpecNode)])
-
-def _get_cspecnode(s):
-    from pypy.rpython.module.support import LLSupport        
-    llstr = lltype.cast_opaque_ptr(llmemory.GCREF, LLSupport.to_rstr(s))
-    box = ConstPtr(llstr)
-    return ConstantSpecNode(box)
-
-def test_equals_specnodes():
-    assert equals_specnodes([prebuiltNotSpecNode, prebuiltNotSpecNode],
-                            [prebuiltNotSpecNode, prebuiltNotSpecNode])
-    vspecnode1 = _get_vspecnode(1)
-    vspecnode2 = _get_vspecnode(2)
-    assert equals_specnodes([vspecnode1], [vspecnode1])
-    assert not equals_specnodes([vspecnode1], [vspecnode2])
-    assert not equals_specnodes([vspecnode1], [prebuiltNotSpecNode])
-    assert not equals_specnodes([prebuiltNotSpecNode], [vspecnode2])
-    aspecnode1 = _get_aspecnode(1)
-    aspecnode2 = _get_aspecnode(2)
-    assert equals_specnodes([aspecnode2], [aspecnode2])
-    assert not equals_specnodes([aspecnode1], [aspecnode2])
-    assert not equals_specnodes([aspecnode1], [prebuiltNotSpecNode])
-    assert not equals_specnodes([prebuiltNotSpecNode], [aspecnode2])
-    sspecnode1 = _get_sspecnode()
-    assert equals_specnodes([sspecnode1], [sspecnode1])
-    assert not equals_specnodes([sspecnode1], [prebuiltNotSpecNode])
-    assert not equals_specnodes([prebuiltNotSpecNode], [sspecnode1])
-    #
-    foonode = _get_cspecnode('foo')
-    barnode = _get_cspecnode('bar')
-    assert equals_specnodes([foonode], [foonode])
-    assert not equals_specnodes([foonode], [barnode])
-    assert not equals_specnodes([foonode], [prebuiltNotSpecNode])
-
-def test_more_general_specnodes():
-    assert more_general_specnodes([prebuiltNotSpecNode, prebuiltNotSpecNode],
-                                  [prebuiltNotSpecNode, prebuiltNotSpecNode])
-    vspecnode1 = _get_vspecnode(1)
-    vspecnode2 = _get_vspecnode(2)
-    assert more_general_specnodes([vspecnode1], [vspecnode1])
-    assert not more_general_specnodes([vspecnode1], [vspecnode2])
-    assert not more_general_specnodes([vspecnode1], [prebuiltNotSpecNode])
-    assert more_general_specnodes([prebuiltNotSpecNode], [vspecnode2])
-    aspecnode1 = _get_aspecnode(1)
-    aspecnode2 = _get_aspecnode(2)
-    assert more_general_specnodes([aspecnode2], [aspecnode2])
-    assert not more_general_specnodes([aspecnode1], [aspecnode2])
-    assert not more_general_specnodes([aspecnode1], [prebuiltNotSpecNode])
-    assert more_general_specnodes([prebuiltNotSpecNode], [aspecnode2])
-    sspecnode1 = _get_sspecnode()
-    assert more_general_specnodes([sspecnode1], [sspecnode1])
-    assert not more_general_specnodes([sspecnode1], [prebuiltNotSpecNode])
-    assert more_general_specnodes([prebuiltNotSpecNode], [sspecnode1])
-    #
-    foonode = _get_cspecnode('foo')
-    barnode = _get_cspecnode('bar')
-    assert more_general_specnodes([foonode], [foonode])
-    assert not more_general_specnodes([foonode], [barnode])
-    assert not more_general_specnodes([foonode], [prebuiltNotSpecNode])
-    assert more_general_specnodes([prebuiltNotSpecNode], [foonode])
-
-def test_extract_runtime_data_0():
-    res = []
-    node = _get_cspecnode('foo')
-    node.extract_runtime_data("cpu", "box1", res)
-    assert res == []
-
-def test_extract_runtime_data_1():
-    res = []
-    prebuiltNotSpecNode.extract_runtime_data("cpu", "box1", res)
-    prebuiltNotSpecNode.extract_runtime_data("cpu", "box2", res)
-    assert res == ["box1", "box2"]
-
-def test_extract_runtime_data_2():
-    structure = lltype.malloc(LLtypeMixin.NODE)
-    structure.value = 515
-    structure.next = lltype.malloc(LLtypeMixin.NODE)
-    structbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, structure))
-    vspecnode = _get_vspecnode()
-    res = []
-    vspecnode.extract_runtime_data(LLtypeMixin.cpu, structbox, res)
-    assert len(res) == 2
-    assert res[0].value == structure.value
-    assert res[1].value._obj.container._as_ptr() == structure.next
-
-def test_extract_runtime_data_3():
-    array = lltype.malloc(lltype.GcArray(lltype.Signed), 2)
-    array[0] = 123
-    array[1] = 456
-    arraybox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, array))
-    aspecnode = _get_aspecnode()
-    res = []
-    aspecnode.extract_runtime_data(LLtypeMixin.cpu, arraybox, res)
-    assert len(res) == 2
-    assert res[0].value == 123
-    assert res[1].value == 456
-
-def test_extract_runtime_data_4():
-    struct = lltype.malloc(LLtypeMixin.S)
-    struct.a = 123
-    struct.b = lltype.malloc(LLtypeMixin.NODE)
-    structbox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, struct))
-    sspecnode = _get_sspecnode()
-    res = []
-    sspecnode.extract_runtime_data(LLtypeMixin.cpu, structbox, res)
-    assert len(res) == 2
-    assert res[0].value == 123
-    assert (lltype.cast_opaque_ptr(lltype.Ptr(LLtypeMixin.NODE), res[1].value)
-            == struct.b)

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
@@ -73,6 +73,10 @@
     def __init__(self):
         self.funcinfo = None
 
+    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+        return OptFfiCall()
+        # FIXME: Should any status be saved for next iteration?
+
     def begin_optimization(self, funcval, op):
         self.rollback_maybe()
         self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)

diff --git a/pypy/doc/release-1.4.0.txt b/pypy/doc/release-1.4.1.txt
copy from pypy/doc/release-1.4.0.txt
copy to pypy/doc/release-1.4.1.txt
--- a/pypy/doc/release-1.4.0.txt
+++ b/pypy/doc/release-1.4.1.txt
@@ -1,59 +1,84 @@
 ===============================
-PyPy 1.4: Ouroboros in practice
+PyPy 1.4.1
 ===============================
 
-We're pleased to announce the 1.4 release of PyPy. This is a major breakthrough
-in our long journey, as PyPy 1.4 is the first PyPy release that can translate
-itself faster than CPython.  Starting today, we are using PyPy more for
-our every-day development.  So may you :) You can download it here:
+We're pleased to announce the 1.4.1 release of PyPy.  This
+release consolidates all the bug fixes that occurred since the
+previous release.  To everyone that took the trouble to report
+them, we want to say thank you.
 
     http://pypy.org/download.html
 
 What is PyPy
 ============
 
-PyPy is a very compliant Python interpreter, almost a drop-in replacement
-for CPython. It's fast (`pypy 1.4 and cpython 2.6`_ comparison)
+PyPy is a very compliant Python interpreter, almost a drop-in
+replacement for CPython.  Note that it still only emulates Python
+2.5 by default; the ``fast-forward`` branch with Python 2.7
+support is slowly getting ready but will only be integrated in
+the next release.
 
-Among its new features, this release includes numerous performance improvements
-(which made fast self-hosting possible), a 64-bit JIT backend, as well
-as serious stabilization. As of now, we can consider the 32-bit and 64-bit
-linux versions of PyPy stable enough to run `in production`_.
+In two words, the advantage of trying out PyPy instead of CPython
+(the default implementation of Python) is, for now, the
+performance.  Not all programs are faster in PyPy, but we are
+confident that any CPU-intensive task will be much faster, at
+least if it runs for long enough (the JIT has a slow warm-up
+phase, which can take several seconds or even one minute on the
+largest programs).
 
-Numerous speed achievements are described on `our blog`_. Normalized speed
-charts comparing `pypy 1.4 and pypy 1.3`_ as well as `pypy 1.4 and cpython 2.6`_
-are available on benchmark website. For the impatient: yes, we got a lot faster!
+Note again that we do support compiling and using C extension
+modules from CPython (``pypy setup.py install``).  However, this
+is still an alpha feature, and the most complex modules typically
+fail for various reasons; others work (e.g. ``PIL``) but take a
+serious performance hit.  Also, for Mac OS X see below.
+
+Please note also that PyPy's performance was optimized almost
+exclusively on Linux.  It seems from some reports that on Windows
+as well as Mac OS X (probably for different reasons) the
+performance might be lower.  We did not investigate much so far.
+
 
 More highlights
 ===============
 
-* PyPy's built-in Just-in-Time compiler is fully transparent and
-  automatically generated; it now also has very reasonable memory
-  requirements.  The total memory used by a very complex and
-  long-running process (translating PyPy itself) is within 1.5x to
-  at most 2x the memory needed by CPython, for a speed-up of 2x.
+* We migrated to Mercurial (thanks to Ronny Pfannschmidt and
+  Antonio Cuni) for the effort) and moved to bitbucket.  The new
+  command to check out a copy of PyPy is::
 
-* More compact instances.  All instances are as compact as if
-  they had ``__slots__``.  This can give programs a big gain in
-  memory.  (In the example of translation above, we already have
-  carefully placed ``__slots__``, so there is no extra win.)
+        hg clone http://bitbucket.org/pypy/pypy
 
-* `Virtualenv support`_: now PyPy is fully compatible with virtualenv_: note that
-  to use it, you need a recent version of virtualenv (>= 1.5).
+* In long-running processes, the assembler generated by old
+  JIT-compilations is now freed.  There should be no more leak,
+  however long the process runs.
 
-* Faster (and JITted) regular expressions - huge boost in speeding up
-  the `re` module.
+* Improve a lot the performance of the ``binascii`` module, and
+  of ``hashlib.md5`` and ``hashlib.sha``.
 
-* Other speed improvements, like JITted calls to functions like map().
+* Made sys.setrecursionlimit() a no-op.  Instead, we rely purely
+  on the built-in stack overflow detection mechanism, which also
+  gives you a RuntimeError -- just not at some exact recursion
+  level.
 
-.. _virtualenv: http://pypi.python.org/pypi/virtualenv
-.. _`Virtualenv support`: http://morepypy.blogspot.com/2010/08/using-virtualenv-with-pypy.html
-.. _`in production`: http://morepypy.blogspot.com/2010/11/running-large-radio-telescope-software.html
-.. _`our blog`: http://morepypy.blogspot.com
-.. _`pypy 1.4 and pypy 1.3`: http://speed.pypy.org/comparison/?exe=1%2B41,1%2B172&ben=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20&env=1&hor=false&bas=1%2B41&chart=normal+bars
-.. _`pypy 1.4 and cpython 2.6`: http://speed.pypy.org/comparison/?exe=2%2B35,1%2B172&ben=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20&env=1&hor=false&bas=2%2B35&chart=normal+bars
+* Fix argument processing (now e.g. ``pypy -OScpass`` works like
+  it does on CPython --- if you have a clue what it does there
+  ``:-)`` )
+
+* cpyext on Mac OS X: it still does not seem to work.  I get
+  systematically a segfault in dlopen().  Contributions welcome.
+
+* Fix two corner cases in the GC (one in minimark, one in
+  asmgcc+JIT).  This notably prevented "pypy translate.py -Ojit"
+  from working on Windows, leading to crashes.
+
+* Fixed a corner case in the JIT's optimizer, leading to "Fatal
+  RPython error: AssertionError".
+
+* Added some missing built-in functions into the 'os' module.
+
+* Fix ctypes (it was not propagating keepalive information from
+  c_void_p).
+
 
 Cheers,
 
-Carl Friedrich Bolz, Antonio Cuni, Maciej Fijalkowski,
-Amaury Forgeot d'Arc, Armin Rigo and the PyPy team
+Armin Rigo, for the rest of the team





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
@@ -8,7 +8,7 @@
 from pypy.rlib.nonconst import NonConstant
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.jit import (PARAMETERS, OPTIMIZER_SIMPLE, OPTIMIZER_FULL,
-                           OPTIMIZER_NO_PERFECTSPEC)
+                           OPTIMIZER_NO_UNROLL)
 from pypy.rlib.jit import BaseJitCell
 from pypy.rlib.debug import debug_start, debug_stop, debug_print
 from pypy.jit.metainterp import history
@@ -224,10 +224,10 @@
             from pypy.jit.metainterp import simple_optimize
             self.optimize_loop = simple_optimize.optimize_loop
             self.optimize_bridge = simple_optimize.optimize_bridge
-        elif optimizer == OPTIMIZER_NO_PERFECTSPEC:
-            from pypy.jit.metainterp import optimize_nopspec
-            self.optimize_loop = optimize_nopspec.optimize_loop
-            self.optimize_bridge = optimize_nopspec.optimize_bridge
+        elif optimizer == OPTIMIZER_NO_UNROLL:
+            from pypy.jit.metainterp import nounroll_optimize
+            self.optimize_loop = nounroll_optimize.optimize_loop
+            self.optimize_bridge = nounroll_optimize.optimize_bridge
         elif optimizer == OPTIMIZER_FULL:
             from pypy.jit.metainterp import optimize
             self.optimize_loop = optimize.optimize_loop

diff --git a/pypy/jit/metainterp/test/test_compile.py b/pypy/jit/metainterp/test/test_compile.py
--- a/pypy/jit/metainterp/test/test_compile.py
+++ b/pypy/jit/metainterp/test/test_compile.py
@@ -1,32 +1,29 @@
 from pypy.jit.metainterp.history import LoopToken, ConstInt, History, Stats
 from pypy.jit.metainterp.history import BoxInt, INT
-from pypy.jit.metainterp.specnode import NotSpecNode, ConstantSpecNode
 from pypy.jit.metainterp.compile import insert_loop_token, compile_new_loop
 from pypy.jit.metainterp.compile import ResumeGuardDescr
 from pypy.jit.metainterp.compile import ResumeGuardCountersInt
 from pypy.jit.metainterp.compile import compile_tmp_callback
-from pypy.jit.metainterp import optimize, jitprof, typesystem, compile
-from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin
+from pypy.jit.metainterp import nounroll_optimize, jitprof, typesystem, compile
+from pypy.jit.metainterp.test.test_optimizeutil import LLtypeMixin
 from pypy.jit.tool.oparser import parse
 
 
 def test_insert_loop_token():
+    # XXX this test is a bit useless now that there are no specnodes
     lst = []
     #
     tok1 = LoopToken()
-    tok1.specnodes = [NotSpecNode()]
     insert_loop_token(lst, tok1)
     assert lst == [tok1]
     #
     tok2 = LoopToken()
-    tok2.specnodes = [ConstantSpecNode(ConstInt(8))]
     insert_loop_token(lst, tok2)
-    assert lst == [tok2, tok1]
+    assert lst == [tok1, tok2]
     #
     tok3 = LoopToken()
-    tok3.specnodes = [ConstantSpecNode(ConstInt(-13))]
     insert_loop_token(lst, tok3)
-    assert lst == [tok2, tok3, tok1]
+    assert lst == [tok1, tok2, tok3]
 
 
 class FakeCPU:
@@ -41,7 +38,10 @@
         pass
 
 class FakeState:
-    optimize_loop = staticmethod(optimize.optimize_loop)
+    optimize_loop = staticmethod(nounroll_optimize.optimize_loop)
+
+    def attach_unoptimized_bridge_from_interp(*args):
+        pass
 
 class FakeGlobalData:
     loopnumbering = 0
@@ -86,7 +86,7 @@
     metainterp.history.inputargs = loop.inputargs[:]
     #
     loop_tokens = []
-    loop_token = compile_new_loop(metainterp, loop_tokens, 0)
+    loop_token = compile_new_loop(metainterp, loop_tokens, [], 0)
     assert loop_tokens == [loop_token]
     assert loop_token.number == 1
     assert staticdata.globaldata.loopnumbering == 2
@@ -102,11 +102,11 @@
     metainterp.history.operations = loop.operations[:]
     metainterp.history.inputargs = loop.inputargs[:]
     #
-    loop_token_2 = compile_new_loop(metainterp, loop_tokens, 0)
+    loop_token_2 = compile_new_loop(metainterp, loop_tokens, [], 0)
     assert loop_token_2 is loop_token
     assert loop_tokens == [loop_token]
     assert len(cpu.seen) == 0
-    assert staticdata.globaldata.loopnumbering == 2    
+    assert staticdata.globaldata.loopnumbering == 2
 
 
 def test_resume_guard_counters():

diff --git a/pypy/jit/metainterp/test/test_loop_spec.py b/pypy/jit/metainterp/test/test_loop_spec.py
deleted file mode 100644
--- a/pypy/jit/metainterp/test/test_loop_spec.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import py
-from pypy.rlib.jit import OPTIMIZER_FULL
-from pypy.jit.metainterp.test import test_loop
-from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
-
-class LoopSpecTest(test_loop.LoopTest):
-    optimizer = OPTIMIZER_FULL
-    automatic_promotion_result = {
-        'int_add' : 3, 'int_gt' : 1, 'guard_false' : 1, 'jump' : 1, 
-        'guard_value' : 1
-    }
-
-    # ====> test_loop.py
-
-class TestLLtype(LoopSpecTest, LLJitMixin):
-    pass
-
-class TestOOtype(LoopSpecTest, OOJitMixin):
-    pass



diff --git a/pypy/jit/metainterp/jitprof.py b/pypy/jit/metainterp/jitprof.py
--- a/pypy/jit/metainterp/jitprof.py
+++ b/pypy/jit/metainterp/jitprof.py
@@ -21,6 +21,7 @@
 ABORT_TOO_LONG
 ABORT_BRIDGE
 ABORT_ESCAPE
+ABORT_BAD_LOOP
 NVIRTUALS
 NVHOLES
 NVREUSED
@@ -177,6 +178,7 @@
         self._print_intline("abort: trace too long", cnt[ABORT_TOO_LONG])
         self._print_intline("abort: compiling", cnt[ABORT_BRIDGE])
         self._print_intline("abort: vable escape", cnt[ABORT_ESCAPE])
+        self._print_intline("abort: bad loop", cnt[ABORT_BAD_LOOP])
         self._print_intline("nvirtuals", cnt[NVIRTUALS])
         self._print_intline("nvholes", cnt[NVHOLES])
         self._print_intline("nvreused", cnt[NVREUSED])




diff --git a/pypy/jit/metainterp/test/test_memmgr.py b/pypy/jit/metainterp/test/test_memmgr.py
--- a/pypy/jit/metainterp/test/test_memmgr.py
+++ b/pypy/jit/metainterp/test/test_memmgr.py
@@ -132,7 +132,7 @@
         res = self.meta_interp(f, [], loop_longevity=1)
         assert res == 42
         # we should see a loop for each call to g()
-        self.check_tree_loop_count(8 + 20*2)
+        self.check_tree_loop_count(8 + 20*2*2)
 
     def test_throw_away_old_loops(self):
         myjitdriver = JitDriver(greens=['m'], reds=['n'])
@@ -157,7 +157,7 @@
 
         res = self.meta_interp(f, [], loop_longevity=3)
         assert res == 42
-        self.check_tree_loop_count(2 + 10*4)   # 42 :-)
+        self.check_tree_loop_count(2 + 10*4*2)
 
     def test_call_assembler_keep_alive(self):
         myjitdriver1 = JitDriver(greens=['m'], reds=['n'])
@@ -191,7 +191,7 @@
 
         res = self.meta_interp(f, [1], loop_longevity=4, inline=True)
         assert res == 42
-        self.check_tree_loop_count(8)
+        self.check_tree_loop_count(12)
 
 # ____________________________________________________________
 

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -5,17 +5,25 @@
 syntax: regexp
 ^testresult$
 ^site-packages$
+^bin$
+^pypy/bin/pypy-c
 ^pypy/module/cpyext/src/.+\.o$
-^pypy/bin/pypy-c
-^pypy/translator/jvm/src/pypy/.+\.class$
+^pypy/module/cpyext/src/.+\.obj$
 ^pypy/module/cpyext/test/.+\.errors$
 ^pypy/module/cpyext/test/.+\.o$
+^pypy/module/cpyext/test/.+\.obj$
+^pypy/module/cpyext/test/.+\.manifest$
 ^pypy/doc/.+\.html$
 ^pypy/doc/basicblock\.asc$
 ^pypy/doc/.+\.svninfo$
+^pypy/translator/c/src/libffi_msvc/.+\.obj$
+^pypy/translator/c/src/libffi_msvc/.+\.dll$
+^pypy/translator/c/src/libffi_msvc/.+\.lib$
+^pypy/translator/c/src/libffi_msvc/.+\.exp$
 ^pypy/translator/jvm/\.project$
 ^pypy/translator/jvm/\.classpath$
 ^pypy/translator/jvm/eclipse-bin$
+^pypy/translator/jvm/src/pypy/.+\.class$
 ^pypy/translator/benchmark/docutils$
 ^pypy/translator/benchmark/templess$
 ^pypy/translator/benchmark/gadfly$
@@ -25,6 +33,7 @@
 ^pypy/translator/goal/pypy-translation-snapshot$
 ^pypy/translator/goal/pypy-c
 ^pypy/translator/goal/.+\.exe$
+^pypy/translator/goal/.+\.dll$
 ^pypy/translator/goal/target.+-c$
 ^pypy/_cache$
 ^site-packages/.+\.egg$

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
@@ -12,7 +12,6 @@
 from pypy.jit.metainterp.history import AbstractFailDescr, BoxInt
 from pypy.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const
 from pypy.jit.metainterp import history
-from pypy.jit.metainterp.specnode import NotSpecNode, more_general_specnodes
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
 from pypy.jit.metainterp.optimizeutil import InvalidLoop
 from pypy.jit.metainterp.resume import NUMBERING
@@ -38,23 +37,24 @@
             extraloops = [loop]
         metainterp_sd.stats.view(errmsg=errmsg, extraloops=extraloops)
 
-def create_empty_loop(metainterp):
+def create_empty_loop(metainterp, name_prefix=''):
     name = metainterp.staticdata.stats.name_for_new_loop()
-    return TreeLoop(name)
+    return TreeLoop(name_prefix + name)
 
 def make_loop_token(nb_args, jitdriver_sd):
     loop_token = LoopToken()
-    loop_token.specnodes = [prebuiltNotSpecNode] * nb_args
     loop_token.outermost_jitdriver_sd = jitdriver_sd
     return loop_token
 
-def record_loop_or_bridge(loop):
+def record_loop_or_bridge(metainterp_sd, loop):
     """Do post-backend recordings and cleanups on 'loop'.
     """
     # get the original loop token (corresponding to 'loop', or if that is
     # a bridge, to the loop that this bridge belongs to)
     looptoken = loop.token
     assert looptoken is not None
+    if metainterp_sd.warmrunnerdesc is not None:    # for tests
+        assert looptoken.generation > 0     # has been registered with memmgr
     wref = weakref.ref(looptoken)
     for op in loop.operations:
         descr = op.getdescr()
@@ -71,14 +71,17 @@
             if descr is not looptoken:
                 looptoken.record_jump_to(descr)
             op.setdescr(None)    # clear reference, mostly for tests
+            if not we_are_translated():
+                op._jumptarget_number = descr.number
     # mostly for tests: make sure we don't keep a reference to the LoopToken
     loop.token = None
     if not we_are_translated():
-        loop._number = looptoken.number
+        loop._looptoken_number = looptoken.number
 
 # ____________________________________________________________
 
-def compile_new_loop(metainterp, old_loop_tokens, start):
+def compile_new_loop(metainterp, old_loop_tokens, greenkey, start,
+                     full_preamble_needed=True):
     """Try to compile a new loop by closing the current history back
     to the first operation.
     """
@@ -95,6 +98,11 @@
     loop_token = make_loop_token(len(loop.inputargs), jitdriver_sd)
     loop.token = loop_token
     loop.operations[-1].setdescr(loop_token)     # patch the target of the JUMP
+
+    loop.preamble = create_empty_loop(metainterp, 'Preamble ')
+    loop.preamble.inputargs = loop.inputargs
+    loop.preamble.token = make_loop_token(len(loop.inputargs), jitdriver_sd)
+
     try:
         old_loop_token = jitdriver_sd.warmstate.optimize_loop(
             metainterp_sd, old_loop_tokens, loop)
@@ -103,23 +111,33 @@
     if old_loop_token is not None:
         metainterp.staticdata.log("reusing old loop")
         return old_loop_token
-    send_loop_to_backend(metainterp_sd, loop, "loop")
-    insert_loop_token(old_loop_tokens, loop_token)
-    record_loop_or_bridge(loop)
-    return loop_token
+
+    if loop.preamble.operations is not None:
+        send_loop_to_backend(metainterp_sd, loop, "loop")
+        record_loop_or_bridge(metainterp_sd, loop)
+        token = loop.preamble.token
+        if full_preamble_needed or not loop.preamble.token.short_preamble:
+            send_loop_to_backend(metainterp_sd, loop.preamble, "entry bridge")
+            insert_loop_token(old_loop_tokens, loop.preamble.token)
+            jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp(
+                greenkey, loop.preamble.token)
+            record_loop_or_bridge(metainterp_sd, loop.preamble)
+        return token
+    else:
+        send_loop_to_backend(metainterp_sd, loop, "loop")
+        insert_loop_token(old_loop_tokens, loop_token)
+        jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp(
+            greenkey, loop.token)
+        record_loop_or_bridge(metainterp_sd, loop)
+        return loop_token
 
 def insert_loop_token(old_loop_tokens, loop_token):
     # Find where in old_loop_tokens we should insert this new loop_token.
     # The following algo means "as late as possible, but before another
     # loop token that would be more general and so completely mask off
     # the new loop_token".
-    for i in range(len(old_loop_tokens)):
-        if more_general_specnodes(old_loop_tokens[i].specnodes,
-                                  loop_token.specnodes):
-            old_loop_tokens.insert(i, loop_token)
-            break
-    else:
-        old_loop_tokens.append(loop_token)
+    # XXX do we still need a list?
+    old_loop_tokens.append(loop_token)
 
 def send_loop_to_backend(metainterp_sd, loop, type):
     globaldata = metainterp_sd.globaldata
@@ -128,6 +146,11 @@
     globaldata.loopnumbering += 1
 
     metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n, type)
+    short = loop.token.short_preamble
+    if short:
+        metainterp_sd.logger_ops.log_short_preamble(short[-1].inputargs,
+                                                    short[-1].operations)
+
     if not we_are_translated():
         show_loop(metainterp_sd, loop)
         loop.check_consistency()
@@ -209,13 +232,10 @@
         raise metainterp_sd.ExitFrameWithExceptionRef(cpu, value)
 
 
-prebuiltNotSpecNode = NotSpecNode()
-
 class TerminatingLoopToken(LoopToken):
     terminating = True
 
     def __init__(self, nargs, finishdescr):
-        self.specnodes = [prebuiltNotSpecNode]*nargs
         self.finishdescr = finishdescr
 
 def make_done_loop_tokens():
@@ -568,14 +588,40 @@
         # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr)
         prepare_last_operation(new_loop, target_loop_token)
         resumekey.compile_and_attach(metainterp, new_loop)
-        record_loop_or_bridge(new_loop)
+        compile_known_target_bridges(metainterp, new_loop)
+        record_loop_or_bridge(metainterp_sd, new_loop)
     return target_loop_token
 
+# For backends that not supports emitting guards with preset jump
+# targets, emit mini-bridges containing the jump
+def compile_known_target_bridges(metainterp, bridge):
+    for op in bridge.operations:
+        if op.is_guard():
+            target = op.getjumptarget()
+            if target:
+                mini = create_empty_loop(metainterp, 'fallback')
+                mini.inputargs = op.getfailargs()[:]
+                jmp = ResOperation(rop.JUMP, mini.inputargs[:], None, target)
+                mini.operations = [jmp]
+                descr = op.getdescr()
+                assert isinstance(descr, ResumeGuardDescr)
+                mini.token = bridge.token
+                
+                #descr.compile_and_attach(metainterp, mini)
+                if not we_are_translated():
+                    descr._debug_suboperations = mini.operations
+                send_bridge_to_backend(metainterp.staticdata, descr,
+                                       mini.inputargs, mini.operations,
+                                       bridge.token)
+                record_loop_or_bridge(metainterp.staticdata, mini)
+
+
 def prepare_last_operation(new_loop, target_loop_token):
     op = new_loop.operations[-1]
     if not isinstance(target_loop_token, TerminatingLoopToken):
         # normal case
-        op.setdescr(target_loop_token)     # patch the jump target
+        #op.setdescr(target_loop_token)     # patch the jump target
+        pass
     else:
         # The target_loop_token is a pseudo loop token,
         # e.g. loop_tokens_done_with_this_frame_void[0]


diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -1,11 +1,12 @@
 import py
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.jit.metainterp.optimizeopt.virtualize import VirtualValue, OptValue, VArrayValue
+from pypy.jit.metainterp.optimizeopt.optimizer import OptValue
+from pypy.jit.metainterp.optimizeopt.virtualize import VirtualValue, VArrayValue
 from pypy.jit.metainterp.optimizeopt.virtualize import VStructValue
 from pypy.jit.metainterp.resume import *
 from pypy.jit.metainterp.history import BoxInt, BoxPtr, ConstInt
 from pypy.jit.metainterp.history import ConstPtr, ConstFloat
-from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin
+from pypy.jit.metainterp.test.test_optimizeutil import LLtypeMixin
 from pypy.jit.metainterp import executor
 from pypy.jit.codewriter import heaptracker
 

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
@@ -328,7 +328,7 @@
                 return 'DoneWithThisFrameVoid()'
 
         class DoneWithThisFrameInt(JitException):
-            def __init__(self, result):
+            def __init__(self, result):                
                 assert lltype.typeOf(result) is lltype.Signed
                 self.result = result
             def __str__(self):

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
@@ -307,8 +307,9 @@
         storage = self.storage
         # make sure that nobody attached resume data to this guard yet
         assert not storage.rd_numb
-        numb, liveboxes_from_env, v = self.memo.number(values,
-                                                       storage.rd_snapshot)
+        snapshot = storage.rd_snapshot
+        assert snapshot is not None # is that true?
+        numb, liveboxes_from_env, v = self.memo.number(values, snapshot)
         self.liveboxes_from_env = liveboxes_from_env
         self.liveboxes = {}
         storage.rd_numb = numb




diff --git a/pypy/tool/win32-build.bat b/pypy/tool/win32-build.bat
deleted file mode 100644
--- a/pypy/tool/win32-build.bat
+++ /dev/null
@@ -1,38 +0,0 @@
-setlocal
-
-set ROOTDIR=%~dp0..\..
-cd %ROOTDIR%
-
-set ZIPEXE=zip
-set PYTHON=c:\python26\python.exe
-set TRANSLATE=pypy/translator/goal/translate.py
-set TRANSLATEOPTS=--batch
-set TARGET=pypy/translator/goal/targetpypystandalone
-set TARGETOPTS=
-
-copy /y ..\expat-2.0.1\win32\bin\release\libexpat.dll .
-
-call :make_pypy pypy-1.2-win32.zip           pypy.exe           -Ojit
-call :make_pypy pypy-1.2-win32-nojit.zip     pypy-nojit.exe
-call :make_pypy pypy-1.2-win32-stackless.zip pypy-stackless.exe --stackless
-REM call :make_pypy pypy-1.2-win32-sandbox.zip   pypy-sandbox.exe   --sandbox
-
-goto :EOF
-
-REM =========================================
-:make_pypy
-REM make_pypy subroutine
-REM %1 is the zip filename
-REM %2 is pypy.exe filename
-REM %3 and others are the translation options
-
-set ZIPFILE=%1
-set PYPYEXE=%2
-set EXTRAOPTS=%3 %4 %5 %6 %7 %8 %9
-
-%PYTHON% %TRANSLATE% --output=%PYPYEXE% %TRANSLATEOPTS% %EXTRAOPTS% %TARGET% %TARGETOPTS%
-del %ZIPFILE%
-del /s pypy\lib\*.pyc lib-python\*.pyc
-%ZIPEXE%    %ZIPFILE% %PYPYEXE% *.dll
-%ZIPEXE% -r %ZIPFILE% pypy\lib lib-python
-%ZIPEXE% -d %ZIPFILE% lib-python\2.5.2\plat-*


diff --git a/pypy/jit/backend/x86/test/test_loop_spec.py b/pypy/jit/backend/x86/test/test_loop_spec.py
deleted file mode 100644
--- a/pypy/jit/backend/x86/test/test_loop_spec.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import py
-from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
-from pypy.jit.metainterp.test import test_loop_spec
-
-class TestLoopSpec(Jit386Mixin, test_loop_spec.LoopSpecTest):
-    # for the individual tests see
-    # ====> ../../../metainterp/test/test_loop.py
-    pass

diff --git a/pypy/jit/metainterp/optimize_nopspec.py b/pypy/jit/metainterp/optimize_nopspec.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimize_nopspec.py
+++ /dev/null
@@ -1,45 +0,0 @@
-
-from pypy.rlib.debug import debug_start, debug_stop
-from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1
-from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder
-from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder
-
-def optimize_loop(metainterp_sd, old_loop_tokens, loop):
-    debug_start("jit-optimize")
-    try:
-        return _optimize_loop(metainterp_sd, old_loop_tokens, loop)
-    finally:
-        debug_stop("jit-optimize")
-
-def _optimize_loop(metainterp_sd, old_loop_tokens, loop):
-    cpu = metainterp_sd.cpu
-    metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations)
-    # XXX the following lines are probably still needed, to discard invalid
-    # loops. bit silly to run a full perfect specialization and throw the
-    # result away.
-    finder = PerfectSpecializationFinder(cpu)
-    finder.find_nodes_loop(loop, False)
-    if old_loop_tokens:
-        return old_loop_tokens[0]
-    optimize_loop_1(metainterp_sd, loop)
-    return None
-
-def optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
-    debug_start("jit-optimize")
-    try:
-        return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge)
-    finally:
-        debug_stop("jit-optimize")
-
-def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
-    cpu = metainterp_sd.cpu    
-    metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
-    # XXX same comment as above applies
-    finder = BridgeSpecializationFinder(cpu)
-    finder.find_nodes_bridge(bridge)
-    if old_loop_tokens:
-        old_loop_token = old_loop_tokens[0]
-        bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
-        optimize_bridge_1(metainterp_sd, bridge)
-        return old_loop_token
-    return None



diff --git a/pypy/module/array/benchmark/loop.py b/pypy/module/array/benchmark/loop.py
deleted file mode 100644
--- a/pypy/module/array/benchmark/loop.py
+++ /dev/null
@@ -1,7 +0,0 @@
-def h():
-    s=0
-    i=0
-    while i<100000:
-        s+=i
-        i+=1
-    return s





More information about the Pypy-commit mailing list